diff --git a/quakec/csaddon/src/cam.qc b/quakec/csaddon/src/cam.qc index f5defffa5..e686f0fb4 100644 --- a/quakec/csaddon/src/cam.qc +++ b/quakec/csaddon/src/cam.qc @@ -1,3 +1,4 @@ +#ifdef CAMQUAKE typedef struct { int numpoints; @@ -32,7 +33,7 @@ static camdata_t *camdata; static var float splinefile = -1; static var int edit_type = 0; static vector submenu_position; -static var float autocvar_cl_demospeed = 1; +var float autocvar_cl_demospeed = 1; void(int *dest, int bytes) memclr = { @@ -481,11 +482,10 @@ void(float attime) spline_overrides = void() editor_spline_add = { local int i; - local float ctime, stime; + local float ctime; + //local float stime; - local vector tdir; - tdir_x = 16 * cos(time); - tdir_y = 16 * sin(time); + local vector tdir = [16 * cos(time), 16 * sin(time)]; // drawlame3dtext("charset", '300 -500 50', tdir, '0 0 -16', "hello jogi\ndoes this string appear for you?", '1 1 1', 1); // drawlame3dtext("charset", '300 -500 50', -tdir, '0 0 -16', "hello jogi\ndoes this string appear for you?", '1 1 1', 1); @@ -503,8 +503,8 @@ void() editor_spline_add = /*sort out the overrides*/ // spline_overrides(simtime); }; -float slidertestfloat1, slidertestfloat2, slidertestfloat3; -static float *curslider; +//float slidertestfloat1, slidertestfloat2, slidertestfloat3; +//static float *curslider; static menu_t menu; void (vector *curmousepos, vector mousediff, float key) submenu = @@ -665,7 +665,7 @@ int (vector *curmousepos, vector mousediff, float key) testmenu = int (vector *curmousepos, vector mousediff) editor_spline_overlay = { local vector pos, v; - local spline_t *s; +// local spline_t *s; local int ri; local string str; local float minv, sec; @@ -1119,8 +1119,10 @@ float(string str) CSQC_ConsoleCommand = } return FALSE; }; + void() Cam_Init = { registercommand("examplecommandforjogi"); // registercommand("crashandcatchfire"); }; +#endif diff --git a/quakec/csaddon/src/csaddon.qc b/quakec/csaddon/src/csaddon.qc index 9dbbd8a9e..1420722fb 100644 --- a/quakec/csaddon/src/csaddon.qc +++ b/quakec/csaddon/src/csaddon.qc @@ -1,17 +1,3 @@ -enum -{ - MODE_INITIAL=0, - - MODE_LIGHTEDIT=1, - MODE_SPLINEEDIT=2, - MODE_TERRAINEDIT=3, - MODE_PARTICLEEDIT=4, - MODE_ENTSEDIT=5, - - MODE_COUNT=5 -}; - -string editornames[] = {"LIGHTS", "SPLINES", "TERRAIN", "PARTICLES", "ENTITIES"}; float autocvar_ca_show; var float autocvar_ca_editormode = MODE_LIGHTEDIT; @@ -23,6 +9,7 @@ vector mousediff; vector originalmousepos; float mousedown; float shiftdown; +float ctrldown; vector mousenear; vector mousefar; @@ -31,10 +18,12 @@ vector pointedsurfacenormal; entity pointedent; float pointedsurface; +float editorrsd[editornames.length]; + /*the renderscene builtin in the parent progs is redirected to here*/ void() wrap_renderscene = { - local int retval; + float x; vector col; local float i; @@ -53,8 +42,11 @@ void() wrap_renderscene = mousefar = unproject((vidsize*0.5) + '0 0 8192'); mousenear = unproject(vidsize*0.5); +#ifdef CAMQUAKE if (isdemo()) spline_overrides(gettime(5)); +#endif + renderscene(); if (autocvar_ca_colourtint) @@ -116,46 +108,65 @@ void() wrap_renderscene = setproperty(VF_DRAWENGINESBAR, 0); setproperty(VF_DRAWCROSSHAIR, 0); - mousefar = unproject(curmousepos + '0 0 8192'); - mousenear = unproject(curmousepos); + vector mn = (vector)getproperty(VF_MIN); + vector sz = (vector)getproperty(VF_SIZE); + if (curmousepos_x >= mn_x && curmousepos_x <= mn_x+sz_x && curmousepos_y >= mn_y && curmousepos_y <= mn_y+sz_y) + { + mousefar = unproject(curmousepos + '0 0 8192'); + mousenear = unproject(curmousepos); + } if (autocvar_ca_editormode == MODE_LIGHTEDIT) editor_lights_add(); else if (autocvar_ca_editormode == MODE_ENTSEDIT) editor_ents_add(); +#ifdef CAMQUAKE else if (autocvar_ca_editormode == MODE_SPLINEEDIT) editor_spline_add(); +#endif else if (autocvar_ca_editormode == MODE_TERRAINEDIT) editor_terrain_add(curmousepos); + else if (autocvar_ca_editormode == MODE_BRUSHEDIT) + editor_brushes_add(curmousepos); renderscene(); - for (i = 0; i < MODE_COUNT; i+=1) + for (i = 0, x = 0; i < editornames.length; i+=1) { - if (autocvar_ca_editormode == i+1) - col = '1 0 0'; - else if (curmousepos_y < 8 && curmousepos_x >= 64*(i) && curmousepos_x < 64*(i+1)) - col = '0 0 1'; - else - col = '1 1 1'; - drawrawstring('64 0 0'*i, editornames[i], '8 8 0', col, 1); + if (editornames[i]) + { + float w = stringwidth(editornames[i], TRUE, '8 8 0') + 8; + if (autocvar_ca_editormode == i) + col = '1 0 0'; + else if (curmousepos_y < 8 && curmousepos_x >= x && curmousepos_x < x+w) + col = '0 0 1'; + else + col = '1 1 1'; + drawrawstring([x,0], editornames[i], '8 8 0', col, 1); + x += w; + } + editorrsd[i] = x; } if (autocvar_ca_editormode == MODE_LIGHTEDIT) editor_lights_overlay(curmousepos); +#ifdef CAMQUAKE else if (autocvar_ca_editormode == MODE_SPLINEEDIT) { editor_spline_overlay(&curmousepos, mousediff); originalmousepos = curmousepos; mousediff = '0 0 0'; } +#endif else if (autocvar_ca_editormode == MODE_PARTICLEEDIT) editor_particles_overlay(curmousepos); else if (autocvar_ca_editormode == MODE_TERRAINEDIT) editor_terrain_overlay(curmousepos); else if (autocvar_ca_editormode == MODE_ENTSEDIT) editor_ents_overlay(curmousepos); + else if (autocvar_ca_editormode == MODE_BRUSHEDIT) + editor_brushes_overlay(curmousepos); drawcharacter(curmousepos - '4 4', '+', '8 8', '1 1 1', 1); }; @@ -163,13 +174,28 @@ void() wrap_renderscene = var float(float,float,float) orig_input_event = __NULL__; float (float event, float parama, float paramb) wrap_InputEvent = { + if (event == IE_KEYDOWN || event == IE_KEYUP) + { + if (parama == K_RSHIFT || parama == K_LSHIFT) + shiftdown = (event == IE_KEYDOWN); + if (parama == K_RCTRL || parama == K_LCTRL) + ctrldown = (event == IE_KEYDOWN); + } if (autocvar_ca_show) { - if (event == 0) + if (event == IE_KEYDOWN) { - if (parama == 512 && curmousepos_y < 8) + if (parama == K_MOUSE1 && curmousepos_y < 8) { - cvar_set("ca_editormode", ftos(floor(curmousepos_x / 64)+1)); + float i, nm = 0; + for (i = editorrsd.length-1; i >= 0; i--) + { + if (curmousepos_x < editorrsd[i]) + nm = i; + else + break; + } + cvar_set("ca_editormode", ftos(nm)); return TRUE; } if (autocvar_ca_editormode == MODE_LIGHTEDIT) @@ -177,6 +203,7 @@ float (float event, float parama, float paramb) wrap_InputEvent = if (editor_lights_key(parama, paramb, curmousepos)) return TRUE; } +#ifdef CAMQUAKE else if (autocvar_ca_editormode == MODE_SPLINEEDIT) { if (editor_spline_key(parama, paramb, &curmousepos, mousediff)) @@ -186,6 +213,7 @@ float (float event, float parama, float paramb) wrap_InputEvent = return TRUE; } } +#endif else if (autocvar_ca_editormode == MODE_PARTICLEEDIT) { if (editor_particles_key(parama, paramb, curmousepos)) @@ -196,23 +224,33 @@ float (float event, float parama, float paramb) wrap_InputEvent = if (editor_terrain_key(parama, paramb, curmousepos)) return TRUE; } - if (parama == 512) + else if (autocvar_ca_editormode == MODE_BRUSHEDIT) + { + if (editor_brushes_key(parama, paramb, curmousepos)) + return TRUE; + } + else if (autocvar_ca_editormode == MODE_ENTSEDIT) + { + if (editor_ents_key(parama, paramb, curmousepos)) + return TRUE; + } + if (parama == K_MOUSE1) { mousedown = 1; return TRUE; } - if (parama == 513) + if (parama == K_MOUSE2) { mousedown = 2; return TRUE; } } - else if (event == 1) + else if (event == IE_KEYUP) { - if (parama == 511+mousedown) + if (parama == K_MOUSE1-1+mousedown) mousedown = FALSE; } - else if (event == 2) + else if (event == IE_MOUSEDELTA) { if (mousedown == 2) return FALSE; @@ -230,7 +268,7 @@ float (float event, float parama, float paramb) wrap_InputEvent = curmousepos_y = vidsize_y; return TRUE; } - else if (event == 3) + else if (event == IE_MOUSEABS) { curmousepos_x = parama; curmousepos_y = paramb; @@ -241,7 +279,7 @@ float (float event, float parama, float paramb) wrap_InputEvent = } else if (pointedshadername != "") { -/* if (parama == 513) +/* if (parama == K_MOUSE2) { strunzone(pointedshadername); pointedshadername = ""; @@ -249,7 +287,7 @@ float (float event, float parama, float paramb) wrap_InputEvent = return TRUE; } */ - if (event == 0 || event == 1) + if (event == IE_KEYDOWN || event == IE_KEYUP) { gecko_keyevent(pointedshadername, parama, event); if (event == 0) @@ -274,16 +312,68 @@ void(float mask) wrap_addentities = addentities(mask); }; - +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) + setproperty(VF_VIEWENTITY, player_localentnum); + if (!intermission) + wrap_addentities(MASK_VIEWMODEL); + } + setproperty(VF_VIEWPORT, mn, sz); + setproperty(VF_AFOV, autocvar_fov); + wrap_renderscene(); +}; /*this is a fallback function, in case the main progs does not have one*/ void(float width, float height, float do2d) CSQC_UpdateView = { + float foo; clearscene(); + setproperty(VF_ACTIVESEAT, 0, TRUE); //in_forceseat can prevent this from already being true, sadly. silly csqc mods with no splitscreen support... setproperty(VF_DRAWENGINESBAR, 1); setproperty(VF_DRAWCROSSHAIR, 1); wrap_addentities(intermission?MASK_ENGINE:(MASK_VIEWMODEL|MASK_ENGINE)); - wrap_renderscene(); + + foo = numclientseats; +// if (autocvar_ca_show) +// foo = 0; + switch(foo) + { + case 0: //just in case someone runs us in dp + case 1: + wrap_renderscene(); + break; + case 2: + renderscene_helper(0, [0, 0], [width, height/2]); + renderscene_helper(1, [0, height/2], [width, height/2]); + setviewprop(VF_LPLAYER, 0, FALSE); + drawline(1, [0, height/2], [width, height/2], '0 0 0', 1); + break; + case 3: + renderscene_helper(0, [0, 0], [width, height/2]); + renderscene_helper(1, [0, height/2], [width/2, height/2]); + renderscene_helper(2, [width/2, height/2], [width/2, height/2]); + setviewprop(VF_LPLAYER, 0, FALSE); + drawline(1, [0, height/2], [width, height/2], '0 0 0', 1); + drawline(1, [width/2, height/2], [width/2, height], '0 0 0', 1); + break; + default: + case 4: + renderscene_helper(0, [0, 0], [width/2, height/2]); + renderscene_helper(1, [width/2, 0], [width/2, height/2]); + renderscene_helper(2, [0, height/2], [width/2, height/2]); + renderscene_helper(3, [width/2, height/2], [width/2, height/2]); + setviewprop(VF_LPLAYER, 0, FALSE); + drawline(1, [0, height/2], [width, height/2], '0 0 0', 1); + drawline(1, [width/2, 0], [width/2, height], '0 0 0', 1); + break; + } }; void() CSQC_Input_Frame = @@ -308,6 +398,7 @@ void() CSQC_Input_Frame = pointedshadername = ""; else { + //fixme: no unfocus! pointedshadername = strzone(pointedshadername); //print("sending input to ", pointedshadername, "\n"); @@ -372,7 +463,7 @@ void() CSQC_Input_Frame = float d1 = dir1*xyz1; float p = dir1*trace_endpos; float d2 = dir1*(xyz1+dir1); - float f1 = (p-d1) / (d2 - d1); + f1 = (p-d1) / (d2 - d1); d1 = dir2*xyz1; p = dir2*trace_endpos; @@ -408,7 +499,11 @@ float (float event, float parama, float paramb, float devid) CSQC_InputEvent = void(float prevprogs) init = { +#ifdef CAMQUAKE Cam_Init(); +#else + editornames[MODE_SPLINEEDIT] = 0; +#endif if (prevprogs >= 0) { @@ -417,7 +512,7 @@ void(float prevprogs) init = externset(0, wrap_addentities, "addentities"); /*wrap the parent's input event function*/ - orig_input_event = externvalue(0, "CSQC_InputEvent"); + orig_input_event = (float(float, float, float))externvalue(0, "CSQC_InputEvent"); externset(0, wrap_InputEvent, "CSQC_InputEvent"); } csfixups(); @@ -430,6 +525,8 @@ void(float prevprogs) init = void() CSQC_Shutdown = { +#ifdef CAMQUAKE spline_shutdown(); +#endif }; diff --git a/quakec/csaddon/src/csaddon.src b/quakec/csaddon/src/csaddon.src index 4eb8c7b0c..8565fda97 100644 --- a/quakec/csaddon/src/csaddon.src +++ b/quakec/csaddon/src/csaddon.src @@ -1,4 +1,4 @@ -../csaddon.dat +../csaddon.dat //pr_dumpplatform -FFTE -Fdefines -TCS -O csplat opts.qc csplat.qc @@ -6,6 +6,7 @@ csfixups.qc editor_lights.qc editor_terrain.qc +editor_brushes.qc editor_ents.qc textfield.qc editor_particles.qc diff --git a/quakec/csaddon/src/csfixups.qc b/quakec/csaddon/src/csfixups.qc index f4026b4fa..f894ed7a8 100644 --- a/quakec/csaddon/src/csfixups.qc +++ b/quakec/csaddon/src/csfixups.qc @@ -1,3 +1,8 @@ +//addons cannot use certain optimisations - the engine MUST be able to find+fix their string+function indexes. +#pragma optimise addon //disables any optimisations that would break addons. + +//#define CAMQUAKE + //with addons, the engine only tracks one set of globals for most things. //thus we use pointers in the addon to refer to that set of globals. //this avoids having to make things shared, and having to pay for every transition. @@ -21,3 +26,20 @@ void() csfixups = vector mousenear; vector mousefar; +float mousedown; + + + +enum +{ + MODE_INITIAL=0, + + MODE_LIGHTEDIT=1, + MODE_SPLINEEDIT=2, + MODE_TERRAINEDIT=3, + MODE_BRUSHEDIT=4, + MODE_PARTICLEEDIT=5, + MODE_ENTSEDIT=6, +}; + +var string editornames[] = {"","LIGHTS","SPLINES", "TERRAIN", "BRUSHES", "PARTICLES", "ENTITIES"}; diff --git a/quakec/csaddon/src/csplat.qc b/quakec/csaddon/src/csplat.qc index bc57539a6..f721b9a8f 100644 --- a/quakec/csaddon/src/csplat.qc +++ b/quakec/csaddon/src/csplat.qc @@ -1,7 +1,7 @@ /* This file was automatically generated by FTE QuakeWorld v1.03 This file can be regenerated by issuing the following command: -pr_dumpplatform -FFTE -Fdefines -TCS -O csplat +pr_dumpplatform -FFTE -Fdefines -TCS -O csplat -Faccessors Available options: -Ffte - target only FTE (optimations and additional extensions) -Tnq - dump specifically NQ fields @@ -22,11 +22,187 @@ Available options: #ifndef CSQC #define CSQC #endif -#define strbuf float -#define searchhandle float -#define hashtable float -#define infostring string -#define filestream float +#define FTE_PEXT_SETVIEW /* NQ's svc_setview works correctly even in quakeworld */ +#define DP_ENT_SCALE +#define FTE_PEXT_LIGHTSTYLECOL +#define DP_ENT_ALPHA +#define FTE_PEXT_VIEW2 +#define FTE_PEXT_ACURATETIMINGS +#define FTE_PEXT_SOUNDDBL +#define FTE_PEXT_FATNESS +#define DP_HALFLIFE_MAP +#define FTE_PEXT_TE_BULLET +#define FTE_PEXT_HULLSIZE +#define FTE_PEXT_MODELDBL +#define FTE_PEXT_ENTITYDBL +#define FTE_PEXT_ENTITYDBL2 +#define FTE_PEXT_FLOATCOORDS +#define FTE_PEXT_VWEAP +#define FTE_PEXT_Q2BSP +#define FTE_PEXT_Q3BSP +#define DP_ENT_COLORMOD +#define FTE_HEXEN2 +#define FTE_PEXT_SPAWNSTATIC +#define FTE_PEXT_CUSTOMTENTS +#define FTE_PEXT_256PACKETENTITIES +#define TEI_SHOWLMP2 +#define DP_GFX_QUAKE3MODELTAGS +#define FTE_PK3DOWNLOADS +#define PEXT_CHUNKEDDOWNLOADS +#define EXT_CSQC_SHARED +#define PEXT_DPFLAGS +#define EXT_CSQC +#define BX_COLOREDTEXT +#define DP_CON_SET /* The 'set' console command exists, and can be used to create/set cvars. */ +#define DP_CON_SETA /* The 'seta' console command exists, like the 'set' command, but also marks the cvar for archiving, allowing it to be written into the user's config. Use this command in your default.cfg file. */ +#define DP_EF_BLUE +#define DP_EF_FULLBRIGHT +#define DP_EF_NODRAW +#define DP_EF_RED +#define DP_ENT_CUSTOMCOLORMAP +#define DP_ENT_EXTERIORMODELTOCLIENT +#define DP_ENT_VIEWMODEL +#define DP_GECKO_SUPPORT +#define DP_GFX_SKINFILES +#define DP_GFX_SKYBOX +#define DP_HALFLIFE_MAP_CVAR +#define DP_INPUTBUTTONS +#define DP_LITSUPPORT +#define DP_MD3_TAGSINFO +#define DP_MONSTERWALK /* MOVETYPE_WALK is valid on non-player entities. Note that only players receive acceleration etc in line with none/bounce/fly/noclip movetypes on the player, thus you will have to provide your own accelerations (incluing gravity) yourself. */ +#define DP_MOVETYPEBOUNCEMISSILE +#define DP_MOVETYPEFOLLOW +#define DP_QC_ASINACOSATANATAN2TAN +#define DP_QC_CHANGEPITCH +#define DP_QC_COPYENTITY +#define DP_QC_CRC16 +#define DP_QC_CVAR_DEFSTRING +#define DP_QC_CVAR_STRING +#define DP_QC_CVAR_TYPE +#define DP_QC_EDICT_NUM +#define DP_QC_ENTITYDATA +#define DP_QC_ETOS +#define DP_QC_FINDCHAIN +#define DP_QC_FINDCHAINFLOAT +#define DP_QC_FINDFLAGS +#define DP_QC_FINDCHAINFLAGS +#define DP_QC_FINDFLOAT +#define DP_QC_FS_SEARCH +#define DP_QC_GETSURFACE +#define DP_QC_GETSURFACEPOINTATTRIBUTE +#define DP_QC_MINMAXBOUND +#define DP_QC_MULTIPLETEMPSTRINGS /* Superseded by DP_QC_UNLIMITEDTEMPSTRINGS. Functions that return a temporary string will not overwrite/destroy previous temporary strings until at least 16 strings are returned (or control returns to the engine). */ +#define DP_QC_RANDOMVEC +#define DP_QC_RENDER_SCENE +#define DP_QC_SINCOSSQRTPOW +#define DP_QC_STRFTIME +#define DP_QC_STRING_CASE_FUNCTIONS +#define DP_QC_STRINGBUFFERS +#define DP_QC_STRINGCOLORFUNCTIONS +#define DP_QC_STRREPLACE +#define DP_QC_TOKENIZEBYSEPARATOR +#define DP_QC_TRACEBOX +#define DP_QC_TRACETOSS +#define DP_QC_TRACE_MOVETYPE_HITMODEL +#define DP_QC_TRACE_MOVETYPE_WORLDONLY +#define DP_QC_TRACE_MOVETYPES +#define DP_QC_UNLIMITEDTEMPSTRINGS /* Supersedes DP_QC_MULTIPLETEMPSTRINGS, superseded by FTE_QC_PERSISTENTTEMPSTRINGS. All temp strings will be valid at least until the QCVM returns. */ +#define DP_QC_URI_ESCAPE +#define DP_QC_URI_GET +#define DP_QC_URI_POST +#define DP_QC_VECTOANGLES_WITH_ROLL +#define DP_QC_VECTORVECTORS +#define DP_QC_WHICHPACK +#define DP_QUAKE2_MODEL +#define DP_QUAKE2_SPRITE +#define DP_QUAKE3_MODEL +#define DP_REGISTERCVAR +#define DP_SND_STEREOWAV +#define DP_SND_OGGVORBIS +#define DP_SOLIDCORPSE +#define DP_SPRITE32 +#define DP_SV_BOTCLIENT +#define DP_SV_CLIENTCOLORS +#define DP_SV_CLIENTNAME +#define DP_SV_DRAWONLYTOCLIENT +#define DP_SV_DROPCLIENT +#define DP_SV_EFFECT +#define DP_SV_EXTERIORMODELFORCLIENT +#define DP_SV_NODRAWTOCLIENT +#define DP_SV_PLAYERPHYSICS +#define DP_SV_POINTSOUND +#define DP_SV_PRECACHEANYTIME +#define DP_SV_SETCOLOR +#define DP_SV_SPAWNFUNC_PREFIX +#define DP_SV_WRITEPICTURE +#define DP_SV_WRITEUNTERMINATEDSTRING +#define DP_TE_BLOOD +#define DP_TE_BLOODSHOWER +#define DP_TE_CUSTOMFLASH +#define DP_TE_EXPLOSIONRGB +#define DP_TE_PARTICLECUBE +#define DP_TE_SMALLFLASH +#define DP_TE_SPARK +#define DP_TE_STANDARDEFFECTBUILTINS +#define DP_VIEWZOOM +#define EXT_BITSHIFT +#define EXT_DIMENSION_VISIBILITY +#define EXT_DIMENSION_PHYSICS +#define EXT_DIMENSION_GHOST +#define FRIK_FILE +#define FTE_CALLTIMEOFDAY +#define FTE_CSQC_ALTCONSOLES_WIP +#define FTE_CSQC_BASEFRAME /* Specifies that .basebone, .baseframe, .baselerpfrac, etc exist. These fields affect all bones in the entity's model with a lower index than the .basebone field, allowing you to give separate control to the legs of a skeletal model, without affecting the torso animations. */ +#define FTE_CSQC_HALFLIFE_MODELS +#define FTE_CSQC_SERVERBROWSER +#define FTE_CSQC_SKELETONOBJECTS +#define FTE_CSQC_RENDERTARGETS_WIP /* VF_DESTCOLOUR etc exist and are supported */ +#define FTE_ENT_SKIN_CONTENTS +#define FTE_ENT_UNIQUESPAWNID +#define FTE_EXTENDEDTEXTCODES +#define FTE_FORCESHADER +#define FTE_FORCEINFOKEY +#define FTE_GFX_QUAKE3SHADERS +#define FTE_ISBACKBUFFERED /* Allows you to check if a client has too many reliable messages pending. */ +#define FTE_MEMALLOC /* Allows dynamically allocating memory. Use pointers to access this memory. Memory will not be saved into saved games. */ +#define FTE_MEDIA_AVI +#define FTE_MEDIA_CIN +#define FTE_MEDIA_ROQ +#define FTE_MULTIPROGS /* Multiple progs.dat files can be loaded inside the same qcvm. */ +#define FTE_MULTITHREADED +#define FTE_NPCCHAT +#define FTE_QC_CHECKCOMMAND +#define FTE_QC_CHECKPVS +#define FTE_QC_HASHTABLES +#define FTE_QC_INTCONV +#define FTE_QC_MATCHCLIENTNAME +#define FTE_QC_PAUSED +#define FTE_QC_PERSISTENTTEMPSTRINGS /* Supersedes DP_QC_MULTIPLETEMPSTRINGS. Temp strings are garbage collected automatically, and do not expire while they're still in use. This makes strzone redundant. */ +#define FTE_QC_RAGDOLL_WIP +#define FTE_QC_SENDPACKET +#define FTE_QC_TRACETRIGGER +#define FTE_SOLID_LADDER +#define FTE_SQL +#define FTE_STRINGS +#define FTE_SV_REENTER +#define FTE_TE_STANDARDEFFECTBUILTINS +#define KRIMZON_SV_PARSECLIENTCOMMAND +#define NEH_CMD_PLAY2 +#define NEH_RESTOREGAME +#define QSG_CVARSTRING +#define QW_ENGINE +#define QWE_MVD_RECORD +#define TEI_MD3_MODEL +#define ZQ_MOVETYPE_FLY +#define ZQ_MOVETYPE_NOCLIP +#define ZQ_MOVETYPE_NONE +#define ZQ_VWEP +#define ZQ_QC_STRINGS +accessor strbuf : float; +accessor searchhandle : float; +accessor hashtable : float; +accessor infostring : string; +accessor filestream : float; entity self; /* The magic me */ entity other; /* Valid in touch functions, this is the entity that we touched. */ entity world; /* The null entity. Hurrah. Readonly after map spawn time. */ @@ -41,7 +217,7 @@ float servercommandframe; /* This is the input-frame that was last acknowledged string mapname; /* The short name of the map. */ float intermission; vector v_forward, v_up, v_right; -vector view_angles; +vector view_angles; /* +x=DOWN */ float trace_allsolid, trace_startsolid, trace_fraction; vector trace_endpos, trace_plane_normal; float trace_plane_dist; @@ -49,48 +225,52 @@ entity trace_ent; float trace_inopen; float trace_inwater; float input_timelength; -vector input_angles; +vector input_angles; /* +x=DOWN */ vector input_movevalues; float input_buttons; float input_impulse; void end_sys_globals; -.float modelindex; -.vector absmin; -.vector absmax; +.float modelindex; /* This is the model precache index for the model that was set on the entity, instead of having to look up the model according to the .model field. Use setmodel to change it. */ +.vector absmin; /* Set by the engine when the entity is relinked (by setorigin, setsize, or setmodel). This is in world coordinates. */ +.vector absmax; /* Set by the engine when the entity is relinked (by setorigin, setsize, or setmodel). This is in world coordinates. */ .float entnum; /* The entity number as its known on the server. */ .float drawmask; /* Acts as a filter in the addentities call. */ .float() predraw; /* Called by addentities after the filter and before the entity is actually drawn. Do your interpolation and animation in here. Should return one of the PREDRAW_* constants. */ -.float movetype; -.float solid; -.vector origin; -.vector oldorigin; -.vector velocity; -.vector angles; -.vector avelocity; +.float movetype; /* Describes how the entity moves. One of the MOVETYPE_ constants. */ +.float solid; /* Describes whether the entity is solid or not, and any special properties infered by that. Must be one of the SOLID_ constants */ +.vector origin; /* The current location of the entity in world space. Inline bsp entities (ie: ones placed by a mapper) will typically have a value of '0 0 0' in their neutral pose, as the geometry is offset from that. It is the reference point of the entity rather than the center of its geometry, for non-bsp models, this is often not a significant distinction. */ +.vector oldorigin; /* This is often used on players to reset the player back to where they were last frame if they somehow got stuck inside something due to fpu precision. Never change a player's oldorigin field to inside a solid, because that might cause them to become pemanently stuck. */ +.vector velocity; /* The direction and speed that the entity is moving in world space. */ +.vector angles; /* The eular angles the entity is facing in, in pitch, yaw, roll order. Due to a legacy bug, mdl/iqm/etc formats use +x=UP, bsp/spr/etc formats use +x=DOWN. */ +.vector avelocity; /* The amount the entity's angles change by each frame. Note that this is direct eular angles, and thus the angular change is non-linear and often just looks buggy. */ .float pmove_flags; -.string classname; +.string classname; /* Identifies the class/type of the entity. Useful for debugging, also used for loading, but its value is not otherwise significant to the engine, this leaves the mod free to set it to whatever it wants and randomly test strings for values in whatever inefficient way it chooses fit. */ .float renderflags; -.string model; -.float frame; +.string model; /* The model name that was set via setmodel, in theory. Often, this is cleared to null to prevent the engine from being seen by clients while not changing modelindex. This behaviour allows inline models to remain solid yet be invisible. */ +.float frame; /* The current frame the entity is meant to be displayed in. In CSQC, note the lerpfrac and frame2 fields as well. if it specifies a framegroup, the framegroup will autoanimate in ssqc, but not in csqc. */ .float frame1time; /* The absolute time into the animation/framegroup specified by .frame. */ -.float frame2; +.float frame2; /* The alternative frame. Visible only when lerpfrac is set to 1. */ .float frame2time; /* The absolute time into the animation/framegroup specified by .frame2. */ .float lerpfrac; /* If 0, use frame1 only. If 1, use frame2 only. Mix them together for values between. */ -.float skin; -.float effects; -.vector mins; -.vector maxs; -.vector size; +.float skin; /* The skin index to use. on a bsp entity, setting this to 1 will switch to the 'activated' texture instead. A negative value will be understood as a replacement contents value, so setting it to CONTENTS_WATER will make a movable pool of water. */ +.float effects; /* Lots of random flags that change random effects. See EF_* constants. */ +.vector mins; /* The minimum extent of the model (ie: the bottom-left coordinate relative to the entity's origin). Change via setsize. May also be changed by setmodel. */ +.vector maxs; /* like mins, but in the other direction. */ +.vector size; /* maxs-mins. Updated when the entity is relinked (by setorigin, setsize, setmodel) */ .void() touch; .void() think; .void() blocked; -.float nextthink; +.float nextthink; /* The time at which the entity is next scheduled to fire its think event. For MOVETYPE_PUSH entities, this is relative to that entity's ltime field, for all other entities it is relative to the time gloal. */ .entity chain; .entity enemy; .float flags; .float colormap; .entity owner; void end_sys_fields; +vector input_cursor_screen; +vector input_cursor_trace_start; +vector input_cursor_trace_endpos; +float input_cursor_trace_entnum; .vector punchangle; .float gravity; .float hull; /* Overrides the hull used by the entity for walkmove/movetogoal and not traceline/tracebox. */ @@ -121,6 +301,10 @@ void end_sys_fields; .float bouncestop; .float idealpitch; .float pitch_speed; +.float frame3; /* Some people just don't understand how to use framegroups... */ +.float frame4; +.float lerpfrac3; +.float lerpfrac4; .float forceshader; /* Contains a shader handle used to replace all surfaces upon the entity. */ .float baseframe; /* See basebone */ .float baseframe2; /* See basebone */ @@ -140,8 +324,12 @@ noref void(float apilevel, string enginename, float engineversion) CSQC_Init; /* 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. +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. */ @@ -154,6 +342,8 @@ noref float(float entnum, float channel, string soundname, float vol, float atte 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 1: DP-compat. Thinks occur, but not true movetypes. 2: movetypes occur just as they do in ssqc. */ @@ -255,6 +445,18 @@ float drawfont; /* Allows you to choose exactly which font is to be used to draw #define INFOKEY_P_PACKETLOSS "pl" /* Reads a player's packetloss, as a percentage. */ #define INFOKEY_P_VOIPSPEAKING "voipspeaking" /* Boolean value that says whether the given player is currently sending voice information. */ #define INFOKEY_P_VOIPLOUDNESS "voiploudness" /* Only valid for the local player. Gives a value between 0 and 1 to indicate to the user how loud their mic is. */ +#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_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. */ +#define SERVERKEY_PROTOCOL "protocol" /* The protocol we are connected to the server with. */ #define FL_FLY 1 #define FL_SWIM 2 #define FL_CLIENT 8 @@ -307,30 +509,32 @@ float drawfont; /* Allows you to choose exactly which font is to be used to draw #define EV_FLOAT 2 #define EV_VECTOR 3 #define EV_ENTITY 4 +#define EV_FIELD 5 #define EV_FUNCTION 6 #define EV_POINTER 7 #define EV_INTEGER 8 #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 STAT_HEALTH 0 -#define STAT_WEAPON 2 -#define STAT_AMMO 3 +#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 */ #define STAT_ARMOR 4 #define STAT_WEAPONFRAME 5 #define STAT_SHELLS 6 #define STAT_NAILS 7 #define STAT_ROCKETS 8 #define STAT_CELLS 9 -#define STAT_ACTIVEWEAPON 10 +#define STAT_ACTIVEWEAPON 10 /* player.weapon */ #define STAT_TOTALSECRETS 11 #define STAT_TOTALMONSTERS 12 #define STAT_FOUNDSECRETS 13 #define STAT_KILLEDMONSTERS 14 -#define STAT_ITEMS 15 -#define STAT_VIEWHEIGHT 16 +#define STAT_ITEMS 15 /* self.items | (self.items2<<23). In order to decode this stat properly, you need to use getstatbits(STAT_ITEMS,0,23) to read self.items, and getstatbits(STAT_ITEMS,23,11) to read self.items2 or getstatbits(STAT_ITEMS,28,4) to read the visible part of serverflags, whichever is applicable. */ +#define STAT_VIEWHEIGHT 16 /* player.view_ofs_z */ #define STAT_VIEW2 20 /* This stat contains the number of the entity in the server's .view2 field. */ -#define STAT_VIEWZOOM 21 +#define STAT_VIEWZOOM 21 /* Scales fov and sensitiity. Part of DP_VIEWZOOM. */ +#define STAT_USER 32 /* Custom user stats start here (lower values are reserved for engine use). */ #define VF_MIN 1 /* The top-left of the 3d viewport in screenspace. The VF_ values are used via the setviewprop/getviewprop builtins. */ #define VF_MIN_X 2 #define VF_MIN_Y 3 @@ -407,6 +611,9 @@ Note that any rendertarget textures may be destroyed on video mode changes or so #define LFIELD_DIFFUSESCALE 11 #define LFIELD_SPECULARSCALE 12 #define LFIELD_ROTATION 13 +#define LFIELD_DIETIME 14 +#define LFIELD_RGBDECAY 15 +#define LFIELD_RADIUSDECAY 16 #define LFLAG_NORMALMODE 1 #define LFLAG_REALTIMEMODE 2 #define LFLAG_LIGHTMAP 4 @@ -455,7 +662,7 @@ Note that any rendertarget textures may be destroyed on video mode changes or so #define SLIST_TEST_STARTSWITH 8 #define SLIST_TEST_NOTSTARTSWITH 9 void(vector vang) makevectors = #1; /* - Takes an angle vector (pitch,yaw,roll). Writes its results into v_forward, v_right, v_up vectors. */ + Takes an angle vector (pitch,yaw,roll) (+x=DOWN). Writes its results into v_forward, v_right, v_up vectors. */ void(entity e, vector o) setorigin = #2; /* Changes e's origin to be equal to o. Also relinks collision state (as well as setting absmin+absmax), which is required after changing .solid */ @@ -593,7 +800,7 @@ void() changeyaw = #49; /* Changes the self.angles_y field towards self.ideal_yaw by up to self.yawspeed. */ vector(vector fwd, optional vector up) vectoangles = #51; /* - Returns the angles 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. */ + 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) cos = #61; /* Part of DP_QC_SINCOSSQRTPOW*/ @@ -623,7 +830,7 @@ vector() randomvec = #91; /* Part of DP_QC_RANDOMVEC Returns a vector with random values. Each axis is independantly a value between -1 and 1 inclusive. */ vector(vector org) getlight = #92; -void(string cvarname, string defaultvalue) registercvar = #93; /* Part of DP_REGISTERCVAR +float(string cvarname, string defaultvalue) registercvar = #93; /* Part of DP_REGISTERCVAR Creates a new cvar on the fly. If it does not already exist, it will be given the specified value. If it does exist, this is a no-op. This builtin has the limitation that it does not apply to configs or commandlines. Such configs will need to use the set or seta command causing this builtin to be a noop. In engines that support it, you will generally find the autocvar feature easier and more efficient to use. */ @@ -655,8 +862,12 @@ 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 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*/ -void(string s) strunzone = #119; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/ +string(string s, ...) strzone = #118; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS + Create a semi-permanent copy of a string that only becomes invalid once strunzone is called on the string (instead of when the engine assumes your string has left scope). This builtin has become redundant in FTEQW due to the FTE_QC_PERSISTENTTEMPSTRINGS extension and is now functionally identical to strcat for compatibility with old engines+mods. */ + +void(string s) strunzone = #119; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS + Destroys a string that was allocated by strunzone. Further references to the string MAY crash the game. In FTE, this function became redundant and now does nothing. */ + float(string modelname, optional float queryonly) getmodelindex = #200; /* Acts as an alternative to precache_model(foo);setmodel(bar, foo); return bar.modelindex; If queryonly is set and the model was not previously precached, the builtin will return 0 without needlessly precaching the model. */ @@ -683,9 +894,6 @@ void(float prnum, __variant newval, string varname) externset = #204; /* Part of prnum=-1 means current progs. prnum=-2 will scan through the active progs and will use the first it finds. */ -float(string input, string token) instr = #206; /* Part of FTE_MULTIPROGS - Returns substring(input, strstrpos(input, token), -1), or the null string if token was not found in input. You're probably better off using strstrpos. */ - void(entity portal, float state) openportal = #207; /* Opens or closes the portals associated with a door or some such on q2 or q3 maps. On Q2BSPs, the entity should be the 'func_areaportal' entity - its style field will say which portal to open. On Q3BSPs, the entity is the door itself, the portal will be determined by the two areas found from a preceding setorigin call. */ @@ -823,8 +1031,36 @@ float(float modidx, float framenum) frameduration = #277; /* Part of FTE_CSQC_SK void(float action, optional vector pos, optional float radius, optional float quant, ...) terrain_edit = #278; /* Realtime terrain editing. Actions are the TEREDIT_ constants. */ -void() touchtriggers = #279; /* - Triggers a touch events between self and every entity that it is in contact with. This should typically just be the triggers touch functions. */ +typedef struct +{ + string shadername; + vector planenormal; + float planedist; + vector sdir; + float sbias; + vector tdir; + float tbias; +} brushface_t; +int(float modelidx, int brushid, brushface_t *out_faces, int maxfaces, int *out_contents) brush_get = #0/*:brush_get*/; /* + 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. */ + +int(float modelidx, brushface_t *in_faces, int numfaces, int contents) brush_create = #0/*:brush_create*/; /* + Inserts a new brush into the model. Return value is the new brush's id. */ + +void(float modelidx, int brushid) brush_delete = #0/*:brush_delete*/; /* + Destroys the specified brush. */ + +float(float modelid, int brushid, int faceid, float selectedstate) brush_selected = #0/*:brush_selected*/; /* + Allows you to easily set transient visual properties of a brush. returns old value. selectedstate=-1 changes nothing (called for its return value). */ + +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(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. */ + +void(optional entity ent, optional vector neworigin) touchtriggers = #279; /* + Triggers a touch events between self and every SOLID_TRIGGER entity that it is in contact with. This should typically just be the triggers touch functions. Also optionally updates the origin of the moved entity. */ float(entity skelent, string dollcmd, float animskel) skel_ragupdate = #281; /* Updates the skeletal object attached to the entity according to its origin and other properties. @@ -920,6 +1156,9 @@ vector (vector v) unproject = #310; /* vector (vector v) project = #311; /* Transform a 3d world-space point into a 2d screen-space point, according the various origin+angle+fov etc settings set via setproperty. */ +void(vector pos, vector size, float alignflags, string text) drawtextfield = #0/*:drawtextfield*/; /* + Draws a multi-line block of text, including word wrapping and alignment. alignflags bits are RTLB, typically 3. */ + void(float width, vector pos1, vector pos2, vector rgb, float alpha, optional float drawflag) drawline = #315; /* Draws a 2d line between the two 2d points. */ @@ -974,6 +1213,12 @@ float(string text, float usecolours, optional vector fontsize) stringwidth = #32 void(vector pos, vector sz, string pic, vector srcpos, vector srcsz, vector rgb, float alpha, optional float drawflag) drawsubpic = #328; /* Draws a rescaled subsection of an image to the screen. */ +void(vector pivot, vector mins, vector maxs, string pic, vector rgb, float alpha, float angle) drawrotpic = #0/*:drawrotpic*/; /* + Draws an image rotating at the pivot. To rotate in the center, use mins+maxs of half the size with mins negated. Angle is in degrees. */ + +void(vector pivot, vector mins, vector maxs, string pic, vector txmin, vector txsize, vector rgb, vector alphaandangles) drawrotsubpic = #0/*:drawrotsubpic*/; /* + Overcomplicated draw function for over complicated people. Positions follow drawrotpic, while texture coords follow drawsubpic. Due to argument count limitations in builtins, the alpha value and angles are combined into separate fields of a vector (tip: use fteqcc's [alpha, angle] feature. */ + float(float stnum) getstati = #330; /* Retrieves the numerical value of the given EV_INTEGER or EV_ENTITY stat (converted to a float). */ @@ -1101,7 +1346,12 @@ void(entity e, string skinfilename, optional string skindata) setcustomskin = #3 Sets an entity's skin overrides. These are custom per-entity surface->shader lookups. The skinfilename/data should be in .skin format: surfacename,shadername - makes the named surface use the named shader replace "surfacename" "shadername" - same. - compose "surfacename" "shader" "imagename@x,y:w,h?r,g,b,a" - compose a skin texture from multiple images. The texture is determined to be sufficient to hold the first named image, additional images can be named as extra tokens on the same line. Use a + at the end of the line to continue reading image tokens from the next line also, the named shader must use 'map $diffuse' to read the composed texture (compatible with the defaultskin shader). */ + qwskin "foo" - use an unmodified quakeworld player skin (including crop+repalette rules) + q1lower 0xff0000 - specify an override for the entity's lower colour, in this case to red + q1upper 0x0000ff - specify an override for the entity's lower colour, in this case to blue + compose "surfacename" "shader" "imagename@x,y:w,h?r,g,b,a" - compose a skin texture from multiple images. + The texture is determined to be sufficient to hold the first named image, additional images can be named as extra tokens on the same line. + Use a + at the end of the line to continue reading image tokens from the next line also, the named shader must use 'map $diffuse' to read the composed texture (compatible with the defaultskin shader). */ __variant*(int size) memalloc = #384; /* Part of FTE_MEMALLOC Allocate an arbitary block of memory */ @@ -1190,6 +1440,12 @@ float(searchhandle handle) search_getsize = #446; /* Part of DP_QC_FS_SEARCH string(searchhandle handle, float num) search_getfilename = #447; /* Part of DP_QC_FS_SEARCH Retrieves name of one of the files that was found by the initial search. */ +float(searchhandle handle, float num) search_getfilesize = #0/*:search_getfilesize*/; /* + Retrieves the size of one of the files that was found by the initial search. */ + +string(searchhandle handle, float num) search_getfilemtime = #0/*:search_getfilemtime*/; /* + Retrieves modification time of one of the files. */ + string(string cvarname) cvar_string = #448; /* Part of DP_QC_CVAR_STRING*/ entity(entity start, .float fld, float match) findflags = #449; /* Part of DP_QC_FINDFLAGS*/ entity(.float fld, float match) findchainflags = #450; /* Part of DP_QC_FINDCHAINFLAGS*/ @@ -1224,18 +1480,45 @@ void(vector origin, string sample, float volume, float attenuation) pointsound = string(string search, string replace, string subject) strreplace = #484; /* Part of DP_QC_STRREPLACE*/ string(string search, string replace, string subject) strireplace = #485; /* Part of DP_QC_STRREPLACE*/ vector(entity e, float s, float n, float a) getsurfacepointattribute = #486; /* Part of DP_QC_GETSURFACEPOINTATTRIBUTE*/ -float(string name) gecko_create = #487; /* Part of DP_GECKO_SUPPORT*/ -void(string name) gecko_destroy = #488; /* Part of DP_GECKO_SUPPORT*/ -void(string name, string URI) gecko_navigate = #489; /* Part of DP_GECKO_SUPPORT*/ -float(string name, float key, float eventtype) gecko_keyevent = #490; /* Part of DP_GECKO_SUPPORT*/ -void(string name, float x, float y) gecko_mousemove = #491; /* Part of DP_GECKO_SUPPORT*/ -void(string name, float w, float h) gecko_resize = #492; /* Part of DP_GECKO_SUPPORT*/ -vector(string name) gecko_get_texture_extent = #493; /* Part of DP_GECKO_SUPPORT*/ +float(string name) gecko_create = #487; /* Part of DP_GECKO_SUPPORT + Create a new 'browser tab' shader with the specified name that can then be drawn via drawpic. In order to function correctly, this builtin depends upon external plugins being available. Use gecko_navigate to navigate it to a page of your choosing. */ + +void(string name) gecko_destroy = #488; /* Part of DP_GECKO_SUPPORT + Destroy a shader. */ + +void(string name, string URI) gecko_navigate = #489; /* Part of DP_GECKO_SUPPORT + Sends a command to the media decoder attached to the specified shader. In the case of a browser decoder, this changes the url that the browser displays. 'cmd:[un]focus' will tell the decoder that it has focus. */ + +float(string name, float key, float eventtype) gecko_keyevent = #490; /* Part of DP_GECKO_SUPPORT + Send a key event to a media decoder. This applies only to interactive decoders like browsers. */ + +void(string name, float x, float y) gecko_mousemove = #491; /* Part of DP_GECKO_SUPPORT + Sets a media decoder shader's mouse position. Values should be 0-1. */ + +void(string name, float w, float h) gecko_resize = #492; /* Part of DP_GECKO_SUPPORT + Request to resize a media decoder. */ + +vector(string name) gecko_get_texture_extent = #493; /* Part of DP_GECKO_SUPPORT + Query a media decoder for its current pixel size. */ + float(float caseinsensitive, string s, ...) crc16 = #494; /* Part of DP_QC_CRC16*/ float(string name) cvar_type = #495; /* Part of DP_QC_CVAR_TYPE*/ -float() numentityfields = #496; /* Part of DP_QC_ENTITYDATA*/ -string(float fieldnum) entityfieldname = #497; /* Part of DP_QC_ENTITYDATA*/ -float(float fieldnum) entityfieldtype = #498; /* Part of DP_QC_ENTITYDATA*/ +float() numentityfields = #496; /* Part of DP_QC_ENTITYDATA + Gives the number of named entity fields. Note that this is not the size of an entity, but rather just the number of unique names (ie: vectors use 4 names rather than 3). */ + +float(string fieldname) findentityfield = #0/*:findentityfield*/; /* + Find a field index by name. */ + +typedef .__variant field_t; +field_t(float fieldnum) entityfieldref = #0/*:entityfieldref*/; /* + Returns a field value that can be directly used to read entity fields. Be sure to validate the type with entityfieldtype before using. */ + +string(float fieldnum) entityfieldname = #497; /* Part of DP_QC_ENTITYDATA + Retrieves the name of the given entity field. */ + +float(float fieldnum) entityfieldtype = #498; /* Part of DP_QC_ENTITYDATA + Provides information about the type of the field specified by the field num. Returns one of the EV_ values. */ + string(float fieldnum, entity ent) getentityfieldstring = #499; /* Part of DP_QC_ENTITYDATA*/ float(float fieldnum, entity ent, string s) putentityfieldstring = #500; /* Part of DP_QC_ENTITYDATA*/ void(float effectindex, entity own, vector org_from, vector org_to, vector dir_from, vector dir_to, float countmultiplier, optional float flags) boxparticles = #502; @@ -1248,14 +1531,14 @@ __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; -float(string uril, float id, optional string postmimetype, optional string postdata) uri_get = #513; /* Part of DP_QC_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 */ float(string str) tokenize_console = #514; float(float idx) argv_start_index = #515; float(float idx) argv_end_index = #516; -void(strbuf strbuf) buf_cvarlist = #517; +void(strbuf strbuf, string pattern, string antipattern) buf_cvarlist = #517; string(string cvarname) cvar_description = #518; float(optional float timetype) gettime = #519; string(float keynum) keynumtostring_omgwtf = #520; @@ -1427,5 +1710,60 @@ string(string digest, string data, ...) digest_hex = #639; #define K_TILDE 126 #define K_BACKQUOTE 96 #define K_BACKSLASH 92 -#endif +#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 + + + + +accessor strbuf : float +{ + inline get float asfloat[float idx] = {return stof(bufstr_get(this, idx));}; + inline set float asfloat[float idx] = {bufstr_set(this, idx, ftos(value));}; + get string[float] = bufstr_get; + set string[float] = bufstr_set; + get float length = buf_getsize; +}; +accessor searchhandle : float +{ + get string[float] = search_getfilename; + get float length = search_getsize; +}; +accessor hashtable : float +{ + inline get vector v[string key] = {return hash_get(this, key, '0 0 0', EV_VECTOR);}; + inline set vector v[string key] = {hash_add(this, key, value, HASH_REPLACE|EV_VECTOR);}; + inline get string s[string key] = {return hash_get(this, key, "", EV_STRING);}; + inline set string s[string key] = {hash_add(this, key, value, HASH_REPLACE|EV_STRING);}; + inline get string f[string key] = {return hash_get(this, key, 0.0, EV_FLOAT);}; + inline set string f[string key] = {hash_add(this, key, value, HASH_REPLACE|EV_FLOAT);}; + inline get __variant[string key] = {return hash_get(this, key, __NULL__);}; + inline set __variant[string key] = {hash_add(this, key, value, HASH_REPLACE);}; +}; +accessor infostring : string +{ + get string[string] = infoget; + inline set* string[string fld] = {(*this) = infoadd(*this, fld, value);}; +}; +accessor filestream : float +{ + get string = fgets; + 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 new file mode 100644 index 000000000..2c6719d7a --- /dev/null +++ b/quakec/csaddon/src/editor_brushes.qc @@ -0,0 +1,907 @@ +#if 0 +typedef struct +{ + string shadername; //aka: texture name + vector planenormal; + float planedist; + vector sdir; + float sbias; + vector tdir; + float tbias; +} brushface_t; +#define out //for annotation only +#define in //for annotation only +#define inout //for annotation only +int brush_get(float modelid, int brushid, out brushface_t *faces, int maxfaces, int *contents) = #0; //returns number of faces, or 0 on error. +int brush_create(float modelid, in brushface_t *faces, int numfaces, int contents) = #0; //returns newly created brushid. +void brush_delete(float modelid, int brushid) = #0; //destroys brush +//float brush_editplane(float modelid, int brushid, int faceid, in brushface_t *face) = #0; //allows changing shader/s/t info, but ignores plane changes. returns success +float brush_selected(float modelid, int brushid, int faceid, float selectedstate) = #0; //allows you to easily set visual properties of a brush. if brush/face is -1, applies to all. returns old value. selectedstate=-1 changes nothing (called for its return value). +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 + + +mod should track selected brush list instead of polling. +to move 50 brushes, mod needs to get+delete+transform+create +brush ids are ints. this allows different clients to use different ranges without float problems. +*/ + +int selectedbrush; +int selectedbrushface; +float selectedbrushmodel; +vector facepoints[64]; + +//history is implemented using a ringbuffer +typedef struct +{ + float timestamp; + int brushmodel; + int wasdelete; + int id; + brushface_t *brushdata; + int numfaces; + int contents; +} history_t; +nosave history_t historyring[128]; +int history_min; //oldest we can go +int history; //updated on each change +int history_max; //max value for redo + +void() brush_undo = +{ + do + { + if (history <= history_min) + return; + history--; + + history_t *h = &historyring[history & (historyring.length-1)]; + + //we're undoing, so deletes create and creates delete. weird, yes. + if (h->wasdelete) + h->id = brush_create(h->brushmodel, h->brushdata, h->numfaces, h->contents); + else + { + brush_delete(h->brushmodel, h->id); + h->id = 0; + } + } while (historyring[(history-1) & (historyring.length-1)].timestamp == h->timestamp); +}; +void() brush_redo = +{ + do + { + if (history >= history_max) + return; + + history_t *h = &historyring[history & (historyring.length-1)]; + + //we're redoing stuff that has previously been doing. yay. + if (h->wasdelete) + { + brush_delete(h->brushmodel, h->id); + h->id = 0; + } + else + { + h->id = brush_create(h->brushmodel, h->brushdata, h->numfaces, h->contents); + } + history++; + } while (historyring[history & (historyring.length-1)].timestamp == h->timestamp); +}; +//create and journal. +int(int mod, brushface_t *faces, int numfaces, int contents) brush_history_create = +{ + history_t *h = &historyring[history & (historyring.length-1)]; + + h->timestamp = time; + memfree(h->brushdata); + h->brushdata = memalloc(sizeof(brushface_t) * h->numfaces); + memcpy(h->brushdata, faces, sizeof(brushface_t) * h->numfaces); + h->numfaces = numfaces; + h->contents = contents; + h->brushmodel = mod; + h->wasdelete = FALSE; + + h->id = brush_create(h->brushmodel, h->brushdata, h->numfaces, h->contents); + + history++; + history_max = history; //always break any pending redos + if (history_min < history_max - historyring.length) + history_min = history_max - historyring.length; + return h->id; +}; +//delete and journal. +void(int mod, int id) brush_history_delete = +{ + int numfaces = brush_get(mod, id, __NULL__, 0, __NULL__); + if (!numfaces) + return; //doesn't exist or something, some kind of error. we can't log a delete if it didn't delete anything, if only because we can't recreate it if its undone. + history_t *h = &historyring[history & (historyring.length-1)]; + + h->timestamp = time; + h->brushmodel = mod; + h->id = 0; + h->contents = 0; + h->wasdelete = TRUE; + + memfree(h->brushdata); + h->brushdata = memalloc(sizeof(brushface_t) * numfaces); + h->numfaces = brush_get(mod, id, h->brushdata, numfaces, &h->contents); + brush_delete(mod, id); + + history++; + history_max = history; //always break any pending redos + if (history_min < history_max - historyring.length) + history_min = history_max - historyring.length; +}; + +brushface_t tmp_faces[64]; +int tmp_numfaces; +int tmp_contents; + +//int selected; +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 +{ + BT_NONE, //selection + BT_MOVE, + BT_MERGE, + BT_MOVEFACE, + BT_CREATE, + BT_CLONEDISPLACE, + BT_SLICE +}; +int brushtool; +int bt_points; +vector bt_point[64]; + +//basic cube plane normals, for selection. +static nosave const vector axis[6] = { + '-1 0 0', + '0 -1 0', + '0 0 -1', + '1 0 0', + '0 1 0', + '0 0 1' +}; +float dists[6]; + +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)) + +vector(vector guess) brush_snappoint = +{ + 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); + best_x = rint(best_x); //snap to grid + best_y = rint(best_y); + best_z = rint(best_z); + best *= autocvar_ca_grid; + + //find surfaces within 32qu of the point (via plane volume). use a tetrahedron instead if you want something more circular + for (facenum = 0; facenum < axis.length; facenum++) + dists[facenum] = (guess * axis[facenum]) + autocvar_ca_grid; + int brushnum, numbrushes = brush_findinvolume(selectedbrushmodel, &axis[0], &dists[0], 6, &brushlist[0], __NULL__, brushlist.length); + + for (brushnum = 0; brushnum < numbrushes; brushnum++) + { + for (facenum = 0; ; ) + { + points = brush_getfacepoints(selectedbrushmodel, brushlist[brushnum], ++facenum, &facepoints[0], facepoints.length); + if (!points) + break; //end of face list, I guess + + //walk the faces we found and use the point if its nearer than our previous guess. + for (point = 0; point < points; point++) + { + vector disp = facepoints[point] - guess; + float dist = disp*disp; + if (dist < bestdist) + best = facepoints[point]; + } + } + } + + return best; +}; + +void(float brushid) editor_drawbbox = +{ + static vector bbox[2]; + int p = brush_getfacepoints(selectedbrushmodel, brushid, 0, &bbox[0], bbox.length); + if (p == 2) + { + R_BeginPolygon("chop"); +#define line(x,y) R_PolygonVertex(x, '0 0', '1 0 0', 1); R_PolygonVertex(y, '0 0', '1 0 0', 1); R_EndPolygon() + line(bbox[0], ([bbox[1][0], bbox[0][1], bbox[0][2]])); + line(bbox[0], ([bbox[0][0], bbox[1][1], bbox[0][2]])); + line(bbox[0], ([bbox[0][0], bbox[0][1], bbox[1][2]])); + line(bbox[1], ([bbox[0][0], bbox[1][1], bbox[1][2]])); + line(bbox[1], ([bbox[1][0], bbox[0][1], bbox[1][2]])); + line(bbox[1], ([bbox[1][0], bbox[1][1], bbox[0][2]])); + + line(([bbox[1][0], bbox[0][1], bbox[0][2]]), ([bbox[1][0], bbox[1][1], bbox[0][2]])); + line(([bbox[1][0], bbox[0][1], bbox[0][2]]), ([bbox[1][0], bbox[0][1], bbox[1][2]])); + line(([bbox[0][0], bbox[1][1], bbox[0][2]]), ([bbox[0][0], bbox[1][1], bbox[1][2]])); + line(([bbox[0][0], bbox[1][1], bbox[0][2]]), ([bbox[1][0], bbox[1][1], bbox[0][2]])); + line(([bbox[0][0], bbox[0][1], bbox[1][2]]), ([bbox[0][0], bbox[1][1], bbox[1][2]])); + line(([bbox[0][0], bbox[0][1], bbox[1][2]]), ([bbox[1][0], bbox[0][1], bbox[1][2]])); +#undef line + } +}; + +void(vector mousepos) editor_brushes_add = +{ + vector col = '0 0 0'; + int points, point; + int facenum = 0; + float intensity = (sin(gettime(5)*4)+1)*0.05; + + //make sure this shader was generated already. + shaderforname("terrainedit", + "{" + "{\n" + "map terrainedit\n" + "blendfunc add\n" + "rgbgen vertex\n" + "alphagen vertex\n" + "}\n" + "}"); + + //make sure this shader was generated already. + shaderforname("chop", + "{" + "cull disable\n" + "{\n" + "map terrainedit\n" + "blendfunc add\n" + "rgbgen vertex\n" + "alphagen vertex\n" + "sort nearest\n" + "nodepthtest\n" + "}\n" + "}"); + + //draw the selected brush. + for(;;) + { + 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]; + + R_BeginPolygon("terrainedit"); + for (point = 0; point < points; point++) + R_PolygonVertex(facepoints[point], '0 0', col, 1); + R_EndPolygon(); + } + editor_drawbbox(selectedbrush); + + vector t = mousefar; + vector o = mousenear; + if (vlen(o - t) > 8192) + t = o + normalize(t)*8192; + traceline(o, t, TRUE, world); + +#if 0 + //find all the brushes within 32qu of the mouse cursor's impact point + //you can tweak this to find the nearest brush vertex efficiently (or snap it to a grid or so). + //you can then slice through a brush by generating a plane between three points found this way and inserting it into the brush, clipping away the extra part. + //(remember, the engine will automatically discard any degenerate planes) + col = '0 0 1'; + + //generate the volume + for (facenum = 0; facenum < 6; facenum++) + dists[facenum] = (trace_endpos * axis[facenum]) + 32; + + int brushnum, numbrushes = brush_findinvolume(selectedbrushmodel, axis, dists, dists.length, &brushlist[0], __NULL__, brushlist.length); + for (brushnum = 0; brushnum < numbrushes; brushnum++) + { + for (facenum = 0; ; ) + { + points = brush_getfacepoints(selectedbrushmodel, brushlist[brushnum], ++facenum, &facepoints[0], facepoints.length); + if (!points) + break; //end of face list, I guess + + R_BeginPolygon("terrainedit"); + for (point = 0; point < points; point++) + R_PolygonVertex(facepoints[point], '0 0', col, 1); + R_EndPolygon(); + } + } +#endif + + //draw a line/triangle to show the selection. + int showpoints = bt_points; + if (brushtool == BT_SLICE) + { +// bt_point[showpoints++] = brush_snappoint(trace_endpos); + showpoints = 3; + } + if (brushtool == BT_CREATE || brushtool == BT_CLONEDISPLACE) + bt_point[showpoints++] = brush_snappoint(trace_endpos); + + //FIXME: if slicing, draw the intersection + + if (showpoints) + { + col = '0 0 1'; + + R_BeginPolygon("chop"); + for (point = 0; point < showpoints; point++) + R_PolygonVertex(bt_point[point], '0 0', col, 1); + R_EndPolygon(); + } + + //FIXME: if creating, draw a wireframe brush. +}; + +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; + break; + case BT_CREATE: + drawrawstring('0 32 0', "Paint+Create", '8 8 0', '1 1 1', 1); + break; + case BT_SLICE: + drawrawstring('0 32 0', "Slice Tool", '8 8 0', '1 1 1', 1); + if (!selectedbrush) + brushtool = BT_NONE; + 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; +}; + +void() editor_brushes_registercommands = +{ + registercommand("brushedit_undo"); + registercommand("brushedit_redo"); + registercommand("brushedit_create"); + registercommand("brushedit_clone"); + registercommand("brushedit_slice"); + registercommand("brushedit_matchface"); + registercommand("brushedit_resettexcoords"); + registercommand("brushedit_settexture"); + registercommand("brushedit_rotate"); + registercommand("brushedit_binds"); +}; +float() editor_brushes_command = +{ + switch(argv(0)) + { + case "brushedit_undo": + brush_undo(); + break; + case "brushedit_redo": + brush_undo(); + break; + case "brushedit_create": + 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_matchface": + //FIXME + brushtool = BT_NONE; + bt_points = 0; + break; + case "brushedit_resettexcoords": + //FIXME + brushtool = BT_NONE; + bt_points = 0; + break; + case "brushedit_settexture": + //FIXME + brushtool = BT_NONE; + bt_points = 0; + break; + case "brushedit_rotate": + //FIXME + brushtool = BT_NONE; + bt_points = 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"); + break; + default: + return FALSE; + } + + //just in case. + cvar_set("ca_show", "1"); + cvar_set("ca_editormode", ftos(MODE_BRUSHEDIT)); + return TRUE; +}; + +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'; +}; + +float(float key, float unic, vector mousepos) editor_brushes_key = +{ + brushface_t *fa; + brushface_t *infa; + 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; + return TRUE; + } + if (key == K_MOUSE1) + { + traceline(o, t, TRUE, world); + + if (brushtool == BT_SLICE || brushtool == BT_CREATE || brushtool == BT_CLONEDISPLACE) + { + if (brushtool == BT_SLICE && bt_points > 2) + bt_points = 2; + +//FIXME: BT_CREATE is planar. so keep the points planar too. +//FIXME: need a way to move points already placed +//FIXME: create needs to ensure verts are clockwise and convex. + if (bt_points < bt_point.length) + { + bt_point[bt_points] = brush_snappoint(trace_endpos); + bt_points++; + } + + if (brushtool == BT_SLICE && bt_points == 1) + { //slice makes assumptions about the brush, so that you don't have to move to the other side of it first. + //it ALWAYS draws 3 points if any are defined, so make sure 2+3 have valid locations once point 1 is defined. + int majoraxis; + traceline(o, t, TRUE, world); + + brush_selected(selectedbrushmodel, selectedbrush, -1, FALSE); + + static vector bbox[2]; + p = brush_getfacepoints(selectedbrushmodel, selectedbrush, 0, &bbox[0], bbox.length); + + t[0] = fabs(trace_plane_normal[0]); + t[1] = fabs(trace_plane_normal[1]); + t[2] = fabs(trace_plane_normal[2]); + if (t[2] > t[0] && t[2] > t[1]) + majoraxis = 2; + else if (t[1] > t[0]) + majoraxis = 1; + else + majoraxis = 0; + + bt_point[1] = bt_point[0]; + bt_point[1][majoraxis] = bbox[trace_plane_normal[majoraxis]<0][majoraxis]; + majoraxis = !majoraxis; + 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) + { + 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); + } + return TRUE; + } + + if (key == K_ENTER) + { + if (brushtool == BT_CREATE && bt_points >= 3) + { + 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]; + + bt_points = 0; + + selectedbrush = brush_history_create(selectedbrushmodel, &tmp_faces[0], tmp_numfaces, 1i); + } + else if (brushtool == BT_SLICE) + { + //get the current faces + tmp_numfaces = brush_get(selectedbrushmodel, selectedbrush, &tmp_faces[0], tmp_faces.length-1, &tmp_contents); + + //generate a new face plane + fa = &tmp_faces[tmp_numfaces]; + 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 = tmp_faces[0].shadername; //find a neighbour? + + //make sure its okay + for (i = 0; i < tmp_numfaces; i++) + { + if (tmp_faces[i].planenormal == fa->planenormal && tmp_faces[i].planedist == fa->planedist) + { + print("that would be co-planar\n"); + return TRUE; + } + } + bt_points = 0; + tmp_numfaces++; + + reset_texturecoords(fa); + + //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); + + //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); + selectedbrushface = 0; + } + return TRUE; + } + + 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) + { + print("cannot merge brush with itself\n"); + 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... + //for each brush that intersects us, split it by every single one of our planes that intesect + //drop the resulting brushes if they contain contain points only within the subtraction region + } +*/ return FALSE; +}; diff --git a/quakec/csaddon/src/editor_ents.qc b/quakec/csaddon/src/editor_ents.qc index ba1e9941a..07da56fb7 100644 --- a/quakec/csaddon/src/editor_ents.qc +++ b/quakec/csaddon/src/editor_ents.qc @@ -1,4 +1,4 @@ -#define ENTS_NYI +//#define ENTS_NYI #ifdef ENTS_NYI void() editor_ents_add = @@ -10,60 +10,149 @@ void(vector mousepos) editor_ents_overlay = drawrawstring('0 32 0', "Not Yet Implemented", '8 8 0', '1 1 1', 1); }; #else -static float selectedent; +static int selectedent; static entity tempent; -typedef struct -{ - string name; - string value; -} entfield_t; - typedef struct { //quick access for rendering. float modelindex; + float isbsp; float alpha; + vector colourmod; vector org; + vector ang; + vector mins; + vector maxs; float scale; - entfield_t *fields; - float numfields; + //full data + hashtable fields; } entedit_t; static entedit_t *editents; -static float numents; +static int numents; -float(entedit_t *ent, string field) editor_ents_findfield = +struct +{ //FIXME: should probably parse quakeed comments or something. + //FIXME: bbox colours. + string classn; + string model; + vector colour; + vector mins, maxs; + float spawnflags_match; + float spawnflags_mask; +} entclasses[] = { - float i; - for (i = 0; i < ent->numfields; i++) - { - if (ent->fields[i].name == field) - return i; - } - return -1; -}; + {"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'}, -void(entedit_t *ent, string field, string value) editor_ents_setfieldvalue = -{ - float f = editor_ents_findfield(ent, field); - if (f < 0) - { - local entfield_t *nf; + {"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'}, - f = ent->numfields; - ent->numfields = ent->numfields + 1; + {"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'}, - //extend the list - nf = memalloc(sizeof(entfield_t)*ent->numfields); - memcpy((__variant*)nf, (__variant*)ent->fields, sizeof(entfield_t)*f); - memfree((__variant*)ent->fields); - ent->fields = nf; - ent->fields[f].name = strzone(field); - } - print(sprintf("set %s to %s\n", field, value)); - ent->fields[f].value = strzone(value); + {"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_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) ? + + {"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 }; entedit_t*() editor_ents_new = @@ -79,7 +168,8 @@ entedit_t*() editor_ents_new = memfree((__variant*)editents); editents = newents; -print("new entity\n"); + editents[nent].fields = hash_createtab(12, EV_STRING); + return &editents[nent]; }; @@ -87,15 +177,68 @@ void(float num) editor_ents_delete = { }; +inline float(string model) modelindexforname = +{ + print("precaching \""); + print(model); + print("\"\n"); + precache_model(model); + setmodel(tempent, model); + return tempent.modelindex; +}; +void(entedit_t *nent) editor_ents_updated = +{ + float ang; + + 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->org = stov(nent->fields["origin"]); + nent->isbsp = FALSE; + + ang = stof(nent->fields["angle"]); + if (ang == -1) + nent->ang = [90, 0, 0]; + else if (ang == -2) + nent->ang = [-90, 0, 0]; + else + nent->ang = [0, ang, 0]; + + if (!nent->modelindex) + { + int i; + string classn; + classn = nent->fields["classname"]; + for (i = 0; i < entclasses.length; i++) + { + if (classn == entclasses[i].classn) + { + nent->modelindex = modelindexforname(entclasses[i].model); + nent->alpha = 0.3; + nent->colourmod = entclasses[i].colour; + nent->mins = entclasses[i].mins; + nent->maxs = entclasses[i].maxs; + break; + } + } + } + else + nent->isbsp = TRUE; + + if (nent->isbsp) + nent->ang = '0 0 0'; //bsp entities should not be displayed rotated, because that just messes everything up. +}; + void() editor_ents_reload = { local string field, value; -print("RELOADING ENTS\n"); //reset ent state getentitytoken(__NULL__); - while (1) + for(;;) { field = getentitytoken(); if not (field) @@ -107,7 +250,7 @@ print("RELOADING ENTS\n"); local entedit_t *nent; nent = editor_ents_new(); - while(1) + for(;;) { field = getentitytoken(); if not (field) @@ -119,17 +262,42 @@ print("RELOADING ENTS\n"); break; value = getentitytoken(); - editor_ents_setfieldvalue(nent, field, value); + nent.fields[field] = value; + print(sprintf("%s: %s\n", field, value)); } + + editor_ents_updated(nent); } else { - print("Corrupt ent lump\n"); + print(sprintf("Corrupt ent lump, found \"%s\"\n", field)); return; } } }; +void(string shadername, vector min, vector max, vector col) editor_ents_drawbbox = +{ + if (min == max) + return; + R_BeginPolygon(shadername); +#define line(x,y) R_PolygonVertex(x, '0 0', col, 1); R_PolygonVertex(y, '0 0', col, 1); R_EndPolygon() + line(min, ([max[0], min[1], min[2]])); + line(min, ([min[0], max[1], min[2]])); + line(min, ([min[0], min[1], max[2]])); + line(max, ([min[0], max[1], max[2]])); + line(max, ([max[0], min[1], max[2]])); + line(max, ([max[0], max[1], min[2]])); + + line(([max[0], min[1], min[2]]), ([max[0], max[1], min[2]])); + line(([max[0], min[1], min[2]]), ([max[0], min[1], max[2]])); + line(([min[0], max[1], min[2]]), ([min[0], max[1], max[2]])); + line(([min[0], max[1], min[2]]), ([max[0], max[1], min[2]])); + line(([min[0], min[1], max[2]]), ([min[0], max[1], max[2]])); + line(([min[0], min[1], max[2]]), ([max[0], min[1], max[2]])); +#undef line +}; + void() editor_ents_add = { float e; @@ -139,30 +307,118 @@ void() editor_ents_add = editor_ents_reload(); } - for (e = 0; e < numents; e++) + //make sure this shader was generated already. + shaderforname("entbox", + "{" + "cull disable\n" + "{\n" + "map $whiteimage\n" + "blendfunc add\n" + "rgbgen vertex\n" + "alphagen vertex\n" + "}\n" + "}"); + //make sure this shader was generated already. + shaderforname("entboxsel", + "{" + "cull disable\n" + "{\n" + "map $whiteimage\n" + "blendfunc add\n" + "rgbgen vertex\n" + "alphagen vertex\n" + "sort nearest\n" + "nodepthtest\n" + "}\n" + "}"); + + for (e = 1; e < numents; e++) { - //skip worldspawn... - if (editents[e].modelindex == 1) - continue; - - tempent.modelindex = editents[e].modelindex; - tempent.alpha = editents[e].alpha; - tempent.scale = editents[e].scale; - if (e == selectedent) { if (gettime(0)*5f & 1) continue; tempent.effects |= 8192f; + editor_ents_drawbbox("entboxsel", editents[e].org+editents[e].mins, editents[e].org+editents[e].maxs, editents[e].colourmod); } else + { tempent.effects &~= 8192f; + editor_ents_drawbbox("entbox", editents[e].org+editents[e].mins, editents[e].org+editents[e].maxs, editents[e].colourmod); + } + + if (!editents[e].modelindex) + continue; + + 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; + setorigin(tempent, editents[e].org); addentity(tempent); } }; +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; + + 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; + return TRUE; +}; + void(vector mousepos) editor_ents_overlay = { + int i; + vector pos = '0 32 0'; + if (selectedent >= numents) + return; + entedit_t *ent = &editents[selectedent]; + + for (i = 0; ; i++) + { + string key, value; + key = hash_getkey(ent->fields, i); + if not (key) + return; + value = ent->fields[key]; + drawrawstring(pos, sprintf("%s: %s", key, value), '8 8 0', '1 1 1', 1); + pos_y += 8; + } }; #endif diff --git a/quakec/csaddon/src/editor_lights.qc b/quakec/csaddon/src/editor_lights.qc index b260f69a3..1a24e6501 100644 --- a/quakec/csaddon/src/editor_lights.qc +++ b/quakec/csaddon/src/editor_lights.qc @@ -133,7 +133,7 @@ static void(float fld, string newval) writefield = dynamiclight_set(selectedlight, LFIELD_RADIUS, stof(newval)); return; case 5: - float fl; + var float fl = 0; if (strstrofs(newval, "d")>=0) fl |= LFLAG_NORMALMODE; if (strstrofs(newval, "w")>=0) fl |= LFLAG_REALTIMEMODE; if (strstrofs(newval, "l")>=0) fl |= LFLAG_LIGHTMAP; @@ -190,7 +190,7 @@ void(vector m) editor_lights_overlay = fldname[NUMLFIELDS+2] = strcat("realtime world: ", cvar("r_shadow_realtime_world")?"on":"off"); fldname[NUMLFIELDS+3] = strcat("realtime dlight: ", cvar("r_shadow_realtime_dynamic")?"on":"off"); - for (i = 1f; i <= NUMLFIELDS; i+=1) + for (i = 1; i <= NUMLFIELDS; i++) { if (editfield == i) s = editvalue; @@ -354,7 +354,7 @@ float(float keyc, float unic, vector m) editor_lights_key = editvalue = strzone(readfield(editfield, 1)); } else if (unic == '=') - selectedlight+=1; + selectedlight++; else if (unic == '-') selectedlight-=1; else if (unic == 'n' || unic == 'N') diff --git a/quakec/csaddon/src/editor_particles.qc b/quakec/csaddon/src/editor_particles.qc index d4d7ddefb..982454de1 100644 --- a/quakec/csaddon/src/editor_particles.qc +++ b/quakec/csaddon/src/editor_particles.qc @@ -1,7 +1,7 @@ static float cureffect; static float curmodified; -textfield_linedesc_t fields[100] = +const textfield_linedesc_t fields[100] = { {"shader", "shader \\n{\\nshaderbody\\n}"}, {"texture", "texture "}, @@ -88,7 +88,7 @@ textfield_linedesc_t fields[100] = {__NULL__} }; -static textfield_t tf_particle; +strip static textfield_t tf_particle; #define MAXPARTICLETYPES 200 typedef struct @@ -97,12 +97,11 @@ typedef struct int start; int end; } particledesc_t; -particledesc_t pdesc[MAXPARTICLETYPES]; +strip particledesc_t pdesc[MAXPARTICLETYPES]; int numptypes; string particlesfile; static entity ptrailent; -float mousedown; static void(string descname) saveparticle; @@ -216,11 +215,14 @@ static void(string descname) loadparticles = particlesfile = (string)0; /*load new string*/ - f = fopen(strcat("particles/", descname, ".cfg"), FILE_READNL); - if (f >= 0) + if (descname != "") { - particlesfile = strzone(fgets(f)); - fclose(f); + f = fopen(strcat("particles/", descname, ".cfg"), FILE_READNL); + if (f >= 0) + { + particlesfile = strzone(fgets(f)); + fclose(f); + } } /*and find out where the particles are*/ diff --git a/quakec/csaddon/src/editor_terrain.qc b/quakec/csaddon/src/editor_terrain.qc index 7c11004aa..b2bce1d0a 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 int painttex; +static float painttex; static float mautorepeattime; static entity tempent; static var float texturesearch = -1; @@ -197,7 +197,6 @@ void(vector m, float repeated) editor_do = else terrain_edit(TEREDIT_TEX_REPLACE, trace_endpos, eradius, tex[painttex]); break; - break; case ter_tex_kill: if (autocvar_mod_terrain_networked && !isserver()) sendevent("teredit", "fvffs", TEREDIT_TEX_KILL, trace_endpos, eradius, equant, tex[painttex]); @@ -282,6 +281,7 @@ float(float keyc, float unic, vector m) editor_terrain_key = txt = itos((int)equant); if (curtool == ter_strength) txt = itos((int)epercent); + if (curtool == ter_mesh) txt = meshname; if (curtool == ter_tintval) @@ -556,8 +556,9 @@ void(vector mousepos) editor_terrain_overlay = } else drawstring(pos, sprintf("Tex%1i: %s", painttex, tex[painttex]), '8 8 0', colour, 1, 0); - if (tex[painttex] != "" && whichpack(strcat("textures/", tex[painttex], ".tga")) != "") - drawpic(pos + '0 8 0', tex[painttex], '128 128 0', '1 1 1', 1); + if (tex[painttex] != "") + if (whichpack(strcat("textures/", tex[painttex], ".tga")) != "") + drawpic(pos + '0 8 0', tex[painttex], '128 128 0', '1 1 1', 1); } else if (i == ter_tintval) diff --git a/quakec/csaddon/src/opts.qc b/quakec/csaddon/src/opts.qc index f60772993..c1f810af4 100644 --- a/quakec/csaddon/src/opts.qc +++ b/quakec/csaddon/src/opts.qc @@ -1,3 +1,3 @@ //this file must contain only definitions+pragmas. if it contains variables it will break the defs+crc. //#pragma flag enable assumeint -//#pragma flag enable typeexplicit \ No newline at end of file +//#pragma flag enable typeexplicit