From da316ce3742ba66714675eb5a9aef3b7452a59e7 Mon Sep 17 00:00:00 2001 From: Spoike Date: Mon, 14 Sep 2015 10:36:42 +0000 Subject: [PATCH] fix gles1 issues (read: android port now works again). update csaddon code. qclib now understands 'objects'. engine+qcc does not use them yet. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4987 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- engine/client/pr_csqc.c | 7 +- engine/client/pr_menu.c | 1 + engine/common/cmd.c | 2 +- engine/common/com_mesh.c | 8 +- engine/common/pr_bgcmd.c | 20 +- engine/common/pr_common.h | 2 + engine/common/q1bsp.c | 2 +- engine/gl/gl_backend.c | 2 +- engine/gl/gl_heightmap.c | 165 ++- engine/gl/gl_shadow.c | 8 +- engine/gl/gl_vidcommon.c | 43 +- engine/gl/gl_vidnt.c | 170 +++ engine/gl/gl_warp.c | 35 +- engine/qclib/execloop.h | 6 +- engine/qclib/initlib.c | 12 +- engine/qclib/pr_edict.c | 131 ++- engine/qclib/progsint.h | 14 +- engine/qclib/progslib.h | 4 +- engine/qclib/qcc_pr_comp.c | 9 + engine/server/pr_cmds.c | 2 +- engine/server/progs.h | 1 + engine/server/sv_init.c | 2 +- quakec/csaddon/src/csaddon.qc | 82 +- quakec/csaddon/src/csfixups.qc | 27 +- quakec/csaddon/src/csplat.qc | 228 ++-- quakec/csaddon/src/editor_brushes.qc | 1421 +++++++++++++++++------- quakec/csaddon/src/editor_ents.qc | 512 ++++++--- quakec/csaddon/src/editor_lights.qc | 15 +- quakec/csaddon/src/editor_particles.qc | 7 +- quakec/csaddon/src/editor_terrain.qc | 15 +- quakec/csaddon/src/menu.qc | 4 +- 31 files changed, 2146 insertions(+), 811 deletions(-) diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index 6e180b50b..4215332a4 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -382,6 +382,7 @@ typedef struct csqcedict_s qboolean isfree; float freetime; // sv.time when the object was freed int entnum; + unsigned int fieldsize; qboolean readonly; //world #ifdef VM_Q1 csqcentvars_t *v; @@ -4384,7 +4385,7 @@ qboolean CSQC_DeltaPlayer(int playernum, player_state_t *state) ent = csqcdelta_playerents[playernum]; if (!ent) { - ent = (csqcedict_t *)ED_Alloc(csqcprogs); + ent = (csqcedict_t *)ED_Alloc(csqcprogs, false, 0); ent->xv->drawmask = MASK_DELTA; } @@ -4460,7 +4461,7 @@ qboolean CSQC_DeltaUpdate(entity_state_t *src) ent = oldent; else { - ent = (csqcedict_t *)ED_Alloc(csqcprogs); + ent = (csqcedict_t *)ED_Alloc(csqcprogs, false, 0); ent->xv->drawmask = MASK_DELTA; } @@ -7300,7 +7301,7 @@ void CSQC_ParseEntities(void) ent = csqcent[entnum]; if (!ent) { - ent = (csqcedict_t*)ED_Alloc(csqcprogs); + ent = (csqcedict_t*)ED_Alloc(csqcprogs, false, 0); csqcent[entnum] = ent; ent->xv->entnum = entnum; G_FLOAT(OFS_PARM0) = true; diff --git a/engine/client/pr_menu.c b/engine/client/pr_menu.c index 07f392d6b..35905caf6 100644 --- a/engine/client/pr_menu.c +++ b/engine/client/pr_menu.c @@ -1057,6 +1057,7 @@ typedef struct menuedict_s qboolean isfree; float freetime; // sv.time when the object was freed int entnum; + unsigned int fieldsize; qboolean readonly; //world void *fields; } menuedict_t; diff --git a/engine/common/cmd.c b/engine/common/cmd.c index b3a0e63b3..96981596f 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -2480,7 +2480,7 @@ static const char *If_Token_Term(const char *func, const char **end) else if (!strcmp(com_token, "eval")) { //read the stuff to the right - func = If_Token(s, end, IFPUNCT); + func = If_Token(s, end, IF_PRI_MAX); //and evaluate it s2 = If_Token(func, &func, IF_PRI_MAX); } diff --git a/engine/common/com_mesh.c b/engine/common/com_mesh.c index 4e2bec8a0..bcda392d3 100644 --- a/engine/common/com_mesh.c +++ b/engine/common/com_mesh.c @@ -1535,14 +1535,14 @@ static void Alias_DrawSkeletalBones(galiasbone_t *bones, float *bonepose, int bo #else PPL_RevertToKnownState(); BE_SelectEntity(currententity); - qglColor3f(1, 0, 0); + qglColor4f(1, 0, 0, 1); { int i; int p; // vec3_t org, dest; qglBegin(GL_LINES); - qglColor3f(0, 0, 1); + qglColor4f(0, 0, 1, 1); for (i = 0; i < basebone; i++) { p = bones[i].parent; @@ -1551,7 +1551,7 @@ static void Alias_DrawSkeletalBones(galiasbone_t *bones, float *bonepose, int bo qglVertex3f(bonepose[i*12+3], bonepose[i*12+7], bonepose[i*12+11]); qglVertex3f(bonepose[p*12+3], bonepose[p*12+7], bonepose[p*12+11]); } - qglColor3f(1, 0, 0); + qglColor4f(1, 0, 0, 1); for (; i < bonecount; i++) { p = bones[i].parent; @@ -1561,7 +1561,7 @@ static void Alias_DrawSkeletalBones(galiasbone_t *bones, float *bonepose, int bo qglVertex3f(bonepose[p*12+3], bonepose[p*12+7], bonepose[p*12+11]); } qglEnd(); - qglColor3f(1, 1, 1); + qglColor4f(1, 1, 1, 1); /* qglBegin(GL_LINES); for (i = 0; i < bonecount; i++) { diff --git a/engine/common/pr_bgcmd.c b/engine/common/pr_bgcmd.c index 2eda614f5..262019a87 100644 --- a/engine/common/pr_bgcmd.c +++ b/engine/common/pr_bgcmd.c @@ -2619,7 +2619,17 @@ void QCBUILTIN PF_nextent (pubprogfuncs_t *prinst, struct globalvars_s *pr_globa void QCBUILTIN PF_Spawn (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { struct edict_s *ed; - ed = ED_Alloc(prinst); + ed = ED_Alloc(prinst, false, 0); + pr_globals = PR_globals(prinst, PR_CURRENT); + RETURN_EDICT(prinst, ed); +} + +void QCBUILTIN PF_spawn_object (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + int obj = G_INT(OFS_PARM0); + int size = G_INT(OFS_PARM1); + struct edict_s *ed; + ed = ED_Alloc(prinst, obj, size); pr_globals = PR_globals(prinst, PR_CURRENT); RETURN_EDICT(prinst, ed); } @@ -2635,16 +2645,20 @@ void QCBUILTIN PF_copyentity (pubprogfuncs_t *prinst, struct globalvars_s *pr_gl in = G_WEDICT(prinst, OFS_PARM0); if (prinst->callargc <= 1) - out = (wedict_t*)ED_Alloc(prinst); + out = (wedict_t*)ED_Alloc(prinst, false, 0); else out = G_WEDICT(prinst, OFS_PARM1); + if (in->isfree) + PR_BIError(prinst, "PF_copyentity: source is free"); if (!out || out->isfree) PR_BIError(prinst, "PF_copyentity: destination is free"); if (out->readonly) PR_BIError(prinst, "PF_copyentity: destination is read-only"); + if (out->fieldsize != in->fieldsize) + PR_BIError(prinst, "PF_copyentity: different object types"); - memcpy(out->v, in->v, w->edict_size); + memcpy(out->v, in->v, out->fieldsize); World_LinkEdict(w, out, false); RETURN_EDICT(prinst, out); diff --git a/engine/common/pr_common.h b/engine/common/pr_common.h index 44ed6f827..9c932e482 100644 --- a/engine/common/pr_common.h +++ b/engine/common/pr_common.h @@ -10,6 +10,7 @@ typedef struct edict_s { float freetime; // realtime when the object was freed unsigned int entnum; + unsigned int fieldsize; pbool readonly; //causes error when QC tries writing to it. (quake's world entity) void *v; } edict_t; @@ -20,6 +21,7 @@ struct wedict_s qboolean isfree; float freetime; // sv.time when the object was freed int entnum; + unsigned int fieldsize; qboolean readonly; //world #ifdef VM_Q1 comentvars_t *v; diff --git a/engine/common/q1bsp.c b/engine/common/q1bsp.c index c27f899f6..74a3112e3 100644 --- a/engine/common/q1bsp.c +++ b/engine/common/q1bsp.c @@ -1015,7 +1015,7 @@ int Fragment_ClipPolyToPlane(float *inverts, float *outverts, int incount, float int outcount = 0; int clippedcount = 0; float d, *p1, *p2, *out; -#define FRAG_EPSILON 0.5 +#define FRAG_EPSILON (1.0/32) //0.5 for (i = 0; i < incount; i++) { diff --git a/engine/gl/gl_backend.c b/engine/gl/gl_backend.c index 45e2f0bff..570bd924e 100644 --- a/engine/gl/gl_backend.c +++ b/engine/gl/gl_backend.c @@ -1018,7 +1018,7 @@ static void RevertToKnownState(void) if (!gl_config_nofixedfunc) { BE_SetPassBlendMode(0, PBM_REPLACE); - qglColor3f(1,1,1); + qglColor4f(1,1,1,1); GL_DeSelectProgram(); } diff --git a/engine/gl/gl_heightmap.c b/engine/gl/gl_heightmap.c index 98b6878ef..08210c281 100644 --- a/engine/gl/gl_heightmap.c +++ b/engine/gl/gl_heightmap.c @@ -3723,7 +3723,6 @@ static void Heightmap_Trace_Square(hmtrace_t *tr, int tx, int ty) vec3_t d[2]; vec3_t p[4]; vec4_t n[6]; - int t; int i; #ifndef STRICTEDGES @@ -5074,7 +5073,7 @@ void Terr_ParseEntityLump(char *data, heightmap_t *heightmap) heightmap->sectionsize = 1024; heightmap->mode = HMM_TERRAIN; - heightmap->culldistance = 4096; + heightmap->culldistance = 4096*4096; heightmap->defaultgroundheight = 0; heightmap->defaultwaterheight = 0; @@ -5113,7 +5112,10 @@ void Terr_ParseEntityLump(char *data, heightmap_t *heightmap) else if (!strcmp("defaultwatertexture", key)) Q_strncpyz(heightmap->defaultwatershader, value, sizeof(heightmap->defaultwatershader)); else if (!strcmp("culldistance", key)) + { heightmap->culldistance = atof(value); + heightmap->culldistance *= heightmap->culldistance; + } else if (!strcmp("skybox", key)) Q_strncpyz(heightmap->skyname, value, sizeof(heightmap->skyname)); else if (!strcmp("tiles", key)) @@ -5247,7 +5249,7 @@ void Terr_FinishTerrain(model_t *mod) int Fragment_ClipPolyToPlane(float *inverts, float *outverts, int incount, float *plane, float planedist); static size_t Terr_GenerateBrushFace(vecV_t *points, size_t maxpoints, vec4_t *planes, size_t numplanes, vec4_t face) { - int p; + int p, a; vec4_t verts[128]; vec4_t verts2[128]; vec4_t *cverts; @@ -5314,7 +5316,14 @@ static size_t Terr_GenerateBrushFace(vecV_t *points, size_t maxpoints, vec4_t *p cverts = verts; for (p = 0; p < numverts; p++) { - VectorCopy(cverts[p], points[p]); + for (a = 0; a < 3; a++) + { + //if its within 1/1000th of a qu, just call it okay. + if ((int)points[p][a] * 1000 == (int)(points[p][a]*1000)) + points[p][a] = floor(cverts[p][a] + 0.5); + else + points[p][a] = cverts[p][a]; + } } return numverts; @@ -5353,7 +5362,7 @@ void Terr_Brush_Draw(heightmap_t *hm, batch_t **batches, entity_t *e) //allocate lightmap space for all surfaces, and then rebuild all textures. //if a surface is modified, clear its lightmap to -1 and when its batches are rebuilt, it'll unlight naturally. - if (hm->recalculatebrushlighting) + if (hm->recalculatebrushlighting && !r_fullbright.ival) { unsigned int lmcount; unsigned int lmblocksize = 512;//LMBLOCK_SIZE_MAX @@ -5439,7 +5448,7 @@ void Terr_Brush_Draw(heightmap_t *hm, batch_t **batches, entity_t *e) } } - if (hm->relightcontext) + if (hm->relightcontext && !r_fullbright.ival) for (i = 0, br = hm->wbrushes; i < hm->numbrushes; i++, br++) { for (j = 0; j < br->numplanes; j++) @@ -5486,17 +5495,69 @@ void Terr_Brush_Draw(heightmap_t *hm, batch_t **batches, entity_t *e) in = br->faces[j].lightdata; out = lm->lightmaps + (br->faces[j].lmbase[1] * lm->width + br->faces[j].lmbase[0]) * lightmap_bytes; - for (t = 0; t < br->faces[j].lmextents[1]; t++) + if (lightmap_bytes == 4) { - for (s = 0; s < br->faces[j].lmextents[0]; s++) + if (lightmap_bgra) { - *out++ = in[2]; - *out++ = in[1]; - *out++ = in[0]; - *out++ = 0xff; - in+=3; + for (t = 0; t < br->faces[j].lmextents[1]; t++) + { + for (s = 0; s < br->faces[j].lmextents[0]; s++) + { + *out++ = in[2]; + *out++ = in[1]; + *out++ = in[0]; + *out++ = 0xff; + in+=3; + } + out += (lm->width - br->faces[j].lmextents[0]) * 4; + } + } + else + { + for (t = 0; t < br->faces[j].lmextents[1]; t++) + { + for (s = 0; s < br->faces[j].lmextents[0]; s++) + { + *out++ = in[0]; + *out++ = in[1]; + *out++ = in[2]; + *out++ = 0xff; + in+=3; + } + out += (lm->width - br->faces[j].lmextents[0]) * 4; + } + } + } + else if (lightmap_bytes == 3) + { + if (lightmap_bgra) + { + for (t = 0; t < br->faces[j].lmextents[1]; t++) + { + for (s = 0; s < br->faces[j].lmextents[0]; s++) + { + *out++ = in[2]; + *out++ = in[1]; + *out++ = in[0]; + in+=3; + } + out += (lm->width - br->faces[j].lmextents[0]) * 3; + } + } + else + { + for (t = 0; t < br->faces[j].lmextents[1]; t++) + { + for (s = 0; s < br->faces[j].lmextents[0]; s++) + { + *out++ = in[0]; + *out++ = in[1]; + *out++ = in[2]; + in+=3; + } + out += (lm->width - br->faces[j].lmextents[0]) * 3; + } } - out += (lm->width - br->faces[j].lmextents[0]) * lightmap_bytes; } } } @@ -5507,25 +5568,33 @@ void Terr_Brush_Draw(heightmap_t *hm, batch_t **batches, entity_t *e) { if (!bt->shader) { + miptex_t *tx = W_GetMipTex(bt->shadername); + if (!Q_strcasecmp(bt->shadername, "clip")) bt->shader = R_RegisterShader(bt->shadername, SUF_LIGHTMAP, "{\nsurfaceparm nodraw\n}"); else bt->shader = R_RegisterCustom (bt->shadername, SUF_LIGHTMAP, Shader_DefaultBSPQ1, NULL); // bt->shader = R_RegisterShader_Lightmap(bt->shadername); - if (!Q_strncasecmp(bt->shadername, "sky", 3)) + if (!Q_strncasecmp(bt->shadername, "sky", 3) && tx) + R_InitSky (bt->shader, bt->shadername, (qbyte*)tx + tx->offsets[0], tx->width, tx->height); + else if (tx) { - miptex_t *tx = W_GetMipTex(bt->shadername); - if (tx) - { - R_InitSky (bt->shader, bt->shadername, (qbyte*)tx + tx->offsets[0], tx->width, tx->height); - BZ_Free(tx); - } - else - R_BuildDefaultTexnums(NULL, bt->shader); + qbyte *mips[4] = {(qbyte*)tx + tx->offsets[0], (qbyte*)tx + tx->offsets[1], (qbyte*)tx + tx->offsets[2], (qbyte*)tx + tx->offsets[3]}; + unsigned int mapflags = SHADER_HASPALETTED | SHADER_HASDIFFUSE | SHADER_HASFULLBRIGHT | SHADER_HASNORMALMAP | SHADER_HASGLOSS; + R_BuildLegacyTexnums(bt->shader, tx->name, NULL, mapflags, TF_BGRA32, tx->width, tx->height, mips, NULL); } else R_BuildDefaultTexnums(NULL, bt->shader); + + if (tx) + { + if (!bt->shader->width) + bt->shader->width = tx->width; + if (!bt->shader->height) + bt->shader->height = tx->height; + BZ_Free(tx); + } } if (bt->rebuild) @@ -5565,7 +5634,7 @@ void Terr_Brush_Draw(heightmap_t *hm, batch_t **batches, entity_t *e) for (j = 0; j < br->numplanes; j++) { - if (br->faces[j].tex == bt /*&& !br->selected*/ && br->faces[j].lightmap == lmnum) + if (br->faces[j].tex == bt && !br->selected && br->faces[j].lightmap == lmnum) { size_t k, o; float s,t; @@ -5640,7 +5709,7 @@ void Terr_Brush_Draw(heightmap_t *hm, batch_t **batches, entity_t *e) { j = 0; if (bb->lightmap >= 0) - b->lightmap[j++] = hm->brushlmremaps[bb->lightmap]; + b->lightmap[j++] = r_fullbright.ival?-1:hm->brushlmremaps[bb->lightmap]; for (; j < MAXRLIGHTMAPS; j++) b->lightmap[j] = -1; b->ent = e; @@ -6035,7 +6104,7 @@ typedef struct float tbias; } qcbrushface_t; -static void *validateqcpointer(pubprogfuncs_t *prinst, size_t qcptr, size_t elementsize, size_t elementcount) +static void *validateqcpointer(pubprogfuncs_t *prinst, size_t qcptr, size_t elementsize, size_t elementcount, qboolean allownull) { //make sure that the sizes can't overflow if (elementcount > 0x10000) @@ -6048,6 +6117,12 @@ static void *validateqcpointer(pubprogfuncs_t *prinst, size_t qcptr, size_t elem PR_BIError(prinst, "brush: invalid qc pointer\n"); return NULL; } + if (!qcptr) + { + if (!allownull) + PR_BIError(prinst, "brush: null qc pointer\n"); + return NULL; + } return prinst->stringtable + qcptr; } // {"brush_get", PF_brush_get, 0, 0, 0, 0, D(qcbrushface "int(float modelidx, int brushid, brushface_t *out_faces, int maxfaces, int *out_contents)", "Queries a brush's information. You must pre-allocate the face array for the builtin to write to. Return value is the number of faces retrieved, 0 on error.")}, @@ -6058,14 +6133,14 @@ void QCBUILTIN PF_brush_get(pubprogfuncs_t *prinst, struct globalvars_s *pr_glob heightmap_t *hm = mod?mod->terrain:NULL; unsigned int brushid = G_INT(OFS_PARM1); unsigned int maxfaces = G_INT(OFS_PARM3); - qcbrushface_t *out_faces = validateqcpointer(prinst, G_INT(OFS_PARM2), sizeof(*out_faces), maxfaces); - unsigned int *out_contents = validateqcpointer(prinst, G_INT(OFS_PARM4), sizeof(*out_contents), 1); + qcbrushface_t *out_faces = validateqcpointer(prinst, G_INT(OFS_PARM2), sizeof(*out_faces), maxfaces, true); + unsigned int *out_contents = validateqcpointer(prinst, G_INT(OFS_PARM4), sizeof(*out_contents), 1, true); unsigned int fa, i; brushes_t *br; //assume the worst. G_INT(OFS_RETURN) = 0; - if (G_INT(OFS_PARM4)) + if (out_contents) *out_contents = 0; if (!hm) @@ -6076,9 +6151,9 @@ void QCBUILTIN PF_brush_get(pubprogfuncs_t *prinst, struct globalvars_s *pr_glob br = &hm->wbrushes[i]; if (br->id == brushid) { - if (G_INT(OFS_PARM4)) + if (out_contents) *out_contents = br->contents; - if (!G_INT(OFS_PARM2)) + if (!out_faces) G_INT(OFS_RETURN) = br->numplanes; else { @@ -6110,7 +6185,7 @@ void QCBUILTIN PF_brush_create(pubprogfuncs_t *prinst, struct globalvars_s *pr_g model_t *mod = vmw->Get_CModel(vmw, G_FLOAT(OFS_PARM0)); heightmap_t *hm = mod?mod->terrain:NULL; unsigned int numfaces = G_INT(OFS_PARM2); - qcbrushface_t *in_faces = validateqcpointer(prinst, G_INT(OFS_PARM1), sizeof(*in_faces), numfaces); + qcbrushface_t *in_faces = validateqcpointer(prinst, G_INT(OFS_PARM1), sizeof(*in_faces), numfaces, false); unsigned int contents = G_INT(OFS_PARM3); unsigned int i; @@ -6269,9 +6344,9 @@ void QCBUILTIN PF_brush_calcfacepoints(pubprogfuncs_t *prinst, struct globalvars { unsigned int faceid = G_INT(OFS_PARM0); unsigned int numfaces = G_INT(OFS_PARM2); - qcbrushface_t *in_faces = validateqcpointer(prinst, G_INT(OFS_PARM1), sizeof(*in_faces), numfaces); + qcbrushface_t *in_faces = validateqcpointer(prinst, G_INT(OFS_PARM1), sizeof(*in_faces), numfaces, false); unsigned int maxpoints = G_INT(OFS_PARM4); - vec3_t *out_verts = validateqcpointer(prinst, G_INT(OFS_PARM3), sizeof(*out_verts), maxpoints); + vec3_t *out_verts = validateqcpointer(prinst, G_INT(OFS_PARM3), sizeof(*out_verts), maxpoints, false); vecV_t facepoints[256]; vec4_t planes[256]; unsigned int j, numpoints; @@ -6325,7 +6400,7 @@ void QCBUILTIN PF_brush_getfacepoints(pubprogfuncs_t *prinst, struct globalvars_ unsigned int brushid = G_INT(OFS_PARM1); unsigned int faceid = G_INT(OFS_PARM2); unsigned int maxpoints = G_INT(OFS_PARM4), p; - vec3_t *out_verts = validateqcpointer(prinst, G_INT(OFS_PARM3), sizeof(*out_verts), maxpoints); + vec3_t *out_verts = validateqcpointer(prinst, G_INT(OFS_PARM3), sizeof(*out_verts), maxpoints, false); size_t i; brushes_t *br; @@ -6374,11 +6449,11 @@ void QCBUILTIN PF_brush_findinvolume(pubprogfuncs_t *prinst, struct globalvars_s model_t *mod = vmw->Get_CModel(vmw, G_FLOAT(OFS_PARM0)); heightmap_t *hm = mod?mod->terrain:NULL; int in_numplanes = G_INT(OFS_PARM3); - vec3_t *in_normals = validateqcpointer(prinst, G_INT(OFS_PARM1), sizeof(*in_normals), in_numplanes); - float *in_distances = validateqcpointer(prinst, G_INT(OFS_PARM2), sizeof(*in_distances), in_numplanes); + vec3_t *in_normals = validateqcpointer(prinst, G_INT(OFS_PARM1), sizeof(*in_normals), in_numplanes, false); + float *in_distances = validateqcpointer(prinst, G_INT(OFS_PARM2), sizeof(*in_distances), in_numplanes, false); unsigned int maxresults = G_INT(OFS_PARM6); - unsigned int *out_brushids = validateqcpointer(prinst, G_INT(OFS_PARM4), sizeof(*out_brushids), maxresults); - unsigned int *out_faceids = G_INT(OFS_PARM5)?validateqcpointer(prinst, G_INT(OFS_PARM5), sizeof(*out_faceids), maxresults):NULL; + unsigned int *out_brushids = validateqcpointer(prinst, G_INT(OFS_PARM4), sizeof(*out_brushids), maxresults, false); + unsigned int *out_faceids = G_INT(OFS_PARM5)?validateqcpointer(prinst, G_INT(OFS_PARM5), sizeof(*out_faceids), maxresults, false):NULL; unsigned int i, j, k, r = 0; brushes_t *br; vec3_t best; @@ -6569,12 +6644,6 @@ void Mod_Terrain_Save_f(void) } else { - if (mod->type != mod_brush) - { - Con_Printf("that model isn't a suitable worldmodel\n"); - return; - } - FS_CreatePath(fname, FS_GAMEONLY); file = FS_OpenVFS(fname, "wb", FS_GAMEONLY); if (!file) @@ -6721,9 +6790,9 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities) else if (inbrush) { //parse a plane - //( -0 -0 16 ) ( -0 -0 32 ) ( 64 -0 16 ) texname 0 -32 rotation sscale tscale - //( -0 -0 16 ) ( -0 -0 32 ) ( 64 -0 16 ) texname [x y z d] [x y z d] rotation sscale tscale - //( 0 0 1 16 ) texname [x y z d] [x y z d] rotation sscale tscale + //Quake: ( -0 -0 16 ) ( -0 -0 32 ) ( 64 -0 16 ) texname 0 -32 rotation sscale tscale + //Valve: ( -0 -0 16 ) ( -0 -0 32 ) ( 64 -0 16 ) texname [x y z d] [x y z d] rotation sscale tscale + //fte : ( px py pz pd ) texname [sx sy sz sd] [tx ty tz td] 0 1 1 brushtex_t *bt; vec3_t d1,d2; vec3_t points[3]; diff --git a/engine/gl/gl_shadow.c b/engine/gl/gl_shadow.c index 972899777..e4c2aba5e 100644 --- a/engine/gl/gl_shadow.c +++ b/engine/gl/gl_shadow.c @@ -2790,9 +2790,13 @@ static void Sh_DrawStencilLightShadows(dlight_t *dl, qbyte *lvis, qbyte *vvis, q if (qrenderer == QR_DIRECT3D11) return; + if (qrenderer != QR_OPENGL) + return; //FIXME: uses glBegin specifics. #ifdef GLQUAKE - if (gl_config.nofixedfunc) + if (gl_config_nofixedfunc) return; /*hackzone*/ + if (gl_config_gles) + return; //FIXME: uses glBegin #endif // draw sprites seperately, because of alpha blending @@ -2943,7 +2947,7 @@ static qboolean Sh_DrawStencilLight(dlight_t *dl, vec3_t colour, vec3_t axis[3], // if (r_shadows.value == 666) //testing (visible shadow volumes) { qglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - qglColor3f(dl->color[0], dl->color[1], dl->color[2]); + qglColor4f(dl->color[0], dl->color[1], dl->color[2], 1); qglDisable(GL_STENCIL_TEST); // qglEnable(GL_POLYGON_OFFSET_FILL); // qglPolygonOffset(-1, -1); diff --git a/engine/gl/gl_vidcommon.c b/engine/gl/gl_vidcommon.c index fd444a0ae..bbe8a7ed6 100644 --- a/engine/gl/gl_vidcommon.c +++ b/engine/gl/gl_vidcommon.c @@ -105,12 +105,12 @@ void (APIENTRY *qglBegin) (GLenum mode); void (APIENTRY *qglCallList) (GLuint list); void (APIENTRY *qglClearDepth) (GLclampd depth); void (APIENTRY *qglClipPlane) (GLenum plane, const GLdouble *equation); -void (APIENTRY *qglColor3f) (GLfloat red, GLfloat green, GLfloat blue); -void (APIENTRY *qglColor3ub) (GLubyte red, GLubyte green, GLubyte blue); +//void (APIENTRY *qglColor3f) (GLfloat red, GLfloat green, GLfloat blue); +//void (APIENTRY *qglColor3ub) (GLubyte red, GLubyte green, GLubyte blue); void (APIENTRY *qglColor4f) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); void (APIENTRY *qglColor4fv) (const GLfloat *v); -void (APIENTRY *qglColor4ub) (GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha); -void (APIENTRY *qglColor4ubv) (const GLubyte *v); +//void (APIENTRY *qglColor4ub) (GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha); +//void (APIENTRY *qglColor4ubv) (const GLubyte *v); void (APIENTRY *qglDepthRange) (GLclampd zNear, GLclampd zFar); void (APIENTRY *qglDrawBuffer) (GLenum mode); void (APIENTRY *qglDrawPixels) (GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); @@ -394,6 +394,12 @@ void APIENTRY GL_DrawRangeElementsEmul(GLenum mode, GLuint start, GLuint end, GL { qglDrawElements(mode, count, type, indices); } + +void APIENTRY GL_Color4fv_Emul(const GLfloat *v) +{ + qglColor4f(v[0], v[1], v[2], v[3]); +} + void APIENTRY GL_BindBufferARBStub(GLenum target, GLuint id) { } @@ -448,6 +454,15 @@ void GL_CheckExtensions (void *(*getglfunction) (char *name)) s++; gl_minor_version = atoi(s); } +#ifdef _DEBUG + { extern cvar_t vid_gl_context_es; + if (vid_gl_context_es.ival == 3) + { + gl_config.gles = true; + gl_major_version = 1; + gl_minor_version = 0; + } } +#endif if (webgl) //webgl version 1 equates to gles 2. { if (gl_major_version < 1) @@ -494,6 +509,14 @@ void GL_CheckExtensions (void *(*getglfunction) (char *name)) Sys_Error("no extensions\n"); } +#ifdef _DEBUG + { extern cvar_t vid_gl_context_es; + if (vid_gl_context_es.ival == 3) + { + gl_extensions = ""; + } } +#endif + if (gl_config.gles) gl_config.nofixedfunc = gl_config.glversion >= 2; else @@ -2482,12 +2505,14 @@ void GL_Init(void *(*getglfunction) (char *name)) qglBegin = (void *)getglcore("glBegin"); qglClearDepth = (void *)getglcore("glClearDepth"); qglClipPlane = (void *)getglcore("glClipPlane"); - qglColor3f = (void *)getglcore("glColor3f"); - qglColor3ub = (void *)getglcore("glColor3ub"); +// qglColor3f = (void *)getglcore("glColor3f"); +// qglColor3ub = (void *)getglcore("glColor3ub"); qglColor4f = (void *)getglcore("glColor4f"); - qglColor4fv = (void *)getglcore("glColor4fv"); - qglColor4ub = (void *)getglcore("glColor4ub"); - qglColor4ubv = (void *)getglcore("glColor4ubv"); + qglColor4fv = (void *)getglext("glColor4fv"); + if (!qglColor4fv) + qglColor4fv = GL_Color4fv_Emul; //can be missing in gles1 +// qglColor4ub = (void *)getglcore("glColor4ub"); +// qglColor4ubv = (void *)getglcore("glColor4ubv"); qglDepthRange = (void *)getglcore("glDepthRange"); qglDrawBuffer = (void *)getglcore("glDrawBuffer"); qglDrawPixels = (void *)getglcore("glDrawPixels"); diff --git a/engine/gl/gl_vidnt.c b/engine/gl/gl_vidnt.c index fb84a306a..e37e0dc36 100644 --- a/engine/gl/gl_vidnt.c +++ b/engine/gl/gl_vidnt.c @@ -345,6 +345,166 @@ static char *gles2funcs[] = f(wglCreateContextAttribsARB) NULL }; + +//this is a list of the functions that exist in opengles2, as well as wglCreateContextAttribsARB. +//functions not in this list *should* be stubs that just return errors, but we can't always depend on drivers for that... they shouldn't get called. +//this list is just to make it easier to test+debug android gles2 stuff using windows. +static char *gles1funcs[] = +{ +#define f(n) #n, + + /* Available only in Common profile */ + f(glAlphaFunc) + f(glClearColor) + f(glClearDepthf) + f(glClipPlanef) + f(glColor4f) + f(glDepthRangef) + f(glFogf) + f(glFogfv) + f(glFrustumf) + f(glGetClipPlanef) + f(glGetFloatv) + f(glGetLightfv) + f(glGetMaterialfv) + f(glGetTexEnvfv) + f(glGetTexParameterfv) + f(glLightModelf) + f(glLightModelfv) + f(glLightf) + f(glLightfv) + f(glLineWidth) + f(glLoadMatrixf) + f(glMaterialf) + f(glMaterialfv) + f(glMultMatrixf) + f(glMultiTexCoord4f) + f(glNormal3f) + f(glOrthof) + f(glPointParameterf) + f(glPointParameterfv) + f(glPointSize) + f(glPolygonOffset) + f(glRotatef) + f(glScalef) + f(glTexEnvf) + f(glTexEnvfv) + f(glTexParameterf) + f(glTexParameterfv) + f(glTranslatef) + + /* Available in both Common and Common-Lite profiles */ + f(glActiveTexture) + f(glAlphaFuncx) + f(glBindBuffer) + f(glBindTexture) + f(glBlendFunc) + f(glBufferData) + f(glBufferSubData) + f(glClear) + f(glClearColorx) + f(glClearDepthx) + f(glClearStencil) + f(glClientActiveTexture) + f(glClipPlanex) + f(glColor4ub) + f(glColor4x) + f(glColorMask) + f(glColorPointer) + f(glCompressedTexImage2D) + f(glCompressedTexSubImage2D) + f(glCopyTexImage2D) + f(glCopyTexSubImage2D) + f(glCullFace) + f(glDeleteBuffers) + f(glDeleteTextures) + f(glDepthFunc) + f(glDepthMask) + f(glDepthRangex) + f(glDisable) + f(glDisableClientState) + f(glDrawArrays) + f(glDrawElements) + f(glEnable) + f(glEnableClientState) + f(glFinish) + f(glFlush) + f(glFogx) + f(glFogxv) + f(glFrontFace) + f(glFrustumx) + f(glGetBooleanv) + f(glGetBufferParameteriv) + f(glGetClipPlanex) + f(glGenBuffers) + f(glGenTextures) + f(glGetError) + f(glGetFixedv) + f(glGetIntegerv) + f(glGetLightxv) + f(glGetMaterialxv) + f(glGetPointerv) + f(glGetString) + f(glGetTexEnviv) + f(glGetTexEnvxv) + f(glGetTexParameteriv) + f(glGetTexParameterxv) + f(glHint) + f(glIsBuffer) + f(glIsEnabled) + f(glIsTexture) + f(glLightModelx) + f(glLightModelxv) + f(glLightx) + f(glLightxv) + f(glLineWidthx) + f(glLoadIdentity) + f(glLoadMatrixx) + f(glLogicOp) + f(glMaterialx) + f(glMaterialxv) + f(glMatrixMode) + f(glMultMatrixx) + f(glMultiTexCoord4x) + f(glNormal3x) + f(glNormalPointer) + f(glOrthox) + f(glPixelStorei) + f(glPointParameterx) + f(glPointParameterxv) + f(glPointSizex) + f(glPolygonOffsetx) + f(glPopMatrix) + f(glPushMatrix) + f(glReadPixels) + f(glRotatex) + f(glSampleCoverage) + f(glSampleCoveragex) + f(glScalex) + f(glScissor) + f(glShadeModel) + f(glStencilFunc) + f(glStencilMask) + f(glStencilOp) + f(glTexCoordPointer) + f(glTexEnvi) + f(glTexEnvx) + f(glTexEnviv) + f(glTexEnvxv) + f(glTexImage2D) + f(glTexParameteri) + f(glTexParameterx) + f(glTexParameteriv) + f(glTexParameterxv) + f(glTexSubImage2D) + f(glTranslatex) + f(glVertexPointer) + f(glViewport) + + /*required to switch stuff around*/ + f(wglCreateContextAttribsARB) + NULL +}; #endif //just GetProcAddress with a safty net. @@ -363,6 +523,16 @@ void *getglfunc(char *name) } #ifdef _DEBUG + if (vid_gl_context_es.ival == 3) + { + int i; + for (i = 0; gles1funcs[i]; i++) + { + if (!strcmp(name, gles1funcs[i])) + return proc; + } + return NULL; + } if (vid_gl_context_es.ival == 2) { int i; diff --git a/engine/gl/gl_warp.c b/engine/gl/gl_warp.c index 8fd4b7573..b86073792 100644 --- a/engine/gl/gl_warp.c +++ b/engine/gl/gl_warp.c @@ -26,7 +26,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include static void R_CalcSkyChainBounds (batch_t *s); -static void GL_DrawSkyGrid (texture_t *tex); +static void GL_DrawSkyGrid (texnums_t *tex); static void GL_DrawSkySphere (batch_t *fa, shader_t *shader); static void GL_SkyForceDepth(batch_t *fa); static void GL_DrawSkyBox (texid_t *texnums, batch_t *s); @@ -90,12 +90,12 @@ void R_DrawSkyChain (batch_t *batch) if (skyshader->numpasses) { #if defined(GLQUAKE) && !defined(ANDROID) - if (*r_fastsky.string && qrenderer == QR_OPENGL && TEXVALID(batch->shader->defaulttextures->base) && TEXVALID(batch->shader->defaulttextures->fullbright)) + if (*r_fastsky.string && qrenderer == QR_OPENGL && !gl_config_gles && !gl_config_nofixedfunc && TEXVALID(batch->shader->defaulttextures->base) && TEXVALID(batch->shader->defaulttextures->fullbright)) { R_CalcSkyChainBounds(batch); R_IBrokeTheArrays(); - GL_DrawSkyGrid(batch->texture); + GL_DrawSkyGrid(skyshader->defaulttextures); R_IBrokeTheArrays(); } else @@ -638,12 +638,12 @@ static void GL_DrawSkyGridFace (int axis) qglEnd (); } -static void GL_DrawSkyGrid (texture_t *tex) +static void GL_DrawSkyGrid (texnums_t *tex) { int i; float time = cl.gametime+realtime-cl.gametimemark; - GL_LazyBind(0, GL_TEXTURE_2D, tex->shader->defaulttextures->base); + GL_LazyBind(0, GL_TEXTURE_2D, tex->base); speedscale = time*8; speedscale -= (int)speedscale & ~127; @@ -655,19 +655,22 @@ static void GL_DrawSkyGrid (texture_t *tex) GL_DrawSkyGridFace (i); } - qglEnable (GL_BLEND); - GL_LazyBind(0, GL_TEXTURE_2D, tex->shader->defaulttextures->fullbright); - - speedscale = time*16; - speedscale -= (int)speedscale & ~127; - - for (i = 0; i < 6; i++) + if (tex->fullbright) { - if ((skymins[0][i] >= skymaxs[0][i] || skymins[1][i] >= skymaxs[1][i])) - continue; - GL_DrawSkyGridFace (i); + qglEnable (GL_BLEND); + GL_LazyBind(0, GL_TEXTURE_2D, tex->fullbright); + + speedscale = time*16; + speedscale -= (int)speedscale & ~127; + + for (i = 0; i < 6; i++) + { + if ((skymins[0][i] >= skymaxs[0][i] || skymins[1][i] >= skymaxs[1][i])) + continue; + GL_DrawSkyGridFace (i); + } + qglDisable (GL_BLEND); } - qglDisable (GL_BLEND); } #endif diff --git a/engine/qclib/execloop.h b/engine/qclib/execloop.h index 0f12a6616..2e9422afe 100644 --- a/engine/qclib/execloop.h +++ b/engine/qclib/execloop.h @@ -834,12 +834,14 @@ reeval: break; case OP_LOADP_V: - i = OPA->_int + OPB->_int*4; //NOTE: inconsistant! + i = OPA->_int + OPB->_int*4; //NOTE: inconsistant, but a bit more practical for the qcc when structs etc are involved errorif ((unsigned int)i > prinst.addressableused-sizeof(vec3_t)) { if (i == -1) { - OPC->_int = 0; + OPC->_vector[0] = 0; + OPC->_vector[1] = 0; + OPC->_vector[2] = 0; break; } pr_xstatement = st-pr_statements; diff --git a/engine/qclib/initlib.c b/engine/qclib/initlib.c index f876c5e18..470f99126 100644 --- a/engine/qclib/initlib.c +++ b/engine/qclib/initlib.c @@ -449,10 +449,10 @@ int PDECL PR_InitEnts(pubprogfuncs_t *ppf, int max_ents) prinst.max_fields_size = prinst.fields_size; - progfuncs->funcs.edicttable = prinst.edicttable = PRHunkAlloc(progfuncs, prinst.maxedicts*sizeof(struct edicts_s *), "edicttable"); + prinst.edicttable = (struct edictrun_s**)(progfuncs->funcs.edicttable = PRHunkAlloc(progfuncs, prinst.maxedicts*sizeof(struct edicts_s *), "edicttable")); sv_edicts = PRHunkAlloc(progfuncs, externs->edictsize, "edict0"); - prinst.edicttable[0] = sv_edicts; - ((edictrun_t*)prinst.edicttable[0])->fields = PRAddressableExtend(progfuncs, NULL, prinst.fields_size, prinst.max_fields_size-prinst.fields_size); + progfuncs->funcs.edicttable[0] = sv_edicts; + prinst.edicttable[0]->fields = PRAddressableExtend(progfuncs, NULL, prinst.fields_size, prinst.max_fields_size-prinst.fields_size); QC_ClearEdict(&progfuncs->funcs, sv_edicts); sv_num_edicts = 1; @@ -515,12 +515,12 @@ static void PDECL PR_Configure (pubprogfuncs_t *ppf, size_t addressable_size, in prinst.profiling = profiling; prinst.maxedicts = 1; - progfuncs->funcs.edicttable = prinst.edicttable = &sv_edicts; + prinst.edicttable = (edictrun_t**)(progfuncs->funcs.edicttable = &sv_edicts); sv_num_edicts = 1; //set up a safty buffer so things won't go horribly wrong too often sv_edicts=(struct edict_s *)&tempedict; tempedict.readonly = true; tempedict.fields = tempedictfields; - tempedict.isfree = false; + tempedict.ereftype = ER_ENTITY; } @@ -543,7 +543,7 @@ struct globalvars_s *PDECL PR_globals (pubprogfuncs_t *ppf, progsnum_t pnum) struct entvars_s *PDECL PR_entvars (pubprogfuncs_t *ppf, struct edict_s *ed) { // progfuncs_t *progfuncs = (progfuncs_t*)ppf; - if (((edictrun_t *)ed)->isfree) + if (((edictrun_t *)ed)->ereftype != ER_ENTITY) return NULL; return (struct entvars_s *)edvars(ed); diff --git a/engine/qclib/pr_edict.c b/engine/qclib/pr_edict.c index 2a5574183..ab55b0411 100644 --- a/engine/qclib/pr_edict.c +++ b/engine/qclib/pr_edict.c @@ -24,21 +24,35 @@ void PDECL QC_ClearEdict (pubprogfuncs_t *ppf, struct edict_s *ed) progfuncs_t *progfuncs = (progfuncs_t*)ppf; edictrun_t *e = (edictrun_t *)ed; int num = e->entnum; - memset (e->fields, 0, prinst.fields_size); - e->isfree = false; + memset (e->fields, 0, e->fieldsize); + e->ereftype = ER_ENTITY; e->entnum = num; } -edictrun_t *ED_AllocIntoTable (progfuncs_t *progfuncs, int num) +edictrun_t *ED_AllocIntoTable (progfuncs_t *progfuncs, int num, pbool object, unsigned int fields_size) { edictrun_t *e; - prinst.edicttable[num] = *(struct edict_s **)&e = (void*)externs->memalloc(externs->edictsize); - memset(e, 0, externs->edictsize); - e->fields = PRAddressableExtend(progfuncs, NULL, prinst.fields_size, 0); - e->entnum = num; - QC_ClearEdict(&progfuncs->funcs, (struct edict_s*)e); + e = prinst.edicttable[num]; + if (!e) + { + e = (void*)externs->memalloc(externs->edictsize); + prinst.edicttable[num] = e; + memset(e, 0, externs->edictsize); + } + if (e->fieldsize != fields_size) + { + if (e->fields) + progfuncs->funcs.AddressableFree(&progfuncs->funcs, e->fields); + e->fields = progfuncs->funcs.AddressableAlloc(&progfuncs->funcs, fields_size); + e->fieldsize = fields_size; +// e->fields = PRAddressableExtend(progfuncs, NULL, fields_size, 0); + } + e->entnum = num; + memset (e->fields, 0, e->fieldsize); + + e->ereftype = object?ER_OBJECT:ER_ENTITY; return e; } @@ -53,11 +67,35 @@ instead of being removed and recreated, which can cause interpolated angles and bad trails. ================= */ -struct edict_s *PDECL ED_Alloc (pubprogfuncs_t *ppf) +struct edict_s *PDECL ED_Alloc (pubprogfuncs_t *ppf, pbool object, size_t extrasize) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; unsigned int i; edictrun_t *e; + unsigned int fields_size; + + fields_size = object?0:prinst.fields_size; + fields_size += extrasize; + + if (object) + { + //objects are allocated at the end. + for ( i=prinst.maxedicts-1 ; i>0 ; i--) + { + e = (edictrun_t*)EDICT_NUM(progfuncs, i); + // the first couple seconds of server time can involve a lot of + // freeing and allocating, so relax the replacement policy + if (!e || (e->ereftype==ER_FREE && ( e->freetime < 2 || *externs->gametime - e->freetime > 0.5 ) )) + { + e = ED_AllocIntoTable(progfuncs, i, object, fields_size); + + if (externs->entspawn) + externs->entspawn((struct edict_s *) e, false); + return (struct edict_s *)e; + } + } + Sys_Error ("ED_Alloc: no free edicts (max is %i)", prinst.maxedicts); + } //define this to wastefully allocate extra ents, to test network capabilities. #define STEP 1//((i <= 32)?1:8) @@ -67,12 +105,9 @@ struct edict_s *PDECL ED_Alloc (pubprogfuncs_t *ppf) e = (edictrun_t*)EDICT_NUM(progfuncs, i); // the first couple seconds of server time can involve a lot of // freeing and allocating, so relax the replacement policy - if (!e || (e->isfree && ( e->freetime < 2 || *externs->gametime - e->freetime > 0.5 ) )) + if (!e || (e->ereftype==ER_FREE && ( e->freetime < 2 || *externs->gametime - e->freetime > 0.5 ) )) { - if (!e) - e = ED_AllocIntoTable(progfuncs, i); - else - QC_ClearEdict (&progfuncs->funcs, (struct edict_s*)e); + e = ED_AllocIntoTable(progfuncs, i, object, fields_size); if (externs->entspawn) externs->entspawn((struct edict_s *) e, false); @@ -87,12 +122,9 @@ struct edict_s *PDECL ED_Alloc (pubprogfuncs_t *ppf) e = (edictrun_t*)EDICT_NUM(progfuncs, i); // the first couple seconds of server time can involve a lot of // freeing and allocating, so relax the replacement policy - if (!e || (e->isfree)) + if (!e || (e->ereftype==ER_FREE)) { - if (!e) - e = ED_AllocIntoTable(progfuncs, i); - else - QC_ClearEdict (&progfuncs->funcs, (struct edict_s*)e); + e = ED_AllocIntoTable(progfuncs, i, object, fields_size); if (externs->entspawn) externs->entspawn((struct edict_s *) e, false); @@ -119,21 +151,16 @@ struct edict_s *PDECL ED_Alloc (pubprogfuncs_t *ppf) e = (edictrun_t*)EDICT_NUM(progfuncs, sv_num_edicts); if (!e) { - e = ED_AllocIntoTable(progfuncs, sv_num_edicts); + e = ED_AllocIntoTable(progfuncs, sv_num_edicts, object, fields_size); if (externs->entspawn) externs->entspawn((struct edict_s *) e, false); - e->isfree = true; + e->ereftype=ER_FREE; } sv_num_edicts++; } sv_num_edicts++; - e = (edictrun_t*)EDICT_NUM(progfuncs, i); - - if (!e) - e = ED_AllocIntoTable(progfuncs, i); - else - QC_ClearEdict (&progfuncs->funcs, (struct edict_s*)e); + e = ED_AllocIntoTable(progfuncs, i, object, fields_size); if (externs->entspawn) externs->entspawn((struct edict_s *) e, false); @@ -154,7 +181,7 @@ void PDECL ED_Free (pubprogfuncs_t *ppf, struct edict_s *ed) edictrun_t *e = (edictrun_t *)ed; // SV_UnlinkEdict (ed); // unlink from world bsp - if (e->isfree) //this happens on start.bsp where an onlyregistered trigger killtargets itself (when all of this sort die after 1 trigger anyway). + if (e->ereftype == ER_FREE) //this happens on start.bsp where an onlyregistered trigger killtargets itself (when all of this sort die after 1 trigger anyway). { if (pr_depth) printf("Tried to free free entity within %s\n", pr_xfunction->s_name+progfuncs->funcs.stringtable); @@ -169,7 +196,7 @@ void PDECL ED_Free (pubprogfuncs_t *ppf, struct edict_s *ed) if (!externs->entcanfree(ed)) //can stop an ent from being freed. return; - e->isfree = true; + e->ereftype = ER_FREE; e->freetime = (float)*externs->gametime; /* @@ -966,7 +993,7 @@ void PDECL ED_Print (pubprogfuncs_t *ppf, struct edict_s *ed) char *name; int type; - if (((edictrun_t *)ed)->isfree) + if (((edictrun_t *)ed)->ereftype == ER_FREE) { printf ("FREE\n"); return; @@ -1403,7 +1430,7 @@ cont: } if (!init) - ent->isfree = true; + ent->ereftype = ER_FREE; return data; } @@ -1757,7 +1784,7 @@ char *PDECL PR_SaveEnts(pubprogfuncs_t *ppf, char *buf, int *bufofs, int bufmax, AddS ("{\n"); - if (!ed->isfree) //free entities write a {} with no data. the loader detects this specifically. + if (ed->ereftype == ER_ENTITY) //free entities write a {} with no data. the loader detects this specifically. ED_WriteEdict(progfuncs, ed, buf, bufofs, bufmax, true); AddS ("}\n"); @@ -1817,7 +1844,7 @@ char *PDECL PR_SaveEnts(pubprogfuncs_t *ppf, char *buf, int *bufofs, int bufmax, { edictrun_t *ed = (edictrun_t *)EDICT_NUM(progfuncs, a); - if (!ed || ed->isfree) + if (!ed || ed->ereftype != ER_ENTITY) continue; AddS (qcva("entity %i{\n", a)); @@ -1901,8 +1928,8 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, float killonspawnfl if (!ed) { - ed = ED_AllocIntoTable(progfuncs, num); - ed->isfree = true; + ed = ED_AllocIntoTable(progfuncs, num, false, prinst.fields_size); + ed->ereftype = ER_FREE; if (externs->entspawn) externs->entspawn((struct edict_s *) ed, true); } @@ -1915,7 +1942,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, float killonspawnfl if (qcc_token[0] != '{') Sys_Error("Progs loading found %s, not '{'", qcc_token); if (!resethunk) - ed = (edictrun_t *)ED_Alloc(&progfuncs->funcs); + ed = (edictrun_t *)ED_Alloc(&progfuncs->funcs, false, 0); else { ed = (edictrun_t *)EDICT_NUM(progfuncs, num); @@ -1923,10 +1950,10 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, float killonspawnfl if (!ed) { Sys_Error("Edict was not allocated\n"); - ed = ED_AllocIntoTable(progfuncs, num); + ed = ED_AllocIntoTable(progfuncs, num, false, prinst.fields_size); } } - ed->isfree = false; + ed->ereftype = ER_ENTITY; if (externs->entspawn) externs->entspawn((struct edict_s *) ed, true); file = ED_ParseEdict(progfuncs, file, ed); @@ -1938,7 +1965,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, float killonspawnfl { if ((int)var->_float & (int)killonspawnflags) { - ed->isfree = true; + ed->ereftype = ER_FREE; continue; } } @@ -2015,8 +2042,8 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, float killonspawnfl if (!ed) { - ed = ED_AllocIntoTable(progfuncs, num); - ed->isfree = true; + ed = ED_AllocIntoTable(progfuncs, num, false, prinst.fields_size); + ed->ereftype = ER_FREE; } if (externs->entspawn) @@ -2134,7 +2161,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, float killonspawnfl sv_num_edicts = 1; //set up a safty buffer so things won't go horribly wrong too often sv_edicts=(struct edict_s *)&tempedict; - progfuncs->funcs.edicttable = prinst.edicttable = &sv_edicts; + prinst.edicttable = (struct edictrun_s**)(progfuncs->funcs.edicttable = &sv_edicts); sv_num_edicts = numents; //should be fine @@ -2192,12 +2219,12 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, float killonspawnfl { ed = (edictrun_t *)EDICT_NUM(progfuncs, numents); if (!ed) - ed = ED_AllocIntoTable(progfuncs, numents); + ed = ED_AllocIntoTable(progfuncs, numents, false, prinst.fields_size); if (externs->entspawn) externs->entspawn((struct edict_s *) ed, true); - ed->isfree = false; + ed->ereftype = ER_ENTITY; file = ED_ParseEdict (progfuncs, file, ed); } sv_num_edicts = ++numents; @@ -2215,8 +2242,8 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, float killonspawnfl if (!ed) { - ed = ED_AllocIntoTable(progfuncs, num); - ed->isfree = true; + ed = ED_AllocIntoTable(progfuncs, num, false, prinst.fields_size); + ed->ereftype = ER_FREE; } } } @@ -2224,8 +2251,8 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, float killonspawnfl if (!ed) //first entity ed = (edictrun_t *)EDICT_NUM(progfuncs, 0); else - ed = (edictrun_t *)ED_Alloc(&progfuncs->funcs); - ed->isfree = false; + ed = (edictrun_t *)ED_Alloc(&progfuncs->funcs, false, 0); + ed->ereftype = ER_ENTITY; if (externs->entspawn) externs->entspawn((struct edict_s *) ed, true); file = ED_ParseEdict(progfuncs, file, ed); @@ -2237,7 +2264,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, float killonspawnfl { if ((int)var->_float & (int)killonspawnflags) { - ed->isfree = true; + ed->ereftype = ER_FREE; ed->freetime = 0; continue; } @@ -2447,11 +2474,11 @@ struct edict_s *PDECL PR_RestoreEnt (pubprogfuncs_t *ppf, const char *buf, int * Sys_Error("Restore Ent with no opening brace"); if (!ed) - ent = (edictrun_t *)ED_Alloc(&progfuncs->funcs); + ent = (edictrun_t *)ED_Alloc(&progfuncs->funcs, false, 0); else ent = (edictrun_t *)ed; - if (ent->isfree && externs->entspawn) + if (ent->ereftype == ER_FREE && externs->entspawn) externs->entspawn((struct edict_s *) ent, false); buf = ED_ParseEdict(progfuncs, buf, ent); @@ -3445,7 +3472,7 @@ struct edict_s *PDECL QC_EDICT_NUM(pubprogfuncs_t *ppf, unsigned int n) if (n >= prinst.maxedicts) Sys_Error ("QCLIB: EDICT_NUM: bad number %i", n); - return prinst.edicttable[n]; + return (struct edict_s*)prinst.edicttable[n]; } unsigned int PDECL QC_NUM_FOR_EDICT(pubprogfuncs_t *ppf, struct edict_s *e) diff --git a/engine/qclib/progsint.h b/engine/qclib/progsint.h index 6dc397c38..047917660 100644 --- a/engine/qclib/progsint.h +++ b/engine/qclib/progsint.h @@ -148,7 +148,7 @@ typedef struct prinst_s size_t addressableused; size_t addressablesize; - struct edict_s **edicttable; + struct edictrun_s **edicttable; } prinst_t; typedef struct progfuncs_s @@ -254,13 +254,19 @@ typedef union eval_s } eval_t; #endif */ - +enum ereftype_e +{ + ER_ENTITY, + ER_FREE, + ER_OBJECT //custom sized, no vm/engine fields. +}; typedef struct edictrun_s { - pbool isfree; + pbool ereftype; float freetime; // realtime when the object was freed unsigned int entnum; + unsigned int fieldsize; pbool readonly; //causes error when QC tries writing to it. (quake's world entity) void *fields; @@ -364,7 +370,7 @@ void *PRHunkAlloc(progfuncs_t *progfuncs, int ammount, char *name); void PR_Profile_f (void); -struct edict_s *PDECL ED_Alloc (pubprogfuncs_t *progfuncs); +struct edict_s *PDECL ED_Alloc (pubprogfuncs_t *progfuncs, pbool object, size_t extrasize); void PDECL ED_Free (pubprogfuncs_t *progfuncs, struct edict_s *ed); pbool PR_RunGC (progfuncs_t *progfuncs); diff --git a/engine/qclib/progslib.h b/engine/qclib/progslib.h index ce1c38c79..84e3c3230 100644 --- a/engine/qclib/progslib.h +++ b/engine/qclib/progslib.h @@ -89,7 +89,7 @@ struct pubprogfuncs_s void (VARGS *RunError) (pubprogfuncs_t *prinst, char *msg, ...) LIKEPRINTF(2); //builtins call this to say there was a problem void (PDECL *PrintEdict) (pubprogfuncs_t *prinst, struct edict_s *ed); //get a listing of all vars on an edict (sent back via 'print') - struct edict_s *(PDECL *EntAlloc) (pubprogfuncs_t *prinst); + struct edict_s *(PDECL *EntAlloc) (pubprogfuncs_t *prinst, pbool object, size_t extrasize); void (PDECL *EntFree) (pubprogfuncs_t *prinst, struct edict_s *ed); struct edict_s *(PDECL *EDICT_NUM) (pubprogfuncs_t *prinst, unsigned int n); //get the nth edict @@ -261,7 +261,7 @@ typedef union eval_s #define PR_RegisterFieldVar(pf,type,name,reqofs,qcofs) (*pf->RegisterFieldVar) (pf,type,name,reqofs,qcofs) -#define ED_Alloc(pf) (*pf->EntAlloc) (pf) +#define ED_Alloc(pf,isobj,extsize) (*pf->EntAlloc) (pf, isobj, extsize) #define ED_Free(pf, ed) (*pf->EntFree) (pf, ed) #define ED_Clear(pf, ed) (*pf->EntClear) (pf, ed) diff --git a/engine/qclib/qcc_pr_comp.c b/engine/qclib/qcc_pr_comp.c index d8734c94c..a219da2ae 100644 --- a/engine/qclib/qcc_pr_comp.c +++ b/engine/qclib/qcc_pr_comp.c @@ -4923,6 +4923,15 @@ QCC_sref_t QCC_PR_ParseFunctionCall (QCC_ref_t *funcref) //warning, the func cou return QCC_MakeIntConst(sz); } } + if (!strcmp(funcname, "alloca")) + { //FIXME: half of these functions with regular expression arguments should be handled later or something + QCC_sref_t sz; + sz = QCC_PR_Expression(TOP_PRIORITY, 0); + QCC_PR_Expect(")"); + sz = QCC_SupplyConversion(sz, ev_integer, true); + sz = QCC_PR_Statement(&pr_opcodes[OP_PUSH], sz, nullsref, NULL); + return sz; + } if (!strcmp(funcname, "_")) { if (!func.sym->initialized) diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index 224ef5c37..6c18797f8 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -3687,7 +3687,7 @@ static void QCBUILTIN PF_h2dprintv (pubprogfuncs_t *prinst, struct globalvars_s static void QCBUILTIN PF_h2spawn_temp (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { edict_t *ed; - ed = ED_Alloc(prinst); + ed = ED_Alloc(prinst, false, 0); RETURN_EDICT(prinst, ed); } #endif diff --git a/engine/server/progs.h b/engine/server/progs.h index 00484e201..81d40a7d3 100644 --- a/engine/server/progs.h +++ b/engine/server/progs.h @@ -67,6 +67,7 @@ typedef struct edict_s qboolean isfree; float freetime; // sv.time when the object was freed int entnum; + unsigned int fieldsize; qboolean readonly; //world #ifdef VM_Q1 stdentvars_t *v; diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index 46b60c701..57f8e59f4 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -1266,7 +1266,7 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us { svs.clients[i].viewent = 0; - ent = ED_Alloc(svprogfuncs);//EDICT_NUM(i+1); + ent = ED_Alloc(svprogfuncs, false, 0);//EDICT_NUM(i+1); svs.clients[i].edict = ent; ent->isfree = false; //ZOID - make sure we update frags right diff --git a/quakec/csaddon/src/csaddon.qc b/quakec/csaddon/src/csaddon.qc index 1420722fb..4274510bb 100644 --- a/quakec/csaddon/src/csaddon.qc +++ b/quakec/csaddon/src/csaddon.qc @@ -1,4 +1,3 @@ - float autocvar_ca_show; var float autocvar_ca_editormode = MODE_LIGHTEDIT; string autocvar_ca_colourtint; @@ -13,6 +12,8 @@ float ctrldown; vector mousenear; vector mousefar; +float releasedmouse; + string pointedshadername; vector pointedsurfacenormal; entity pointedent; @@ -20,12 +21,39 @@ float pointedsurface; float editorrsd[editornames.length]; +float(float key, float unic, vector mouse) editor_options_key = +{ + if (key == K_MOUSE1) + if (mouse_y >= 16 && mouse_y < 16+8) + { + cvar_set("ca_show", "0"); + return TRUE; + } + return FALSE; +}; +void(vector mouse) editor_options_overlay = +{ + if (mouse_y >= 16 && mouse_y < 16+8) + drawstring('0 16', "Close", '8 8', '0 0 1', 1, 0); + else + drawstring('0 16', "Close", '8 8', '1 1 1', 1, 0); +}; + /*the renderscene builtin in the parent progs is redirected to here*/ void() wrap_renderscene = { float x; vector col; local float i; + + if (ca_checksave) + { + if (ca_checksave(autocvar_ca_show?autocvar_ca_editormode:0)) + { + ca_checksave = __NULL__; + localcmd("changelevel . .\n"); + } + } //don't do anything if this is a subview. this avoids getting confused. if (!(float)getproperty(VF_DRAWWORLD)) @@ -101,8 +129,32 @@ void() wrap_renderscene = ); drawpic(getproperty(VF_MIN), shdrname, getproperty(VF_SIZE), '1 1 1', 1); } + + if (releasedmouse) + { //remove the cursor if we activated it + releasedmouse = FALSE; + setcursormode(FALSE); + } + mousedown = 0; return; } + + if (mousedown == 2) + { //RMB re-enables mlook + if (releasedmouse) + { + releasedmouse = FALSE; + setcursormode(FALSE); + } + } + else + { + if (FALSE == getcursormode(FALSE)) + { //only release it again if it should be released. + setcursormode(TRUE); + releasedmouse = TRUE; + } + } /*hide hud and crosshair*/ setproperty(VF_DRAWENGINESBAR, 0); @@ -149,7 +201,9 @@ void() wrap_renderscene = editorrsd[i] = x; } - if (autocvar_ca_editormode == MODE_LIGHTEDIT) + if (autocvar_ca_editormode == MODE_OPTIONS) + editor_options_overlay(curmousepos); + else if (autocvar_ca_editormode == MODE_LIGHTEDIT) editor_lights_overlay(curmousepos); #ifdef CAMQUAKE else if (autocvar_ca_editormode == MODE_SPLINEEDIT) @@ -180,6 +234,8 @@ float (float event, float parama, float paramb) wrap_InputEvent = shiftdown = (event == IE_KEYDOWN); if (parama == K_RCTRL || parama == K_LCTRL) ctrldown = (event == IE_KEYDOWN); + if (parama == K_RALT || parama == K_LALT) + altdown = (event == IE_KEYDOWN); } if (autocvar_ca_show) { @@ -198,7 +254,12 @@ float (float event, float parama, float paramb) wrap_InputEvent = cvar_set("ca_editormode", ftos(nm)); return TRUE; } - if (autocvar_ca_editormode == MODE_LIGHTEDIT) + if (autocvar_ca_editormode == MODE_OPTIONS) + { + if (editor_options_key(parama, paramb, curmousepos)) + return TRUE; + } + else if (autocvar_ca_editormode == MODE_LIGHTEDIT) { if (editor_lights_key(parama, paramb, curmousepos)) return TRUE; @@ -315,9 +376,6 @@ void(float mask) wrap_addentities = var float autocvar_fov = 90; static void(float seat, vector mn, vector sz) renderscene_helper = { -#ifndef VF_ACTIVESEAT -#define VF_ACTIVESEAT VF_LPLAYER -#endif if (seat) { setproperty(VF_ACTIVESEAT, seat, TRUE); //updates globals, true means resets view properties too (but not ents) @@ -379,6 +437,9 @@ void(float width, float height, float do2d) CSQC_UpdateView = void() CSQC_Input_Frame = { vector t, o; + if (autocvar_ca_show) //when we're using the UI, don't send any attack or jump stuff. these are generally annoying modifiers or so + input_buttons = 0; + if ((input_buttons & 1) && pointedshadername == "") { t = mousefar; @@ -491,6 +552,12 @@ float(string txt, string info) CSQC_ConsoleLink = }; */ +float(string cmd) CSQC_ConsoleCommand = +{ + tokenize(cmd); + return editor_brushes_command(); +}; + /*this is a fallback function, in case the main progs does not have one*/ float (float event, float parama, float paramb, float devid) CSQC_InputEvent = { @@ -521,6 +588,9 @@ void(float prevprogs) init = localcmd(sprintf("alias terrain_editor \"set ca_show 1; set ca_editormode %g\"\n", MODE_TERRAINEDIT)); localcmd(sprintf("alias r_part_editor \"set ca_show 1; set ca_editormode %g\"\n", MODE_PARTICLEEDIT)); localcmd(sprintf("alias entities_editor \"set ca_show 1; set ca_editormode %g\"\n", MODE_ENTSEDIT)); + + //brush editor needs some commands for easier binds. + editor_brushes_registercommands(); }; void() CSQC_Shutdown = diff --git a/quakec/csaddon/src/csfixups.qc b/quakec/csaddon/src/csfixups.qc index f894ed7a8..9978799d4 100644 --- a/quakec/csaddon/src/csfixups.qc +++ b/quakec/csaddon/src/csfixups.qc @@ -13,6 +13,9 @@ vector *ptr_trace_endpos; vector *ptr_trace_plane_normal; #define trace_plane_normal (*ptr_trace_plane_normal) +entity *ptr_trace_ent; +#define trace_ent (*ptr_trace_ent) + entity *ptr_self; #define self (*ptr_self) @@ -20,6 +23,7 @@ void() csfixups = { ptr_trace_endpos = (vector*)externvalue(0, "&trace_endpos"); ptr_trace_plane_normal = (vector*)externvalue(0, "&trace_plane_normal"); + ptr_trace_ent = (entity*)externvalue(0, "&trace_ent"); ptr_self = (entity*)externvalue(0, "&self"); }; @@ -27,19 +31,26 @@ void() csfixups = vector mousenear; vector mousefar; float mousedown; +float shiftdown; +float ctrldown; +float altdown; enum { - MODE_INITIAL=0, + MODE_OPTIONS, - MODE_LIGHTEDIT=1, - MODE_SPLINEEDIT=2, - MODE_TERRAINEDIT=3, - MODE_BRUSHEDIT=4, - MODE_PARTICLEEDIT=5, - MODE_ENTSEDIT=6, + MODE_LIGHTEDIT, + MODE_SPLINEEDIT, + MODE_TERRAINEDIT, + MODE_BRUSHEDIT, + MODE_PARTICLEEDIT, + MODE_ENTSEDIT, + + MODE_COUNT }; -var string editornames[] = {"","LIGHTS","SPLINES", "TERRAIN", "BRUSHES", "PARTICLES", "ENTITIES"}; +var string editornames[MODE_COUNT] = {"OPTIONS","LIGHTS","SPLINES", "TERRAIN", "BRUSHES", "PARTICLES", "ENTITIES"}; + +var float(float vismode) ca_checksave; diff --git a/quakec/csaddon/src/csplat.qc b/quakec/csaddon/src/csplat.qc index f721b9a8f..d9a66bd02 100644 --- a/quakec/csaddon/src/csplat.qc +++ b/quakec/csaddon/src/csplat.qc @@ -13,6 +13,7 @@ Available options: -O - write to a different qc file */ #pragma noref 1 +//#pragma flag enable logicops #pragma warning error Q101 /*too many parms*/ #pragma warning error Q105 /*too few parms*/ #pragma warning error Q208 /*system crc unknown*/ @@ -142,7 +143,7 @@ Available options: #define DP_TE_EXPLOSIONRGB #define DP_TE_PARTICLECUBE #define DP_TE_SMALLFLASH -#define DP_TE_SPARK +#define DP_TE_SPARK #define DP_TE_STANDARDEFFECTBUILTINS #define DP_VIEWZOOM #define EXT_BITSHIFT @@ -173,6 +174,7 @@ Available options: #define FTE_NPCCHAT #define FTE_QC_CHECKCOMMAND #define FTE_QC_CHECKPVS +#define FTE_QC_HARDWARECURSORS /* setcursormode exists in both csqc+menuqc, and accepts additional arguments to specify a cursor image to use when this module has focus. If the image exceeds hardware limits, it will be emulated using regular draws - this at least still avoids conflicting cursors. */ #define FTE_QC_HASHTABLES #define FTE_QC_INTCONV #define FTE_QC_MATCHCLIENTNAME @@ -271,6 +273,8 @@ vector input_cursor_screen; vector input_cursor_trace_start; vector input_cursor_trace_endpos; float input_cursor_trace_entnum; +int trace_brush_id; +int trace_brush_faceid; .vector punchangle; .float gravity; .float hull; /* Overrides the hull used by the entity for walkmove/movetogoal and not traceline/tracebox. */ @@ -288,7 +292,7 @@ float input_cursor_trace_entnum; .entity tag_entity; .float tag_index; .float skeletonindex; /* This object serves as a container for the skeletal bone states used to override the animation data. */ -.vector colormod; +.vector colormod; /* Provides a colour tint for the entity. */ .vector glowmod; .vector gravitydir; /* Specifies the direction in which gravity acts. Must be normalised. '0 0 0' also means down. Use '0 0 1' if you want the player to be able to run on ceilings. */ .vector(vector org, vector ang) camera_transform; /* Provides portal transform information for portal surfaces attached to this entity. Also used to open up pvs in ssqc. */ @@ -319,32 +323,33 @@ float input_cursor_trace_entnum; .float bonecontrol5; /* Halflife model format bone controller. This typically affects the mouth. */ .float subblendfrac; /* Weird animation value specific to halflife models. On player models, this typically affects the spine's pitch. */ .float basesubblendfrac; /* See basebone */ -noref void(float reqid, float responsecode, string resourcebody) URI_Get_Callback; /* Called as an eventual result of the uri_get builtin. */ -noref void(float apilevel, string enginename, float engineversion) CSQC_Init; /* Called at startup. enginename and engineversion are arbitary hints and can take any form. enginename should be consistant between revisions, but this cannot truely be relied upon. */ -noref void() CSQC_WorldLoaded; /* Called after model+sound precaches have been executed. Gives a chance for the qc to read the entity lump from the bsp. */ -noref void() CSQC_Shutdown; /* Specifies that the csqc is going down. Save your persistant settings here. */ -noref void(float vwidth, float vheight, float notmenu) CSQC_UpdateView; /* Called every single video frame. The CSQC is responsible for rendering the entire screen. */ -noref void(float vwidth, float vheight, float notmenu) CSQC_UpdateViewLoading; /* Alternative to CSQC_UpdateView, called when the engine thinks there should be a loading screen. If present, will inhibit the engine's normal loading screen, deferring to qc to draw it. */ -noref void(string msg) CSQC_Parse_StuffCmd; /* Gives the CSQC a chance to intercept stuffcmds. Use the tokenize builtin to parse the message. Unrecognised commands would normally be localcmded, but its probably better to drop unrecognised stuffcmds completely. */ -noref float(string msg) CSQC_Parse_CenterPrint; /* Gives the CSQC a chance to intercept centerprints. Return true if you wish the engine to otherwise ignore the centerprint. */ -noref float(float save, float take, vector inflictororg) CSQC_Parse_Damage; /* Called as a result of player.dmg_save or player.dmg_take being set on the server. +void(float reqid, float responsecode, string resourcebody) URI_Get_Callback; /* Called as an eventual result of the uri_get builtin. */ +void(float apilevel, string enginename, float engineversion) CSQC_Init; /* Called at startup. enginename and engineversion are arbitary hints and can take any form. enginename should be consistant between revisions, but this cannot truely be relied upon. */ +void() CSQC_WorldLoaded; /* Called after model+sound precaches have been executed. Gives a chance for the qc to read the entity lump from the bsp. */ +void() CSQC_Shutdown; /* Specifies that the csqc is going down. Save your persistant settings here. */ +void(float vwidth, float vheight, float notmenu) CSQC_UpdateView; /* Called every single video frame. The CSQC is responsible for rendering the entire screen. */ +void(float vwidth, float vheight, float notmenu) CSQC_UpdateViewLoading; /* Alternative to CSQC_UpdateView, called when the engine thinks there should be a loading screen. If present, will inhibit the engine's normal loading screen, deferring to qc to draw it. */ +void(string msg) CSQC_Parse_StuffCmd; /* Gives the CSQC a chance to intercept stuffcmds. Use the tokenize builtin to parse the message. Unrecognised commands would normally be localcmded, but its probably better to drop unrecognised stuffcmds completely. */ +float(string msg) CSQC_Parse_CenterPrint; /* Gives the CSQC a chance to intercept centerprints. Return true if you wish the engine to otherwise ignore the centerprint. */ +float(float save, float take, vector inflictororg) CSQC_Parse_Damage; /* Called as a result of player.dmg_save or player.dmg_take being set on the server. Return true to completely inhibit the engine's colour shift and damage rolls, allowing you to do your own thing. You can use punch_roll += (normalize(inflictororg-player.origin)*v_right)*(take+save)*autocvar_v_kickroll; as a modifier for the roll angle should the player be hit from the side, and slowly fade it away over time. */ -noref void(string printmsg, float printlvl) CSQC_Parse_Print; /* Gives the CSQC a chance to intercept sprint/bprint builtin calls. CSQC should filter by the client's current msg setting and then pass the message on to the print command, or handle them itself. */ -noref void() CSQC_Parse_Event; /* Called when the client receives an SVC_CGAMEPACKET. The csqc should read the data or call the error builtin if it does not recognise the message. */ -noref float(float evtype, float scanx, float chary, float devid) CSQC_InputEvent; /* Called whenever a key is pressed, the mouse is moved, etc. evtype will be one of the IE_* constants. The other arguments vary depending on the evtype. Key presses are not guarenteed to have both scan and unichar values set at the same time. */ -noref void() CSQC_Input_Frame; /* Called just before each time clientcommandframe is updated. You can edit the input_* globals in order to apply your own player inputs within csqc, which may allow you a convienient way to pass certain info to ssqc. */ -noref float(string cmd) CSQC_ConsoleCommand; /* Called if the user uses any console command registed via registercommand. */ -noref float(string text, string info) CSQC_ConsoleLink; /* Called if the user clicks a ^[text\infokey\infovalue^] link. Use infoget to read/check each supported key. Return true if you wish the engine to not attempt to handle the link itself. */ -noref void(float isnew) CSQC_Ent_Update; -noref void() CSQC_Ent_Remove; -noref float(float entnum, float channel, string soundname, float vol, float attenuation, vector pos, float pitchmod) CSQC_Event_Sound; -noref float(string resname, string restype) CSQC_LoadResource; /* Called each time some resource is being loaded. CSQC can invoke various draw calls to provide a loading screen, until WorldLoaded is called. */ -noref float() CSQC_Parse_TempEntity; /* Please don't use this. Use CSQC_Parse_Event and multicasts instead. */ -noref void(string cmdtext) GameCommand; -noref void(float prevprogs) init; /* Part of FTE_MULTIPROGS. Called as soon as a progs is loaded, called at a time when entities are not valid. This is the only time when it is safe to call addprogs without field assignment. As it is also called as part of addprogs, this also gives you a chance to hook functions in modules that are already loaded (via externget+externget). */ -noref void() initents; /* Part of FTE_MULTIPROGS. Called after fields have been finalized. This is the first point at which it is safe to call spawn(), and is called before any entity fields have been parsed. You can use this entrypoint to send notifications to other modules. */ -var float physics_mode = 2; /* 0: original csqc - physics are not run +void(string printmsg, float printlvl) CSQC_Parse_Print; /* Gives the CSQC a chance to intercept sprint/bprint builtin calls. CSQC should filter by the client's current msg setting and then pass the message on to the print command, or handle them itself. */ +void() CSQC_Parse_Event; /* Called when the client receives an SVC_CGAMEPACKET. The csqc should read the data or call the error builtin if it does not recognise the message. */ +float(float evtype, float scanx, float chary, float devid) CSQC_InputEvent; /* Called whenever a key is pressed, the mouse is moved, etc. evtype will be one of the IE_* constants. The other arguments vary depending on the evtype. Key presses are not guarenteed to have both scan and unichar values set at the same time. */ +__used void() CSQC_Input_Frame; /* Called just before each time clientcommandframe is updated. You can edit the input_* globals in order to apply your own player inputs within csqc, which may allow you a convienient way to pass certain info to ssqc. */ +void(void) CSQC_RendererRestarted; /* Called by the engine after the video was restarted. This serves to notify the CSQC that any render targets that it may have cached were purged, and will need to be regenerated. */ +float(string cmd) CSQC_ConsoleCommand; /* Called if the user uses any console command registed via registercommand. */ +float(string text, string info) CSQC_ConsoleLink; /* Called if the user clicks a ^[text\infokey\infovalue^] link. Use infoget to read/check each supported key. Return true if you wish the engine to not attempt to handle the link itself. */ +void(float isnew) CSQC_Ent_Update; +void() CSQC_Ent_Remove; +float(float entnum, float channel, string soundname, float vol, float attenuation, vector pos, float pitchmod, float flags) CSQC_Event_Sound; +float(string resname, string restype) CSQC_LoadResource; /* Called each time some resource is being loaded. CSQC can invoke various draw calls to provide a loading screen, until WorldLoaded is called. */ +float() CSQC_Parse_TempEntity; /* Please don't use this. Use CSQC_Parse_Event and multicasts instead. */ +void(string cmdtext) GameCommand; +void(float prevprogs) init; /* Part of FTE_MULTIPROGS. Called as soon as a progs is loaded, called at a time when entities are not valid. This is the only time when it is safe to call addprogs without field assignment. As it is also called as part of addprogs, this also gives you a chance to hook functions in modules that are already loaded (via externget+externget). */ +void() initents; /* Part of FTE_MULTIPROGS. Called after fields have been finalized. This is the first point at which it is safe to call spawn(), and is called before any entity fields have been parsed. You can use this entrypoint to send notifications to other modules. */ +__used var float physics_mode = 2; /* 0: original csqc - physics are not run 1: DP-compat. Thinks occur, but not true movetypes. 2: movetypes occur just as they do in ssqc. */ float gamespeed; /* Set by the engine, this is the value of the sv_gamespeed cvar */ @@ -428,6 +433,9 @@ float drawfont; /* Allows you to choose exactly which font is to be used to draw #define CHAN_VOICE 2 #define CHAN_ITEM 3 #define CHAN_BODY 4 +#define SOUNDFLAG_ABSVOLUME 16 +#define SOUNDFLAG_FORCELOOP 2 +#define SOUNDFLAG_NOSPACIALISE 4 #define ATTN_NONE 0 /* Sounds with this attenuation can be heard throughout the map */ #define ATTN_NORM 1 /* Standard attenuation */ #define ATTN_IDLE 2 /* Extra attenuation so that sounds don't travel too far. */ @@ -448,15 +456,16 @@ float drawfont; /* Allows you to choose exactly which font is to be used to draw #define SERVERKEY_IP "ip" /* The address of the server we connected to. */ #define SERVERKEY_SERVERNAME "servername" /* The hostname that was last passed to the connect command. */ #define SERVERKEY_CONSTATE "constate" /* The current connection state. Will be set to one of: disconnected (menu-only mode), active (gamestate received and loaded), connecting(connecting, downloading, or precaching content, aka: loading screen). */ -#define SERVERKEY_TRANSFERRING "transferring" /* Set to the hostname of the server that we are attempting to connect or transfer to. */ +#define SERVERKEY_TRANSFERRING "transferring" /* Set to the hostname of the server that we are attempting to connect or transfer to. */ #define SERVERKEY_LOADSTATE "loadstate" /* loadstage, loading image name, current step, max steps Stages are: 1=connecting, 2=serverside, 3=clientside Key will be empty if we are not loading. */ #define SERVERKEY_PAUSESTATE "pausestate" /* 1 if the server claimed to be paused. 0 otherwise */ #define SERVERKEY_DLSTATE "dlstate" /* The progress of any current downloads. Empty string if no download is active, otherwise a tokenizable string containing this info: files-remaining, total-size, unknown-sizes-flag, file-localname, file-remotename, file-percent, file-rate, file-received-bytes, file-total-bytes -If the current file info is omitted, then we are waiting for a download to start. */ +If the current file info is omitted, then we are waiting for a download to start. */ #define SERVERKEY_PROTOCOL "protocol" /* The protocol we are connected to the server with. */ +#define SERVERKEY_MAXPLAYERS "maxplayers" /* The protocol we are connected to the server with. */ #define FL_FLY 1 #define FL_SWIM 2 #define FL_CLIENT 8 @@ -493,6 +502,7 @@ If the current file info is omitted, then we are waiting for a download to start #define EF_ADDITIVE 32 #define EF_BLUE 64 #define EF_RED 128 +#define EF_GREEN 262144 #define EF_FULLBRIGHT 512 #define EF_NODEPTHTEST 8192 #define MF_ROCKET 1 @@ -516,6 +526,7 @@ If the current file info is omitted, then we are waiting for a download to start #define EV_VARIANT 9 hashtable gamestate; /* Special hash table index for hash_add and hash_get. Entries in this table will persist over map changes (and doesn't need to be created/deleted). */ #define HASH_REPLACE 256 /* Used with hash_add. Attempts to remove the old value instead of adding two values for a single key. */ +#define HASH_ADD 512 /* Used with hash_add. The new entry will be inserted in addition to the existing entry. */ #define STAT_HEALTH 0 /* Player's health. */ #define STAT_WEAPONMODELI 2 /* This is the modelindex of the current viewmodel (renamed from the original name 'STAT_WEAPON' due to confusions). */ #define STAT_AMMO 3 /* player.currentammo */ @@ -561,7 +572,8 @@ hashtable gamestate; /* Special hash table index for hash_add and hash_get. Entr #define VF_CL_VIEWANGLES_Y 35 #define VF_CL_VIEWANGLES_Z 36 #define VF_PERSPECTIVE 200 /* 1: regular rendering. Fov specifies the angle. 0: isometric-style. Fov specifies the number of Quake Units each side of the viewport. */ -#define VF_LPLAYER 202 /* The 'seat' number, used when running splitscreen. */ +#define VF_LPLAYER VF_ACTIVESEAT +const float VF_ACTIVESEAT = 202; /* The 'seat' number, used when running splitscreen. */ #define VF_AFOV 203 /* Aproximate fov. Matches the 'fov' cvar. The engine handles the aspect ratio for you. */ #define VF_SCREENVSIZE 204 /* Provides a reliable way to retrieve the current virtual screen size (even if the screen is automatically scaled to retain aspect). */ #define VF_SCREENPSIZE 205 /* Provides a reliable way to retrieve the current physical screen size (cvars need vid_restart for them to take effect). */ @@ -636,13 +648,17 @@ Note that any rendertarget textures may be destroyed on video mode changes or so #define TEREDIT_TEX_UNIFY 11 #define TEREDIT_TEX_NOISE 12 #define TEREDIT_TEX_BLUR 13 +#define TEREDIT_TEX_REPLACE 19 +#define TEREDIT_TEX_SETMASK 25 #define TEREDIT_WATER_SET 14 #define TEREDIT_MESH_ADD 15 #define TEREDIT_MESH_KILL 16 #define TEREDIT_TINT 17 -#define TEREDIT_TEX_REPLACE 19 #define TEREDIT_RESET_SECT 20 #define TEREDIT_RELOAD_SECT 21 +#define TEREDIT_ENTS_WIPE 22 +#define TEREDIT_ENTS_CONCAT 23 +#define TEREDIT_ENTS_GET 24 #define SLIST_HOSTCACHEVIEWCOUNT 0 #define SLIST_HOSTCACHETOTALCOUNT 1 #define SLIST_MASTERQUERYCOUNT 2 @@ -676,12 +692,13 @@ void(entity e, vector min, vector max) setsize = #4; /* float() random = #7; /* Returns a random value between 0 and 1. Be warned, this builtin can return 1 in most engines, which can break arrays. */ -void(entity e, float chan, string samp, float vol, float atten, optional float speedpct, optional float flags) sound = #8; /* +void(entity e, float chan, string samp, float vol, float atten, optional float speedpct, optional float flags, optional float timeofs) sound = #8; /* Starts a sound centered upon the given entity. chan is the entity sound channel to use, channel 0 will allow you to mix many samples at once, others will replace the old sample 'samp' must have been precached first if specified, 'speedpct' should normally be around 100 (or =0), 200 for double speed or 50 for half speed. - flags&1 means the sound should be sent reliably. */ + If flags is specified, the reliable flag in the channels argument is used for additional channels. Flags should be made from SOUNDFLAG_* constants + timeofs should be negative in order to provide a delay before the sound actually starts. */ vector(vector v) normalize = #9; /* Shorten or lengthen a direction vector such that it is only one quake unit long. */ @@ -758,7 +775,7 @@ float(float yaw, float dist, optional float settraceglobals) walkmove = #32; /* This function will trigger touch events. */ float() droptofloor = #34; /* - Instantly moves the entity downwards until it hits the ground. If the entity would need to drop more than 'pr_droptofloorunits' quake units, its position will be considered invalid and the builtin will abort. */ + Instantly moves the entity downwards until it hits the ground. If the entity is in solid or would need to drop more than 'pr_droptofloorunits' quake units, its position will be considered invalid and the builtin will abort, returning FALSE, otherwise TRUE. */ void(float lightstyle, string stylestring, optional vector rgb) lightstyle = #35; /* Specifies an auto-animating string that specifies the light intensity for entities using that lightstyle. @@ -797,18 +814,22 @@ void(vector pos, vector dir, float colour, float count) particle = #48; /* #define ChangeYaw changeyaw void() changeyaw = #49; /* - Changes the self.angles_y field towards self.ideal_yaw by up to self.yawspeed. */ + Changes the self.angles_y field towards self.ideal_yaw by up to self.yaw_speed. */ vector(vector fwd, optional vector up) vectoangles = #51; /* Returns the angles (+x=UP) required to orient an entity to look in the given direction. The 'up' argument is required if you wish to set a roll angle, otherwise it will be limited to just monster-style turning. */ -float(float angle) sin = #60; /* Part of DP_QC_SINCOSSQRTPOW*/ +float(float angle) sin = #60; /* Part of DP_QC_SINCOSSQRTPOW + Forgive me father, for I have trigonometry homework. */ + float(float angle) cos = #61; /* Part of DP_QC_SINCOSSQRTPOW*/ float(float value) sqrt = #62; /* Part of DP_QC_SINCOSSQRTPOW*/ void(entity ent) changepitch = #63; /* Part of DP_QC_CHANGEPITCH*/ void(entity ent, entity ignore) tracetoss = #64; string(entity ent) etos = #65; /* Part of DP_QC_ETOS*/ -void(float step) movetogoal = #67; +void(float step) movetogoal = #67; /* + Runs lots and lots of fancy logic in order to try to step the entity the specified distance towards its goalentity. */ + string(string s) precache_file = #68; /* This builtin does nothing. It was used only as a hint for pak generation. */ @@ -845,10 +866,17 @@ float(float minimum, float val, float maximum) bound = #96; /* Part of DP_QC_MIN Returns val, unless minimum is higher, or maximum is less. */ float(float value, float exp) pow = #97; /* Part of DP_QC_SINCOSSQRTPOW*/ +float(float v, optional float base) logarithm = #0/*:logarithm*/; /* + Determines the logarithm of the input value according to the specified base. This can be used to calculate how much something was shifted by. */ + entity(entity start, .float fld, float match) findfloat = #98; /* Part of DP_QC_FINDFLOAT Equivelent to the find builtin, but instead of comparing strings, this builtin compares floats. This builtin requires multiple calls in order to scan all entities - set start to the previous call's return value. world is returned when there are no more entities. */ +entity(entity start, .entity fld, entity match) findentity = #98; /* + Equivelent to the find builtin, but instead of comparing strings, this builtin compares entities. This builtin requires multiple calls in order to scan all entities - set start to the previous call's return value. + world is returned when there are no more entities. */ + float(string extname) checkextension = #99; /* Checks for an extension by its name (eg: checkextension("FRIK_FILE") says that its okay to go ahead and use strcat). Use cvar("pr_checkextension") to see if this builtin exists. */ @@ -859,7 +887,7 @@ void(filestream fhandle) fclose = #111; /* Part of FRIK_FILE*/ string(filestream fhandle) fgets = #112; /* Part of FRIK_FILE*/ void(filestream fhandle, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7) fputs = #113; /* Part of FRIK_FILE*/ float(string s) strlen = #114; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/ -string(string s1, optional string s2, ...) strcat = #115; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/ +string(string s1, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7, optional string s8) strcat = #115; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/ string(string s, float start, float length) substring = #116; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/ vector(string s) stov = #117; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/ string(string s, ...) strzone = #118; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS @@ -952,6 +980,9 @@ float(string s1, string s2, float len, optional float s1ofs, optional float s2of Compares up to 'len' chars in the two strings without case sensitivity. s1ofs allows you to treat s2 as a substring to compare against, or should be 0. Returns 0 if they are equal. The sign of the return value may be significant, but should not be depended upon. */ +string(string s) strtrim = #0/*:strtrim*/; /* + Trims the whitespace from the start+end of the string. */ + void() calltimeofday = #231; /* Part of FTE_CALLTIMEOFDAY Asks the engine to instantly call the qc's 'timeofday' function, before returning. For compatibility with mvdsv. timeofday should have the prototype: void(float secs, float mins, float hour, float day, float mon, float year, string strvalue) @@ -969,10 +1000,10 @@ void(vector org, optional float count) te_bloodqw = #239; /* Part of FTE_TE_STAN float(vector viewpos, entity entity) checkpvs = #240; /* Part of FTE_QC_CHECKPVS*/ vector(entity ent, float tagnum) rotatevectorsbytag = #244; int(string) stoi = #259; /* Part of FTE_QC_INTCONV - Converts the given string into an integer. Base 8, 10, or 16 is determined based upon the format of the string. */ + Converts the given string into a true integer. Base 8, 10, or 16 is determined based upon the format of the string. */ string(int) itos = #260; /* Part of FTE_QC_INTCONV - Converts the passed integer into a base10 string. */ + Converts the passed true integer into a base10 string. */ int(string) stoh = #261; /* Part of FTE_QC_INTCONV Reads a base-16 string (with or without 0x prefix) as an integer. Bugs out if given a base 8 or base 10 string. :P */ @@ -980,6 +1011,12 @@ int(string) stoh = #261; /* Part of FTE_QC_INTCONV string(int) htos = #262; /* Part of FTE_QC_INTCONV Formats an integer as a base16 string, with leading 0s and no prefix. Always returns 8 characters. */ +int(float) ftoi = #0/*:ftoi*/; /* + Converts the given float into a true integer without depending on extended qcvm instructions. */ + +float(int) itof = #0/*:itof*/; /* + Converts the given true integer into a float without depending on extended qcvm instructions. */ + float(float modlindex, optional float useabstransforms) skel_create = #263; /* Part of FTE_CSQC_SKELETONOBJECTS Allocates a new uninitiaised skeletal object, with enough bone info to animate the given model. eg: self.skeletonobject = skel_create(self.modelindex); */ @@ -1056,6 +1093,9 @@ float(float modelid, int brushid, int faceid, float selectedstate) brush_selecte int(float modelid, int brushid, int faceid, vector *points, int maxpoints) brush_getfacepoints = #0/*:brush_getfacepoints*/; /* Returns the list of verticies surrounding the given face. If face is 0, returns the center of the brush (if space for 1 point) or the mins+maxs (if space for 2 points). */ +int(int faceid, brushface_t *in_faces, int numfaces, vector *points, int maxpoints) brush_calcfacepoints = #0/*:brush_calcfacepoints*/; /* + Determines the points of the specified face, if the specified brush were to actually be created. */ + int(float modelid, vector *planes, float *dists, int numplanes, int *out_brushes, int *out_faces, int maxresults) brush_findinvolume = #0/*:brush_findinvolume*/; /* Allows you to easily obtain a list of brushes+faces within the given bounding region. If out_faces is not null, the same brush might be listed twice. */ @@ -1136,9 +1176,9 @@ void() renderscene = #304; /* float(vector org, float radius, vector lightcolours, optional float style, optional string cubemapname, optional float pflags) dynamiclight_add = #305; /* Adds a temporary dlight, ready to be drawn via addscene. Cubemap orientation will be read from v_forward/v_right/v_up. */ -void(string texturename, optional float flags) R_BeginPolygon = #306; /* +void(string texturename, optional float flags, optional float is2d) R_BeginPolygon = #306; /* Specifies the shader to use for the following polygons, along with optional flags. - If flags&4, the polygon will be drawn as soon as the EndPolygon call is made, rather than waiting for renderscene. This allows complex 2d effects. */ + If is2d, the polygon will be drawn as soon as the EndPolygon call is made, rather than waiting for renderscene. This allows complex 2d effects. */ void(vector org, vector texcoords, vector rgb, float alpha) R_PolygonVertex = #307; /* Specifies a polygon vertex with its various properties. */ @@ -1259,11 +1299,14 @@ float(string keyname) stringtokeynum = #341; /* Looks up the key name in the same way that the bind command would, returning the keycode for that key. */ string(float keynum) getkeybind = #342; /* - Finds the current binding for the given key (ignores modifiers like shift/alt/ctrl). */ + Returns the current binding for the given key (returning only the command executed when no modifiers are pressed). */ void(float usecursor, optional string cursorimage, optional vector hotspot, optional float scale) setcursormode = #343; /* Pass TRUE if you want the engine to release the mouse cursor (absolute input events + touchscreen mode). Pass FALSE if you want the engine to grab the cursor (relative input events + standard looking). If the image name is specified, the engine will use that image for a cursor (use an empty string to clear it again), in a way that will not conflict with the console. Images specified this way will be hardware accelerated, if supported by the platform/port. */ +float(float effective) getcursormode = #0/*:getcursormode*/; /* + Reports the cursor mode this module previously attempted to use. If 'effective' is true, reports the cursor mode currently active (if was overriden by a different module which has precidence, for instance, or if there is only a touchscreen and no mouse). */ + vector() getmousepos = #344; /* Nasty convoluted DP extension. Typically returns deltas instead of positions. Use CSQC_InputEvent for such things in csqc mods. */ @@ -1374,6 +1417,9 @@ void(__variant *dst, float ofs, __variant val) memsetval = #389; /* __variant*(__variant *base, float ofs) memptradd = #390; /* Perform some pointer maths. Woo. */ +float(string s) memstrsize = #0/*:memstrsize*/; /* + strlen, except ignores utf-8 */ + string(string conname, string field, optional string newvalue) con_getset = #391; /* Part of FTE_CSQC_ALTCONSOLES_WIP Reads or sets a property from a console object. The old value is returned. Iterrate through consoles with the 'next' field. Valid properties: title, name, next, unseen, markup, forceutf8, close, clear, hidden, linecount */ @@ -1386,6 +1432,9 @@ void(string conname, vector pos, vector size, float fontsize) con_draw = #393; / float(string conname, float inevtype, float parama, float paramb, float paramc) con_input = #394; /* Part of FTE_CSQC_ALTCONSOLES_WIP Forwards input events to the named console. Mouse updates should be absolute only. */ +float() cvars_haveunsaved = #0/*:cvars_haveunsaved*/; /* + Returns true if any archived cvar has an unsaved value. */ + void(entity from, entity to) copyentity = #400; /* Part of DP_QC_COPYENTITY*/ entity(.string field, string match) findchain = #402; /* Part of DP_QC_FINDCHAIN*/ entity(.float fld, float match) findchainfloat = #403; /* Part of DP_QC_FINDCHAINFLOAT*/ @@ -1468,9 +1517,15 @@ float(float s) asin = #471; /* Part of DP_QC_ASINACOSATANATAN2TAN*/ float(float c) acos = #472; /* Part of DP_QC_ASINACOSATANATAN2TAN*/ float(float t) atan = #473; /* Part of DP_QC_ASINACOSATANATAN2TAN*/ float(float c, float s) atan2 = #474; /* Part of DP_QC_ASINACOSATANATAN2TAN*/ -float(float a) tan = #475; /* Part of DP_QC_ASINACOSATANATAN2TAN*/ -float(string s) strlennocol = #476; /* Part of DP_QC_STRINGCOLORFUNCTIONS*/ -string(string s) strdecolorize = #477; /* Part of DP_QC_STRINGCOLORFUNCTIONS*/ +float(float a) tan = #475; /* Part of DP_QC_ASINACOSATANATAN2TAN + Forgive me father, for I have a sunbed and I'm not afraid to use it. */ + +float(string s) strlennocol = #476; /* Part of DP_QC_STRINGCOLORFUNCTIONS + Returns the number of characters in the string after any colour codes or other markup has been parsed. */ + +string(string s) strdecolorize = #477; /* Part of DP_QC_STRINGCOLORFUNCTIONS + Flattens any markup/colours, removing them from the string. */ + string(float uselocaltime, string format, ...) strftime = #478; /* Part of DP_QC_STRFTIME*/ float(string s, string separator1, ...) tokenizebyseparator = #479; /* Part of DP_QC_TOKENIZEBYSEPARATOR*/ string(string s) strtolower = #480; /* Part of DP_QC_STRING_CASE_FUNCTIONS*/ @@ -1531,28 +1586,56 @@ __variant(float entnum, float fieldnum) getentity = #504; /* string(string in) uri_escape = #510; /* Part of DP_QC_URI_ESCAPE*/ string(string in) uri_unescape = #511; /* Part of DP_QC_URI_ESCAPE*/ float(entity ent) num_for_edict = #512; +#define uri_post uri_get float(string uril, float id, optional string postmimetype, optional string postdata) uri_get = #513; /* Part of DP_QC_URI_GET, DP_QC_URI_POST uri_get() gets content from an URL and calls a callback "uri_get_callback" with it set as string; an unique ID of the transfer is returned - returns 1 on success, and then calls the callback with the ID, 0 or the HTTP status code, and the received data in a string */ + returns 1 on success, and then calls the callback with the ID, 0 or the HTTP status code, and the received data in a string + For a POST request, you will typically want the postmimetype set to application/x-www-form-urlencoded. + For a GET request, omit the mime+data entirely. + Consult your webserver/php/etc documentation for best-practise. */ + +float(string str) tokenize_console = #514; /* + Tokenize a string exactly as the console's tokenizer would do so. The regular tokenize builtin became bastardized for convienient string parsing, which resulted in a large disparity that can be exploited to bypass checks implemented in a naive SV_ParseClientCommand function, therefore you can use this builtin to make sure it exactly matches. */ + +float(float idx) argv_start_index = #515; /* + Returns the character index that the tokenized arg started at. */ + +float(float idx) argv_end_index = #516; /* + Returns the character index that the tokenized arg stopped at. */ -float(string str) tokenize_console = #514; -float(float idx) argv_start_index = #515; -float(float idx) argv_end_index = #516; void(strbuf strbuf, string pattern, string antipattern) buf_cvarlist = #517; -string(string cvarname) cvar_description = #518; +string(string cvarname) cvar_description = #518; /* + Retrieves the description of a cvar, which might be useful for tooltips or help files. This may still not be useful. */ + float(optional float timetype) gettime = #519; string(float keynum) keynumtostring_omgwtf = #520; -string(string command, optional float bindmap) findkeysforcommand = #521; +string(string command, optional float bindmap) findkeysforcommand = #521; /* + Returns a list of keycodes that perform the given console command in a format that can only be parsed via tokenize (NOT tokenize_console). This only and always returns two values - if only one key is actually bound, -1 will be returned. The bindmap argument is listed for compatibility with dp-specific defs, but is ignored in FTE. */ + +string(string command, optional float bindmap) findkeysforcommandex = #0/*:findkeysforcommandex*/; /* + Returns a list of key bindings in keyname format instead of keynums. Use tokenize to parse. This list may contain modifiers. May return large numbers of keys. */ + void(string s) loadfromdata = #529; /* Reads a set of entities from the given string. This string should have the same format as a .ent file or a saved game. Entities will be spawned as required. If you need to see the entities that were created, you should use parseentitydata instead. */ void(string s) loadfromfile = #530; /* Reads a set of entities from the named file. This file should have the same format as a .ent file or a saved game. Entities will be spawned as required. If you need to see the entities that were created, you should use parseentitydata instead. */ -float(entity e, float channel) getsoundtime = #533; -float(string sample) soundlength = #534; -float(string filename, strbuf bufhandle) buf_loadfile = #535; -float(filestream filehandle, strbuf bufhandle, optional float startpos, optional float numstrings) buf_writefile = #536; +float(entity e, float channel, string newsample, float volume, float attenuation, float pitchpct, float flags, float timeoffset) soundupdate = #0/*:soundupdate*/; /* + Changes the properties of the current sound being played on the given entity channel. newsample may be empty, and will be ignored in this case. timeoffset is relative to the current position (subtract the result of getsoundtime for absolute positions). Negative volume can be used to stop the sound. Return value is a fractional value based upon the number of audio devices that could be updated - test against TRUE rather than non-zero. */ + +float(entity e, float channel) getsoundtime = #533; /* + Returns the current playback time of the sample on the given entity's channel. Beware CHAN_AUTO (in csqc, channels are not limited by network protocol). */ + +float(string sample) soundlength = #534; /* + Provides a way to query the duration of a sound sample, allowing you to set up a timer to chain samples. */ + +float(string filename, strbuf bufhandle) buf_loadfile = #535; /* + Appends the named file into a string buffer (which must have been created in advance). The return value merely says whether the file was readable. */ + +float(filestream filehandle, strbuf bufhandle, optional float startpos, optional float numstrings) buf_writefile = #536; /* + Writes the contents of a string buffer onto the end of the supplied filehandle (you must have already used fopen). Additional optional arguments permit you to constrain the writes to a subsection of the stringbuffer. */ + void(entity e, float physics_enabled) physics_enable = #540; /* Enable or disable the physics attached to a MOVETYPE_PHYSICS entity. Entities which have been disabled in this way will stop taking so much cpu time. */ @@ -1570,10 +1653,13 @@ void(.../*, string funcname*/) callfunction = #605; /* void(filestream fh, entity e) writetofile = #606; /* Writes an entity's fields to the named frik_file file handle. */ -float(string s) isfunction = #607; -vector(float vidmode, optional float forfullscreen) getresolution = #608; +float(string s) isfunction = #607; /* + Returns true if the named function exists and can be called with the callfunction builtin. */ + +vector(float vidmode, optional float forfullscreen) getresolution = #608; /* + Supposed to query the driver for supported video modes. FTE does not query drivers in this way, nor would it trust drivers anyway. */ + string(float keynum) keynumtostring_menu = #609; -string(string command, optional float bindmap) findkeysforcommand_dp = #610; float(float type) gethostcachevalue = #611; /* Part of FTE_CSQC_SERVERBROWSER*/ string(float type, float hostnr) gethostcachestring = #612; /* Part of FTE_CSQC_SERVERBROWSER*/ void(entity e, string s) parseentitydata = #613; /* @@ -1594,6 +1680,8 @@ string(string dnsname, optional float defport) netaddress_resolve = #625; string(string fmt, ...) sprintf = #627; float(entity e, float s) getsurfacenumtriangles = #628; vector(entity e, float s, float n) getsurfacetriangle = #629; +vector() getbindmaps = #631; +float(vector bm) setbindmaps = #632; string(string digest, string data, ...) digest_hex = #639; #if defined(CSQC) || defined(MENU) #define K_TAB 9 @@ -1707,28 +1795,11 @@ string(string digest, string data, ...) digest_hex = #639; #define K_CAPSLOCK 155 #define K_SCROLLLOCK 156 #define K_SEMICOLON 59 +#define K_PLUS 43 #define K_TILDE 126 #define K_BACKQUOTE 96 #define K_BACKSLASH 92 -#endif - -#pragma target fte -inline float(float a) retarg = {return a;}; - var float glob = 9; -inline float(vector a, vector b, float c) inlinedfunc = -{ -// float c = 4; - return a*b*c*glob * (retarg(c)*retarg(a_z)); -}; -float() itest = -{ - return 5*glob*inlinedfunc('0 1 2', '1 2 0', 4); -}; -#pragma target fte - - - - +#endif accessor strbuf : float { inline get float asfloat[float idx] = {return stof(bufstr_get(this, idx));}; @@ -1764,6 +1835,3 @@ accessor filestream : float inline set string = {fputs(this,value);}; }; #pragma noref 0 - - - diff --git a/quakec/csaddon/src/editor_brushes.qc b/quakec/csaddon/src/editor_brushes.qc index 2c6719d7a..63ec65b64 100644 --- a/quakec/csaddon/src/editor_brushes.qc +++ b/quakec/csaddon/src/editor_brushes.qc @@ -20,8 +20,7 @@ float brush_selected(float modelid, int brushid, int faceid, float selectedstate int(float modelid, int brushid, int faceid, vector *points, int maxpoints) brush_getfacepoints = #0; int(float modelid, vector *planes, float *dists, int numplanes, int *out_brushes, int *out_faces, int maxresults) brush_findinvolume = #0; #endif -int trace_brush_id; //set by traceline -int trace_brush_faceid; + /*traceline/tracebox returns trace_brush_id and trace_brush_faceid @@ -32,9 +31,11 @@ brush ids are ints. this allows different clients to use different ranges withou int selectedbrush; int selectedbrushface; -float selectedbrushmodel; +int selectedbrushmodel; vector facepoints[64]; +#define EPSILON (1.0 / 32) //inprecision sucks. + //history is implemented using a ringbuffer typedef struct { @@ -100,8 +101,8 @@ int(int mod, brushface_t *faces, int numfaces, int contents) brush_history_creat h->timestamp = time; memfree(h->brushdata); - h->brushdata = memalloc(sizeof(brushface_t) * h->numfaces); - memcpy(h->brushdata, faces, sizeof(brushface_t) * h->numfaces); + h->brushdata = memalloc(sizeof(brushface_t) * numfaces); + memcpy(h->brushdata, faces, sizeof(brushface_t) * numfaces); h->numfaces = numfaces; h->contents = contents; h->brushmodel = mod; @@ -140,6 +141,30 @@ void(int mod, int id) brush_history_delete = history_min = history_max - historyring.length; }; +int(int mod, int id, brushface_t *faces, int numfaces, int contents) brush_history_edit = +{ + brush_history_delete(mod, id); + return brush_history_create(mod, faces, numfaces, contents); +}; + + + +vector(vector a, vector b) cross = +{ + return [ a_y * b_z - a_z * b_y, + a_z * b_x - a_x * b_z, + a_x * b_y - a_y * b_x]; +}; +#define dot(a,b) ((a)*(b)) + +typedef struct +{ + int numverts; + int numidx; + vector *p; + int *i; +} vertsoup_t; +vertsoup_t vertedit; brushface_t tmp_faces[64]; int tmp_numfaces; int tmp_contents; @@ -149,23 +174,27 @@ int brushlist[64]; var string autocvar_ca_newbrushtexture = "metal4_2"; var float autocvar_ca_newbrushheight = 64; -var float autocvar_ca_brushdisplacement = 16; -var float autocvar_ca_brushrotation = 45.0/2; var float autocvar_ca_grid = 16; -enum +enum int { BT_NONE, //selection - BT_MOVE, + BT_MOVE = BT_NONE, + BT_ROTATE, BT_MERGE, - BT_MOVEFACE, + BT_PUSHFACE, BT_CREATE, BT_CLONEDISPLACE, - BT_SLICE + 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 //basic cube plane normals, for selection. static nosave const vector axis[6] = { @@ -178,16 +207,64 @@ static nosave const vector axis[6] = { }; float dists[6]; -vector(vector a, vector b) cross = +//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 = { - return [ a_y * b_z - a_z * b_y, - a_z * b_x - a_x * b_z, - a_x * b_y - a_y * b_x]; + //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; }; -#define dot(a,b) ((a)*(b)) + +static int(brushface_t *fa, int famax, vector *points, int numpoints, float height) BrushFromPoints = +{ + int count = 0, p; + + fa->planenormal = normalize(cross(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(cross(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 (p = 0; p < numpoints; p++) + { + int n = p + 1; + if (n == numpoints) + n = 0; + fa->planenormal = normalize(cross(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, point; 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); @@ -199,13 +276,13 @@ vector(vector guess) brush_snappoint = //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 brushnum, numbrushes = brush_findinvolume(selectedbrushmodel, &axis[0], &dists[0], 6, &brushlist[0], __NULL__, brushlist.length); + int brushnum, numbrushes = brush_findinvolume(selectedbrushmodel, axis, dists, 6, brushlist, __NULL__, brushlist.length); for (brushnum = 0; brushnum < numbrushes; brushnum++) { for (facenum = 0; ; ) { - points = brush_getfacepoints(selectedbrushmodel, brushlist[brushnum], ++facenum, &facepoints[0], facepoints.length); + points = brush_getfacepoints(selectedbrushmodel, brushlist[brushnum], ++facenum, facepoints, facepoints.length); if (!points) break; //end of face list, I guess @@ -223,10 +300,10 @@ vector(vector guess) brush_snappoint = return best; }; -void(float brushid) editor_drawbbox = +void(int brushid) editor_drawbbox = { static vector bbox[2]; - int p = brush_getfacepoints(selectedbrushmodel, brushid, 0, &bbox[0], bbox.length); + int p = brush_getfacepoints(selectedbrushmodel, brushid, 0, bbox, bbox.length); if (p == 2) { R_BeginPolygon("chop"); @@ -248,12 +325,554 @@ void(float brushid) editor_drawbbox = } }; +//yay pointers! +//move a brush so that its planes all move without any translations in positions or texcoords +static 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. +static void brushface_rotate(brushface_t *fa, int numfaces) +{ + int i; + for (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; + int f, p; + + //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(f = 0; f < numfaces; f++) + { + vector n = faces[f].planenormal; + float d = faces[f].planedist + EPSILON; //epsilon to cover precision issues + for (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; + } + + tmp_numfaces = brush_get(model, brush1, tmp_faces, tmp_faces.length, &tmp_contents); + 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); + } +}; + +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, facepoints.length); + 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, facepoints.length); + 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, facepoints.length); + 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. + 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(); + } + } +}; + +#if 0 +void(vertsoup_t *soup, brushface_t **trifaces, brushface_t **splits, int internalsplits, int drawit) Rebrushify_r = +{ + //clip the soup by the internal splits. + //if any triangle is outside, remove that plane from the sub-brush + //add the splits to the volume. call it done. + + brushface_t faces[64]; + int numfaces; + int point; + + for(point = 0; point+2 < vertedit.numidx; point+=3) + { + vector v1 = vertedit.p[vertedit.i[point+0]]; + vector v2 = vertedit.p[vertedit.i[point+1]]; + vector v3 = vertedit.p[vertedit.i[point+2]]; + + if ( v1 * s->planenormal < s->planedist && + v2 * s->planenormal < s->planedist && + v3 * s->planenormal < s->planedist) + continue; + } + + if (drawit) + { + //draw it wireframe without depth testing + DrawQCBrushWireframe(faces, numfaces, "chop", '1 0 0'); + } +}; +#endif + +void(vertsoup_t *soup, int drawit) Rebrushify = +{ + brushface_t *internalsplit[64]; + brushface_t faces[64]; + brushface_t *trifaces[128]; + int numfaces, f, point; + string shader = 0; + + tmp_numfaces = brush_get(selectedbrushmodel, selectedbrush, tmp_faces, tmp_faces.length, &tmp_contents); + + //generate the plane info + numfaces = 0; + for(point = 0; point+2 < vertedit.numidx; point+=3) + { + vector v1 = vertedit.p[vertedit.i[point+0]]; + vector v2 = vertedit.p[vertedit.i[point+1]]; + vector v3 = vertedit.p[vertedit.i[point+2]]; + vector d1 = v3 - v1; + vector d2 = v2 - v1; + vector d3 = v3 - v2; + vector n = normalize(cross(d1, d2)); + float d = n * v1; + + if (!d1 || !d2 || !d3 || !n) + { + trifaces[point/3] = 0; + 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) + { + trifaces[point/3] = &faces[f]; + 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; + trifaces[point/3] = &faces[numfaces]; + 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]); + trifaces[point/3] = &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 < vertedit.numidx; point++) //would ideally use points, but I want to cover dead ones + { + if (vertedit.p[vertedit.i[point]] * n > d + EPSILON) + { + internalsplit[internals++] = &faces[f]; + break; + } + } + } + cprint(sprintf("%i internal splits, %i faces\n", internals, numfaces)); + +// Rebrushify_r(soup, trifaces, internalsplit, internals, drawit); + + + 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", '0 0 1', 1); + } + else + { + if (selectedbrush) + brush_history_delete(selectedbrushmodel, selectedbrush); + selectedbrush = brush_history_create(selectedbrushmodel, faces, numfaces, 1i); + selectedbrushface = 0; + + vertedit.numidx = 0; + vertedit.numverts = 0; + } +}; + +//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(void) Debrushify = +{ + int *ni; + int i, j, k; + int points; + vector p; + vector *np; + static int fi[64]; + for (i = 0; ; ) + { + points = brush_getfacepoints(selectedbrushmodel, selectedbrush, ++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]; + } + } +}; + void(vector mousepos) editor_brushes_add = { vector col = '0 0 0'; int points, point; - int facenum = 0; + int facenum; float intensity = (sin(gettime(5)*4)+1)*0.05; + vector displace, tmp; + + if ((brushtool == BT_PUSHFACE || brushtool == BT_CLONEDISPLACE || brushtool == BT_MOVE || brushtool == BT_MOVETEXTURE) && bt_points) + { + makevectors(input_angles); + vector dir = v_forward; + if (!altdown) //if alt is pressed, we'll axialize later. + dir = axialize(dir); + tmp = normalize(mousefar-mousenear) + mousenear; + tmp = planelinepoint(mousenear, tmp, dir, bt_point[0] * dir); //find where the cursor impacts the screen grid (moved along the view vector to match where the drag was last frame) + displace = tmp - bt_point[0]; + displace = brush_snappoint(displace); + if (altdown) //if alt is held, rotate the move by 90 degrees, and move ONLY in the axial dir instead of the screen's xy plane + bt_displace_z = (displace * v_right + displace * v_up); + else + { + bt_displace_x = displace * axialize(v_right); + bt_displace_y = displace * axialize(v_up); + } + displace = bt_displace_x * axialize(v_right) + bt_displace_y * axialize(v_up) - bt_displace_z * axialize(v_forward); + } + else if (brushtool == BT_ROTATE) + { + te_lightning2(world, bt_point[0], bt_point[0]+-bt_displace); + col = vectoangles(bt_point[0]+bt_displace - tmp) - vectoangles(bt_point[0] - tmp); + col = channelizeangle([col_x*-1,col_y,col_z]); + } + + if (vertedit.numidx && mousetool != BT_VERTEXEDIT) + { + vertedit.numidx = 0; + vertedit.numverts = 0; + } //make sure this shader was generated already. shaderforname("terrainedit", @@ -279,25 +898,225 @@ void(vector mousepos) editor_brushes_add = "nodepthtest\n" "}\n" "}"); - - //draw the selected brush. - for(;;) + + if (mousetool == BT_VERTEXEDIT && vertedit.numidx) { - points = brush_getfacepoints(selectedbrushmodel, selectedbrush, ++facenum, &facepoints[0], facepoints.length); - if (!points) - break; //end of face list, I guess - - if (facenum == selectedbrushface) - col = [0,intensity,0]; - else - col = [intensity,0,0]; - +#if 0 + //draw it solid WITH depth testing R_BeginPolygon("terrainedit"); - for (point = 0; point < points; point++) - R_PolygonVertex(facepoints[point], '0 0', col, 1); - R_EndPolygon(); + for(point = 0; point+2 < vertedit.numidx; point+=3) + { + col = '0 0 0.1'; + R_PolygonVertex(vertedit.p[vertedit.i[point+0]], '0 0', col, 1); + R_PolygonVertex(vertedit.p[vertedit.i[point+1]], '0 0', col, 1); + R_PolygonVertex(vertedit.p[vertedit.i[point+2]], '0 0', col, 1); + R_EndPolygon(); + } +#endif + //draw the wires (no depth) + R_BeginPolygon("chop"); + for(point = 0; point+2 < vertedit.numidx; point+=3) + { + col = '0 0.1 0'; + R_PolygonVertex(vertedit.p[vertedit.i[point+0]], '0 0', col, 1); + R_PolygonVertex(vertedit.p[vertedit.i[point+1]], '0 0', col, 1); + R_EndPolygon(); + R_PolygonVertex(vertedit.p[vertedit.i[point+1]], '0 0', col, 1); + R_PolygonVertex(vertedit.p[vertedit.i[point+2]], '0 0', col, 1); + R_EndPolygon(); + R_PolygonVertex(vertedit.p[vertedit.i[point+2]], '0 0', col, 1); + R_PolygonVertex(vertedit.p[vertedit.i[point+0]], '0 0', col, 1); + R_EndPolygon(); + } + + Rebrushify(&vertedit, TRUE); } - editor_drawbbox(selectedbrush); + else if (brushtool == BT_CREATE) + { + if (bt_points > 2) + { + tmp_numfaces = BrushFromPoints(tmp_faces, tmp_faces.length, bt_point, bt_points, autocvar_ca_newbrushheight); + + DrawQCBrushWireframe(tmp_faces, tmp_numfaces, "chop", '1 0 0', 1); + } + } + else if ((mousetool == BT_PUSHFACE || mousetool == BT_MOVETEXTURE || mousetool == BT_MOVE || mousetool == BT_CLONEDISPLACE || mousetool == BT_ROTATE) && bt_points) + { + tmp_numfaces = brush_get(selectedbrushmodel, selectedbrush, tmp_faces, tmp_faces.length, &tmp_contents); + + if (mousetool == BT_PUSHFACE) + tmp_faces[selectedbrushface-1].planedist += tmp_faces[selectedbrushface-1].planenormal * displace; + else if (mousetool == BT_MOVETEXTURE) + { + tmp_faces[selectedbrushface-1].sbias -= tmp_faces[selectedbrushface-1].sdir * displace; + tmp_faces[selectedbrushface-1].tbias -= tmp_faces[selectedbrushface-1].tdir * displace; + } + else if (mousetool == BT_MOVE || mousetool == BT_CLONEDISPLACE) + brushface_translate(tmp_faces, tmp_numfaces, displace); + else if (mousetool == BT_ROTATE) + { + //find the brush's middle (based on its bbox) + brush_getfacepoints(selectedbrushmodel, selectedbrush, 0, &tmp, 1); + + makevectors(col); + + //move it so its pivot is at the origin + brushface_translate(tmp_faces, tmp_numfaces, -tmp); + //rotate it + brushface_rotate(tmp_faces, tmp_numfaces); + //reposition it around its pivot again + brushface_translate(tmp_faces, tmp_numfaces, tmp); + } + + if (mousetool == BT_MOVETEXTURE) + { + points = brush_calcfacepoints(selectedbrushface, tmp_faces, tmp_numfaces, facepoints, facepoints.length); + 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. + shaderforname(tmp_faces[selectedbrushface-1].shadername, + sprintf("{" + "{\n" + "map \"%s.lmp\"\n" + "rgbgen vertex\n" + "alphagen vertex\n" + "}\n" + "}", tmp_faces[selectedbrushface-1].shadername)); + + col = '0.7 0.7 0.7'; //fullbright is typically TOO bright. overbrights? meh! + vector sz = drawgetimagesize(tmp_faces[selectedbrushface-1].shadername); + R_BeginPolygon(tmp_faces[selectedbrushface-1].shadername); + for (point = 0; point < points; point++) + R_PolygonVertex(facepoints[point] + tmp_faces[selectedbrushface-1].planenormal*0.1, [(facepoints[point] * tmp_faces[selectedbrushface-1].sdir + tmp_faces[selectedbrushface-1].sbias)/sz_x, (facepoints[point] * tmp_faces[selectedbrushface-1].tdir + tmp_faces[selectedbrushface-1].tbias)/sz_y], col, 1); + R_EndPolygon(); + } + } + else + { + //draw it wireframe + for(facenum = 0; facenum < tmp_numfaces;) + { + points = brush_calcfacepoints(++facenum, tmp_faces, tmp_numfaces, facepoints, facepoints.length); + if (!points) + continue; //should probably warn somehow about this + //should we use two colour channels? one depth one not? + if (facenum == selectedbrushface) + col = '1 0 0'; + else + col = '0 0.5 0'; + R_BeginPolygon("chop"); + R_PolygonVertex(facepoints[0], '0 0', col, 1); + for (point = 0; point < points-1; ) + { + 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(); + } + } + + //draw it wireframe WITH depth testing + DrawQCBrushWireframe(tmp_faces, tmp_numfaces, "terrainedit", '0 0 1', 1); + + if (!mousedown) + { + brush_selected(selectedbrushmodel, selectedbrush, -1, FALSE); + if (mousetool == BT_CLONEDISPLACE) //doesn't affect the original brush. + { + if (displace*displace > 1) + selectedbrush = brush_history_create(selectedbrushmodel, tmp_faces, tmp_numfaces, tmp_contents); + } + else + selectedbrush = brush_history_edit(selectedbrushmodel, selectedbrush, tmp_faces, tmp_numfaces, tmp_contents); + brush_selected(selectedbrushmodel, selectedbrush, selectedbrushface, TRUE); + bt_points = 0; + mousetool = BT_NONE; + } + } + else if (brushtool == BT_SLICE && bt_points == 3) + { + tmp_numfaces = brush_get(selectedbrushmodel, selectedbrush, tmp_faces, tmp_faces.length, &tmp_contents); + tmp_faces[tmp_numfaces].planenormal = normalize(cross(bt_point[2] - bt_point[0], bt_point[1] - bt_point[0])); + tmp_faces[tmp_numfaces].planedist = bt_point[0] * tmp_faces[tmp_numfaces].planenormal; + + //draw it wireframe + DrawQCBrushWireframe(tmp_faces, tmp_numfaces+1, "chop", '1 0 0', 1); + + //flip the split + tmp_faces[tmp_numfaces].planenormal *= -1; + tmp_faces[tmp_numfaces].planedist *= -1; + + //draw the other side wireframe + DrawQCBrushWireframe(tmp_faces, tmp_numfaces+1, "chop", '0 1 0', 1); + } + else + { + if (brushtool == BT_PUSHFACE) + { //selected face (not brush) follows cursor when we're not actively dragging a face + float bestdist = 0, dist; + vector mid; + + for(facenum = 0;;) + { + points = brush_getfacepoints(selectedbrushmodel, selectedbrush, ++facenum, facepoints, facepoints.length); + if (!points) + break; //end of face list, I guess + + mid = 0; + for (point = 0; point < points; point++) + mid += facepoints[point]; //FIXME: find the centroid for greater accuracy + mid = project(mid * (1.0/points)); + dist = (mousepos_x - mid_x) * (mousepos_x - mid_x) + (mousepos_y - mid_y) * (mousepos_y - mid_y); + if (facenum == 1 || dist < bestdist) + { + bestdist = dist; + selectedbrushface = facenum; + } + } + } + + //draw the selected brush faces. + for(facenum = 0;;) + { + points = brush_getfacepoints(selectedbrushmodel, selectedbrush, ++facenum, facepoints, facepoints.length); + if (!points) + break; //end of face list, I guess + + if (facenum == selectedbrushface) + col = [0,intensity,0]; + else + col = [intensity,0,0]; + + R_BeginPolygon("terrainedit"); + for (point = 0; point < points; point++) + R_PolygonVertex(facepoints[point], '0 0', col, 1); + R_EndPolygon(); + } + //now draw wireframe + for(facenum = 0;;) + { + points = brush_getfacepoints(selectedbrushmodel, selectedbrush, ++facenum, &facepoints[0], facepoints.length); + if (!points) + break; //end of face list, I guess + + col = '1 0 0'; + R_BeginPolygon("chop"); + R_PolygonVertex(facepoints[0], '0 0', col, 1); + for (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(); + } + } +// editor_drawbbox(selectedbrush); vector t = mousefar; vector o = mousenear; @@ -340,12 +1159,12 @@ void(vector mousepos) editor_brushes_add = // bt_point[showpoints++] = brush_snappoint(trace_endpos); showpoints = 3; } - if (brushtool == BT_CREATE || brushtool == BT_CLONEDISPLACE) + if (brushtool == BT_CREATE) bt_point[showpoints++] = brush_snappoint(trace_endpos); //FIXME: if slicing, draw the intersection - if (showpoints) + if (showpoints > 1) { col = '0 0 1'; @@ -363,9 +1182,7 @@ void(vector mousepos) editor_brushes_overlay = switch(brushtool) { case BT_CLONEDISPLACE: - drawrawstring('0 32 0', "Clone+Displace", '8 8 0', '1 1 1', 1); - if (!selectedbrush) - brushtool = BT_NONE; + drawrawstring('0 32 0', "Clone", '8 8 0', '1 1 1', 1); break; case BT_CREATE: drawrawstring('0 32 0', "Paint+Create", '8 8 0', '1 1 1', 1); @@ -375,83 +1192,73 @@ void(vector mousepos) editor_brushes_overlay = if (!selectedbrush) brushtool = BT_NONE; break; + case BT_PUSHFACE: + drawrawstring('0 32 0', "Push Face", '8 8 0', '1 1 1', 1); + break; + case BT_MOVE: + drawrawstring('0 32 0', "Move Brush", '8 8 0', '1 1 1', 1); + break; + case BT_ROTATE: + drawrawstring('0 32 0', "Rotate Brush", '8 8 0', '1 1 1', 1); + break; + case BT_MOVETEXTURE: + drawrawstring('0 32 0', "Move Texture", '8 8 0', '1 1 1', 1); + break; } }; -//yay pointers! -//move a brush so that its planes all move without any translations in positions or texcoords -static 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. -static void brushface_rotate(brushface_t *fa, int numfaces) -{ - int i; - for (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; - } -}; - -//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; -}; +#define brusheditormodes +//#append brusheditormodes brusheditormode("move", BT_MOVE) +#append brusheditormodes brusheditormode("clone", BT_CLONEDISPLACE) +#append brusheditormodes brusheditormode("rotate", BT_ROTATE) +#append brusheditormodes brusheditormode("pushface", BT_PUSHFACE) +#append brusheditormodes brusheditormode("scrolltex", BT_MOVETEXTURE) +#append brusheditormodes brusheditormode("vertex", BT_VERTEXEDIT) void() editor_brushes_registercommands = { +#define brusheditormode(n,v) registercommand("+brushedit_"n);registercommand("-brushedit_"n); +brusheditormodes +#undef brusheditormode registercommand("brushedit_undo"); registercommand("brushedit_redo"); + registercommand("brushedit_delete"); registercommand("brushedit_create"); - registercommand("brushedit_clone"); registercommand("brushedit_slice"); registercommand("brushedit_matchface"); registercommand("brushedit_resettexcoords"); registercommand("brushedit_settexture"); - registercommand("brushedit_rotate"); registercommand("brushedit_binds"); + registercommand("+brushedit_nogrid"); + registercommand("-brushedit_nogrid"); +}; +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); + selectedbrush = brush_history_create(selectedbrushmodel, tmp_faces, tmp_numfaces, tmp_contents); }; float() editor_brushes_command = { switch(argv(0)) { +#define brusheditormode(n,v) case "+brushedit_"n: brushtool = v; bt_points = 0; break; case "-brushedit_"n: if (brushtool == v) brushtool = BT_NONE; break; +brusheditormodes +#undef brusheditormode + case "brushedit_undo": brush_undo(); break; @@ -462,41 +1269,47 @@ float() editor_brushes_command = brushtool = BT_CREATE; bt_points = 0; break; - case "brushedit_clone": - brushtool = BT_CLONEDISPLACE; - bt_points = 0; - break; case "brushedit_slice": brushtool = BT_SLICE; bt_points = 0; break; + + case "+brushedit_nogrid": nogrid = TRUE; break; + case "-brushedit_nogrid": nogrid = FALSE; break; + case "brushedit_matchface": - //FIXME + //IMPLEMENTME brushtool = BT_NONE; bt_points = 0; break; case "brushedit_resettexcoords": - //FIXME + //IMPLEMENTME brushtool = BT_NONE; bt_points = 0; break; case "brushedit_settexture": - //FIXME + //IMPLEMENTME brushtool = BT_NONE; bt_points = 0; break; - case "brushedit_rotate": - //FIXME - brushtool = BT_NONE; - bt_points = 0; + case "brushedit_delete": + if (selectedbrushmodel && selectedbrush) + brush_history_delete(selectedbrushmodel, selectedbrush); + selectedbrush = 0; break; case "brushedit_binds": - localcmd("conmenu\n"); - localcmd("menubind 0 0 \"Creation Tool\" \"brushedit_create\"\n"); - localcmd("menubind 0 8 \"Clone Tool\" \"brushedit_clone\"\n"); - localcmd("menubind 0 16 \"Slice Tool\" \"brushedit_slice\"\n"); - localcmd("menubind 0 24 \"Undo\" \"brushedit_undo\"\n"); - localcmd("menubind 0 32 \"Redo\" \"brushedit_redo\"\n"); + localcmd("conmenu \"\"\n"); + localcmd("menubind 0 8 \"brushedit_create\" \"Creation Tool\"\n"); + localcmd("menubind 0 16 \"brushedit_slice\" \"Slice Tool\"\n"); + localcmd("menubind 0 24 \"brushedit_delete\" \"Delete\"\n"); + localcmd("menubind 0 32 \"brushedit_undo\" \"Undo\"\n"); + localcmd("menubind 0 40 \"brushedit_redo\" \"Redo\"\n"); + localcmd("menubind 0 48 \"brushedit_nogrid\" \"Disable Grid\"\n"); + + float foo = 48; +#define brusheditormode(n,v) localcmd(sprintf("menubind 0 %g \"+brushedit_"n"\" \""n"\"\n", foo)); foo+=8; +brusheditormodes +#undef brusheditormode break; default: return FALSE; @@ -508,40 +1321,47 @@ float() editor_brushes_command = return TRUE; }; -vector(vector dir) axialize = +void(entity e) editor_brush_set_entity { - 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'; +// faesf = e; }; float(float key, float unic, vector mousepos) editor_brushes_key = { - brushface_t *fa; - brushface_t *infa; + brushface_t *fa; vector t = mousefar; vector o = mousenear; - vector displacement; int i, p; + if (vlen(o - t) > 8192) t = o + normalize(t)*8192; if (key == K_ESCAPE) { if (brushtool) brushtool = BT_NONE; + else + { + brush_selected(selectedbrushmodel, selectedbrush, -1, FALSE); + selectedbrush = 0; + selectedbrushface = 0; + } return TRUE; } if (key == K_MOUSE1) { traceline(o, t, TRUE, world); + float tracemodel = trace_ent.modelindex; - if (brushtool == BT_SLICE || brushtool == BT_CREATE || brushtool == BT_CLONEDISPLACE) + if (brushtool == BT_PUSHFACE && selectedbrushface) + { + trace_brush_faceid = selectedbrushface; + trace_brush_id = selectedbrush; + tracemodel = selectedbrushmodel; + } + if (brushtool != BT_PUSHFACE && brushtool != BT_MOVETEXTURE) + trace_brush_faceid = 0; + + if (brushtool == BT_SLICE || brushtool == BT_CREATE) { if (brushtool == BT_SLICE && bt_points > 2) bt_points = 2; @@ -564,7 +1384,7 @@ float(float key, float unic, vector mousepos) editor_brushes_key = brush_selected(selectedbrushmodel, selectedbrush, -1, FALSE); static vector bbox[2]; - p = brush_getfacepoints(selectedbrushmodel, selectedbrush, 0, &bbox[0], bbox.length); + p = brush_getfacepoints(selectedbrushmodel, selectedbrush, 0, bbox, bbox.length); t[0] = fabs(trace_plane_normal[0]); t[1] = fabs(trace_plane_normal[1]); @@ -582,70 +1402,63 @@ float(float key, float unic, vector mousepos) editor_brushes_key = bt_point[2] = bt_point[0]; bt_point[2][majoraxis] = bbox[trace_plane_normal[majoraxis]<0][majoraxis]; } - if (brushtool == BT_CLONEDISPLACE && bt_points == 2) - { - displacement = bt_point[1] - bt_point[0]; - if (selectedbrush && displacement != '0 0 0') - { - bt_points = 0; - tmp_numfaces = brush_get(selectedbrushmodel, selectedbrush, &tmp_faces[0], tmp_faces.length, &tmp_contents); - brushface_translate(&tmp_faces[0], tmp_numfaces, displacement); - selectedbrush = brush_history_create(selectedbrushmodel, &tmp_faces[0], tmp_numfaces, tmp_contents); - } - } } - else if (trace_brush_id != selectedbrush || selectedbrushface != trace_brush_faceid || selectedbrushmodel != trace_ent.modelindex) + //FIXME: selecting a brush by face should select either the front or the back. ideally depending on which one is closest to its respective face center, I suppose. + else if (trace_brush_id != selectedbrush || selectedbrushface != trace_brush_faceid || selectedbrushmodel != tracemodel) { brush_selected(selectedbrushmodel, selectedbrush, -1, FALSE); selectedbrush = trace_brush_id; selectedbrushface = trace_brush_faceid; - selectedbrushmodel = trace_ent.modelindex; - brush_selected(selectedbrushmodel, selectedbrush, trace_brush_faceid, TRUE); + selectedbrushmodel = tracemodel; + brush_selected(selectedbrushmodel, selectedbrush, selectedbrushface, TRUE); + + vertedit.numidx = 0; + vertedit.numverts = 0; + } + else if (selectedbrush == trace_brush_id && selectedbrushface == trace_brush_faceid && selectedbrushmodel == tracemodel) + { + mousedown = TRUE; + mousetool = brushtool; + bt_point[0] = brush_snappoint(trace_endpos); + bt_points = 1; + bt_displace = '0 0 0'; + } + if (brushtool == BT_VERTEXEDIT && !vertedit.numidx && selectedbrush) + { + Debrushify(); + brush_selected(selectedbrushmodel, selectedbrush, selectedbrushface, TRUE); } return TRUE; } if (key == K_ENTER) { - if (brushtool == BT_CREATE && bt_points >= 3) + if (mousetool == BT_VERTEXEDIT) { - fa = &tmp_faces[0]; - fa->planenormal = normalize(cross(bt_point[2] - bt_point[0], bt_point[1] - bt_point[0])); - fa->planedist = bt_point[0] * fa->planenormal + autocvar_ca_newbrushheight; - fa->shadername = autocvar_ca_newbrushtexture; - reset_texturecoords(fa); - fa++; - - fa->planenormal = -normalize(cross(bt_point[2] - bt_point[0], bt_point[1] - bt_point[0])); - fa->planedist = (bt_point[0] * fa->planenormal); - fa->shadername = autocvar_ca_newbrushtexture; - reset_texturecoords(fa); - fa++; - tmp_numfaces = 2; - - for (p = 0; p < bt_points; p++) - { - int n = p + 1i; - if (n == bt_points) - n = 0; - fa->planenormal = normalize(cross(bt_point[p] - bt_point[n], tmp_faces[0].planenormal)); - fa->planedist = bt_point[p] * fa->planenormal; - fa->shadername = autocvar_ca_newbrushtexture; - reset_texturecoords(fa); - fa++; - tmp_numfaces++; - } - -// tmp_numfaces = fa - &tmp_faces[0]; - + Rebrushify(&vertedit, FALSE); + mousetool = BT_NONE; bt_points = 0; - - selectedbrush = brush_history_create(selectedbrushmodel, &tmp_faces[0], tmp_numfaces, 1i); + mousedown = FALSE; + return TRUE; + } + else if (brushtool == BT_CREATE) + { + if (bt_points >= 3) + { + brush_selected(selectedbrushmodel, selectedbrush, -1, FALSE); + + tmp_numfaces = BrushFromPoints(tmp_faces, tmp_faces.length, bt_point, bt_points, autocvar_ca_newbrushheight); + bt_points = 0; + selectedbrush = brush_history_create(selectedbrushmodel, tmp_faces, tmp_numfaces, 1i); + + brush_selected(selectedbrushmodel, selectedbrush, selectedbrushface, TRUE); + } + return TRUE; } else if (brushtool == BT_SLICE) { //get the current faces - tmp_numfaces = brush_get(selectedbrushmodel, selectedbrush, &tmp_faces[0], tmp_faces.length-1, &tmp_contents); + tmp_numfaces = brush_get(selectedbrushmodel, selectedbrush, tmp_faces, tmp_faces.length-1, &tmp_contents); //generate a new face plane fa = &tmp_faces[tmp_numfaces]; @@ -669,234 +1482,58 @@ float(float key, float unic, vector mousepos) editor_brushes_key = //delete the old one and insert the new brush_history_delete(selectedbrushmodel, selectedbrush); - selectedbrush = brush_history_create(selectedbrushmodel, &tmp_faces[0], tmp_numfaces, tmp_contents); + selectedbrush = brush_history_create(selectedbrushmodel, tmp_faces, tmp_numfaces, tmp_contents); //and insert another new one too, because inserting a plane like this generates two fragments and I'm too lazy to work out which is the front and which is the back. fa->planenormal *= -1; fa->planedist *= -1; - selectedbrush = brush_history_create(selectedbrushmodel, &tmp_faces[0], tmp_numfaces, tmp_contents); + selectedbrush = brush_history_create(selectedbrushmodel, tmp_faces, tmp_numfaces, tmp_contents); selectedbrushface = 0; + return TRUE; } - return TRUE; + else return FALSE; } - if (key == 'p') - { - brushtool = BT_CREATE; - bt_points = 0; - return TRUE; - } - if (key == 'l') - { - brushtool = BT_CLONEDISPLACE; - bt_points = 0; - return TRUE; - } - - //begin the cut tool - if (key == 'o') //I'd use #, but that would cause a problem for americans - { - brushtool = BT_SLICE; - bt_points = 0; - return TRUE; - } if (key == 'm') //I'd use #, but that would cause a problem for americans { traceline(o, t, TRUE, world); - - int extrafaces; - float found = FALSE; - - if (selectedbrush == trace_brush_id) + + i = mergebrushes(selectedbrushmodel, selectedbrush, trace_brush_id, selectedbrushface, trace_brush_faceid); + if (i) { - print("cannot merge brush with itself\n"); + brush_selected(selectedbrushmodel, selectedbrush, -1, FALSE); + selectedbrush = i; + selectedbrushface = 0; + brush_selected(selectedbrushmodel, selectedbrush, selectedbrushface, TRUE); + } + + return TRUE; + } + + if (vertedit.numidx) + { + vector dir = '0 0 0'; + makevectors(input_angles); + if (key == K_KP_PLUS) + dir = axialize(v_up); + else if (key == K_KP_MINUS) + dir = -axialize(v_up); + else if (key == K_KP_UPARROW) + dir = axialize(v_forward); + else if (key == K_KP_DOWNARROW) + dir = -axialize(v_forward); + else if (key == K_KP_RIGHTARROW) + dir = axialize(v_right); + else if (key == K_KP_LEFTARROW) + dir = -axialize(v_right); + if (dir != '0 0 0') + { + vertedit.p[0] += dir; return TRUE; - } - if (!trace_brush_id || !selectedbrush) - { - print("no brush targetted\n"); - return TRUE; - } - - tmp_numfaces = brush_get(selectedbrushmodel, selectedbrush, &tmp_faces[0], tmp_faces.length, &tmp_contents); - infa = &tmp_faces[tmp_numfaces]; - extrafaces = brush_get(selectedbrushmodel, trace_brush_id, &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[0], i = 0; i < tmp_numfaces; i++, fa++) - { -//print(sprintf("%v %g vs %v %g\n", infa->planenormal, infa->planedist, fa->planenormal, fa->planedist)); - if (fa->planenormal == -infa->planenormal && fa->planedist == -infa->planedist) - { -//print(sprintf("inverted (%i)\n", ((tmp_numfaces-i)-1i))); - //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 && 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"); - - //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(selectedbrushmodel, selectedbrush, &tmp_faces[0], tmp_faces.length, &tmp_contents); - brush_get(selectedbrushmodel, trace_brush_id, &tmp_faces[tmp_numfaces], tmp_faces.length-tmp_numfaces, &tmp_contents); - infa = &tmp_faces[tmp_numfaces + trace_brush_faceid-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; - - brush_history_delete(selectedbrushmodel, selectedbrush); - selectedbrush = brush_history_create(selectedbrushmodel, &tmp_faces[0], tmp_numfaces, tmp_contents); - } - return TRUE; - } - //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(selectedbrushmodel, trace_brush_id); - brush_history_delete(selectedbrushmodel, selectedbrush); - - selectedbrush = brush_history_create(selectedbrushmodel, &tmp_faces[0], tmp_numfaces, tmp_contents); - selectedbrushface = 0; - brush_selected(selectedbrushmodel, selectedbrush, -1, FALSE); - return TRUE; + } } + - if (key == K_DEL || key == K_BACKSPACE) - { - if (brushtool) - brushtool = BT_NONE; - if (selectedbrush) - { - brush_history_delete(selectedbrushmodel, selectedbrush); - selectedbrush = 0; - } - return TRUE; - } - displacement = '0 0 0'; - if (key == K_PGUP) - displacement = autocvar_ca_brushdisplacement * axialize(v_up); - if (key == K_PGDN) - displacement = autocvar_ca_brushdisplacement * axialize(-v_up); - if (key == K_KP_UPARROW) - displacement = autocvar_ca_brushdisplacement * axialize(v_forward); - if (key == K_KP_DOWNARROW) - displacement = autocvar_ca_brushdisplacement * axialize(-v_forward); - if (key == K_KP_RIGHTARROW) - displacement = autocvar_ca_brushdisplacement * axialize(v_right); - if (key == K_KP_LEFTARROW) - displacement = autocvar_ca_brushdisplacement * axialize(-v_right); - if (displacement != '0 0 0') - { - //this is just an example to show how to move a brush without hurting texture coords at all. - //note: due to precision loss, this is probably NOT a good way to move brushes around. cache the original position if possible and express transformations relative to that. - if (selectedbrush) - { - tmp_numfaces = brush_get(selectedbrushmodel, selectedbrush, &tmp_faces[0], tmp_faces.length, &tmp_contents); - brushface_translate(&tmp_faces[0], tmp_numfaces, displacement); - brush_history_delete(selectedbrushmodel, selectedbrush); - selectedbrush = brush_history_create(selectedbrushmodel, &tmp_faces[0], tmp_numfaces, tmp_contents); - } - return TRUE; - } - if (key == '[' || key == ']') - { - //this is just an example to show how to rotate a brush. - //note: due to precision loss, this is probably NOT a good way to move brushes around. cache the original position if possible and express transformations relative to that. - - //determine rotation matrix - if (key == '[') - makevectors([0, -autocvar_ca_brushrotation, 0]); - else - makevectors([0, autocvar_ca_brushrotation, 0]); - - //rotate the selected brush - if (selectedbrush) - { - //find the brush's middle (based on its bbox) - brush_getfacepoints(selectedbrushmodel, selectedbrush, 0, &displacement, 1); - - //grab the existing brush's planes - tmp_numfaces = brush_get(selectedbrushmodel, selectedbrush, &tmp_faces[0], tmp_faces.length, &tmp_contents); - //move it so its pivot is at the origin - brushface_translate(&tmp_faces[0], tmp_numfaces, -displacement); - //rotate it - brushface_rotate(&tmp_faces[0], tmp_numfaces); - //reposition it around its pivot again - brushface_translate(&tmp_faces[0], tmp_numfaces, displacement); - //delete the old version of the brush and insert the newly translated/rotated version of it. - brush_history_delete(selectedbrushmodel, selectedbrush); - selectedbrush = brush_history_create(selectedbrushmodel, &tmp_faces[0], tmp_numfaces, tmp_contents); - } - return TRUE; - } - - if (key == K_KP_PLUS || key == K_KP_MINUS) - { - //push a plane along its normal. yes, this will deform it if its not a cube, as the adjacent planes will not be changed, allowing them to cut into it differently. - if (selectedbrush && selectedbrushface) - { - fa = &tmp_faces[selectedbrushface-1]; - i = tmp_numfaces = brush_get(selectedbrushmodel, selectedbrush, &tmp_faces[0], tmp_faces.length, &tmp_contents); - displacement = fa->planenormal; - if (key == K_KP_MINUS) - displacement *= -1; - fa->planedist += fa->planenormal * displacement; - fa->sbias -= fa->sdir * displacement; - fa->tbias -= fa->tdir * displacement; - brush_history_delete(selectedbrushmodel, selectedbrush); - selectedbrush = brush_history_create(selectedbrushmodel, &tmp_faces[0], tmp_numfaces, tmp_contents); - - tmp_numfaces = brush_get(selectedbrushmodel, selectedbrush, &tmp_faces[0], tmp_faces.length, &tmp_contents); - if (i != tmp_numfaces) - selectedbrushface = 0; //if the engine rejected a plane, then it's because something became invalid. don't edit a different face. - - } - return TRUE; - } - if (key == 'u') - { - brush_undo(); - return TRUE; - } - if (key == 'i') - { - brush_redo(); - return TRUE; - } /* if (key == 's') { //CSG subtraction is actually quite easy... diff --git a/quakec/csaddon/src/editor_ents.qc b/quakec/csaddon/src/editor_ents.qc index 07da56fb7..b4f0c824c 100644 --- a/quakec/csaddon/src/editor_ents.qc +++ b/quakec/csaddon/src/editor_ents.qc @@ -1,17 +1,7 @@ -//#define ENTS_NYI - -#ifdef ENTS_NYI -void() editor_ents_add = -{ -}; - -void(vector mousepos) editor_ents_overlay = -{ - drawrawstring('0 32 0', "Not Yet Implemented", '8 8 0', '1 1 1', 1); -}; -#else static int selectedent; static entity tempent; +static string editkey; +static int editfieldtype; //0 = not editing. 1 = editing the fieldname. 2 = editing the value typedef struct { @@ -44,123 +34,123 @@ struct float spawnflags_mask; } entclasses[] = { - {"info_player_start", "progs/player.mdl", '0 1 0', '-16 -16 -24', '16 16 32'}, - {"info_player_start", "progs/player.mdl", '1 0 0', '-16 -16 -24', '16 16 32'}, - {"info_player_start2", "progs/player.mdl", '1 0 0', '-16 -16 -24', '16 16 32'}, - {"info_player_deathmatch", "progs/player.mdl", '1 0 1', '-16 -16 -24', '16 16 32'}, - {"info_player_coop", "progs/player.mdl", '1 0 1', '-16 -16 -24', '16 16 32'}, + {"info_player_start", "progs/player.mdl", '0 1 0', '-16 -16 -24', '16 16 32'}, + {"info_player_start", "progs/player.mdl", '1 0 0', '-16 -16 -24', '16 16 32'}, + {"info_player_start2", "progs/player.mdl", '1 0 0', '-16 -16 -24', '16 16 32'}, + {"info_player_deathmatch", "progs/player.mdl", '1 0 1', '-16 -16 -24', '16 16 32'}, + {"info_player_coop", "progs/player.mdl", '1 0 1', '-16 -16 -24', '16 16 32'}, - {"item_armor1", "progs/armor.mdl", '0 0.5 0.8', '-16 -16 0', '16 16 56'}, - {"item_armor2", "progs/armor.mdl", '0 0.5 0.8', '-16 -16 0', '16 16 56'}, - {"item_armorInv", "progs/armor.mdl", '0 0.5 0.8', '-16 -16 0', '16 16 56'}, + {"item_armor1", "progs/armor.mdl", '0 0.5 0.8', '-16 -16 0', '16 16 56'}, + {"item_armor2", "progs/armor.mdl", '0 0.5 0.8', '-16 -16 0', '16 16 56'}, + {"item_armorInv", "progs/armor.mdl", '0 0.5 0.8', '-16 -16 0', '16 16 56'}, - {"weapon_supershotgun", "progs/g_shot.mdl", '0 0.5 0.8', '-16 -16 0', '16 16 56'}, - {"weapon_nailgun", "progs/g_nail.mdl", '0 0.5 0.8', '-16 -16 0', '16 16 56'}, - {"weapon_supernailgun", "progs/g_nail2.mdl", '0 0.5 0.8', '-16 -16 0', '16 16 56'}, - {"weapon_grenadelauncher", "progs/g_rock.mdl", '0 0.5 0.8', '-16 -16 0', '16 16 56'}, - {"weapon_rocketlauncher", "progs/g_rock2.mdl", '0 0.5 0.8', '-16 -16 0', '16 16 56'}, - {"weapon_lightning", "progs/g_light.mdl", '0 0.5 0.8', '-16 -16 0', '16 16 56'}, + {"weapon_supershotgun", "progs/g_shot.mdl", '0 0.5 0.8', '-16 -16 0', '16 16 56'}, + {"weapon_nailgun", "progs/g_nail.mdl", '0 0.5 0.8', '-16 -16 0', '16 16 56'}, + {"weapon_supernailgun", "progs/g_nail2.mdl", '0 0.5 0.8', '-16 -16 0', '16 16 56'}, + {"weapon_grenadelauncher", "progs/g_rock.mdl", '0 0.5 0.8', '-16 -16 0', '16 16 56'}, + {"weapon_rocketlauncher", "progs/g_rock2.mdl", '0 0.5 0.8', '-16 -16 0', '16 16 56'}, + {"weapon_lightning", "progs/g_light.mdl", '0 0.5 0.8', '-16 -16 0', '16 16 56'}, - {"item_key1", "progs/w_s_key.mdl", '0 0.5 0.8', '-16 -16 -24', '16 16 32'}, - {"item_key2", "progs/w_g_key.mdl", '0 0.5 0.8', '-16 -16 -24', '16 16 32'}, - {"item_sigil", "progs/end1.mdl", '0 0.5 0.8', '-16 -16 -24', '16 16 32'}, + {"item_key1", "progs/w_s_key.mdl", '0 0.5 0.8', '-16 -16 -24', '16 16 32'}, + {"item_key2", "progs/w_g_key.mdl", '0 0.5 0.8', '-16 -16 -24', '16 16 32'}, + {"item_sigil", "progs/end1.mdl", '0 0.5 0.8', '-16 -16 -24', '16 16 32'}, - {"item_artifact_invulnerability", "progs/invulner.mdl", '0 0.5 0.8', '-16 -16 -24', '16 16 32'}, - {"item_artifact_envirosuit", "progs/suit.mdl", '0 0.5 0.8', '-16 -16 -24', '16 16 32'}, - {"item_artifact_invisibility", "progs/invisibl.mdl", '0 0.5 0.8', '-16 -16 -24', '16 16 32'}, + {"item_artifact_invulnerability", "progs/invulner.mdl", '0 0.5 0.8', '-16 -16 -24', '16 16 32'}, + {"item_artifact_envirosuit", "progs/suit.mdl", '0 0.5 0.8', '-16 -16 -24', '16 16 32'}, + {"item_artifact_invisibility", "progs/invisibl.mdl", '0 0.5 0.8', '-16 -16 -24', '16 16 32'}, {"item_artifact_super_damage", "progs/quaddama.mdl", '0 0.5 0.8', '-16 -16 -24', '16 16 32'}, -// {"func_button", 0, '0 .5 .8', ? -// {"trigger_changelevel", 0, '0.5 0.5 0.5' ? //NO_INTERMISSION -// {"func_door", 0, '0 .5 .8,' ? //START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE -// {"func_door_secret", 0, '0 .5 .8', ? //open_once 1st_left 1st_down no_shoot always_shoot -// {"trigger_portaldespawn", 0, '.5 .5 .5', ? -// {"func_wall", 0, '0 .5 .8', ? -// {"func_illusionary", 0, '0 .5 .8', ? -// {"func_episodegate", 0, '0 .5 .8', ? //E1 E2 E3 E4 -// {"func_bossgate", 0, '0 .5 .8', ? -// {"func_plat", 0, '0 .5 .8' ? PLAT_LOW_TRIGGER -// {"func_train", 0, '0 .5 .8' ? -// {"trigger_multiple", 0, '.5 .5 .5' ? notouch -// {"trigger_once", 0, '.5 .5 .5' ? notouch -// {"trigger_secret", 0, '.5 .5 .5' ? -// {"trigger_counter", 0, '.5 .5 .5' ? nomessage -// {"trigger_teleport", 0, '.5 .5 .5' ? PLAYER_ONLY SILENT -// {"trigger_setskill", 0, '.5 .5 .5' ? -// {"trigger_onlyregistered", 0, '.5 .5 .5' ? -// {"trigger_hurt", 0, '.5 .5 .5' ? -// {"trigger_push", 0, '.5 .5 .5' ? PUSH_ONCE -// {"trigger_monsterjump", 0, '.5 .5 .5', ? -// {"trigger_motionsickness", 0, '.5 .5 .5', ? -// {"worldspawn", 0, '0 0 0) ? +// {"func_button", "*", '0 .5 .8', ? +// {"trigger_changelevel", "*", '0.5 0.5 0.5' ? //NO_INTERMISSION +// {"func_door", "*", '0 .5 .8,' ? //START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE +// {"func_door_secret", "*", '0 .5 .8', ? //open_once 1st_left 1st_down no_shoot always_shoot +// {"trigger_portaldespawn", "*", '.5 .5 .5', ? +// {"func_wall", "*", '0 .5 .8', ? +// {"func_illusionary", "*", '0 .5 .8', ? +// {"func_episodegate", "*", '0 .5 .8', ? //E1 E2 E3 E4 +// {"func_bossgate", "*", '0 .5 .8', ? +// {"func_plat", "*", '0 .5 .8' ? PLAT_LOW_TRIGGER +// {"func_train", "*", '0 .5 .8' ? +// {"trigger_multiple", "*", '.5 .5 .5' ? notouch +// {"trigger_once", "*", '.5 .5 .5' ? notouch +// {"trigger_secret", "*", '.5 .5 .5' ? +// {"trigger_counter", "*", '.5 .5 .5' ? nomessage +// {"trigger_teleport", "*", '.5 .5 .5' ? PLAYER_ONLY SILENT +// {"trigger_setskill", "*", '.5 .5 .5' ? +// {"trigger_onlyregistered", "*", '.5 .5 .5' ? +// {"trigger_hurt", "*", '.5 .5 .5' ? +// {"trigger_push", "*", '.5 .5 .5' ? PUSH_ONCE +// {"trigger_monsterjump", "*", '.5 .5 .5', ? +// {"trigger_motionsickness", "*", '.5 .5 .5', ? +// {"worldspawn", "*", '0 0 0) ? - {"path_corner", 0, '0.5 0.3 0', '-8 -8 -8', '8 8 8'}, - {"event_lightning", 0, '0 1 1', '-16 -16 -16', '16 16 16'}, - {"info_intermission", 0, '1 0.5 0.5', '-16 -16 -16', '16 16 16'}, - {"misc_portalspawn", 0, '.5 .5 .5', '-8 -8 -8', '8 8 8'}, - {"noclass", 0, '0 0 0', '-8 -8 -8', '8 8 8'}, - {"item_health", "maps/b_bh25.bsp", '.3 .3 1', '0 0 0', '32 32 32'}, //rotten megahealth - {"item_shells", "maps/b_shell0.bsp", '0 .5 .8', '0 0 0', '32 32 32'}, //big - {"item_spikes", "maps/b_nail0.bsp", '0 .5 .8', '0 0 0', '32 32 32'}, //big - {"item_rockets", "maps/b_rock0.bsp", '0 .5 .8', '0 0 0', '32 32 32'}, //big - {"item_cells", "maps/b_batt0.bsp", '0 .5 .8', '0 0 0', '32 32 32'}, //big - {"item_weapon", 0, '0 .5 .8', '0 0 0', '32 32 32'}, //shotgun rocket spikes big - {"info_null", 0, '0 0.5 0', '-4 -4 -4', '4 4 4'}, - {"info_notnull", 0, '0 0.5 0', '-4 -4 -4', '4 4 4'}, - {"light", 0, '0 1 0', '-8 -8 -8', '8 8 8'}, //START_OFF - {"light_fluoro", 0, '0 1 0', '-8 -8 -8', '8 8 8'},//START_OFF - {"light_fluorospark", 0, '0 1 0', '-8 -8 -8', '8 8 8'}, - {"light_globe", 0, '0 1 0', '-8 -8 -8', '8 8 8'}, - {"light_torch_small_walltorch", 0, '0 .5 0', '-10 -10 -20', '10 10 20'}, - {"light_flame_large_yellow", 0, '0 1 0', '-10 -10 -12', '12 12 18'}, - {"light_flame_small_yellow", 0, '0 1 0', '-8 -8 -8', '8 8 8'}, //START_OFF - {"light_flame_small_white", 0, '0 1 0', '-10 -10 -40', '10 10 40'}, //START_OFF - {"misc_fireball", 0, '0 .5 .8', '-8 -8 -8', '8 8 8'}, - {"misc_explobox", 0, '0 .5 .8', '0 0 0', '32 32 64'}, - {"misc_explobox2", 0, '0 .5 .8', '0 0 0', '32 32 64'}, - {"trap_spikeshooter", 0, '0 .5 .8', '-8 -8 -8', '8 8 8'}, //superspike laser - {"trap_shooter", 0, '0 .5 .8', '-8 -8 -8', '8 8 8'}, //superspike laser - {"air_bubbles", 0, '0 .5 .8', '-8 -8 -8', '8 8 8'}, - {"viewthing", 0, '0 .5 .8', '-8 -8 -8', '8 8 8'}, - {"ambient_suck_wind", 0, '0.3 0.1 0.6', '-10 -10 -8', '10 10 8'}, - {"ambient_drone", 0, '0.3 0.1 0.6', '-10 -10 -8', '10 10 8'}, - {"ambient_flouro_buzz", 0, '0.3 0.1 0.6', '-10 -10 -8', '10 10 8'}, - {"ambient_drip", 0, '0.3 0.1 0.6', '-10 -10 -8', '10 10 8'}, - {"ambient_comp_hum", 0, '0.3 0.1 0.6', '-10 -10 -8', '10 10 8'}, - {"ambient_thunder", 0, '0.3 0.1 0.6', '-10 -10 -8', '10 10 8'}, - {"ambient_light_buzz", 0, '0.3 0.1 0.6', '-10 -10 -8', '10 10 8'}, - {"ambient_swamp1", 0, '0.3 0.1 0.6', '-10 -10 -8', '10 10 8'}, - {"ambient_swamp2", 0, '0.3 0.1 0.6', '-10 -10 -8', '10 10 8'}, - {"misc_noisemaker", 0, '1 0.5 0', '-10 -10 -10', '10 10 10'}, - {"misc_teleporttrain", 0, '0 .5 .8', '-8 -8 -8', '8 8 8'}, - {"trigger_relay", 0, '.5 .5 .5', '-8 -8 -8', '8 8 8'}, - {"info_teleport_destination", 0, '.5 .5 .5', '-8 -8 -8', '8 8 32'}, + {"path_corner", 0, '0.5 0.3 0', '-8 -8 -8', '8 8 8'}, + {"event_lightning", 0, '0 1 1', '-16 -16 -16', '16 16 16'}, + {"info_intermission", 0, '1 0.5 0.5', '-16 -16 -16', '16 16 16'}, + {"misc_portalspawn", 0, '.5 .5 .5', '-8 -8 -8', '8 8 8'}, + {"noclass", 0, '0 0 0', '-8 -8 -8', '8 8 8'}, + {"item_health", "maps/b_bh25.bsp", '.3 .3 1', '0 0 0', '32 32 32'}, //rotten megahealth + {"item_shells", "maps/b_shell0.bsp", '0 .5 .8', '0 0 0', '32 32 32'}, //big + {"item_spikes", "maps/b_nail0.bsp", '0 .5 .8', '0 0 0', '32 32 32'}, //big + {"item_rockets", "maps/b_rock0.bsp", '0 .5 .8', '0 0 0', '32 32 32'}, //big + {"item_cells", "maps/b_batt0.bsp", '0 .5 .8', '0 0 0', '32 32 32'}, //big + {"item_weapon", 0, '0 .5 .8', '0 0 0', '32 32 32'}, //shotgun rocket spikes big + {"info_null", 0, '0 0.5 0', '-4 -4 -4', '4 4 4'}, + {"info_notnull", 0, '0 0.5 0', '-4 -4 -4', '4 4 4'}, + {"light", 0, '0 1 0', '-8 -8 -8', '8 8 8'}, //START_OFF + {"light_fluoro", 0, '0 1 0', '-8 -8 -8', '8 8 8'},//START_OFF + {"light_fluorospark", 0, '0 1 0', '-8 -8 -8', '8 8 8'}, + {"light_globe", 0, '0 1 0', '-8 -8 -8', '8 8 8'}, + {"light_torch_small_walltorch", 0, '0 .5 0', '-10 -10 -20', '10 10 20'}, + {"light_flame_large_yellow", 0, '0 1 0', '-10 -10 -12', '12 12 18'}, + {"light_flame_small_yellow", 0, '0 1 0', '-8 -8 -8', '8 8 8'}, //START_OFF + {"light_flame_small_white", 0, '0 1 0', '-10 -10 -40', '10 10 40'}, //START_OFF + {"misc_fireball", 0, '0 .5 .8', '-8 -8 -8', '8 8 8'}, + {"misc_explobox", 0, '0 .5 .8', '0 0 0', '32 32 64'}, + {"misc_explobox2", 0, '0 .5 .8', '0 0 0', '32 32 64'}, + {"trap_spikeshooter", 0, '0 .5 .8', '-8 -8 -8', '8 8 8'}, //superspike laser + {"trap_shooter", 0, '0 .5 .8', '-8 -8 -8', '8 8 8'}, //superspike laser + {"air_bubbles", 0, '0 .5 .8', '-8 -8 -8', '8 8 8'}, + {"viewthing", 0, '0 .5 .8', '-8 -8 -8', '8 8 8'}, + {"ambient_suck_wind", 0, '0.3 0.1 0.6', '-10 -10 -8', '10 10 8'}, + {"ambient_drone", 0, '0.3 0.1 0.6', '-10 -10 -8', '10 10 8'}, + {"ambient_flouro_buzz", 0, '0.3 0.1 0.6', '-10 -10 -8', '10 10 8'}, + {"ambient_drip", 0, '0.3 0.1 0.6', '-10 -10 -8', '10 10 8'}, + {"ambient_comp_hum", 0, '0.3 0.1 0.6', '-10 -10 -8', '10 10 8'}, + {"ambient_thunder", 0, '0.3 0.1 0.6', '-10 -10 -8', '10 10 8'}, + {"ambient_light_buzz", 0, '0.3 0.1 0.6', '-10 -10 -8', '10 10 8'}, + {"ambient_swamp1", 0, '0.3 0.1 0.6', '-10 -10 -8', '10 10 8'}, + {"ambient_swamp2", 0, '0.3 0.1 0.6', '-10 -10 -8', '10 10 8'}, + {"misc_noisemaker", 0, '1 0.5 0', '-10 -10 -10', '10 10 10'}, + {"misc_teleporttrain", 0, '0 .5 .8', '-8 -8 -8', '8 8 8'}, + {"trigger_relay", 0, '.5 .5 .5', '-8 -8 -8', '8 8 8'}, + {"info_teleport_destination", 0, '.5 .5 .5', '-8 -8 -8', '8 8 32'}, - {"monster_boss", "progs/boss.mdl", '1 0 0', '-128 -128 -24','128 128 256'}, - {"monster_hell_knight", "progs/hknight.mdl", '1 0 0', '-16 -16 -24', '16 16 40'}, //Ambush - {"monster_demon1", "progs/demon.mdl", '1 0 0', '-32 -32 -24', '32 32 64'}, //Ambush - {"monster_dog", "progs/dog.mdl", '1 0 0', '-32 -32 -24', '32 32 40'}, //Ambush - {"monster_enforcer", "progs/enforcer.mdl", '1 0 0', '-16 -16 -24', '16 16 40'}, //Ambush - {"monster_fish", "progs/fish.mdl", '1 0 0', '-16 -16 -24', '16 16 24'}, //Ambush - {"monster_ogre", "progs/ogre.mdl", '1 0 0', '-32 -32 -24', '32 32 64'}, //Ambush - {"monster_oldone", "progs/oldone.mdl", '1 0 0', '-16 -16 -24', '16 16 32'}, - {"monster_knight", "progs/knight.mdl", '1 0 0', '-16 -16 -24', '16 16 40'}, //Ambush - {"monster_shalrath", "progs/shalrath.mdl", '1 0 0', '-32 -32 -24', '32 32 48'}, //Ambush - {"monster_shambler", "progs/shambler.mdl",'1 0 0', '-32 -32 -24', '32 32 64'}, //Ambush - {"monster_army", "progs/soldier.mdl", '1 0 0', '-16 -16 -24', '16 16 40'}, //Ambush - {"monster_tarbaby", "progs/tarbaby.mdl", '1 0 0', '-16 -16 -24', '16 16 24'}, //Ambush - {"monster_wizard", "progs/wizard.mdl", '1 0 0', '-16 -16 -24', '16 16 40'}, // Ambush - {"monster_zombie", "progs/zombie.mdl", '1 0 0', '-16 -16 -24', '16 16 32'} //Crucified ambush + {"monster_boss", "progs/boss.mdl", '1 0 0', '-128 -128 -24','128 128 256'}, + {"monster_hell_knight", "progs/hknight.mdl", '1 0 0', '-16 -16 -24', '16 16 40'}, //Ambush + {"monster_demon1", "progs/demon.mdl", '1 0 0', '-32 -32 -24', '32 32 64'}, //Ambush + {"monster_dog", "progs/dog.mdl", '1 0 0', '-32 -32 -24', '32 32 40'}, //Ambush + {"monster_enforcer", "progs/enforcer.mdl", '1 0 0', '-16 -16 -24', '16 16 40'}, //Ambush + {"monster_fish", "progs/fish.mdl", '1 0 0', '-16 -16 -24', '16 16 24'}, //Ambush + {"monster_ogre", "progs/ogre.mdl", '1 0 0', '-32 -32 -24', '32 32 64'}, //Ambush + {"monster_oldone", "progs/oldone.mdl", '1 0 0', '-16 -16 -24', '16 16 32'}, + {"monster_knight", "progs/knight.mdl", '1 0 0', '-16 -16 -24', '16 16 40'}, //Ambush + {"monster_shalrath", "progs/shalrath.mdl", '1 0 0', '-32 -32 -24', '32 32 48'}, //Ambush + {"monster_shambler", "progs/shambler.mdl", '1 0 0', '-32 -32 -24', '32 32 64'}, //Ambush + {"monster_army", "progs/soldier.mdl", '1 0 0', '-16 -16 -24', '16 16 40'}, //Ambush + {"monster_tarbaby", "progs/tarbaby.mdl", '1 0 0', '-16 -16 -24', '16 16 24'}, //Ambush + {"monster_wizard", "progs/wizard.mdl", '1 0 0', '-16 -16 -24', '16 16 40'}, // Ambush + {"monster_zombie", "progs/zombie.mdl", '1 0 0', '-16 -16 -24', '16 16 32'} //Crucified ambush }; entedit_t*() editor_ents_new = { - local float nent; + local int nent; local entedit_t *newents; nent = numents; - numents += 1; + numents += 1i; //extend the list newents = memalloc(sizeof(entedit_t)*numents); @@ -175,13 +165,62 @@ entedit_t*() editor_ents_new = void(float num) editor_ents_delete = { + if (num >= 0 && num < numents) + { + hash_destroytab(editents[num].fields); + numents--; + memcpy(&editents[num], &editents[num+1], sizeof(entedit_t) * (numents-num)); + } }; +float(float mode) editor_ents_poll = +{ + if (mode != MODE_ENTSEDIT) + { + int i, e; + self = world; + terrain_edit(TEREDIT_ENTS_WIPE); + for (e = 0; e < numents; e++) + { + local entedit_t *ent = &editents[e]; + string n; + n = "{\n"; + for (i = 0; ; i++) + { + string key, value; + key = hash_getkey(ent->fields, i); + if not (key) + break; + value = ent->fields[key]; + //inject markup into the value so that it doesn't get too corrupted + value = strreplace("\\", "\\\\", value); + value = strreplace("\"", "\\\"", value); + value = strreplace("\n", "\\n", value); + //these are more optional + value = strreplace("\t", "\\t", value); + value = strreplace("\r", "\\r", value); + n = strcat(n, key, " \"", value, "\"\n"); + } + n = strcat(n, "}\n"); + + terrain_edit(TEREDIT_ENTS_CONCAT, n); + } + localcmd("mod_terrain_save\n"); //saves a .ent if its a bsp, a .map if it has brushes, and a .hmp if otherwise. or something. + ca_checksave = __NULL__; + return TRUE; + } + return FALSE; +} +void() editor_ents_edited = +{ + ca_checksave = editor_ents_poll; +} + inline float(string model) modelindexforname = { - print("precaching \""); - print(model); - print("\"\n"); +// print("precaching \""); +// print(model); +// print("\"\n"); precache_model(model); setmodel(tempent, model); return tempent.modelindex; @@ -193,10 +232,11 @@ void(entedit_t *nent) editor_ents_updated = nent->alpha = stof(nent->fields["alpha"]); nent->scale = stof(nent->fields["scale"]); nent->modelindex = modelindexforname(nent->fields["model"]); - nent->mins = tempent.mins; - nent->maxs = tempent.maxs; + nent->mins = '-8 -8 -8'; + nent->maxs = '8 8 8'; nent->org = stov(nent->fields["origin"]); nent->isbsp = FALSE; + nent->colourmod = '1 1 1'; ang = stof(nent->fields["angle"]); if (ang == -1) @@ -231,6 +271,27 @@ void(entedit_t *nent) editor_ents_updated = nent->ang = '0 0 0'; //bsp entities should not be displayed rotated, because that just messes everything up. }; +entedit_t*(entedit_t *o) editor_ents_clone = +{ + int i; + entedit_t *n = editor_ents_new(); + + + for (i = 0; ; i++) + { + string key, value; + key = hash_getkey(o->fields, i); + if not (key) + break; + value = o->fields[key]; + n->fields[key] = value; + } + + editor_ents_updated(n); + return n; +}; + + void() editor_ents_reload = { local string field, value; @@ -263,7 +324,7 @@ void() editor_ents_reload = value = getentitytoken(); nent.fields[field] = value; - print(sprintf("%s: %s\n", field, value)); +// print(sprintf("%s: %s\n", field, value)); } editor_ents_updated(nent); @@ -300,7 +361,7 @@ void(string shadername, vector min, vector max, vector col) editor_ents_drawbbox void() editor_ents_add = { - float e; + int e; if (!tempent) { tempent = spawn(); @@ -336,14 +397,14 @@ void() editor_ents_add = { if (e == selectedent) { - if (gettime(0)*5f & 1) + if ((gettime(0)*5f) & 1) continue; - tempent.effects |= 8192f; + tempent.effects |= EF_NODEPTHTEST; editor_ents_drawbbox("entboxsel", editents[e].org+editents[e].mins, editents[e].org+editents[e].maxs, editents[e].colourmod); } else { - tempent.effects &~= 8192f; + tempent.effects &= ~EF_NODEPTHTEST; editor_ents_drawbbox("entbox", editents[e].org+editents[e].mins, editents[e].org+editents[e].maxs, editents[e].colourmod); } @@ -363,62 +424,203 @@ void() editor_ents_add = float(float key, float unic, vector mousepos) editor_ents_key = { - if (key != K_MOUSE1) - return FALSE; - float bestfrac = 1; - int bestent = selectedent, e; - vector t = mousefar; vector o = mousenear; - if (vlen(o - t) > 8192) - t = o + normalize(t)*8192; + entedit_t *ent; - for (e = 1; e < numents; e++) + if (key == K_MOUSE1) { - if (editents[e].isbsp) - tempent.solid = SOLID_BSP; - else - tempent.solid = SOLID_BBOX; - tempent.modelindex = editents[e].modelindex; - tempent.alpha = editents[e].alpha; - tempent.scale = editents[e].scale; - tempent.angles = editents[e].ang; - tempent.colormod = editents[e].colourmod; - tempent.mins = editents[e].mins; - tempent.maxs = editents[e].maxs; - setorigin(tempent, editents[e].org); - - traceline(o, t, 256, tempent); - - if (bestfrac > trace_fraction) + float bestfrac = 1; + int bestent = selectedent, e; + + if (mousepos_x < 128) { - bestfrac = trace_fraction; - bestent = e; + editfieldtype = editkey?2:1; + return TRUE; + } + editfieldtype = 0; + + if (vlen(o - t) > 8192) + t = o + normalize(t)*8192; + + for (e = 1; e < numents; e++) + { + if (editents[e].isbsp) + tempent.solid = SOLID_BSP; + else + tempent.solid = SOLID_BBOX; + tempent.modelindex = editents[e].modelindex; + tempent.alpha = editents[e].alpha; + tempent.scale = editents[e].scale; + tempent.angles = editents[e].ang; + tempent.colormod = editents[e].colourmod; + tempent.mins = editents[e].mins; + tempent.maxs = editents[e].maxs; + setorigin(tempent, editents[e].org); + + traceline(o, t, 256, tempent); + + if (bestfrac > trace_fraction) + { + bestfrac = trace_fraction; + bestent = e; + } + } + tempent.modelindex = SOLID_NOT; + + selectedent = bestent; + } + else if (editfieldtype == 1) + { + if (selectedent >= numents) + return FALSE; + + if (key == K_ESCAPE) + { + editkey = ""; + editfieldtype = 0; + } + else if (key == K_BACKSPACE || key == K_DEL) + { + if (!editkey) + editfieldtype = 0; + else + editkey = substring(editkey, 0, -2); + } + else if (key == K_ENTER && !shiftdown) + { + if (editkey != "") + { + editents[selectedent].fields[editkey] = ""; + editfieldtype = 2; + } + } + else if (unic) + editkey = strcat(editkey, chr2str(unic)); + else + return FALSE; + } + else if (editfieldtype == 2) + { + if (selectedent >= numents) + return FALSE; + string value = editents[selectedent].fields[editkey]; + + if (key == K_ESCAPE) + { + editkey = ""; + editfieldtype = 0; + } + else if (key == K_BACKSPACE || key == K_DEL) + { + if (!value) + { + hash_delete(editents[selectedent].fields, editkey); + editfieldtype = 0; + } + else + value = substring(value, 0, -2); + } + else if (key == K_ENTER && !shiftdown) + editfieldtype = 0; + else if (key == K_ENTER) + value = strcat(value, chr2str('\n')); + else if (unic) + value = strcat(value, chr2str(unic)); + else + return FALSE; + + if (editfieldtype) + { + editents[selectedent].fields[editkey] = value; + editor_ents_updated(&editents[selectedent]); } } - tempent.modelindex = SOLID_NOT; + else if (key == K_ESCAPE && selectedent) + selectedent = 0; + else if (key == K_BACKSPACE || key == K_DEL) + editor_ents_delete(selectedent); + else if (unic == 'm' || unic == 'M' || unic == 'i' || unic == 'I') + { + traceline(o, t, TRUE, world); + + ent = &editents[selectedent]; + if (unic == 'i' || unic == 'I') + ent = editor_ents_clone(ent); - selectedent = bestent; + //figure out how far along the plane normal to push the entity in order to ensure that its mins/maxs is on the floor/slope/ceiling/wall/etc + //yay dotproducts + float ext = [ + (trace_plane_normal[0] < 0)?ent->mins[0]:ent->maxs[0], + (trace_plane_normal[1] < 0)?ent->mins[1]:ent->maxs[1], + (trace_plane_normal[2] < 0)?ent->mins[2]:ent->maxs[2] + ] * trace_plane_normal; + //update the all important origin + string str = sprintf("%v", trace_endpos + trace_plane_normal * ext); + ent->fields["origin"] = str; + //and fix up the quick-access stuff + ent->org = stov(ent->fields["origin"]); + + editor_ents_edited(); + } + else + return FALSE; return TRUE; }; void(vector mousepos) editor_ents_overlay = { int i; - vector pos = '0 32 0'; + vector pos; + vector col; + float foundedit = FALSE; + float pickedit; if (selectedent >= numents) return; entedit_t *ent = &editents[selectedent]; + + pos = '128 0 0'; + pos_y = vidsize_y - 32; + drawfill('0 16 0', pos, '0 0 0', 0.3); + pos = '0 32 0'; + + pickedit = !editfieldtype && mousepos_x < 128; + if (pickedit) + editkey = 0; for (i = 0; ; i++) { string key, value; key = hash_getkey(ent->fields, i); if not (key) - return; + break; value = ent->fields[key]; - drawrawstring(pos, sprintf("%s: %s", key, value), '8 8 0', '1 1 1', 1); + col = '1 1 1'; + if (pickedit && mousepos_y >= pos_y && mousepos_y < pos_y + 8) + { + col_y = 0; + editkey = key; + } + if (key == editkey && editfieldtype) + { + col = '0 0 1'; + foundedit = TRUE; + } + + //provide some markup so that its actually readable. + value = strreplace("^", "^^", value); + value = strreplace("\\", "^5\\\\^7", value); + value = strreplace("\"", "^5\\\"^7", value); + value = strreplace("\n", "^5\\n^7", value); + value = strreplace("\t", "^5\\t^7", value); + value = strreplace("\r", "^5\\r^7", value); + + drawstring(pos, sprintf("%s: %s", key, value), '8 8 0', col, 1, 0); + pos_y += 8; + } + if (editfieldtype && !foundedit) + { + drawrawstring(pos, sprintf("%s: ", editkey==""?"":editkey), '8 8 0', '0 0 1', 1); pos_y += 8; } }; -#endif diff --git a/quakec/csaddon/src/editor_lights.qc b/quakec/csaddon/src/editor_lights.qc index 1a24e6501..6528430ee 100644 --- a/quakec/csaddon/src/editor_lights.qc +++ b/quakec/csaddon/src/editor_lights.qc @@ -75,9 +75,9 @@ static string(int fld, float foredit) readfield = return ftos(selectedlight); return strcat(ftos(selectedlight), " / ", ftos(dynamiclight_get(-1f, -1f))); case 2: - return sprintf("%v", dynamiclight_get(selectedlight, LFIELD_ORIGIN)); + return sprintf("%v", (vector)dynamiclight_get(selectedlight, LFIELD_ORIGIN)); case 3: - return sprintf("%v", dynamiclight_get(selectedlight, LFIELD_COLOUR)); + return sprintf("%v", (vector)dynamiclight_get(selectedlight, LFIELD_COLOUR)); case 4: return ftos(dynamiclight_get(selectedlight, LFIELD_RADIUS)); case 5: @@ -94,7 +94,7 @@ static string(int fld, float foredit) readfield = case 6: return ftos(dynamiclight_get(selectedlight, LFIELD_STYLE)); case 7: - return sprintf("%v", dynamiclight_get(selectedlight, LFIELD_ANGLES)); + return sprintf("%v", (vector)dynamiclight_get(selectedlight, LFIELD_ANGLES)); case 8: return ftos(dynamiclight_get(selectedlight, LFIELD_FOV)); case 9: @@ -110,7 +110,7 @@ static string(int fld, float foredit) readfield = case 14: return ftos(dynamiclight_get(selectedlight, LFIELD_SPECULARSCALE)); case 15: - return sprintf("%v", dynamiclight_get(selectedlight, LFIELD_ROTATION)); + return sprintf("%v", (vector)dynamiclight_get(selectedlight, LFIELD_ROTATION)); default: return ""; } @@ -430,19 +430,16 @@ float(float keyc, float unic, vector m) editor_lights_key = } else if (unic == '[') { - o = (vector)getproperty(11); traceline(o, t, TRUE, world); - dynamiclight_set(selectedlight, LFIELD_ORIGIN, dynamiclight_get(selectedlight, LFIELD_ORIGIN) - trace_plane_normal); + dynamiclight_set(selectedlight, LFIELD_ORIGIN, (vector)dynamiclight_get(selectedlight, LFIELD_ORIGIN) - trace_plane_normal); } else if (unic == ']') { - o = (vector)getproperty(11); traceline(o, t, TRUE, world); - dynamiclight_set(selectedlight, LFIELD_ORIGIN, dynamiclight_get(selectedlight, LFIELD_ORIGIN) + trace_plane_normal); + dynamiclight_set(selectedlight, LFIELD_ORIGIN, (vector)dynamiclight_get(selectedlight, LFIELD_ORIGIN) + trace_plane_normal); } else if (unic == '\'' || unic == '@') { - o = (vector)getproperty(11); traceline(o, t, TRUE, world); dynamiclight_set(selectedlight, LFIELD_RADIUS, 1.5*vlen(trace_endpos - (vector)dynamiclight_get(selectedlight, LFIELD_ORIGIN))); } diff --git a/quakec/csaddon/src/editor_particles.qc b/quakec/csaddon/src/editor_particles.qc index 982454de1..f6057e0e0 100644 --- a/quakec/csaddon/src/editor_particles.qc +++ b/quakec/csaddon/src/editor_particles.qc @@ -200,7 +200,7 @@ static void() updateloadedparticles = } ls = le+1; } - + cureffect = -1; selecteffect(ce); }; @@ -213,6 +213,7 @@ static void(string descname) loadparticles = if (particlesfile) strunzone(particlesfile); particlesfile = (string)0; + cureffect = 0; /*load new string*/ if (descname != "") @@ -240,7 +241,7 @@ static void(string descname) saveparticle = /*only do this if there's something to save*/ if (cureffect < 0) return; - + newfile = textfield_strcat(&tf_particle); newfile = strcat(substring(particlesfile, 0, pdesc[cureffect].start), newfile, substring(particlesfile, pdesc[cureffect].end, -1)); @@ -377,6 +378,8 @@ void(vector mousepos) editor_particles_overlay = pointparticles(particleeffectnum(pdesc[cureffect].efname), trace_endpos, '0 0 -1', 1); } + + //fixme: should ONLY be done if we started dragging. trailparticles(particleeffectnum(pdesc[cureffect].efname), ptrailent, ptrailent.origin, trace_endpos); ptrailent.origin = trace_endpos; } diff --git a/quakec/csaddon/src/editor_terrain.qc b/quakec/csaddon/src/editor_terrain.qc index b2bce1d0a..e590ee8ed 100644 --- a/quakec/csaddon/src/editor_terrain.qc +++ b/quakec/csaddon/src/editor_terrain.qc @@ -52,7 +52,7 @@ static var string tint[8] = {"1 1 1", "1.2 0.9 0.9", "0 1 0"}; static string meshname; static var float curtool = ter_blank; static var float lasttool = ter_blank; -static float painttex; +static int painttex; static float mautorepeattime; static entity tempent; static var float texturesearch = -1; @@ -148,6 +148,12 @@ void(vector m, float repeated) editor_do = else terrain_edit(TEREDIT_HEIGHT_SPREAD, trace_endpos, eradius, epercent/100.0); break; + case ter_height_flatten: + if (autocvar_mod_terrain_networked && !isserver()) + sendevent("teredit", "fvff", TEREDIT_HEIGHT_FLATTEN, trace_endpos, eradius, epercent/100.0); + else + terrain_edit(TEREDIT_HEIGHT_FLATTEN, trace_endpos, eradius, epercent/100.0); + break; case ter_water_set: if (autocvar_mod_terrain_networked && !isserver()) sendevent("teredit", "fvff", TEREDIT_WATER_SET, trace_endpos, eradius, equant); @@ -441,6 +447,10 @@ void(vector mousepos) editor_terrain_add = float r; vector tx, p, col; float a; + + if (curtool == ter_tex_paint || curtool == ter_tex_paint_single || curtool == ter_tex) + terrain_edit(22, tex[painttex]); + if (mousepos_x < 128) return; @@ -503,6 +513,9 @@ void(vector mousepos) editor_terrain_overlay = float i; vector pos; vector colour; + +// if (curtool == ter_tex_paint || curtool == ter_tex_paint_single || curtool == ter_tex) + terrain_edit(22, 0); float ctime = gettime(5); if (mautorepeattime) diff --git a/quakec/csaddon/src/menu.qc b/quakec/csaddon/src/menu.qc index 0ad7bde92..790e59c41 100644 --- a/quakec/csaddon/src/menu.qc +++ b/quakec/csaddon/src/menu.qc @@ -290,7 +290,7 @@ void(string fieldname, vector pos, vector *mousepos_in, float *value, float key) void(menu_t *menu, string fieldname, vector pos, int *value) checkboxi_widgit = { - local vector rpos, epos, spos, col; + local vector epos, spos, col; local int render_only = 0; local vector mousepos = *menu->mousepos; local vector mousediff = menu->mousediff; @@ -338,7 +338,7 @@ void(menu_t *menu, string fieldname, vector pos, int *value) checkboxi_widgit = int (menu_t *menu, string fieldname, vector pos) button_widget = { - local vector rpos, epos, spos, col; + local vector epos, spos, col; local int render_only = 0; local vector mousepos = *menu->mousepos; local vector mousediff = menu->mousediff;