diff --git a/engine/client/cl_demo.c b/engine/client/cl_demo.c
index cd84fc672..c777fb2f8 100644
--- a/engine/client/cl_demo.c
+++ b/engine/client/cl_demo.c
@@ -922,6 +922,8 @@ void CL_Stop_f (void)
cls.demooutfile = NULL;
cls.demorecording = false;
Con_Printf ("Completed demo\n");
+
+ FS_FlushFSHash();
}
diff --git a/engine/client/cl_ents.c b/engine/client/cl_ents.c
index a81825b82..b29079ade 100644
--- a/engine/client/cl_ents.c
+++ b/engine/client/cl_ents.c
@@ -4829,6 +4829,8 @@ void CL_LinkViewModel(void)
&& r_drawviewmodelinvis.value < 1)
alpha *= r_drawviewmodelinvis.value;
+ //FIXME: scale alpha by the player's alpha too
+
if (alpha <= 0)
return;
@@ -4910,6 +4912,7 @@ void CL_LinkViewModel(void)
*/
CLQ1_AddPowerupShell(V_AddEntity(&ent), true, plstate?plstate->effects:0);
+ //small hack to mask depth so only the front faces of the weaponmodel appear (no glitchy intra faces).
if (alpha < 1 && qrenderer == QR_OPENGL)
{
ent.forcedshader = R_RegisterShader("viewmodeldepthmask", SUF_NONE,
diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c
index f65b6f976..a25486f26 100644
--- a/engine/client/cl_main.c
+++ b/engine/client/cl_main.c
@@ -5219,6 +5219,7 @@ void CL_ExecInitialConfigs(char *resetcommand)
Cbuf_Execute (); //make sure any pending console commands are done with. mostly, anyway...
SCR_ShowPic_Clear(true);
+ Cbuf_AddText("alias restart_ents \"changelevel . .\"\n",RESTRICT_LOCAL);
Cbuf_AddText("alias restart \"changelevel .\"\n",RESTRICT_LOCAL);
Cbuf_AddText("alias startmap_sp \"map start\"\n", RESTRICT_LOCAL);
Cbuf_AddText("unbindall\n", RESTRICT_LOCAL);
diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c
index 0f04efbb7..8c2427c81 100644
--- a/engine/client/cl_parse.c
+++ b/engine/client/cl_parse.c
@@ -24,7 +24,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
void CL_GetNumberedEntityInfo (int num, float *org, float *ang);
void CLDP_ParseDarkPlaces5Entities(void);
-void CL_SetStatInt (int pnum, int stat, int value);
+static void CL_SetStatNumeric (int pnum, int stat, int ivalue, float fvalue);
+static void CL_SetStatInt (int pnum, int stat, int ivalue);
static qboolean CL_CheckModelResources (char *name);
char cl_dp_csqc_progsname[128];
@@ -2898,13 +2899,14 @@ void CLQW_ParseServerData (void)
#ifndef CLIENTONLY
Info_SetValueForStarKey (svs.info, "*gamedir", str, MAX_SERVERINFO_STRING);
#endif
- Cvar_ForceCallback(Cvar_FindVar("r_particlesdesc"));
}
CL_ClearState ();
Stats_NewMap();
cl.servercount = svcnt;
+ Cvar_ForceCallback(Cvar_FindVar("r_particlesdesc"));
+
cl.teamfortress = !Q_strcasecmp(str, "fortress");
if (cl.gamedirchanged)
@@ -3569,10 +3571,10 @@ void CLNQ_ParseClientdata (void)
}
else
{
- int weaponmodel = 0, armor = 0, weaponframe = 0, health = 0, currentammo = 0, shells = 0, nails = 0, rockets = 0, cells = 0, activeweapon = 0;
+ int weaponmodel = 0, armour = 0, weaponframe = 0, health = 0, currentammo = 0, shells = 0, nails = 0, rockets = 0, cells = 0, activeweapon = 0;
if (bits & SU_WEAPONFRAME) weaponframe |= (unsigned char)MSG_ReadByte();
- if (bits & SU_ARMOR) armor |= (unsigned char)MSG_ReadByte();
+ if (bits & SU_ARMOR) armour |= (unsigned char)MSG_ReadByte();
if (bits & SU_WEAPONMODEL) weaponmodel |= (unsigned char)MSG_ReadByte();
health |= MSG_ReadShort();
currentammo |= MSG_ReadByte();
@@ -3587,7 +3589,7 @@ void CLNQ_ParseClientdata (void)
if (bits & FITZSU_WEAPONMODEL2)
weaponmodel |= MSG_ReadByte() << 8;
if (bits & FITZSU_ARMOR2)
- armor |= MSG_ReadByte() << 8;
+ armour |= MSG_ReadByte() << 8;
if (bits & FITZSU_AMMO2)
currentammo |= MSG_ReadByte() << 8;
if (bits & FITZSU_SHELLS2)
@@ -3605,7 +3607,7 @@ void CLNQ_ParseClientdata (void)
}
CL_SetStatInt(0, STAT_WEAPONFRAME, weaponframe);
- CL_SetStatInt(0, STAT_ARMOR, armor);
+ CL_SetStatInt(0, STAT_ARMOR, armour);
CL_SetStatInt(0, STAT_WEAPONMODELI, weaponmodel);
CL_SetStatInt(0, STAT_HEALTH, health);
@@ -4814,7 +4816,8 @@ void CL_SetStatMovevar(int pnum, int stat, float value)
}
}
-void CL_SetStatInt (int pnum, int stat, int value)
+//the two values are expected to be the same, they're just both provided for precision.
+static void CL_SetStatNumeric (int pnum, int stat, int ivalue, float fvalue)
{
if (stat < 0 || stat >= MAX_CL_STATS)
return;
@@ -4825,57 +4828,39 @@ void CL_SetStatInt (int pnum, int stat, int value)
cl.oldgametime = cl.gametime;
cl.oldgametimemark = cl.gametimemark;
- cl.gametime = value * 0.001;
+ cl.gametime = fvalue * 0.001;
cl.gametimemark = realtime;
}
if (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV)
{
extern int cls_lastto;
- cl.players[cls_lastto].stats[stat]=value;
- cl.players[cls_lastto].statsf[stat]=value;
+ cl.players[cls_lastto].stats[stat]=ivalue;
+ cl.players[cls_lastto].statsf[stat]=fvalue;
for (pnum = 0; pnum < cl.splitclients; pnum++)
if (cl.playerview[pnum].cam_spec_track == cls_lastto && cl.playerview[pnum].cam_state != CAM_FREECAM)
- CL_SetStat_Internal(pnum, stat, value, value);
+ CL_SetStat_Internal(pnum, stat, ivalue, fvalue);
}
else
- CL_SetStat_Internal(pnum, stat, value, value);
-
- if (cls.protocol == CP_NETQUAKE && CPNQ_IS_DP && !(cls.fteprotocolextensions2 & PEXT2_PREDINFO))
- CL_SetStatMovevar(pnum, stat, *(float*)&value); //DP sucks.
-}
-void CL_SetStatFloat (int pnum, int stat, float value)
-{
- if (stat < 0 || stat >= MAX_CL_STATS)
- return;
-// Host_EndGame ("CL_SetStat: %i is invalid", stat);
-
- if (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV)
- {
- extern int cls_lastto;
- cl.players[cls_lastto].statsf[stat]=value;
- cl.players[cls_lastto].stats[stat]=value;
-
- for (pnum = 0; pnum < cl.splitclients; pnum++)
- if (cl.playerview[pnum].cam_spec_track == cls_lastto && cl.playerview[pnum].cam_state != CAM_FREECAM)
- {
- cl.playerview[pnum].statsf[stat] = value;
- cl.playerview[pnum].stats[stat] = value;
- }
- }
- else
- {
- cl.playerview[pnum].statsf[stat] = value;
- cl.playerview[pnum].stats[stat] = value;
- }
+ CL_SetStat_Internal(pnum, stat, ivalue, fvalue);
if (stat == STAT_VIEWHEIGHT && ((cls.z_ext & Z_EXT_VIEWHEIGHT) || cls.protocol == CP_NETQUAKE))
- cl.playerview[pnum].viewheight = value;
+ cl.playerview[pnum].viewheight = fvalue;
- if (cls.fteprotocolextensions2 & PEXT2_PREDINFO)
- CL_SetStatMovevar(pnum, stat, value);
+ if (cls.protocol == CP_NETQUAKE && CPNQ_IS_DP)
+ {
+ if (cls.fteprotocolextensions2 & PEXT2_PREDINFO)
+ CL_SetStatMovevar(pnum, stat, fvalue);
+ else
+ CL_SetStatMovevar(pnum, stat, *(float*)&ivalue); //DP sucks.
+ }
}
+static void CL_SetStatInt (int pnum, int stat, int ivalue)
+{
+ CL_SetStatNumeric(pnum,stat,ivalue,ivalue);
+}
+
void CL_SetStatString (int pnum, int stat, char *value)
{
if (stat < 0 || stat >= MAX_CL_STATS)
@@ -6039,6 +6024,14 @@ void CLQW_ParseServerMessage (void)
inf->packet_entities.fixangles[0] = 2;
VectorCopy(demoangles, inf->packet_entities.fixedangles[0]);
}
+ else if (cl.intermission)
+ {
+ for (destsplit = 0; destsplit < cl.splitclients; destsplit++)
+ {
+ inf->packet_entities.fixangles[destsplit] = 2;
+ VectorCopy(cl.playerview[destsplit].intermissionangles, inf->packet_entities.fixedangles[destsplit]);
+ }
+ }
//
// if recording demos, copy the message out
@@ -6191,7 +6184,8 @@ void CLQW_ParseServerMessage (void)
case svcfte_setangledelta:
for (i=0 ; i<3 ; i++)
cl.playerview[destsplit].viewangles[i] += MSG_ReadAngle16 ();
-// VectorCopy (cl.playerview[destsplit].viewangles, cl.playerview[destsplit].simangles);
+ VectorCopy (cl.playerview[destsplit].viewangles, cl.playerview[destsplit].simangles);
+ VectorCopy (cl.playerview[destsplit].viewangles, cl.playerview[destsplit].intermissionangles);
break;
case svc_setangle:
if (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV)
@@ -6362,14 +6356,12 @@ void CLQW_ParseServerMessage (void)
case svcqw_updatestatbyte:
i = MSG_ReadByte ();
j = MSG_ReadByte ();
- CL_SetStatFloat (destsplit, i, j);
- CL_SetStatInt (destsplit, i, j);
+ CL_SetStatNumeric(destsplit, i, j, j);
break;
case svcqw_updatestatlong:
i = MSG_ReadByte ();
j = MSG_ReadLong (); //make qbyte if nq compatability?
- CL_SetStatFloat (destsplit, i, j);
- CL_SetStatInt (destsplit, i, j);
+ CL_SetStatNumeric (destsplit, i, j, j);
break;
case svcfte_updatestatstring:
@@ -6380,8 +6372,7 @@ void CLQW_ParseServerMessage (void)
case svcfte_updatestatfloat:
i = MSG_ReadByte();
f = MSG_ReadFloat();
- CL_SetStatInt (destsplit, i, f);
- CL_SetStatFloat (destsplit, i, f);
+ CL_SetStatNumeric (destsplit, i, f, f);
break;
case svc_spawnstaticsound:
@@ -7201,14 +7192,12 @@ void CLNQ_ParseServerMessage (void)
case svcnq_updatestatlong:
i = MSG_ReadByte ();
j = MSG_ReadLong ();
- CL_SetStatFloat (0, i, j);
- CL_SetStatInt (0, i, j);
+ CL_SetStatNumeric (0, i, j, j);
break;
case svcdp_updatestatbyte:
i = MSG_ReadByte ();
j = MSG_ReadByte ();
- CL_SetStatFloat (0, i, j);
- CL_SetStatInt (0, i, j);
+ CL_SetStatNumeric (0, i, j, j);
break;
case svcfte_updatestatstring:
i = MSG_ReadByte();
@@ -7219,8 +7208,7 @@ void CLNQ_ParseServerMessage (void)
i = MSG_ReadByte();
{
float f = MSG_ReadFloat();
- CL_SetStatInt (destsplit, i, f);
- CL_SetStatFloat (destsplit, i, f);
+ CL_SetStatNumeric (destsplit, i, f, f);
}
break;
case svc_setangle:
diff --git a/engine/client/cl_pred.c b/engine/client/cl_pred.c
index 816f23726..bf9ea8a6a 100644
--- a/engine/client/cl_pred.c
+++ b/engine/client/cl_pred.c
@@ -918,12 +918,14 @@ void CL_PredictMovePNum (int seat)
return;
}
- if (cl.intermission==1 && cls.protocol == CP_QUAKEWORLD)
+ if (0)//cl.intermission==1 && cls.protocol == CP_QUAKEWORLD)
{
//quakeworld locks view position once you hit intermission.
VectorCopy (pv->intermissionangles, pv->simangles);
return;
}
+ else if (cl.intermission)
+ lerpangles = false; //will do angles later.
else
{
if (cl.currentpackentities && cl.currentpackentities->fixangles[seat])
@@ -974,7 +976,7 @@ void CL_PredictMovePNum (int seat)
//these things also force-disable prediction
if ((cls.demoplayback==DPB_MVD || cls.demoplayback == DPB_EZTV) ||
- cl.paused || pv->pmovetype == PM_NONE || pv->pmovetype == PM_FREEZE || CAM_ISLOCKED(pv))
+ cl.intermission || cl.paused || pv->pmovetype == PM_NONE || pv->pmovetype == PM_FREEZE || CAM_ISLOCKED(pv))
{
nopred = true;
}
@@ -1268,7 +1270,12 @@ void CL_PredictMovePNum (int seat)
else
VectorCopy(pmove.gravitydir, pv->gravitydir);
- if (le && pv->cam_state == CAM_FREECAM)
+ if (cl.intermission && le)
+ {
+ VectorCopy(le->angles, pv->simangles);
+ VectorCopy(pv->simangles, pv->viewangles);
+ }
+ else if (le && pv->cam_state == CAM_FREECAM)
{
//keep the entity tracking the prediction position, so mirrors don't go all weird
VectorMA(pv->simorg, -pv->crouch, pv->gravitydir, le->origin);
diff --git a/engine/client/cl_screen.c b/engine/client/cl_screen.c
index b7cf69948..34b689903 100644
--- a/engine/client/cl_screen.c
+++ b/engine/client/cl_screen.c
@@ -541,6 +541,8 @@ void SCR_DrawCenterString (vrect_t *rect, cprint_t *p, struct font_s *font)
{
int w, h;
R_GetShaderSizes(pic, &w, &h, false);
+ w *= 24.0/h;
+ h = 24;
y+= 16;
R2D_ScalePic ( (vid.width-w)/2, 16, w, h, pic);
y+= h;
diff --git a/engine/client/cl_tent.c b/engine/client/cl_tent.c
index 523574449..681b6df45 100644
--- a/engine/client/cl_tent.c
+++ b/engine/client/cl_tent.c
@@ -370,9 +370,9 @@ tentsfx_t tentsfx[] =
vec3_t playerbeam_end[MAX_SPLITS];
-struct associatedeffect
+typedef struct associatedeffect_s
{
- struct associatedeffect *next;
+ struct associatedeffect_s *next;
char mname[MAX_QPATH];
char pname[MAX_QPATH];
enum
@@ -381,13 +381,14 @@ struct associatedeffect
AE_EMIT,
AE_REPLACE
} type;
-} *associatedeffect;
+} associatedeffect_t;
+associatedeffect_t *associatedeffect;
void CL_AssociateEffect_f(void)
{
char *modelname = Cmd_Argv(1);
char *effectname = Cmd_Argv(2);
int type = atoi(Cmd_Argv(3));
- struct associatedeffect *ae;
+ struct associatedeffect_s *ae;
if (!strcmp(Cmd_Argv(0), "r_trail"))
type = AE_TRAIL;
else
@@ -473,7 +474,7 @@ void CL_InitTEnts (void)
void CL_ShutdownTEnts (void)
{
- struct associatedeffect *ae;
+ struct associatedeffect_s *ae;
while(associatedeffect)
{
ae = associatedeffect;
@@ -496,7 +497,7 @@ void CL_ClearTEntParticleState (void)
void P_LoadedModel(model_t *mod)
{
- struct associatedeffect *ae;
+ struct associatedeffect_s *ae;
mod->particleeffect = P_INVALID;
mod->particletrail = P_INVALID;
@@ -1104,7 +1105,7 @@ void CL_ParseTEnt (void)
type = TEQW_BEAM;
break;
case TE_EXPLOSION:
- type = TE_EXPLOSIONNOSPRITE;
+ type = TEQW_EXPLOSIONNOSPRITE;
break;
default:
break;
@@ -1334,7 +1335,7 @@ void CL_ParseTEnt (void)
ex->endalpha = ex->startalpha; //don't fade out
}
break;
- case TE_EXPLOSIONNOSPRITE: //nq-style, no sprite
+ case TEQW_EXPLOSIONNOSPRITE: //nq-style, no sprite
case TE_EXPLOSION: //qw-style, with (optional) sprite
// particles
pos[0] = MSG_ReadCoord ();
@@ -1379,13 +1380,17 @@ void CL_ParseTEnt (void)
{
int colorStart;
int colorLength;
+ int ef;
pos[0] = MSG_ReadCoord ();
pos[1] = MSG_ReadCoord ();
pos[2] = MSG_ReadCoord ();
colorStart = MSG_ReadByte ();
colorLength = MSG_ReadByte ();
- if (P_RunParticleEffectType(pos, NULL, 1, pt_explosion))
- P_RunParticleEffect(pos, NULL, (colorStart + colorLength/2), 512);
+
+ ef = P_FindParticleType(va("TE_EXPLOSION2_%i_%i", colorStart, colorLength));
+ if (ef == P_INVALID)
+ ef = pt_explosion;
+ P_RunParticleEffectType(pos, NULL, 1, ef);
if (r_explosionlight.value)
{
dl = CL_AllocDlight (0);
@@ -1591,11 +1596,11 @@ void CL_ParseTEnt (void)
break;
case TEDP_SPARK:
- pos[0] = MSG_ReadCoord ();
+ pos[0] = MSG_ReadCoord (); //org
pos[1] = MSG_ReadCoord ();
pos[2] = MSG_ReadCoord ();
- pos2[0] = MSG_ReadChar ();
+ pos2[0] = MSG_ReadChar (); //vel
pos2[1] = MSG_ReadChar ();
pos2[2] = MSG_ReadChar ();
@@ -1606,22 +1611,21 @@ void CL_ParseTEnt (void)
break;
case TEDP_BLOODSHOWER:
- pos[0] = MSG_ReadCoord ();
- pos[1] = MSG_ReadCoord ();
- pos[2] = MSG_ReadCoord ();
-
- pos2[0] = MSG_ReadCoord ();
- pos2[1] = MSG_ReadCoord ();
- pos2[2] = MSG_ReadCoord ();
-
- cnt = MSG_ReadCoord (); //speed
-
- cnt = MSG_ReadShort ();
-
{
- VectorAdd(pos, pos2, pos);
- VectorScale(pos, 0.5, pos);
- P_RunParticleEffectTypeString(pos, NULL, cnt, "te_bloodshower");
+ vec3_t vel = {0,0,0};
+ pos[0] = MSG_ReadCoord ();
+ pos[1] = MSG_ReadCoord ();
+ pos[2] = MSG_ReadCoord ();
+
+ pos2[0] = MSG_ReadCoord ();
+ pos2[1] = MSG_ReadCoord ();
+ pos2[2] = MSG_ReadCoord ();
+
+ vel[2] = -MSG_ReadCoord ();
+
+ cnt = MSG_ReadShort ();
+
+ P_RunParticleCube(P_FindParticleType("te_bloodshower"), pos, pos2, vel, vel, cnt, 0, false, 0);
}
break;
@@ -1684,21 +1688,11 @@ void CL_ParseTEnt (void)
pos[1] = MSG_ReadCoord ();
pos[2] = MSG_ReadCoord ();
- // light
- dl = CL_AllocDlight (0);
- VectorCopy (pos, dl->origin);
- dl->radius = 200;
- dl->decay = 1000;
- dl->die = cl.time + 0.2;
- dl->color[0] = 1.0;
- dl->color[1] = 1.0;
- dl->color[2] = 1.0;
-
// stain (Hopefully this is close to how DP does it)
if (cl_legacystains.ival) Surf_AddStain(pos, -10, -10, -10, 30);
- if (P_ParticleTrail(pos, pos2, P_FindParticleType("te_plasmaburn"), 0, NULL, NULL))
- P_ParticleTrailIndex(pos, pos2, 15, 0, NULL);
+ if (P_RunParticleEffectType(pos, NULL, 1, P_FindParticleType("te_plasmaburn")))
+ P_RunParticleEffect(pos, vec3_origin, 15, 50);
break;
case TEDP_TEI_G3: //nexuiz's nex beam
diff --git a/engine/client/in_win.c b/engine/client/in_win.c
index 53b01f16d..c5fbe063e 100644
--- a/engine/client/in_win.c
+++ b/engine/client/in_win.c
@@ -770,6 +770,7 @@ void INS_CloseDInput (void)
FreeLibrary(hInstDI);
hInstDI = NULL;
pDirectInputCreate = NULL;
+ pDirectInputCreateEx = NULL;
}
}
diff --git a/engine/client/m_options.c b/engine/client/m_options.c
index 23b4c483a..25341a248 100644
--- a/engine/client/m_options.c
+++ b/engine/client/m_options.c
@@ -832,11 +832,11 @@ static void ApplyPreset (int presetnum)
//this function is written backwards, to ensure things work properly in configs etc.
// TODO: work backwards and only set cvars once
+ Cbuf_InsertText("vid_reload\n", RESTRICT_LOCAL, true);
for (i = presetnum; i >= 0; i--)
{
Cbuf_InsertText(presetexec[i], RESTRICT_LOCAL, true);
}
- Cbuf_InsertText("vid_reload\n", RESTRICT_LOCAL, true);
forcesaveprompt = true;
}
@@ -848,7 +848,7 @@ void M_Menu_Preset_f (void)
{
MB_REDTEXT("Please Choose Preset", false),
MB_TEXT("^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082", false),
- MB_CONSOLECMD("simple (untextured)", "fps_preset 286;menupop\n", "Lacks textures, particles, pretty much everything."),
+ MB_CONSOLECMD("simple (untextured)", "fps_preset 286;menupop\n", "Lacks textures, particles, pretty much everything."),
MB_CONSOLECMD("fast (deathmatch)", "fps_preset fast;menupop\n", "Fullscreen effects off to give consistant framerates"),
MB_CONSOLECMD("vanilla (softwarey)", "fps_preset vanilla;menupop\n", "This is for purists! Party like its 1995! No sanity spared!"),
MB_CONSOLECMD("normal (faithful)", "fps_preset normal;menupop\n", "An updated but still faithful appearance, using content replacements where applicable"),
@@ -857,10 +857,26 @@ void M_Menu_Preset_f (void)
MB_END()
};
static menuresel_t resel;
+ int item;
+ extern cvar_t r_drawflat;
menu = M_Options_Title(&y, 0);
MC_AddBulk(menu, &resel, bulk, 16, 216, y);
- //bottoms up! highlight 'normal' as the default option
- menu->selecteditem = menu->options->common.next->common.next->common.next;
+ menu->selecteditem = menu->options;
+ //bottoms up!
+ if (r_shadow_realtime_world.ival)
+ item = 1; //realtime
+ else if (r_deluxemapping_cvar.ival)
+ item = 2; //nice
+ else if (gl_load24bit.ival)
+ item = 3; //normal
+ else if (r_softwarebanding_cvar.ival)
+ item = 4;
+ else if (!r_drawflat.ival)
+ item = 5;
+ else
+ item = 6;
+ while (item --> 0)
+ menu->selecteditem = menu->selecteditem->common.next;
menu->cursoritem->common.posy = menu->selecteditem->common.posy;
}
diff --git a/engine/client/p_classic.c b/engine/client/p_classic.c
index 6c8924e50..29c75fdb4 100644
--- a/engine/client/p_classic.c
+++ b/engine/client/p_classic.c
@@ -27,6 +27,18 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define POLYS
+#ifdef FTE_TARGET_WEB
+#define rand myrand //emscripten's libc is doing a terrible job of this.
+static int rand(void)
+{ //ripped from glibc
+ static int state = 0xdeadbeef;
+ int val = ((state * 1103515245) + 12345) & 0x7fffffff;
+ state = val;
+ return val;
+}
+#endif
+
+
typedef enum {
DODGY,
@@ -44,6 +56,7 @@ typedef enum {
BLOBEXPLOSION_POINT,
LAVASPLASH_POINT,
EXPLOSION_POINT,
+ EXPLOSION2_POINT,
TELEPORTSPLASH_POINT,
MUZZLEFLASH_POINT,
@@ -78,7 +91,7 @@ typedef struct cparticle_s
#define ABSOLUTE_MIN_PARTICLES 512
#define ABSOLUTE_MAX_PARTICLES 8192
static int r_numparticles;
-static cparticle_t *particles, *fte_restrict active_particles, *free_particles;
+static cparticle_t *particles, *active_particles, *free_particles;
extern cvar_t r_part_density, r_part_classic_expgrav;
static unsigned int particleframe;
@@ -132,6 +145,14 @@ static int PClassic_FindParticleType(const char *name)
return LAVASPLASH_POINT;
if (!stricmp("te_explosion", name))
return EXPLOSION_POINT;
+ if (!strnicmp("te_explosion2_", name, 14))
+ {
+ char *e;
+ int start = strtoul(name+14, &e, 10);
+ int len = strtoul((*e == '_')?e+1:e, &e, 10);
+ if (!*e && start >= 0 && start <= 255 && len >= 0 && len <= 255)
+ return EXPLOSION2_POINT | (start<<8)|(len<<16);
+ }
if (!stricmp("te_teleport", name))
return TELEPORTSPLASH_POINT;
if (!stricmp("te_muzzleflash", name))
@@ -145,7 +166,7 @@ static int PClassic_FindParticleType(const char *name)
static qboolean PClassic_Query(int type, int body, char *outstr, int outstrlen)
{
char *n = NULL;
- switch(type)
+ switch(type&0xff)
{
case ROCKET_TRAIL:
n = "tr_rocket";
@@ -181,6 +202,9 @@ static qboolean PClassic_Query(int type, int body, char *outstr, int outstrlen)
case EXPLOSION_POINT:
n = "te_explosion";
break;
+ case EXPLOSION2_POINT:
+ n = va("te_explosion2_%i_%i", (type>>8)&0xff, (type>>16)&0xff);
+ break;
case TELEPORTSPLASH_POINT:
n = "te_teleport";
break;
@@ -627,6 +651,34 @@ static void Classic_ParticleExplosion (vec3_t org)
}
}
+static void Classic_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
+{
+ int i, j;
+ cparticle_t *p;
+ int colorMod = 0;
+
+ for (i=0; i<512; i++)
+ {
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+
+ p->die = cl.time + 0.3;
+ p->rgb = d_8to24rgbtable[(colorStart + (colorMod % colorLength)) & 255];
+ colorMod++;
+
+ p->type = pt_blob;
+ for (j=0 ; j<3 ; j++)
+ {
+ p->org[j] = org[j] + ((rand()%32)-16);
+ p->vel[j] = (rand()%512)-256;
+ }
+ }
+}
+
static void Classic_BlobExplosion (vec3_t org)
{
int i, j;
@@ -852,7 +904,7 @@ static void Classic_BrightField (vec3_t org)
//use the trail state so fast/slow frames keep the correct particle counts on certain every-frame effects
static int PClassic_RunParticleEffectState (vec3_t org, vec3_t dir, float count, int typenum, trailstate_t **tsk)
{
- switch(typenum)
+ switch(typenum&0xff)
{
case BRIGHTFIELD_POINT:
Classic_BrightField(org);
@@ -866,6 +918,9 @@ static int PClassic_RunParticleEffectState (vec3_t org, vec3_t dir, float count,
case EXPLOSION_POINT:
Classic_ParticleExplosion(org);
break;
+ case EXPLOSION2_POINT:
+ Classic_ParticleExplosion2(org, (typenum>>8)&0xff, (typenum>>16)&0xff);
+ break;
case TELEPORTSPLASH_POINT:
Classic_TeleportSplash(org);
break;
diff --git a/engine/client/p_script.c b/engine/client/p_script.c
index dfde6708c..aa8338016 100644
--- a/engine/client/p_script.c
+++ b/engine/client/p_script.c
@@ -29,6 +29,18 @@ The engine has a few builtins.
#ifdef PSET_SCRIPT
+
+#ifdef FTE_TARGET_WEB
+#define rand myrand //emscripten's libc is doing a terrible job of this.
+static int rand(void)
+{ //ripped from glibc
+ static int state = 0xdeadbeef;
+ int val = ((state * 1103515245) + 12345) & 0x7fffffff;
+ state = val;
+ return val;
+}
+#endif
+
#ifdef GLQUAKE
#include "glquake.h"//hack
#endif
@@ -55,7 +67,6 @@ extern particleengine_t pe_classic;
particleengine_t *fallback = NULL; //does this really need to be 'extern'?
#define FALLBACKBIAS 0x1000000
-static int pt_pointfile = P_INVALID;
static int pe_default = P_INVALID;
static int pe_size2 = P_INVALID;
static int pe_size3 = P_INVALID;
@@ -407,7 +418,7 @@ static struct {
{NULL}
};
-static part_type_t *P_GetParticleType(char *config, char *name)
+static part_type_t *P_GetParticleType(const char *config, const char *name)
{
int i;
part_type_t *ptype;
@@ -467,19 +478,66 @@ static part_type_t *P_GetParticleType(char *config, char *name)
}
//unconditionally allocates a particle object. this allows out-of-order allocations.
-static int P_AllocateParticleType(char *config, char *name) //guarentees that the particle type exists, returning it's index.
+static int P_AllocateParticleType(const char *config, const char *name) //guarentees that the particle type exists, returning it's index.
{
part_type_t *pt = P_GetParticleType(config, name);
return pt - part_type;
}
+static void PScript_RetintEffect(part_type_t *to, part_type_t *from, const char *colourcodes)
+{
+ char name[sizeof(to->name)];
+ char config[sizeof(to->config)];
+
+ Q_strncpyz(name, to->name, sizeof(to->name));
+ Q_strncpyz(config, to->config, sizeof(to->config));
+
+ //'to' was already purged, so we don't need to care about that.
+ memcpy(to, from, sizeof(*to));
+
+ Q_strncpyz(to->name, name, sizeof(to->name));
+ Q_strncpyz(to->config, config, sizeof(to->config));
+
+ //make sure 'to' has its own copy of any lists, so that we don't have issues when freeing this memory again.
+ if (to->models)
+ {
+ to->models = BZ_Malloc(to->nummodels * sizeof(*to->models));
+ memcpy(to->models, from->models, to->nummodels * sizeof(*to->models));
+ }
+ if (to->sounds)
+ {
+ to->sounds = BZ_Malloc(to->numsounds * sizeof(*to->sounds));
+ memcpy(to->sounds, from->sounds, to->numsounds * sizeof(*to->sounds));
+ }
+ if (to->ramp)
+ {
+ to->ramp = BZ_Malloc(to->rampindexes * sizeof(*to->ramp));
+ memcpy(to->ramp, from->ramp, to->rampindexes * sizeof(*to->ramp));
+ }
+
+ //'from' might still have some links so we need to clear those out.
+ to->nexttorun = NULL;
+ to->particles = NULL;
+ to->clippeddecals = NULL;
+ to->beams = NULL;
+ to->skytris = NULL;
+ to->slooks = &to->looks;
+ r_plooksdirty = true;
+
+ to->colorindex = strtoul(colourcodes, (char**)&colourcodes, 10);
+ if (*colourcodes == '_')
+ colourcodes++;
+ to->colorrand = strtoul(colourcodes, (char**)&colourcodes, 10);
+}
+
//public interface. get without creating.
-static int PScript_FindParticleType(const char *name)
+static int PScript_FindParticleType(const char *fullname)
{
int i;
part_type_t *ptype = NULL;
char cfg[MAX_QPATH];
char *dot;
+ const char *name = fullname;
dot = strchr(name, '.');
if (dot && (dot - name) < MAX_QPATH-1)
{
@@ -528,6 +586,16 @@ static int PScript_FindParticleType(const char *name)
}
if (!ptype || !ptype->loaded)
{
+ if (!strnicmp(name, "te_explosion2_", 14))
+ {
+ int from = PScript_FindParticleType(va("%s.te_explosion2", cfg));
+ if (from != P_INVALID)
+ {
+ int to = P_AllocateParticleType(cfg, name);
+ PScript_RetintEffect(&part_type[to], &part_type[from], name+14);
+ return to;
+ }
+ }
if (*cfg)
P_LoadParticleSet(cfg, true);
@@ -2757,13 +2825,6 @@ static qboolean PScript_InitParticles (void)
Cmd_AddCommand("r_beaminfo", P_BeamInfo_f);
//#endif
-
- pt_pointfile = P_AllocateParticleType("", "PT_POINTFILE");
- pe_default = P_AllocateParticleType("", "PE_DEFAULT");
- pe_size2 = P_AllocateParticleType("", "PE_SIZE2");
- pe_size3 = P_AllocateParticleType("", "PE_SIZE3");
- pe_defaulttrail = P_AllocateParticleType("", "PE_DEFAULTTRAIL");
-
Cvar_Hook(&r_particledesc, R_ParticleDesc_Callback);
Cvar_ForceCallback(&r_particledesc);
@@ -2811,6 +2872,11 @@ static void PScript_Shutdown (void)
Cmd_RemoveCommand("r_beaminfo");
#endif
+ pe_default = P_INVALID;
+ pe_size2 = P_INVALID;
+ pe_size3 = P_INVALID;
+ pe_defaulttrail = P_INVALID;
+
while(loadedconfigs)
{
pcfg_t *cfg;
@@ -3078,6 +3144,7 @@ static void P_ReadPointFile_f (void)
char name[MAX_OSPATH];
char line[1024];
char *s;
+ int pt_pointfile;
COM_StripExtension(cl.worldmodel->name, name, sizeof(name));
strcat(name, ".pts");
@@ -3093,6 +3160,8 @@ static void P_ReadPointFile_f (void)
Con_Printf ("Reading %s...\n", name);
c = 0;
+ pt_pointfile = PScript_FindParticleType("PT_POINTFILE");
+ if (pt_pointfile != P_INVALID)
for ( ;; )
{
VFS_GETS(f, line, sizeof(line));
@@ -4372,25 +4441,25 @@ static void PScript_RunParticleEffect (vec3_t org, vec3_t dir, int color, int co
ptype = P_FindParticleType(va("pe_%i", color));
if (P_RunParticleEffectType(org, dir, count, ptype))
{
- if (count > 130 && part_type[pe_size3].loaded)
+ if (count > 130 && pe_size3 != P_INVALID)
{
part_type[pe_size3].colorindex = color & ~0x7;
part_type[pe_size3].colorrand = 8;
P_RunParticleEffectType(org, dir, count, pe_size3);
}
- else if (count > 20 && part_type[pe_size2].loaded)
+ else if (count > 20 && pe_size2 != P_INVALID)
{
part_type[pe_size2].colorindex = color & ~0x7;
part_type[pe_size2].colorrand = 8;
P_RunParticleEffectType(org, dir, count, pe_size2);
}
- else if (part_type[pe_default].loaded || !fallback)
+ else if (pe_default != P_INVALID)
{
part_type[pe_default].colorindex = color & ~0x7;
part_type[pe_default].colorrand = 8;
P_RunParticleEffectType(org, dir, count, pe_default);
}
- else
+ else if (fallback)
fallback->RunParticleEffect(org, dir, color, count);
}
}
@@ -5106,9 +5175,12 @@ static int PScript_ParticleTrail (vec3_t startpos, vec3_t end, int type, int dlk
static void PScript_ParticleTrailIndex (vec3_t start, vec3_t end, int color, int crnd, trailstate_t **tsk)
{
- part_type[pe_defaulttrail].colorindex = color;
- part_type[pe_defaulttrail].colorrand = crnd;
- P_ParticleTrail(start, end, pe_defaulttrail, 0, NULL, tsk);
+ if (pe_defaulttrail != P_INVALID)
+ {
+ part_type[pe_defaulttrail].colorindex = color;
+ part_type[pe_defaulttrail].colorrand = crnd;
+ P_ParticleTrail(start, end, pe_defaulttrail, 0, NULL, tsk);
+ }
}
static vec3_t pright, pup;
@@ -5805,6 +5877,12 @@ static void PScript_DrawParticleTypes (void)
if (r_plooksdirty)
{
int i, j;
+
+ pe_default = PScript_FindParticleType("PE_DEFAULT");
+ pe_size2 = PScript_FindParticleType("PE_SIZE2");
+ pe_size3 = PScript_FindParticleType("PE_SIZE3");
+ pe_defaulttrail = PScript_FindParticleType("PE_DEFAULTTRAIL");
+
for (i = 0; i < numparticletypes; i++)
{
//set the fallback
@@ -5938,6 +6016,8 @@ static void PScript_DrawParticleTypes (void)
// prediction takes care of the rest
switch(type->looks.type)
{
+ case PT_INVISIBLE:
+ break;
case PT_BEAM:
bdraw = GL_DrawParticleBeam;
break;
diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c
index 7d341b39d..1da44d98c 100644
--- a/engine/client/pr_csqc.c
+++ b/engine/client/pr_csqc.c
@@ -2983,15 +2983,14 @@ static void QCBUILTIN PF_cs_getentitytoken (pubprogfuncs_t *prinst, struct globa
}
else
{
- com_tokentype = TTP_LINEENDING;
- while(com_tokentype == TTP_LINEENDING)
- {
- csqcmapentitydata = COM_ParseToken(csqcmapentitydata, "{}()\'\":,");
- }
+ char *QCC_COM_Parse (const char *data);
+ extern char qcc_token[];
+ csqcmapentitydata = QCC_COM_Parse(csqcmapentitydata);
+
if (!csqcmapentitydata) //hit the end
G_INT(OFS_RETURN) = 0;
else
- RETURN_TSTRING(com_token);
+ RETURN_TSTRING(qcc_token);
}
}
@@ -3757,6 +3756,12 @@ static void QCBUILTIN PF_cl_te_customflash (pubprogfuncs_t *prinst, struct globa
static void QCBUILTIN PF_cl_te_bloodshower (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
+ float *minb = G_VECTOR(OFS_PARM0);
+ float *maxb = G_VECTOR(OFS_PARM1);
+ vec3_t vel = {0,0,-G_FLOAT(OFS_PARM2)};
+ float howmany = G_FLOAT(OFS_PARM3);
+
+ P_RunParticleCube(P_FindParticleType("te_bloodshower"), minb, maxb, vel, vel, howmany, 0, false, 0);
}
static void QCBUILTIN PF_cl_te_particlecube (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
@@ -3772,12 +3777,41 @@ static void QCBUILTIN PF_cl_te_particlecube (pubprogfuncs_t *prinst, struct glob
}
static void QCBUILTIN PF_cl_te_spark (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
+ float *pos = G_VECTOR(OFS_PARM0);
+ float *pos2 = G_VECTOR(OFS_PARM1);
+ float cnt = G_FLOAT(OFS_PARM2);
+ P_RunParticleEffectType(pos, pos2, cnt, ptdp_spark);
}
static void QCBUILTIN PF_cl_te_smallflash (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
+ float *pos = G_VECTOR(OFS_PARM0);
+ dlight_t *dl = CL_AllocDlight (0);
+ VectorCopy (pos, dl->origin);
+ dl->radius = 200;
+ dl->decay = 1000;
+ dl->die = cl.time + 0.2;
+ dl->color[0] = 2.0;
+ dl->color[1] = 2.0;
+ dl->color[2] = 2.0;
}
static void QCBUILTIN PF_cl_te_explosion2 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
+ float *pos = G_VECTOR(OFS_PARM0);
+ int colorStart = G_FLOAT(OFS_PARM1);
+ int colorLength = G_FLOAT(OFS_PARM2);
+ int ef = P_FindParticleType(va("TE_EXPLOSION2_%i_%i", colorStart, colorLength));
+ if (ef == P_INVALID)
+ ef = pt_explosion;
+ P_RunParticleEffectType(pos, NULL, 1, ef);
+ if (r_explosionlight.value)
+ {
+ dlight_t *dl = CL_AllocDlight (0);
+ VectorCopy (pos, dl->origin);
+ dl->radius = 350;
+ dl->die = cl.time + 0.5;
+ dl->decay = 300;
+ }
+ S_StartSound (0, 0, cl_sfx_r_exp3, pos, 1, 1, 0, 0, 0);
}
static void QCBUILTIN PF_cl_te_lightning1 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
@@ -3813,6 +3847,10 @@ static void QCBUILTIN PF_cl_te_beam (pubprogfuncs_t *prinst, struct globalvars_s
}
static void QCBUILTIN PF_cl_te_plasmaburn (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
+ float *pos = G_VECTOR(OFS_PARM0);
+
+ if (P_RunParticleEffectType(pos, NULL, 1, P_FindParticleType("te_plasmaburn")))
+ P_RunParticleEffect(pos, vec3_origin, 15, 50);
}
static void QCBUILTIN PF_cl_te_explosionrgb (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
diff --git a/engine/client/quakedef.h b/engine/client/quakedef.h
index 277b7d86b..0744eb20f 100644
--- a/engine/client/quakedef.h
+++ b/engine/client/quakedef.h
@@ -321,7 +321,7 @@ void Host_InitCommands (void);
void Host_Init (quakeparms_t *parms);
void Host_FinishInit(void);
void Host_Shutdown(void);
-qboolean com_fatalerror; //supresses shutdown prints+threads
+qboolean com_workererror; //supresses shutdown prints+threads
NORETURN void VARGS Host_Error (char *error, ...) LIKEPRINTF(1);
NORETURN void VARGS Host_EndGame (char *message, ...) LIKEPRINTF(1);
qboolean Host_SimulationTime(float time);
diff --git a/engine/client/r_2d.c b/engine/client/r_2d.c
index 9c87ea815..806fa02d0 100644
--- a/engine/client/r_2d.c
+++ b/engine/client/r_2d.c
@@ -20,7 +20,7 @@ shader_t *shader_crosshair;
static mpic_t *conback;
static mpic_t *draw_backtile;
-static shader_t *shader_draw_fill, *shader_draw_fill_trans;
+shader_t *shader_draw_fill, *shader_draw_fill_trans;
mpic_t *draw_disc;
shader_t *shader_contrastup;
diff --git a/engine/client/r_partset.c b/engine/client/r_partset.c
index f1bf31093..bda80061a 100644
--- a/engine/client/r_partset.c
+++ b/engine/client/r_partset.c
@@ -1593,12 +1593,85 @@ char *particle_set_high =
//hide it in nq - WARNING: some mods use this sprite as a flame thrower.
//r_effect "progs/s_explod.spr" hidden 1
+
+//////////////////////////////////////////
+//rogue te_explosion2 effect
+//note: if not otherwise defined, te_explosion2_BASE_RAND maps to this, and specifies the palette colours.
+"r_part te_explosion2\n"
+"{\n"
+"type texturedspark\n"
+"texture \"particles/fteparticlefont.tga\"\n"
+"tcoords 1 65 31 95 256 8 32\n"
+"count 256\n"
+"scale 5\n"
+"scalefactor 1\n"
+"scaledelta -15\n"
+"alpha 0.2\n"
+"die 0.5\n"
+"blend add\n"
+"spawnmode ball\n"
+"spawnorg 1\n"
+"randomvel 1000\n"
+"friction 0.01\n"
+"gravity 100\n"
+"stretchfactor -80\n"
+"}\n"
+//dragon fireball
+//r_part te_explosion2_228_5
+
+//rogue multigrenade sub explosion
+//also triggered from a shielded rogue player touching another player (and doing some damage)
+//also used during the ending.
+//red particles
+//r_part te_explosion2_230_5
+
+//rogue plasma explosion
+//also rogue timemachine explosion
+//white particles splaying outwards
+//r_part te_explosion2_244_3
+
//////////////////////////////////////////
//for when a spawn dies.
//also used by TF for emp explosions.
-//r_part te_tarexplosion
-//{
-//}
+"r_part te_tarexplosion\n"
+"{\n"
+"type texturedspark\n"
+"texture \"particles/fteparticlefont.tga\"\n"
+"tcoords 1 65 31 95 256 8 32\n"
+"count 128\n"
+"scale 5\n"
+"scalefactor 1\n"
+"scaledelta -15\n"
+"rgb 0 0 17\n"
+"alpha 0.5\n"
+"die 0.5\n"
+"spawnmode ball\n"
+"spawnorg 1\n"
+"randomvel 500\n"
+"friction 0.01\n"
+"gravity 100\n"
+"stretchfactor -80\n"
+"}\n"
+"r_part +te_tarexplosion\n"
+"{\n"
+"type texturedspark\n"
+"texture \"particles/fteparticlefont.tga\"\n"
+"tcoords 1 65 31 95 256 8 32\n"
+"count 256\n"
+"scale 5\n"
+"scalefactor 1\n"
+"scaledelta -15\n"
+"rgb 83 67 115\n"
+"alpha 0.3\n"
+"die 0.5\n"
+"blend add\n"
+"spawnmode ball\n"
+"spawnorg 1\n"
+"randomvel 500\n"
+"friction 0.01\n"
+"gravity 100\n"
+"stretchfactor -80\n"
+"}\n"
//////////////////////////////////////////
//cthon falling into lava.
@@ -1742,6 +1815,8 @@ char *particle_set_high =
//rygel's pack sucks
"r_trail \"progs/v_spike.mdl\" tr_vorespike\n"
+
+////////////////////
//enforcer laser effect
"r_part tr_enforcerlaser\n"
"{\n"
@@ -1769,6 +1844,79 @@ char *particle_set_high =
"}\n"
"r_trail \"progs/laser.mdl\" tr_enforcerlaser\n"
+/////////////////////////////////////////
+//rogue wrath enemy's projectiles
+"r_part tr_wrathball\n"
+"{\n"
+"type texturedspark\n"
+"texture \"particles/fteparticlefont.tga\"\n"
+"tcoords 1 97 95 191 256\n"
+"scale 15\n"
+"step 4\n"
+"alpha 0.3\n"
+"die 0.5\n"
+"rgb 255 0 0\n"
+"veladd -32\n"
+"spawnmode spiral\n"
+"spawnvel 16\n"
+"randomvel 32\n"
+"friction 0\n"
+"scalefactor 1\n"
+"blend add\n"
+"lighttime 0.2\n"
+"lightshadows 0\n"
+"lightradius 150\n"
+"lightrgb 1 0.27 0\n"
+"lightrgbfade 5 1 0\n"
+"lightcorona 2 0.5\n"
+"}\n"
+"r_trail \"progs/w_ball.mdl\" tr_wrathball\n"
+
+//wrath death
+//grey particles
+//no difference from the fallback except for the blend mode. this should ensure that we are not quite so invisible.
+"r_part te_explosion2_0_4\n"
+"{\n"
+"type texturedspark\n"
+"texture \"particles/fteparticlefont.tga\"\n"
+"tcoords 1 65 31 95 256 8 32\n"
+"count 256\n"
+"scale 5\n"
+"scalefactor 1\n"
+"scaledelta -15\n"
+"alpha 0.2\n"
+"die 0.5\n"
+"spawnmode ball\n"
+"spawnorg 1\n"
+"randomvel 1000\n"
+"friction 0.01\n"
+"gravity 100\n"
+"stretchfactor -80\n"
+"}\n"
+
+/////////////////////////////////////////
+//rogue lavaspikes
+"r_part tr_lavaspike\n"
+"{\n"
+"type spark\n"
+"texture \"particles/fteparticlefont.tga\"\n"
+"tcoords 1 97 95 191 256\n"
+"scale 15\n"
+"step 4\n"
+"alpha 0.3\n"
+"die 0.5\n"
+"rgb 255 0 0\n"
+"veladd -32\n"
+"spawnmode spiral\n"
+"spawnvel 16\n"
+"randomvel 32\n"
+"friction 0\n"
+"scalefactor 1\n"
+"blend add\n"
+"}\n"
+"r_trail \"progs/lspike.mdl\" tr_lavaspike\n"
+
+
/////////////////////////////////////////
//scrag missiles.
"r_part tr_wizspike\n"
diff --git a/engine/client/renderer.c b/engine/client/renderer.c
index f07bd3bb4..95eba677a 100644
--- a/engine/client/renderer.c
+++ b/engine/client/renderer.c
@@ -256,19 +256,23 @@ extern cvar_t r_novis;
extern cvar_t r_speeds;
extern cvar_t r_waterwarp;
-#ifdef ANDROID
+#if defined(ANDROID)
//on android, these numbers seem to be generating major weirdness, so disable these.
-cvar_t r_polygonoffset_submodel_factor = SCVAR("r_polygonoffset_submodel_factor", "0");
-cvar_t r_polygonoffset_submodel_offset = SCVAR("r_polygonoffset_submodel_offset", "0");
+cvar_t r_polygonoffset_submodel_factor = CVAR("r_polygonoffset_submodel_factor", "0");
+cvar_t r_polygonoffset_submodel_offset = CVAR("r_polygonoffset_submodel_offset", "0");
+#elif defined(FTE_TARGET_WEB)
+//on firefox (but not chrome or ie), these numbers seem to be generating major weirdness, so tone them down significantly by default.
+cvar_t r_polygonoffset_submodel_factor = CVAR("r_polygonoffset_submodel_factor", "0.05");
+cvar_t r_polygonoffset_submodel_offset = CVAR("r_polygonoffset_submodel_offset", "1");
#else
-cvar_t r_polygonoffset_submodel_factor = SCVAR("r_polygonoffset_submodel_factor", "0.05");
-cvar_t r_polygonoffset_submodel_offset = SCVAR("r_polygonoffset_submodel_offset", "25");
+cvar_t r_polygonoffset_submodel_factor = CVAR("r_polygonoffset_submodel_factor", "0.05");
+cvar_t r_polygonoffset_submodel_offset = CVAR("r_polygonoffset_submodel_offset", "25");
#endif
-cvar_t r_polygonoffset_shadowmap_offset = SCVAR("r_polygonoffset_shadowmap_factor", "0.05");
-cvar_t r_polygonoffset_shadowmap_factor = SCVAR("r_polygonoffset_shadowmap_offset", "0");
+cvar_t r_polygonoffset_shadowmap_offset = CVAR("r_polygonoffset_shadowmap_factor", "0.05");
+cvar_t r_polygonoffset_shadowmap_factor = CVAR("r_polygonoffset_shadowmap_offset", "0");
-cvar_t r_polygonoffset_stencil_factor = SCVAR("r_polygonoffset_stencil_factor", "0.01");
-cvar_t r_polygonoffset_stencil_offset = SCVAR("r_polygonoffset_stencil_offset", "1");
+cvar_t r_polygonoffset_stencil_factor = CVAR("r_polygonoffset_stencil_factor", "0.01");
+cvar_t r_polygonoffset_stencil_offset = CVAR("r_polygonoffset_stencil_offset", "1");
rendererstate_t currentrendererstate;
diff --git a/engine/client/sbar.c b/engine/client/sbar.c
index 693b4f2b3..c959a387f 100644
--- a/engine/client/sbar.c
+++ b/engine/client/sbar.c
@@ -1611,8 +1611,9 @@ void Sbar_DrawInventory (playerview_t *pv)
float time;
int flashon;
qboolean headsup;
- qboolean hudswap;
+ qboolean hudswap;
float wleft, wtop;
+ apic_t *ibar;
headsup = !(cl_sbar.value || (scr_viewsize.value<100&&cl.splitclients==1));
hudswap = cl_hudswap.value; // Get that nasty float out :)
@@ -1623,18 +1624,19 @@ void Sbar_DrawInventory (playerview_t *pv)
if (sbar_hipnotic)
wtop -= 16*2;
- if (!headsup)
+ if (sbar_rogue)
{
- if (sbar_rogue)
- {
- if ( pv->stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN )
- Sbar_DrawPic (0, -24, 320, 24, rsb_invbar[0]);
- else
- Sbar_DrawPic (0, -24, 320, 24, rsb_invbar[1]);
- }
+ if ( pv->stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN )
+ ibar = rsb_invbar[0];
else
- Sbar_DrawPic (0, -24, 320, 24, sb_ibar);
+ ibar = rsb_invbar[1];
}
+ else
+ ibar = sb_ibar;
+
+ if (!headsup)
+ Sbar_DrawPic (0, -24, 320, 24, ibar);
+
// weapons
for (i=0 ; i<7 ; i++)
{
@@ -1825,22 +1827,23 @@ void Sbar_DrawInventory (playerview_t *pv)
if (headsup)
{
for (i=0 ; i<4 ; i++)
- Sbar_DrawSubPic((hudswap) ? sbar_rect_left : (sbar_rect.width-42), -24 - (4-i)*11, 42, 11, sb_ibar, 3+(i*48), 0, 320, 24);
+ Sbar_DrawSubPic((hudswap) ? sbar_rect_left : (sbar_rect.width-42), -24 - (4-i)*11, 42, 11, ibar, 3+(i*48), 0, 320, 24);
}
for (i=0 ; i<4 ; i++)
{
- snprintf (num, sizeof(num), "%3i", pv->stats[STAT_SHELLS+i] );
+ snprintf (num, sizeof(num), "%4i", pv->stats[STAT_SHELLS+i] );
numc[0] = CON_WHITEMASK|0xe000|((num[0]!=' ')?(num[0] + 18-'0'):' ');
numc[1] = CON_WHITEMASK|0xe000|((num[1]!=' ')?(num[1] + 18-'0'):' ');
numc[2] = CON_WHITEMASK|0xe000|((num[2]!=' ')?(num[2] + 18-'0'):' ');
- numc[3] = 0;
+ numc[3] = CON_WHITEMASK|0xe000|((num[3]!=' ')?(num[3] + 18-'0'):' ');
+ numc[4] = 0;
if (headsup)
{
- Sbar_DrawExpandedString((hudswap) ? sbar_rect_left+3 : (sbar_rect.width-39), -24 - (4-i)*11, numc);
+ Sbar_DrawExpandedString(((hudswap) ? sbar_rect_left+3 : (sbar_rect.width-39)) - 4, -24 - (4-i)*11, numc);
}
else
{
- Sbar_DrawExpandedString((6*i+1)*8 - 2, -24, numc);
+ Sbar_DrawExpandedString((6*i+1)*8 - 2 - 4, -24, numc);
}
}
}
diff --git a/engine/client/snd_al.c b/engine/client/snd_al.c
index a01df6ad4..1bbef2d64 100644
--- a/engine/client/snd_al.c
+++ b/engine/client/snd_al.c
@@ -777,7 +777,7 @@ static qboolean OpenAL_InitLibrary(void)
#if FTE_TARGET_WEB
firefoxstaticsounds = !!strstr(emscripten_run_script_string("navigator.userAgent"), "Firefox");
if (firefoxstaticsounds)
- Con_Printf("Firefox detected - disabling static sounds to avoid SORRY, I CAN'T HEAR YOU\n");
+ Con_DPrintf("Firefox detected - disabling static sounds to avoid SORRY, I CAN'T HEAR YOU\n");
#endif
#ifdef OPENAL_STATIC
diff --git a/engine/common/common.c b/engine/common/common.c
index 21d51be6d..6aeae6b97 100644
--- a/engine/common/common.c
+++ b/engine/common/common.c
@@ -4721,7 +4721,7 @@ static void *com_workercondition[WORKERTHREADS];
static qboolean com_workerdone[WORKERTHREADS];
static void *com_workerthread[WORKERTHREADS];
static unsigned int mainthreadid;
-qboolean com_fatalerror;
+qboolean com_workererror;
static struct com_work_s
{
struct com_work_s *next;
@@ -4739,9 +4739,9 @@ void COM_WorkerAbort(char *message)
{
int us;
struct com_work_s work;
- com_fatalerror = true;
if (Sys_IsMainThread())
return;
+ com_workererror = true;
if (!com_workercondition[0])
return; //Sys_IsMainThread was probably called too early...
@@ -4813,7 +4813,7 @@ void COM_AddWork(int thread, void(*func)(void *ctx, void *data, size_t a, size_t
struct com_work_s *work;
//no worker there, just do it immediately on this thread instead of pushing it to the worker.
- if (thread && (!com_workerthread[thread] || com_fatalerror))
+ if (thread && (!com_workerthread[thread] || com_workererror))
{
func(ctx, data, a, b);
return;
@@ -4928,7 +4928,7 @@ void COM_DestroyWorkerThread(void)
{
int i;
COM_WorkerFullSync();
- com_fatalerror = false;
+// com_workererror = false;
for (i = 0; i < WORKERTHREADS; i++)
{
if (com_workerthread[i])
@@ -4942,7 +4942,7 @@ void COM_DestroyWorkerThread(void)
Sys_LockConditional(com_workercondition[0]);
do
{
- if (com_fatalerror)
+ if (com_workererror)
break;
while(COM_DoWork(0, true))
;
@@ -4995,7 +4995,7 @@ void COM_WorkerFullSync(void)
Sys_LockConditional(com_workercondition[0]);
do
{
- if (com_fatalerror)
+ if (com_workererror)
break;
while(COM_DoWork(0, true))
cmds++;
@@ -5003,7 +5003,7 @@ void COM_WorkerFullSync(void)
break;
} while (Sys_ConditionWait(com_workercondition[0]));
Sys_UnlockConditional(com_workercondition[0]);
- if (com_fatalerror)
+ if (com_workererror)
break;
if (cmds > 1)
repeat = true;
@@ -5064,7 +5064,7 @@ void COM_WorkerPartialSync(void *priorityctx, int *address, int value)
Sys_LockConditional(com_workercondition[0]);
do
{
- if (com_fatalerror)
+ if (com_workererror)
break;
while(COM_DoWork(0, true))
{
diff --git a/engine/common/common.h b/engine/common/common.h
index 791e83230..85441b67e 100644
--- a/engine/common/common.h
+++ b/engine/common/common.h
@@ -489,6 +489,7 @@ void COM_WriteFile (const char *filename, enum fs_relative fsroot, const void *d
void FS_FlushFSHashReally(qboolean domutexes);
void FS_FlushFSHashWritten(void);
void FS_FlushFSHashRemoved(void);
+void FS_FlushFSHash(void);
void FS_CreatePath(const char *pname, enum fs_relative relativeto);
qboolean FS_Rename(const char *oldf, const char *newf, enum fs_relative relativeto); //0 on success, non-0 on error
qboolean FS_Rename2(const char *oldf, const char *newf, enum fs_relative oldrelativeto, enum fs_relative newrelativeto);
diff --git a/engine/common/fs.c b/engine/common/fs.c
index 65d7e9785..0f775d2f7 100644
--- a/engine/common/fs.c
+++ b/engine/common/fs.c
@@ -828,6 +828,10 @@ void FS_FlushFSHashRemoved(void)
{
FS_FlushFSHashReally(true);
}
+void FS_FlushFSHash(void)
+{
+ FS_FlushFSHashReally(true);
+}
static void QDECL FS_AddFileHash(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle)
{
@@ -4377,11 +4381,20 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean
qboolean builtingame = false;
flocation_t loc;
+ char *vidfile[] = {"gfx.wad", "gfx/conback.lmp"};
+ searchpathfuncs_t *vidpath[countof(vidfile)];
+
//if any of these files change location, the configs will be re-execed.
//note that we reuse path handles if they're still valid, so we can just check the pointer to see if it got unloaded/replaced.
- char *conffile[] = {"quake.rc", "hexen.rc", "default.cfg", "server.cfg", NULL};
- searchpathfuncs_t *confpath[sizeof(conffile)/sizeof(conffile[0])];
- for (i = 0; conffile[i]; i++)
+ char *conffile[] = {"quake.rc", "hexen.rc", "default.cfg", "server.cfg"};
+ searchpathfuncs_t *confpath[countof(conffile)];
+
+ for (i = 0; i < countof(vidfile); i++)
+ {
+ FS_FLocateFile(vidfile[i], FSLFRT_IFFOUND, &loc); //q1
+ vidpath[i] = loc.search?loc.search->handle:NULL;
+ }
+ for (i = 0; i < countof(conffile); i++)
{
FS_FLocateFile(conffile[i], FSLFRT_IFFOUND, &loc); //q1
confpath[i] = loc.search?loc.search->handle:NULL;
@@ -4552,7 +4565,22 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean
if (allowreloadconfigs)
{
- for (i = 0; conffile[i]; i++)
+ qboolean vidrestart = false;
+
+ if (qrenderer != QR_NONE)
+ {
+ for (i = 0; i < countof(vidfile); i++)
+ {
+ FS_FLocateFile(vidfile[i], FSLFRT_IFFOUND, &loc);
+ if (vidpath[i] != (loc.search?loc.search->handle:NULL))
+ {
+ vidrestart = true;
+ Con_DPrintf("Restarting video because %s has changed\n", vidfile[i]);
+ }
+ }
+ }
+
+ for (i = 0; i < countof(conffile); i++)
{
FS_FLocateFile(conffile[i], FSLFRT_IFFOUND, &loc);
if (confpath[i] != (loc.search?loc.search->handle:NULL))
@@ -4583,6 +4611,10 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean
#endif
}
}
+#ifndef SERVERONLY
+ else if (vidrestart)
+ Cbuf_AddText ("vid_reload\n", RESTRICT_LOCAL);
+#endif
}
//rebuild the cache now, should be safe to waste some cycles on it
diff --git a/engine/common/gl_q2bsp.c b/engine/common/gl_q2bsp.c
index 7ef9a2633..be8f26555 100644
--- a/engine/common/gl_q2bsp.c
+++ b/engine/common/gl_q2bsp.c
@@ -2037,7 +2037,7 @@ void CMod_LoadEntityString (model_t *mod, qbyte *mod_base, lump_t *l)
// if (l->filelen > MAX_Q2MAP_ENTSTRING)
// Host_Error ("Map has too large entity lump");
- mod->entities = ZG_Malloc(&mod->memgroup, l->filelen+1);
+ mod->entities = Z_Malloc(l->filelen+1);
memcpy (mod->entities, mod_base + l->fileofs, l->filelen);
}
diff --git a/engine/common/plugin.c b/engine/common/plugin.c
index c403bffd4..d4544e68b 100644
--- a/engine/common/plugin.c
+++ b/engine/common/plugin.c
@@ -1782,7 +1782,7 @@ void Plug_Close(plugin_t *plug)
prev->next = plug->next;
}
- if (!com_fatalerror)
+ if (!com_workererror)
Con_DPrintf("Closing plugin %s\n", plug->name);
//ensure any active contexts provided by the plugin are closed (stuff with destroy callbacks)
diff --git a/engine/common/pmove.c b/engine/common/pmove.c
index 9962170f2..88795962d 100644
--- a/engine/common/pmove.c
+++ b/engine/common/pmove.c
@@ -362,7 +362,7 @@ int PM_StepSlideMove (qboolean in_air)
if (!blocked)
return blocked; // moved the entire distance
- if (in_air)
+ if (in_air)
{
// don't let us step up unless it's indeed a step we bumped in
// (that is, there's solid ground below)
@@ -808,7 +808,13 @@ void PM_AirMove (void)
// add gravity
VectorMA(pmove.velocity, movevars.entgravity * movevars.gravity * frametime, pmove.gravitydir, pmove.velocity);
- if (movevars.airstep)
+ if (DotProduct(pmove.velocity,pmove.velocity) > 1000*1000)
+ {
+ //when in a windtunnel, step up from where we are rather than the actual ground in order to more closely match nq.
+ //this is needed for r1m5 (770 800 192), just beyond the silver key door.
+ blocked = PM_StepSlideMove (false);
+ }
+ else if (movevars.airstep)
blocked = PM_StepSlideMove (true);
else
blocked = PM_SlideMove ();
diff --git a/engine/common/pr_bgcmd.c b/engine/common/pr_bgcmd.c
index 84c4976b2..bc2c0d1bf 100644
--- a/engine/common/pr_bgcmd.c
+++ b/engine/common/pr_bgcmd.c
@@ -1645,7 +1645,7 @@ void QCBUILTIN PF_hash_add (pubprogfuncs_t *prinst, struct globalvars_s *pr_glob
int flags = (prinst->callargc>3)?G_FLOAT(OFS_PARM3):0;
int type = flags & 0xff;
pf_hashentry_t *ent = NULL;
- if (tab)
+ if (tab && *name) //our hash tables can't cope with empty keys.
{
if (!type)
type = tab->defaulttype;
@@ -1658,6 +1658,7 @@ void QCBUILTIN PF_hash_add (pubprogfuncs_t *prinst, struct globalvars_s *pr_glob
BZ_Free(ent);
}
}
+
if (type == ev_string)
{ //strings copy their value out.
const char *value = PR_GetStringOfs(prinst, OFS_PARM2);
@@ -1667,7 +1668,8 @@ void QCBUILTIN PF_hash_add (pubprogfuncs_t *prinst, struct globalvars_s *pr_glob
ent->name = (char*)(ent+1);
ent->type = ev_string;
ent->stringdata = ent->name+(nlen+1);
- memcpy(ent->name, name, nlen+1);
+ memcpy(ent->name, name, nlen);
+ ent->name[nlen] = 0;
memcpy(ent->stringdata, value, vlen+1);
Hash_Add(&tab->tab, ent->name, ent, &ent->buck);
}
@@ -1677,7 +1679,8 @@ void QCBUILTIN PF_hash_add (pubprogfuncs_t *prinst, struct globalvars_s *pr_glob
ent = BZ_Malloc(sizeof(*ent) + nlen + 1);
ent->name = (char*)(ent+1);
ent->type = type;
- memcpy(ent->name, name, nlen+1);
+ memcpy(ent->name, name, nlen);
+ ent->name[nlen] = 0;
memcpy(ent->data, data, sizeof(vec3_t));
Hash_Add(&tab->tab, ent->name, ent, &ent->buck);
}
@@ -4058,7 +4061,7 @@ void QCBUILTIN PF_uri_get (pubprogfuncs_t *prinst, struct globalvars_s *pr_glob
const unsigned char *url = PR_GetStringOfs(prinst, OFS_PARM0);
float id = G_FLOAT(OFS_PARM1);
const char *mimetype = (prinst->callargc >= 3)?PR_GetStringOfs(prinst, OFS_PARM2):"";
- const char *dataorsep = PR_GetStringOfs(prinst, OFS_PARM3);
+ const char *dataorsep = (prinst->callargc >= 4)?PR_GetStringOfs(prinst, OFS_PARM3):"";
int strbufid = (prinst->callargc >= 5)?G_FLOAT(OFS_PARM4):0;
//float cryptokey = (prinst->callargc >= 5)?G_FLOAT(OFS_PARM5):0; //DP feature, not supported in FTE.
@@ -5865,7 +5868,9 @@ lh_extension_t QSG_Extensions[] = {
{"FTE_CALLTIMEOFDAY", 1, NULL, {"calltimeofday"}},
{"FTE_CSQC_ALTCONSOLES_WIP", 4, NULL, {"con_getset", "con_printf", "con_draw", "con_input"}},
{"FTE_CSQC_BASEFRAME", 0, NULL, {NULL}, "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."},
+#ifdef HALFLIFEMODELS
{"FTE_CSQC_HALFLIFE_MODELS"}, //hl-specific skeletal model control
+#endif
{"FTE_CSQC_SERVERBROWSER", 12, NULL, { "gethostcachevalue", "gethostcachestring", "resethostcachemasks", "sethostcachemaskstring", "sethostcachemasknumber",
"resorthostcache", "sethostcachesort", "refreshhostcache", "gethostcachenumber", "gethostcacheindexforkey",
"addwantedhostcachekey", "getextresponse"}}, //normally only available to the menu. this also adds them to csqc.
diff --git a/engine/common/pr_common.h b/engine/common/pr_common.h
index f0075b233..d9d94a758 100644
--- a/engine/common/pr_common.h
+++ b/engine/common/pr_common.h
@@ -740,6 +740,11 @@ enum terrainedit_e
ter_tex_replace, //vector pos, float radius, string texname
ter_reset, //vector pos, float radius
ter_reloadsect, //vector pos, float radius
+
+ ter_ents_wipe, //none
+ ter_ents_concat, //string
+ ter_ents_get, //none
+
// ter_poly_add, //add a poly, woo
// ter_poly_remove, //remove polys
diff --git a/engine/common/protocol.h b/engine/common/protocol.h
index b09395203..44395c802 100644
--- a/engine/common/protocol.h
+++ b/engine/common/protocol.h
@@ -842,7 +842,7 @@ enum {
TE_SPIKE = 0,
TE_SUPERSPIKE = 1,
TE_GUNSHOT = 2,
- TE_EXPLOSION = 3,
+ TE_EXPLOSION = 3, //remapped to TEQW_EXPLOSIONNOSPRITE for nq.
TE_TAREXPLOSION = 4,
TE_LIGHTNING1 = 5,
TE_LIGHTNING2 = 6,
@@ -865,7 +865,7 @@ enum {
TE_RAILTRAIL = 17, //use the builtin, luke.
TEQW_BEAM = 18, //use the builtin, luke.
TEQW_EXPLOSION2 = 19, //use the builtin, luke.
- TE_EXPLOSIONNOSPRITE = 20, //use the builtin, luke.
+ TEQW_EXPLOSIONNOSPRITE = 20,
// hexen 2
TEH2_STREAM_LIGHTNING_SMALL = 24,
diff --git a/engine/dotnet2005/emscripten.vcproj b/engine/dotnet2005/emscripten.vcproj
index 62b9d1dd7..8d489d8b7 100644
--- a/engine/dotnet2005/emscripten.vcproj
+++ b/engine/dotnet2005/emscripten.vcproj
@@ -59,6 +59,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/engine/gl/gl_heightmap.c b/engine/gl/gl_heightmap.c
index 141aea3ef..3d0741b41 100644
--- a/engine/gl/gl_heightmap.c
+++ b/engine/gl/gl_heightmap.c
@@ -41,6 +41,7 @@ cluster:
*/
int Surf_NewLightmaps(int count, int width, int height, qboolean deluxe);
+static size_t Terr_GenerateBrushFace(vecV_t *points, size_t maxpoints, vec4_t *planes, size_t numplanes, vec4_t face);
#define MAXCLUSTERS 64
#define MAXSECTIONS 64 //this many sections within each cluster in each direction
@@ -170,6 +171,23 @@ struct hmwater_s
shader_t *shader;
qbyte holes[8];
float heights[9*9];
+
+/*
+ qboolean facesdown;
+ unsigned int contentmask;
+ float heights[SECTHEIGHTSIZE*SECTHEIGHTSIZE];
+#ifndef SERVERONLY
+ byte_vec4_t colours[SECTHEIGHTSIZE*SECTHEIGHTSIZE];
+ char texname[4][MAX_QPATH];
+ int lightmap;
+ int lmx, lmy;
+
+ texnums_t textures;
+ vbo_t vbo;
+ mesh_t mesh;
+ mesh_t *amesh;
+#endif
+*/
};
enum
{
@@ -193,6 +211,7 @@ typedef struct
float minh, maxh;
struct heightmap_s *hmmod;
+ //FIXME: make layers, each with their own holes+heights+contents+textures+shader+mixes. water will presumably have specific values set for each part.
struct hmwater_s *water;
size_t traceseq;
@@ -315,7 +334,7 @@ typedef struct heightmap_s
size_t drawnframe; //don't add it to the scene multiple times.
size_t traceseq; //don't trace through this entity multiple times if its in different sections.
int refs; //entity is free/reusable when its no longer referenced by any sections
- entity_t ent;
+ entity_t ent; //note: only model+modelmatrix info is relevant. fixme: implement instancing.
struct hmentity_s *next; //used for freeing/allocating an entity
} *entities;
@@ -772,6 +791,9 @@ static void Terr_AddMesh(heightmap_t *hm, int loadflags, model_t *mod, vec3_t ep
e->ent.drawflags = SCALE_ORIGIN_ORIGIN;
e->ent.scale = scale;
e->ent.playerindex = -1;
+ e->ent.framestate.g[FS_REG].lerpweight[0] = 1;
+ e->ent.topcolour = TOP_DEFAULT;
+ e->ent.bottomcolour = BOTTOM_DEFAULT;
e->ent.shaderRGBAf[0] = 1;
e->ent.shaderRGBAf[1] = 1;
e->ent.shaderRGBAf[2] = 1;
@@ -3164,6 +3186,19 @@ void Terr_DrawTerrainModel (batch_t **batches, entity_t *e)
tdibctx.wmodel = e->model;
tdibctx.pvs = (e->model == cl.worldmodel)?frustumvis:NULL;
Terr_DrawInBounds(&tdibctx, bounds[0], bounds[2], bounds[1]-bounds[0], bounds[3]-bounds[2]);
+
+
+ /*{
+ trace_t trace;
+ vec3_t player_mins = {-16, -16, -24};
+ vec3_t player_maxs = {16, 16, 32};
+ vec3_t start, end;
+ VectorCopy(cl.playerview[0].simorg, start);
+ VectorCopy(start, end);
+ start[0] += 5;
+ end[2] -= 100;
+ Heightmap_Trace(cl.worldmodel, 0, 0, NULL, start, end, player_mins, player_maxs, false, ~0, &trace);
+ }*/
}
void Terrain_ClipDecal(fragmentdecal_t *dec, float *center, float radius, model_t *model)
@@ -3460,6 +3495,8 @@ typedef struct {
vec3_t maxs;
vec3_t absmins;
vec3_t absmaxs;
+ vec3_t up;
+ vec3_t capsulesize;
enum {ispoint, iscapsule, isbox} shape;
float frac;
float htilesize;
@@ -3467,6 +3504,10 @@ typedef struct {
int contents;
int hitcontentsmask;
trace_t *result;
+
+#ifdef _DEBUG
+ qboolean debug;
+#endif
} hmtrace_t;
static int Heightmap_Trace_Brush(hmtrace_t *tr, vec4_t *planes, int numplanes)
@@ -3503,7 +3544,11 @@ static int Heightmap_Trace_Brush(hmtrace_t *tr, vec4_t *planes, int numplanes)
dist = DotProduct (ofs, planes[i]);
dist = planes[i][3] - dist;
break;
-// capsuledist(dist,plane,mins,maxs)
+ case iscapsule:
+ dist = DotProduct(tr->up, planes[i]);
+ dist = dist*(tr->capsulesize[(dist<0)?1:2]) - tr->capsulesize[0];
+ dist = planes[i][3] - dist;
+ break;
case ispoint: // special point case
dist = planes[i][3];
break;
@@ -3548,6 +3593,57 @@ static int Heightmap_Trace_Brush(hmtrace_t *tr, vec4_t *planes, int numplanes)
if (!startout)
{
+
+#if 0//def _DEBUG
+ if (tr->debug)
+ {
+ vecV_t facepoints[256];
+ unsigned int numpoints;
+
+ for (i = 0; i < numplanes; i++)
+ {
+ scenetris_t *t;
+ extern shader_t *shader_draw_fill;
+ //generate points now (so we know the correct mins+maxs for the brush, and whether the plane is relevent)
+ numpoints = Terr_GenerateBrushFace(facepoints, countof(facepoints), planes, numplanes, planes[i]);
+
+
+ if (cl_numstrisvert+numpoints > cl_maxstrisvert)
+ break;
+ if (cl_numstrisidx+(numpoints-2)*3 > cl_maxstrisidx)
+ break;
+
+ if (cl_numstris == cl_maxstris)
+ {
+ cl_maxstris+=8;
+ cl_stris = BZ_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris);
+ }
+ t = &cl_stris[cl_numstris++];
+ t->shader = shader_draw_fill;
+ t->flags = 0;
+ t->firstidx = cl_numstrisidx;
+ t->firstvert = cl_numstrisvert;
+ for (j = 2; j < numpoints; j++)
+ {
+ cl_strisidx[cl_numstrisidx++] = 0;
+ cl_strisidx[cl_numstrisidx++] = j-1;
+ cl_strisidx[cl_numstrisidx++] = j;
+ }
+ for (j = 0; j < numpoints; j++)
+ {
+ VectorCopy(facepoints[j], cl_strisvertv[cl_numstrisvert]);
+ cl_strisvertv[cl_numstrisvert][2] += 1;
+ Vector4Set(cl_strisvertc[cl_numstrisvert], 1, 0, 0, 0.2);
+ Vector2Set(cl_strisvertt[cl_numstrisvert], 0, 0);
+ cl_numstrisvert++;
+ }
+ t->numidx = cl_numstrisidx - t->firstidx;
+ t->numvert = cl_numstrisvert-t->firstvert;
+ }
+ }
+#endif
+
+
tr->frac = -1;
return false;
}
@@ -3561,6 +3657,58 @@ static int Heightmap_Trace_Brush(hmtrace_t *tr, vec4_t *planes, int numplanes)
tr->frac = nearfrac;//enterfrac;
tr->plane[3] = enterdist;
VectorCopy(enterplane, tr->plane);
+
+
+#if 0//def _DEBUG
+ if (tr->debug)
+ {
+ vecV_t facepoints[256];
+ unsigned int numpoints;
+
+ for (i = 0; i < numplanes; i++)
+ {
+ scenetris_t *t;
+ extern shader_t *shader_draw_fill;
+ //generate points now (so we know the correct mins+maxs for the brush, and whether the plane is relevent)
+ numpoints = Terr_GenerateBrushFace(facepoints, countof(facepoints), planes, numplanes, planes[i]);
+
+
+ if (cl_numstrisvert+numpoints > cl_maxstrisvert)
+ break;
+ if (cl_numstrisidx+(numpoints-2)*3 > cl_maxstrisidx)
+ break;
+
+ if (cl_numstris == cl_maxstris)
+ {
+ cl_maxstris+=8;
+ cl_stris = BZ_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris);
+ }
+ t = &cl_stris[cl_numstris++];
+ t->shader = shader_draw_fill;
+ t->flags = 0;
+ t->firstidx = cl_numstrisidx;
+ t->firstvert = cl_numstrisvert;
+ for (j = 2; j < numpoints; j++)
+ {
+ cl_strisidx[cl_numstrisidx++] = 0;
+ cl_strisidx[cl_numstrisidx++] = j-1;
+ cl_strisidx[cl_numstrisidx++] = j;
+ }
+ for (j = 0; j < numpoints; j++)
+ {
+ VectorCopy(facepoints[j], cl_strisvertv[cl_numstrisvert]);
+ cl_strisvertv[cl_numstrisvert][2] += 1;
+ Vector4Set(cl_strisvertc[cl_numstrisvert], 0, 1, 0, 0.2);
+ Vector2Set(cl_strisvertt[cl_numstrisvert], 0, 0);
+ cl_numstrisvert++;
+ }
+ t->numidx = cl_numstrisidx - t->firstidx;
+ t->numvert = cl_numstrisvert-t->firstvert;
+ }
+ }
+#endif
+
+
return ((vec4_t*)enterplane - planes)+1;
}
}
@@ -3573,7 +3721,7 @@ static void Heightmap_Trace_Square(hmtrace_t *tr, int tx, int ty)
{
vec3_t d[2];
vec3_t p[4];
- vec4_t n[5];
+ vec4_t n[6];
int t;
int i;
@@ -3630,8 +3778,8 @@ static void Heightmap_Trace_Square(hmtrace_t *tr, int tx, int ty)
//figure out where on the submodel the trace is.
VectorSubtract (tr->start, s->ents[i]->ent.origin, start_l);
VectorSubtract (tr->end, s->ents[i]->ent.origin, end_l);
- start_l[2] -= tr->mins[2];
- end_l[2] -= tr->mins[2];
+// start_l[2] -= tr->mins[2];
+// end_l[2] -= tr->mins[2];
VectorScale(start_l, s->ents[i]->ent.scale, start_l);
VectorScale(end_l, s->ents[i]->ent.scale, end_l);
@@ -3649,16 +3797,37 @@ static void Heightmap_Trace_Square(hmtrace_t *tr, int tx, int ty)
etr.fraction = 1;
model->funcs.NativeTrace (model, 0, frame, s->ents[i]->ent.axis, start_l, end_l, tr->mins, tr->maxs, tr->shape == iscapsule, tr->hitcontentsmask, &etr);
- tr->result->startsolid |= etr.startsolid;
- tr->result->allsolid |= etr.allsolid;
- if (etr.fraction < tr->frac)
+ if (etr.startsolid)
+ { //many many bsp objects are not enclosed 'properly' (qbsp strips any surfaces outside the world).
+ //this means that such bsps extend to infinity, resulting in sudden glitchy stuck issues when you enter a section containing such a bsp
+ //so if we started solid, constrain that solidity to the volume of the submodel
+ VectorCopy (s->ents[i]->ent.axis[0], n[0]);
+ VectorNegate(s->ents[i]->ent.axis[0], n[1]);
+ VectorCopy (s->ents[i]->ent.axis[1], n[2]);
+ VectorNegate(s->ents[i]->ent.axis[1], n[3]);
+ VectorCopy (s->ents[i]->ent.axis[2], n[4]);
+ VectorNegate(s->ents[i]->ent.axis[2], n[5]);
+ n[0][3] = DotProduct(n[0], s->ents[i]->ent.origin) + model->maxs[0];
+ n[1][3] = DotProduct(n[1], s->ents[i]->ent.origin) + -model->mins[0];
+ n[2][3] = DotProduct(n[2], s->ents[i]->ent.origin) + model->maxs[1];
+ n[3][3] = DotProduct(n[3], s->ents[i]->ent.origin) + -model->mins[1];
+ n[4][3] = DotProduct(n[4], s->ents[i]->ent.origin) + model->maxs[2];
+ n[5][3] = DotProduct(n[5], s->ents[i]->ent.origin) + -model->mins[2];
+ Heightmap_Trace_Brush(tr, n, 6);
+ }
+ else
{
- tr->contents = etr.contents;
- tr->frac = etr.fraction;
- tr->plane[3] = etr.plane.dist;
- tr->plane[0] = etr.plane.normal[0];
- tr->plane[1] = etr.plane.normal[1];
- tr->plane[2] = etr.plane.normal[2];
+ tr->result->startsolid |= etr.startsolid;
+ tr->result->allsolid |= etr.allsolid;
+ if (etr.fraction < tr->frac)
+ {
+ tr->contents = etr.contents;
+ tr->frac = etr.fraction;
+ tr->plane[3] = etr.plane.dist;
+ tr->plane[0] = etr.plane.normal[0];
+ tr->plane[1] = etr.plane.normal[1];
+ tr->plane[2] = etr.plane.normal[2];
+ }
}
}
Sys_UnlockMutex(tr->hm->entitylock);
@@ -3697,110 +3866,116 @@ static void Heightmap_Trace_Square(hmtrace_t *tr, int tx, int ty)
VectorSet(p[2], tr->htilesize*(sx+0), tr->htilesize*(sy+1), s->heights[(tx+0)+(ty+1)*SECTHEIGHTSIZE]);
VectorSet(p[3], tr->htilesize*(sx+1), tr->htilesize*(sy+1), s->heights[(tx+1)+(ty+1)*SECTHEIGHTSIZE]);
+ VectorSet(n[5], 0, 0, 1);
#ifndef STRICTEDGES
d1 = fabs(p[0][2] - p[3][2]);
d2 = fabs(p[1][2] - p[2][2]);
if (d1 < d2)
{
- for (t = 0; t < 2; t++)
+ /*generate the brush (in world space*/
{
- /*generate the brush (in world space*/
- if (t == 0)
- {
- VectorSubtract(p[3], p[2], d[0]);
- VectorSubtract(p[2], p[0], d[1]);
- //left-most
- Vector4Set(n[0], -1, 0, 0, -tr->htilesize*(sx+0));
- //bottom-most
- Vector4Set(n[1], 0, 1, 0, tr->htilesize*(sy+1));
- //top-right
- VectorSet(n[2], 0.70710678118654752440084436210485, -0.70710678118654752440084436210485, 0);
- n[2][3] = DotProduct(n[2], p[0]);
- //top
- VectorNormalize(d[0]);
- VectorNormalize(d[1]);
- CrossProduct(d[0], d[1], n[3]);
- VectorNormalize(n[3]);
- n[3][3] = DotProduct(n[3], p[0]);
- //down
- VectorNegate(n[3], n[4]);
- n[4][3] = DotProduct(n[4], p[0]) - n[4][2]*TERRAINTHICKNESS;
- }
- else
- {
- VectorSubtract(p[1], p[0], d[0]);
- VectorSubtract(p[3], p[1], d[1]);
+ VectorSubtract(p[3], p[0], d[0]);
+ VectorSubtract(p[2], p[0], d[1]);
+ //left-most
+ Vector4Set(n[0], -1, 0, 0, -tr->htilesize*(sx+0));
+ //bottom-most
+ Vector4Set(n[1], 0, 1, 0, tr->htilesize*(sy+1));
+ //top-right
+ VectorSet(n[2], 0.70710678118654752440084436210485, -0.70710678118654752440084436210485, 0);
+ n[2][3] = DotProduct(n[2], p[0]);
+ //top
+ VectorNormalize(d[0]);
+ VectorNormalize(d[1]);
+ CrossProduct(d[0], d[1], n[3]);
+ VectorNormalize(n[3]);
+ n[3][3] = DotProduct(n[3], p[0]);
+ //down
+ VectorNegate(n[3], n[4]);
+ n[4][3] = DotProduct(n[4], p[0]) - n[4][2]*TERRAINTHICKNESS;
- //right-most
- Vector4Set(n[0], 1, 0, 0, tr->htilesize*(sx+1));
- //top-most
- Vector4Set(n[1], 0, -1, 0, -tr->htilesize*(sy+0));
- //bottom-left
- VectorSet(n[2], -0.70710678118654752440084436210485, 0.70710678118654752440084436210485, 0);
- n[2][3] = DotProduct(n[2], p[0]);
- //top
- VectorNormalize(d[0]);
- VectorNormalize(d[1]);
- CrossProduct(d[0], d[1], n[3]);
- VectorNormalize(n[3]);
- n[3][3] = DotProduct(n[3], p[0]);
- //down
- VectorNegate(n[3], n[4]);
- n[4][3] = DotProduct(n[4], p[0]) - n[4][2]*TERRAINTHICKNESS;
- }
- Heightmap_Trace_Brush(tr, n, 5);
+ n[5][3] = max(p[0][2], p[2][2]);
+ n[5][3] = max(n[5][3], p[3][2]);
+ Heightmap_Trace_Brush(tr, n, 6);
+ }
+
+ {
+ VectorSubtract(p[3], p[0], d[0]);
+ VectorSubtract(p[3], p[1], d[1]);
+
+ //right-most
+ Vector4Set(n[0], 1, 0, 0, tr->htilesize*(sx+1));
+ //top-most
+ Vector4Set(n[1], 0, -1, 0, -tr->htilesize*(sy+0));
+ //bottom-left
+ VectorSet(n[2], -0.70710678118654752440084436210485, 0.70710678118654752440084436210485, 0);
+ n[2][3] = DotProduct(n[2], p[0]);
+ //top
+ VectorNormalize(d[0]);
+ VectorNormalize(d[1]);
+ CrossProduct(d[0], d[1], n[3]);
+ VectorNormalize(n[3]);
+ n[3][3] = DotProduct(n[3], p[0]);
+ //down
+ VectorNegate(n[3], n[4]);
+ n[4][3] = DotProduct(n[4], p[0]) - n[4][2]*TERRAINTHICKNESS;
+
+ n[5][3] = max(p[0][2], p[1][2]);
+ n[5][3] = max(n[5][3], p[3][2]);
+ Heightmap_Trace_Brush(tr, n, 6);
}
}
else
#endif
{
- for (t = 0; t < 2; t++)
+ /*generate the brush (in world space*/
{
- /*generate the brush (in world space*/
- if (t == 0)
- {
- VectorSubtract(p[1], p[0], d[0]);
- VectorSubtract(p[2], p[0], d[1]);
- //left-most
- Vector4Set(n[0], -1, 0, 0, -tr->htilesize*(sx+0));
- //top-most
- Vector4Set(n[1], 0, -1, 0, -tr->htilesize*(sy+0));
- //bottom-right
- VectorSet(n[2], 0.70710678118654752440084436210485, 0.70710678118654752440084436210485, 0);
- n[2][3] = DotProduct(n[2], p[1]);
- //top
- VectorNormalize(d[0]);
- VectorNormalize(d[1]);
- CrossProduct(d[0], d[1], n[3]);
- VectorNormalize(n[3]);
- n[3][3] = DotProduct(n[3], p[1]);
- //down
- VectorNegate(n[3], n[4]);
- n[4][3] = DotProduct(n[4], p[1]) - n[4][2]*TERRAINTHICKNESS;
- }
- else
- {
- VectorSubtract(p[3], p[2], d[0]);
- VectorSubtract(p[3], p[1], d[1]);
+ VectorSubtract(p[1], p[0], d[0]);
+ VectorSubtract(p[2], p[0], d[1]);
+ //left-most
+ Vector4Set(n[0], -1, 0, 0, -tr->htilesize*(sx+0));
+ //top-most
+ Vector4Set(n[1], 0, -1, 0, -tr->htilesize*(sy+0));
+ //bottom-right
+ VectorSet(n[2], 0.70710678118654752440084436210485, 0.70710678118654752440084436210485, 0);
+ n[2][3] = DotProduct(n[2], p[1]);
+ //top
+ VectorNormalize(d[0]);
+ VectorNormalize(d[1]);
+ CrossProduct(d[0], d[1], n[3]);
+ VectorNormalize(n[3]);
+ n[3][3] = DotProduct(n[3], p[1]);
+ //down
+ VectorNegate(n[3], n[4]);
+ n[4][3] = DotProduct(n[4], p[1]) - n[4][2]*TERRAINTHICKNESS;
- //right-most
- Vector4Set(n[0], 1, 0, 0, tr->htilesize*(sx+1));
- //bottom-most
- Vector4Set(n[1], 0, 1, 0, tr->htilesize*(sy+1));
- //top-left
- VectorSet(n[2], -0.70710678118654752440084436210485, -0.70710678118654752440084436210485, 0);
- n[2][3] = DotProduct(n[2], p[1]);
- //top
- VectorNormalize(d[0]);
- VectorNormalize(d[1]);
- CrossProduct(d[0], d[1], n[3]);
- VectorNormalize(n[3]);
- n[3][3] = DotProduct(n[3], p[1]);
- //down
- VectorNegate(n[3], n[4]);
- n[4][3] = DotProduct(n[4], p[1]) - n[4][2]*TERRAINTHICKNESS;
- }
- Heightmap_Trace_Brush(tr, n, 5);
+ n[5][3] = max(p[0][2], p[1][2]);
+ n[5][3] = max(n[5][3], p[2][2]);
+ Heightmap_Trace_Brush(tr, n, 6);
+ }
+ {
+ VectorSubtract(p[3], p[2], d[0]);
+ VectorSubtract(p[3], p[1], d[1]);
+
+ //right-most
+ Vector4Set(n[0], 1, 0, 0, tr->htilesize*(sx+1));
+ //bottom-most
+ Vector4Set(n[1], 0, 1, 0, tr->htilesize*(sy+1));
+ //top-left
+ VectorSet(n[2], -0.70710678118654752440084436210485, -0.70710678118654752440084436210485, 0);
+ n[2][3] = DotProduct(n[2], p[1]);
+ //top
+ VectorNormalize(d[0]);
+ VectorNormalize(d[1]);
+ CrossProduct(d[0], d[1], n[3]);
+ VectorNormalize(n[3]);
+ n[3][3] = DotProduct(n[3], p[1]);
+ //down
+ VectorNegate(n[3], n[4]);
+ n[4][3] = DotProduct(n[4], p[1]) - n[4][2]*TERRAINTHICKNESS;
+
+ n[5][3] = max(p[1][2], p[2][2]);
+ n[5][3] = max(n[5][3], p[3][2]);
+ Heightmap_Trace_Brush(tr, n, 6);
}
}
break;
@@ -3848,6 +4023,21 @@ qboolean Heightmap_Trace(struct model_s *model, int hulloverride, int frame, vec
{
hmtrace.shape = iscapsule;
zbias = 0;
+
+ if (mataxis)
+ VectorSet(hmtrace.up, mataxis[0][2], -mataxis[1][2], mataxis[2][2]);
+ else
+ VectorSet(hmtrace.up, 0, 0, 1);
+
+ //determine the capsule sizes
+ hmtrace.capsulesize[0] = ((maxs[0]-mins[0]) + (maxs[1]-mins[1]))/4.0;
+ hmtrace.capsulesize[1] = maxs[2];
+ hmtrace.capsulesize[2] = mins[2];
+// zbias = (trace_capsulesize[1] > -hmtrace.capsulesize[2])?hmtrace.capsulesize[1]:-hmtrace.capsulesize[2];
+ hmtrace.capsulesize[1] -= hmtrace.capsulesize[0];
+ hmtrace.capsulesize[2] += hmtrace.capsulesize[0];
+
+ zbias = 0;
}
else if (mins[0] || mins[1] || mins[2] || maxs[0] || maxs[1] || maxs[2])
{
@@ -4190,6 +4380,21 @@ static void ted_heightsmooth(void *ctx, hmsection_t *s, int idx, float wx, float
else
s->heights[idx] = s->heights[idx]*(1-w) + w**(float*)ctx;
}
+static void ted_heightdebug(void *ctx, hmsection_t *s, int idx, float wx, float wy, float w)
+{
+ int tx = idx/SECTHEIGHTSIZE, ty = idx % SECTHEIGHTSIZE;
+ s->flags |= TSF_NOTIFY|TSF_DIRTY|TSF_EDITED|TSF_RELIGHT;
+ /*interpolate the terrain towards a certain value*/
+
+ if (tx == 16)
+ tx = 0;
+ if (ty == 16)
+ ty = 0;
+
+// if (ty < tx)
+// tx = ty;
+ s->heights[idx] = (tx>>1) * 32 + (ty>>1) * 32;
+}
static void ted_heightraise(void *ctx, hmsection_t *s, int idx, float wx, float wy, float strength)
{
s->flags |= TSF_NOTIFY|TSF_DIRTY|TSF_EDITED|TSF_RELIGHT;
@@ -4583,18 +4788,70 @@ void QCBUILTIN PF_terrain_edit(pubprogfuncs_t *prinst, struct globalvars_s *pr_g
{
if (mod && mod->loadstate == MLS_LOADING)
COM_WorkerPartialSync(mod, &mod->loadstate, MLS_LOADING);
- if (mod && mod->loadstate == MLS_LOADED)
+ }
+ if (mod->loadstate != MLS_LOADED)
+ return;
+
+ switch(action)
+ {
+ case ter_ents_wipe:
+ G_INT(OFS_RETURN) = PR_TempString(prinst, mod->entities);
+ mod->entities = Z_Malloc(1);
+ return;
+ case ter_ents_concat:
{
- char basename[MAX_QPATH];
- COM_FileBase(mod->name, basename, sizeof(basename));
- mod->terrain = Mod_LoadTerrainInfo(mod, basename, true);
- hm = mod->terrain;
- if (!hm)
- return;
- Terr_FinishTerrain(mod);
+ char *olds = mod->entities;
+ const char *news = PR_GetStringOfs(prinst, OFS_PARM1);
+ size_t oldlen = strlen(olds);
+ size_t newlen = strlen(news);
+ mod->entities = Z_Malloc(oldlen + newlen + 1);
+ memcpy(mod->entities, olds, oldlen);
+ memcpy(mod->entities+oldlen, news, newlen);
+ mod->entities[oldlen + newlen] = 0;
+ Z_Free(olds);
+ G_FLOAT(OFS_RETURN) = oldlen + newlen;
+ }
+ return;
+ case ter_ents_get:
+ G_INT(OFS_RETURN) = PR_TempString(prinst, mod->entities);
+ return;
+ case ter_save:
+ if (mod->terrain)
+ {
+ quant = Heightmap_Save(mod->terrain);
+ Con_DPrintf("ter_save: %g sections saved\n", quant);
+ }
+ G_FLOAT(OFS_RETURN) = quant;
+ /*
+ if (mod->type == mod_brush)
+ {
+ Con_Printf("that model isn't a suitable worldmodel\n");
+ return;
}
else
+ {
+ FS_CreatePath(fname, FS_GAMEONLY);
+ file = FS_OpenVFS(fname, "wb", FS_GAMEONLY);
+ if (!file)
+ Con_Printf("unable to open %s\n", fname);
+ else
+ {
+ Terr_WriteMapFile(file, mod);
+ VFS_CLOSE(file);
+ }
+ }*/
+ return;
+ }
+
+ if (!mod->terrain)
+ {
+ char basename[MAX_QPATH];
+ COM_FileBase(mod->name, basename, sizeof(basename));
+ mod->terrain = Mod_LoadTerrainInfo(mod, basename, true);
+ hm = mod->terrain;
+ if (!hm)
return;
+ Terr_FinishTerrain(mod);
}
hm = mod->terrain;
@@ -4608,11 +4865,6 @@ void QCBUILTIN PF_terrain_edit(pubprogfuncs_t *prinst, struct globalvars_s *pr_g
G_FLOAT(OFS_RETURN) = 1;
Terr_PurgeTerrainModel(mod, false, true);
break;
- case ter_save:
- quant = Heightmap_Save(hm);
- Con_DPrintf("ter_save: %g sections saved\n", quant);
- G_FLOAT(OFS_RETURN) = quant;
- break;
case ter_sethole:
/* {
int x, y;
@@ -4643,6 +4895,8 @@ void QCBUILTIN PF_terrain_edit(pubprogfuncs_t *prinst, struct globalvars_s *pr_g
if (IS_NAN(tally[0]))
tally[0] = 0;
ted_itterate(hm, tid_exponential, pos, radius, quant, SECTHEIGHTSIZE, ted_heightsmooth, &tally);
+
+ ted_itterate(hm, tid_exponential, pos, radius, quant, SECTHEIGHTSIZE, ted_heightdebug, &tally);
break;
case ter_height_smooth:
tally[0] = 0;
@@ -6242,7 +6496,7 @@ void Mod_Terrain_Save_f(void)
vfsfile_t *file;
model_t *mod;
const char *mapname = Cmd_Argv(1);
- const char *fname = Cmd_Argv(2);
+ char fname[MAX_QPATH];
if (Cmd_IsInsecure())
{
Con_Printf("Please use this command via the console\n");
@@ -6262,24 +6516,51 @@ void Mod_Terrain_Save_f(void)
Con_Printf("no model loaded by that name\n");
return;
}
- if (!mod->terrain || mod->loadstate != MLS_LOADED)
+ if (mod->loadstate != MLS_LOADED)
{
- Con_Printf("that model has no content worth saving, or isn't fully loaded\n");
+ Con_Printf("that model isn't fully loaded\n");
return;
}
- if (!*fname)
- fname = mod->name;
+ if (*Cmd_Argv(2))
+ Q_snprintfz(fname, sizeof(fname), "maps/%s.map", Cmd_Argv(2));
else
- fname = va("maps/%s.map", fname);
- FS_CreatePath(fname, FS_GAMEONLY);
- file = FS_OpenVFS(fname, "wb", FS_GAMEONLY);
- if (!file)
- Con_Printf("unable to open %s\n", fname);
+ Q_snprintfz(fname, sizeof(fname), "%s", mod->name);
+
+ if (mod->type != mod_heightmap)
+ {
+ //warning: brushes are not saved unless its a .map
+ COM_StripExtension(mod->name, fname, sizeof(fname));
+ Q_strncatz(fname, ".ent", sizeof(fname));
+
+ FS_CreatePath(fname, FS_GAMEONLY);
+ file = FS_OpenVFS(fname, "wb", FS_GAMEONLY);
+ if (!file)
+ Con_Printf("unable to open %s\n", fname);
+ else
+ {
+ VFS_WRITE(file, mod->entities, strlen(mod->entities));
+ VFS_CLOSE(file);
+ }
+ }
else
{
- Terr_WriteMapFile(file, mod);
- VFS_CLOSE(file);
+ if (mod->type != mod_brush)
+ {
+ Con_Printf("that model isn't a suitable worldmodel\n");
+ return;
+ }
+
+ FS_CreatePath(fname, FS_GAMEONLY);
+ file = FS_OpenVFS(fname, "wb", FS_GAMEONLY);
+ if (!file)
+ Con_Printf("unable to open %s\n", fname);
+ else
+ {
+ Terr_WriteMapFile(file, mod);
+ VFS_CLOSE(file);
+ }
}
+ FS_FlushFSHash();
}
qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities)
{
@@ -6306,7 +6587,7 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities)
#endif
/*FIXME: we need to re-form the entities lump to insert model fields as appropriate*/
- mod->entities = out = ZG_Malloc(&mod->memgroup, buflen+1);
+ mod->entities = out = Z_Malloc(buflen+1);
while(entities)
{
@@ -6369,7 +6650,6 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities)
if (submod->loadstate == MLS_NOTLOADED)
{
submod->type = mod_heightmap;
- submod->entities = "";
subhm = submod->terrain = Mod_LoadTerrainInfo(submod, submod->name, true);
subhm->exteriorcontents = FTECONTENTS_EMPTY;
diff --git a/engine/gl/gl_model.c b/engine/gl/gl_model.c
index 237c3de32..858c0f4c7 100644
--- a/engine/gl/gl_model.c
+++ b/engine/gl/gl_model.c
@@ -520,6 +520,9 @@ void Mod_Purge(enum mod_purge_e ptype)
mod->meshinfo = NULL;
}
+ Z_Free(mod->entities);
+ mod->entities = NULL;
+
//and obliterate anything else remaining in memory.
ZG_FreeGroup(&mod->memgroup);
mod->meshinfo = NULL;
@@ -2085,24 +2088,24 @@ void Mod_LoadEntities (model_t *loadmodel, qbyte *mod_base, lump_t *l)
Q_snprintfz(fname, sizeof(fname), "maps/%s/%s", mod_loadentfiles_dir.string, loadmodel->name+5);
COM_StripExtension(fname, fname, sizeof(fname));
Q_strncatz(fname, ".ent", sizeof(fname));
- loadmodel->entities = FS_LoadMallocGroupFile(&loadmodel->memgroup, fname, &sz);
+ loadmodel->entities = FS_LoadMallocFile(fname, &sz);
}
}
if (mod_loadentfiles.value && !loadmodel->entities)
{
COM_StripExtension(loadmodel->name, fname, sizeof(fname));
Q_strncatz(fname, ".ent", sizeof(fname));
- loadmodel->entities = FS_LoadMallocGroupFile(&loadmodel->memgroup, fname, &sz);
+ loadmodel->entities = FS_LoadMallocFile(fname, &sz);
}
if (mod_loadentfiles.value && !loadmodel->entities)
{ //tenebrae compat
COM_StripExtension(loadmodel->name, fname, sizeof(fname));
Q_strncatz(fname, ".edo", sizeof(fname));
- loadmodel->entities = FS_LoadMallocGroupFile(&loadmodel->memgroup, fname, &sz);
+ loadmodel->entities = FS_LoadMallocFile(fname, &sz);
}
if (!loadmodel->entities)
{
- loadmodel->entities = ZG_Malloc(&loadmodel->memgroup, l->filelen + 1);
+ loadmodel->entities = Z_Malloc(l->filelen + 1);
memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen);
loadmodel->entities[l->filelen] = 0;
}
@@ -4761,6 +4764,9 @@ TRACE(("LoadBrushModel %i\n", __LINE__));
submod->numclusters = bm->visleafs;
+ if (i)
+ submod->entities = NULL;
+
memset(&submod->batches, 0, sizeof(submod->batches));
submod->vbos = NULL;
TRACE(("LoadBrushModel %i\n", __LINE__));
diff --git a/engine/http/httpclient.c b/engine/http/httpclient.c
index 632f3127d..5dfa009ca 100644
--- a/engine/http/httpclient.c
+++ b/engine/http/httpclient.c
@@ -70,7 +70,7 @@ static void DL_OnError(void *c)
#else
dl->replycode = 404; //we don't actually know. should we not do this?
#endif
- Con_Printf("download %p: error %i\n", dl, dl->replycode);
+ Con_Printf("download: %s: error %i\n", dl->url, dl->replycode);
dl->status = DL_FAILED;
}
static void DL_OnProgress(void *c, int position, int totalsize)
diff --git a/engine/partcfgs/high.cfg b/engine/partcfgs/high.cfg
index 0a8245a3d..162b5508b 100644
--- a/engine/partcfgs/high.cfg
+++ b/engine/partcfgs/high.cfg
@@ -295,12 +295,85 @@ cl_expsprite 0
//hide it in nq - WARNING: some mods use this sprite as a flame thrower.
//r_effect "progs/s_explod.spr" hidden 1
+
+//////////////////////////////////////////
+//rogue te_explosion2 effect
+//note: if not otherwise defined, te_explosion2_BASE_RAND maps to this, and specifies the palette colours.
+r_part te_explosion2
+{
+ type texturedspark
+ texture "particles/fteparticlefont.tga"
+ tcoords 1 65 31 95 256 8 32
+ count 256
+ scale 5
+ scalefactor 1
+ scaledelta -15
+ alpha 0.2
+ die 0.5
+ blend add
+ spawnmode ball
+ spawnorg 1
+ randomvel 1000
+ friction 0.01
+ gravity 100
+ stretchfactor -80
+}
+//dragon fireball
+//r_part te_explosion2_228_5
+
+//rogue multigrenade sub explosion
+//also triggered from a shielded rogue player touching another player (and doing some damage)
+//also used during the ending.
+//red particles
+//r_part te_explosion2_230_5
+
+//rogue plasma explosion
+//also rogue timemachine explosion
+//white particles splaying outwards
+//r_part te_explosion2_244_3
+
//////////////////////////////////////////
//for when a spawn dies.
//also used by TF for emp explosions.
-//r_part te_tarexplosion
-//{
-//}
+r_part te_tarexplosion
+{
+ type texturedspark
+ texture "particles/fteparticlefont.tga"
+ tcoords 1 65 31 95 256 8 32
+ count 128
+ scale 5
+ scalefactor 1
+ scaledelta -15
+ rgb 0 0 17
+ alpha 0.5
+ die 0.5
+ spawnmode ball
+ spawnorg 1
+ randomvel 500
+ friction 0.01
+ gravity 100
+ stretchfactor -80
+}
+r_part +te_tarexplosion
+{
+ type texturedspark
+ texture "particles/fteparticlefont.tga"
+ tcoords 1 65 31 95 256 8 32
+ count 256
+ scale 5
+ scalefactor 1
+ scaledelta -15
+ rgb 83 67 115
+ alpha 0.3
+ die 0.5
+ blend add
+ spawnmode ball
+ spawnorg 1
+ randomvel 500
+ friction 0.01
+ gravity 100
+ stretchfactor -80
+}
//////////////////////////////////////////
//cthon falling into lava.
@@ -444,6 +517,8 @@ r_part tr_vorespike
//rygel's pack sucks
r_trail "progs/v_spike.mdl" tr_vorespike
+
+////////////////////
//enforcer laser effect
r_part tr_enforcerlaser
{
@@ -471,6 +546,79 @@ r_part tr_enforcerlaser
}
r_trail "progs/laser.mdl" tr_enforcerlaser
+/////////////////////////////////////////
+//rogue wrath enemy's projectiles
+r_part tr_wrathball
+{
+ type texturedspark
+ texture "particles/fteparticlefont.tga"
+ tcoords 1 97 95 191 256
+ scale 15
+ step 4
+ alpha 0.3
+ die 0.5
+ rgb 255 0 0
+ veladd -32
+ spawnmode spiral
+ spawnvel 16
+ randomvel 32
+ friction 0
+ scalefactor 1
+ blend add
+ lighttime 0.2
+ lightshadows 0
+ lightradius 150
+ lightrgb 1 0.27 0
+ lightrgbfade 5 1 0
+ lightcorona 2 0.5
+}
+r_trail "progs/w_ball.mdl" tr_wrathball
+
+//wrath death
+//grey particles
+//no difference from the fallback except for the blend mode. this should ensure that we are not quite so invisible.
+r_part te_explosion2_0_4
+{
+ type texturedspark
+ texture "particles/fteparticlefont.tga"
+ tcoords 1 65 31 95 256 8 32
+ count 256
+ scale 5
+ scalefactor 1
+ scaledelta -15
+ alpha 0.2
+ die 0.5
+ spawnmode ball
+ spawnorg 1
+ randomvel 1000
+ friction 0.01
+ gravity 100
+ stretchfactor -80
+}
+
+/////////////////////////////////////////
+//rogue lavaspikes
+r_part tr_lavaspike
+{
+ type spark
+ texture "particles/fteparticlefont.tga"
+ tcoords 1 97 95 191 256
+ scale 15
+ step 4
+ alpha 0.3
+ die 0.5
+ rgb 255 0 0
+ veladd -32
+ spawnmode spiral
+ spawnvel 16
+ randomvel 32
+ friction 0
+ scalefactor 1
+ blend add
+}
+r_trail "progs/lspike.mdl" tr_lavaspike
+
+
/////////////////////////////////////////
//scrag missiles.
r_part tr_wizspike
diff --git a/engine/server/net_preparse.c b/engine/server/net_preparse.c
index 117ee62c5..ee3149e4f 100644
--- a/engine/server/net_preparse.c
+++ b/engine/server/net_preparse.c
@@ -794,9 +794,32 @@ void NPP_NQFlush(void)
buffer[0] = svcfte_cgamepacket;
}
break;
+ case TE_EXPLOSION:
+ if (writedest == &sv.datagram)
+ { //for old clients, use a te_explosion.
+ //for clients that support it, use a TEQW_EXPLOSIONNOSPRITE
+ vec3_t org;
+ coorddata cd;
+ if (sv.multicast.cursize + bufferlen > sv.multicast.maxsize)
+ SV_FlushBroadcasts();
+ SZ_Write(&sv.multicast, buffer, bufferlen);
+
+ memcpy(&cd, &buffer[2+destprim->coordsize*0], destprim->coordsize);
+ org[0] = MSG_FromCoord(cd, destprim->coordsize);
+ memcpy(&cd, &buffer[2+destprim->coordsize*1], destprim->coordsize);
+ org[1] = MSG_FromCoord(cd, destprim->coordsize);
+ memcpy(&cd, &buffer[2+destprim->coordsize*2], destprim->coordsize);
+ org[2] = MSG_FromCoord(cd, destprim->coordsize);
+
+ requireextension = PEXT_TE_BULLET;
+ SV_MulticastProtExt(org, multicasttype, pr_global_struct->dimension_send, 0, requireextension);
+ buffer[1] = TEQW_EXPLOSIONNOSPRITE;
+ }
+ break;
case TENQ_EXPLOSION2: //happens with rogue.
- bufferlen -= 2; //trim the colour
- buffer[1] = TE_EXPLOSION;
+ //bufferlen -= 2; //trim the colour
+ //buffer[1] = TE_EXPLOSION;
+ buffer[1] = TEQW_EXPLOSION2;
break;
}
break;
@@ -1119,7 +1142,7 @@ void NPP_NQWriteByte(int dest, qbyte data) //replacement write func (nq to qw)
case TE_SPIKE:
case TE_SUPERSPIKE:
multicastpos=2;
- multicasttype=MULTICAST_PHS_R;
+ multicasttype=MULTICAST_PHS;
protocollen = destprim->coordsize*3+sizeof(qbyte)*2;
break;
case TE_LAVASPLASH:
@@ -1143,7 +1166,7 @@ void NPP_NQWriteByte(int dest, qbyte data) //replacement write func (nq to qw)
data = TEQW_EXPLOSION2;
protocollen = sizeof(qbyte)*4 + destprim->coordsize*3;
multicastpos=2;
- multicasttype=MULTICAST_PHS_R;
+ multicasttype=MULTICAST_PHS;
break;
case TE_EXPLOSIONSMALL2:
data = TE_EXPLOSION;
diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c
index eac2a7009..43f52fa6c 100644
--- a/engine/server/pr_cmds.c
+++ b/engine/server/pr_cmds.c
@@ -4998,6 +4998,14 @@ void SV_point_tempentity (vec3_t o, int type, int count) //count (usually 1) is
MSG_WriteByte (&sv.nqmulticast, type); //nq doesn't have a count.
#endif
break;
+ case TEQW_EXPLOSIONNOSPRITE:
+ MSG_WriteByte (&sv.multicast, TE_EXPLOSION);
+#ifdef NQPROT
+ MSG_WriteByte (&sv.nqmulticast, TE_EXPLOSION);
+#endif
+ type = TEQW_EXPLOSIONNOSPRITE;
+ split = PEXT_TE_BULLET;
+ break;
case TE_LIGHTNING1:
case TE_LIGHTNING2:
case TE_LIGHTNING3:
@@ -7964,7 +7972,10 @@ static void QCBUILTIN PF_te_superspikequad(pubprogfuncs_t *prinst, struct global
//void(vector org) te_explosion = #421;
static void QCBUILTIN PF_te_explosion(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
- SV_point_tempentity(G_VECTOR(OFS_PARM0), TE_EXPLOSION, 1);
+ if (progstype != PROG_QW)
+ SV_point_tempentity(G_VECTOR(OFS_PARM0), TEQW_EXPLOSIONNOSPRITE, 1);
+ else
+ SV_point_tempentity(G_VECTOR(OFS_PARM0), TE_EXPLOSION, 1);
}
//DP_TE_QUADEFFECTS1
static void QCBUILTIN PF_te_explosionquad(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
@@ -8008,11 +8019,32 @@ static void QCBUILTIN PF_te_teleport(pubprogfuncs_t *prinst, struct globalvars_s
}
//DP_TE_STANDARDEFFECTBUILTINS
-//void(vector org, float color) te_explosion2 = #427;
+//void(vector org, float color, float length) te_explosion2 = #427;
static void QCBUILTIN PF_te_explosion2(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
- //FIXME: QW doesn't support TE_EXPLOSION2...
- SV_point_tempentity(G_VECTOR(OFS_PARM0), TE_EXPLOSION, 1);
+ float *org = G_VECTOR(OFS_PARM0);
+ int start = G_FLOAT(OFS_PARM1);
+ int length = G_FLOAT(OFS_PARM2);
+ start = bound(0, start, 255);
+ length = bound(0, length, 255-start);
+
+ MSG_WriteByte (&sv.multicast, svc_temp_entity);
+ MSG_WriteByte (&sv.multicast, TEQW_EXPLOSION2);
+ MSG_WriteCoord (&sv.multicast, org[0]);
+ MSG_WriteCoord (&sv.multicast, org[1]);
+ MSG_WriteCoord (&sv.multicast, org[2]);
+ MSG_WriteByte (&sv.multicast, start);
+ MSG_WriteByte (&sv.multicast, length);
+#ifdef NQPROT
+ MSG_WriteByte (&sv.nqmulticast, svc_temp_entity);
+ MSG_WriteByte (&sv.nqmulticast, TENQ_EXPLOSION2);
+ MSG_WriteCoord (&sv.nqmulticast, org[0]);
+ MSG_WriteCoord (&sv.nqmulticast, org[1]);
+ MSG_WriteCoord (&sv.nqmulticast, org[2]);
+ MSG_WriteByte (&sv.nqmulticast, start);
+ MSG_WriteByte (&sv.nqmulticast, length);
+#endif
+ SV_MulticastProtExt(org, MULTICAST_PHS, pr_global_struct->dimension_send, 0, 0);
}
//DP_TE_STANDARDEFFECTBUILTINS
@@ -8784,7 +8816,7 @@ static void QCBUILTIN PF_runclientphys(pubprogfuncs_t *prinst, struct globalvars
pmove.groundent = 0;
pmove.waterlevel = 0;
pmove.watertype = 0;
- pmove.capsule = (ent->xv->geomtype == GEOMTYPE_CYLINDER);
+ pmove.capsule = (ent->xv->geomtype == GEOMTYPE_CAPSULE);
for (i=0 ; i<3 ; i++)
{
@@ -11152,6 +11184,9 @@ void PR_DumpPlatform_f(void)
{"TEREDIT_TINT", "const float", CS, NULL, ter_tint},
{"TEREDIT_RESET_SECT", "const float", CS, NULL, ter_reset},
{"TEREDIT_RELOAD_SECT", "const float", CS, NULL, ter_reloadsect},
+ {"TEREDIT_ENTS_WIPE", "const float", CS, NULL, ter_ents_wipe},
+ {"TEREDIT_ENTS_CONCAT", "const float", CS, NULL, ter_ents_concat},
+ {"TEREDIT_ENTS_GET", "const float", CS, NULL, ter_ents_get},
{"SLIST_HOSTCACHEVIEWCOUNT", "const float", CS|MENU, NULL, SLIST_HOSTCACHEVIEWCOUNT},
{"SLIST_HOSTCACHETOTALCOUNT", "const float", CS|MENU, NULL, SLIST_HOSTCACHETOTALCOUNT},
diff --git a/engine/server/progdefs.h b/engine/server/progdefs.h
index 727b7d523..d78f4a056 100644
--- a/engine/server/progdefs.h
+++ b/engine/server/progdefs.h
@@ -265,6 +265,19 @@ and the extension fields are added on the end and can have extra vm-specific stu
comfieldfloat(uniquespawnid,"Incremented by 1 whenever the entity is respawned. Persists across remove calls, for when the two-second grace period is insufficient.")/*FTE_ENT_UNIQUESPAWNID*/\
comfieldfunction(customizeentityforclient, ".float()","Called just before an entity is sent to a client (non-csqc protocol). This gives you a chance to tailor 'self' according to what 'other' should see.")
+#ifdef HALFLIFEMODELS
+#define HALFLIFEMODEL_FIELDS \
+ comfieldfloat(bonecontrol1,"Halflife model format bone controller. On player models, this typically affects the spine's yaw.") /*FTE_CSQC_HALFLIFE_MODELS*/\
+ comfieldfloat(bonecontrol2,"Halflife model format bone controller. On player models, this typically affects the spine's yaw.") /*FTE_CSQC_HALFLIFE_MODELS*/\
+ comfieldfloat(bonecontrol3,"Halflife model format bone controller. On player models, this typically affects the spine's yaw.") /*FTE_CSQC_HALFLIFE_MODELS*/\
+ comfieldfloat(bonecontrol4,"Halflife model format bone controller. On player models, this typically affects the spine's yaw.") /*FTE_CSQC_HALFLIFE_MODELS*/\
+ comfieldfloat(bonecontrol5,"Halflife model format bone controller. This typically affects the mouth.") /*FTE_CSQC_HALFLIFE_MODELS*/\
+ comfieldfloat(subblendfrac,"Weird animation value specific to halflife models. On player models, this typically affects the spine's pitch.") /*FTE_CSQC_HALFLIFE_MODELS*/\
+ comfieldfloat(basesubblendfrac,"See basebone") /*FTE_CSQC_HALFLIFE_MODELS+FTE_CSQC_BASEFRAME*/
+#else
+#define HALFLIFEMODEL_FIELDS
+#endif
+
//this is the list for all the csqc fields.
//(the #define is so the list always matches the ones pulled out)
#define csqcextfields \
@@ -286,15 +299,7 @@ and the extension fields are added on the end and can have extra vm-specific stu
comfieldfloat(baseframe2time,"See basebone") /*FTE_CSQC_BASEFRAME*/\
comfieldfloat(baselerpfrac,"See basebone") /*FTE_CSQC_BASEFRAME*/\
comfieldfloat(basebone,"The base* frame animations are equivelent to their non-base versions, except that they only affect bone numbers below the 'basebone' value. This means that the base* animation can affect the legs of a skeletal model independantly of the normal animation fields affecting the torso area. For more complex animation than this, use skeletal objects.") /*FTE_CSQC_BASEFRAME*/\
- \
- comfieldfloat(bonecontrol1,"Halflife model format bone controller. On player models, this typically affects the spine's yaw.") /*FTE_CSQC_HALFLIFE_MODELS*/\
- comfieldfloat(bonecontrol2,"Halflife model format bone controller. On player models, this typically affects the spine's yaw.") /*FTE_CSQC_HALFLIFE_MODELS*/\
- comfieldfloat(bonecontrol3,"Halflife model format bone controller. On player models, this typically affects the spine's yaw.") /*FTE_CSQC_HALFLIFE_MODELS*/\
- comfieldfloat(bonecontrol4,"Halflife model format bone controller. On player models, this typically affects the spine's yaw.") /*FTE_CSQC_HALFLIFE_MODELS*/\
- comfieldfloat(bonecontrol5,"Halflife model format bone controller. This typically affects the mouth.") /*FTE_CSQC_HALFLIFE_MODELS*/\
- comfieldfloat(subblendfrac,"Weird animation value specific to halflife models. On player models, this typically affects the spine's pitch.") /*FTE_CSQC_HALFLIFE_MODELS*/\
- comfieldfloat(basesubblendfrac,"See basebone") /*FTE_CSQC_HALFLIFE_MODELS+FTE_CSQC_BASEFRAME*/\
- \
+ HALFLIFEMODEL_FIELDS \
comfieldfloat(drawmask, "Matces the bitmask passed to the addentities builtin, to easily submit entities to the renderer. Not otherwise meaningful.") /*So that the qc can specify all rockets at once or all bannanas at once*/ \
comfieldfunction(predraw, ".float()","Called as part of the addentities builtin. Returns one of the PREDRAW_ constants. This gives you a chance to interpolate or animate entities as desired.") /*If present, is called just before it's drawn.*/
diff --git a/engine/server/sv_ccmds.c b/engine/server/sv_ccmds.c
index abb688e22..3fa342943 100644
--- a/engine/server/sv_ccmds.c
+++ b/engine/server/sv_ccmds.c
@@ -397,6 +397,7 @@ void SV_Map_f (void)
char spot[MAX_QPATH];
char expanded[MAX_QPATH];
char *nextserver;
+ qboolean preserveplayers= false;
qboolean isrestart = false; //don't hurt settings
qboolean newunit = false; //no hubcache
qboolean flushparms = false; //flush parms+serverflags
@@ -407,6 +408,7 @@ void SV_Map_f (void)
qboolean waschangelevel = false;
int i;
char *startspot;
+ float oldtime;
nextserver = 0;
@@ -486,9 +488,17 @@ void SV_Map_f (void)
Q_strncpyz(level, "start", sizeof(level));
}
- //override the startspot
- Q_strncpyz(spot, Info_ValueForKey(svs.info, "*startspot"), sizeof(spot));
- startspot = spot;
+ if (startspot && !strcmp(startspot, "."))
+ {
+ preserveplayers = true;
+ startspot = NULL;
+ }
+ if (!startspot)
+ {
+ //revert the startspot if its not overridden
+ Q_strncpyz(spot, Info_ValueForKey(svs.info, "*startspot"), sizeof(spot));
+ startspot = spot;
+ }
}
// check to make sure the level exists
@@ -614,10 +624,31 @@ void SV_Map_f (void)
MP_Toggle(0);
#endif
- for (i=0 ; ics_connected) //so that we don't send a datagram
- svs.clients[i].state=cs_connected;
+ for (i=0 ; ics_connected)
+ {
+ buf = svprogfuncs->saveent(svprogfuncs, buffer, &bufsize, sizeof(buffer), svs.clients[i].edict);
+ if (svs.clients[i].spawninfo)
+ Z_Free(svs.clients[i].spawninfo);
+ svs.clients[i].spawninfo = Z_Malloc(bufsize+1);
+ memcpy(svs.clients[i].spawninfo, buf, bufsize+1);
+ svs.clients[i].spawninfotime = sv.time;
+ }
+ }
+ }
+ else
+ {
+ for (i=0 ; ics_connected) //so that we don't send a datagram
+ svs.clients[i].state=cs_connected;
+ }
}
#ifndef SERVERONLY
@@ -626,31 +657,34 @@ void SV_Map_f (void)
SCR_ImageName(level);
#endif
- for (i=0, host_client = svs.clients ; icontroller == NULL)
+ for (i=0, host_client = svs.clients ; icontroller == NULL)
{
- if (ISDPCLIENT(host_client))
+ if (ISNQCLIENT(host_client))
{
- //DP clients cannot cope with being told the next map's name
- SV_StuffcmdToClient(host_client, "reconnect\n");
+ if (ISDPCLIENT(host_client))
+ {
+ //DP clients cannot cope with being told the next map's name
+ SV_StuffcmdToClient(host_client, "reconnect\n");
+ }
+ else
+ SV_StuffcmdToClient(host_client, va("reconnect \"%s\"\n", level));
}
else
- SV_StuffcmdToClient(host_client, va("reconnect \"%s\"\n", level));
+ SV_StuffcmdToClient(host_client, va("changing \"%s\"\n", level));
}
- else
- SV_StuffcmdToClient(host_client, va("changing \"%s\"\n", level));
+ host_client->prespawn_stage = PRESPAWN_INVALID;
+ host_client->prespawn_idx = 0;
}
- host_client->prespawn_stage = PRESPAWN_INVALID;
- host_client->prespawn_idx = 0;
- }
- SV_SendMessagesToAll ();
+ SV_SendMessagesToAll ();
- if (flushparms)
- svs.serverflags = 0;
+ if (flushparms)
+ svs.serverflags = 0;
+ }
SCR_SetLoadingFile("spawnserver");
if (newunit || !startspot || cinematic || !SV_LoadLevelCache(NULL, level, startspot, false))
@@ -680,6 +714,14 @@ void SV_Map_f (void)
SV_GetNewSpawnParms(host_client);
}
+ if (preserveplayers && svprogfuncs && host_client->state == cs_spawned && host_client->spawninfo)
+ {
+ int j = 0;
+ svprogfuncs->restoreent(svprogfuncs, host_client->spawninfo, &j, host_client->edict);
+ host_client->istobeloaded = true;
+ host_client->state=cs_connected;
+ }
+
if (host_client->controller)
continue;
if (host_client->state>=cs_connected)
diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c
index 682e94022..a35004815 100644
--- a/engine/server/sv_init.c
+++ b/engine/server/sv_init.c
@@ -923,7 +923,6 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us
Q_strncpyz(cl.serverinfo, svs.info, sizeof(cl.serverinfo));
if (!isDedicated)
CL_CheckServerInfo();
- Cvar_ForceCallback(Cvar_FindVar("r_particlesdesc"));
#endif
diff --git a/engine/server/sv_phys.c b/engine/server/sv_phys.c
index 674bc01ce..079027ace 100644
--- a/engine/server/sv_phys.c
+++ b/engine/server/sv_phys.c
@@ -1329,7 +1329,12 @@ static void WPhys_Physics_Toss (world_t *w, wedict_t *ent)
// move origin
VectorScale (ent->v->velocity, host_frametime, move);
if (!DotProduct(move, move))
+ {
+ //rogue buzzsaws are vile and jerkily move via setorigin, and need to be relinked so that they can touch path corners.
+ if (ent->v->solid && ent->v->nextthink)
+ World_LinkEdict (w, ent, true);
return;
+ }
fl = 0;
#ifndef CLIENTONLY
@@ -1340,7 +1345,7 @@ static void WPhys_Physics_Toss (world_t *w, wedict_t *ent)
trace = WPhys_PushEntity (w, ent, move, fl);
- if (trace.allsolid)
+ if (trace.allsolid && ent->v->solid != SOLID_NOT && ent->v->solid != SOLID_TRIGGER)
{
#ifndef CLIENTONLY
if (progstype != PROG_H2)
diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c
index 65342f7f7..ebf24cc5f 100644
--- a/engine/server/sv_user.c
+++ b/engine/server/sv_user.c
@@ -6397,7 +6397,7 @@ void SV_RunCmd (usercmd_t *ucmd, qboolean recurse)
//
// angles
// show 1/3 the pitch angle and all the roll angle
- if (sv_player->v->health > 0)
+ if (sv_player->v->health > 0 && sv_player->v->movetype)
{
if (sv_player->v->movetype == MOVETYPE_6DOF)
{
diff --git a/engine/web/ftejslib.h b/engine/web/ftejslib.h
index 4ba893941..a33959075 100644
--- a/engine/web/ftejslib.h
+++ b/engine/web/ftejslib.h
@@ -35,6 +35,7 @@ void emscriptenfte_al_loadaudiofile(int al_buf, void *data, int datasize);
//avoid all of emscripten's sdl emulation.
//this resolves input etc issues.
unsigned long emscriptenfte_ticks_ms(void);
+void emscriptenfte_updatepointerlock(int wantpointerlock, int hidecursor);
void emscriptenfte_polljoyevents(void);
void emscriptenfte_settitle(const char *text);
int emscriptenfte_setupcanvas(
diff --git a/engine/web/ftejslib.js b/engine/web/ftejslib.js
index 49acaf3ed..a0d2aa0dc 100644
--- a/engine/web/ftejslib.js
+++ b/engine/web/ftejslib.js
@@ -59,6 +59,8 @@ mergeInto(LibraryManager.library,
$FTEC:
{
ctxwarned:0,
+ pointerislocked:0,
+ pointerwantlock:0,
linebuffer:'',
w: -1,
h: -1,
@@ -128,13 +130,21 @@ mergeInto(LibraryManager.library,
break;
case 'mousedown':
window.focus();
+ //older browsers need fullscreen in order for requestPointerLock to work.
+ //newer browsers can still break pointer locks when alt-tabbing, even without breaking fullscreen.
+ //so lets spam requests for it
if (Browser.isFullScreen == 0)
if (FTEC.evcb.wantfullscreen != 0)
if (Runtime.dynCall('i', FTEC.evcb.wantfullscreen, []))
{
Browser.requestFullScreen(true, true);
+ }
+ if (FTEC.pointerwantlock != 0 && FTEC.pointerislocked == 0)
+ {
+ FTEC.pointerislocked = -1; //don't repeat the request on every click. firefox has a fit at that, so require the mouse to leave the element or something before we retry.
Module['canvas'].requestPointerLock();
}
+ //fallthrough
case 'mouseup':
if (FTEC.evcb.button != 0)
{
@@ -156,6 +166,16 @@ mergeInto(LibraryManager.library,
for (var i = 0; i < 8; i++)
Runtime.dynCall('viii', FTEC.evcb.button, [0, false, i]);
}
+ if (FTEC.pointerislocked == -1)
+ FTEC.pointerislocked = 0;
+ break;
+ case 'focus':
+ case 'blur':
+ Runtime.dynCall('iiiii', FTEC.evcb.key, [0, false, 16, 0]); //shift
+ Runtime.dynCall('iiiii', FTEC.evcb.key, [0, false, 17, 0]); //alt
+ Runtime.dynCall('iiiii', FTEC.evcb.key, [0, false, 18, 0]); //ctrl
+ if (FTEC.pointerislocked == -1)
+ FTEC.pointerislocked = 0;
break;
case 'keypress':
if (FTEC.evcb.key != 0)
@@ -237,13 +257,39 @@ mergeInto(LibraryManager.library,
Runtime.dynCall('viid', FTEC.evcb.jbutton, [gp.index, j, 0]);
console.log("Gamepad disconnected from index %d: %s", gp.index, gp.id);
break;
+ case 'pointerlockchange':
+ case 'mozpointerlockchange':
+ case 'webkitpointerlockchange':
+ FTEC.pointerislocked = document.pointerLockElement === Module['canvas'] ||
+ document.mozPointerLockElement === Module['canvas'] ||
+ document.webkitPointerLockElement === Module['canvas'];
+ console.log("Pointer lock now " + FTEC.pointerislocked);
+ break;
default:
console.log(event);
break;
}
}
},
- emscriptenfte_polljoyevents : function(be,ae)
+ emscriptenfte_updatepointerlock : function(wantlock, softcursor)
+ {
+ FTEC.pointerwantlock = wantlock;
+ //we can only apply locks when we're clicked, but should be able to unlock any time.
+ if (wantlock == 0 && FTEC.pointerislocked != 0)
+ {
+ document.exitPointerLock = document.exitPointerLock ||
+ document.mozExitPointerLock ||
+ document.webkitExitPointerLock;
+ FTEC.pointerislocked = 0;
+ if (document.exitPointerLock)
+ document.exitPointerLock();
+ }
+ if (softcursor)
+ Module.canvas.style.cursor = "none"; //hide the cursor, we'll do a soft-cursor when one is needed.
+ else
+ Module.canvas.style.cursor = "default"; //restore the cursor
+ },
+ emscriptenfte_polljoyevents : function()
{
//with events, we can do unplug stuff properly.
//otherwise hot unplug might be buggy.
@@ -301,13 +347,21 @@ mergeInto(LibraryManager.library,
if (!FTEC.donecb)
{
FTEC.donecb = 1;
- var events = ['mousedown', 'mouseup', 'mousemove', 'wheel', 'mousewheel', 'mouseout', 'keypress', 'keydown', 'keyup', 'touchstart', 'touchend', 'touchcancel', 'touchleave', 'touchmove', 'dragenter', 'dragover', 'drop', 'gamepadconnected', 'gamepaddisconnected', 'message'];
+ var events = ['mousedown', 'mouseup', 'mousemove', 'wheel', 'mousewheel', 'mouseout',
+ 'keypress', 'keydown', 'keyup',
+ 'touchstart', 'touchend', 'touchcancel', 'touchleave', 'touchmove',
+ 'dragenter', 'dragover', 'drop',
+ 'gamepadconnected', 'gamepaddisconnected',
+ 'message',
+ 'pointerlockchange', 'mozpointerlockchange', 'webkitpointerlockchange',
+ 'focus', 'blur']; //try to fix alt-tab
events.forEach(function(event)
{
Module['canvas'].addEventListener(event, FTEC.handleevent, true);
});
- var docevents = ['keypress', 'keydown', 'keyup'];
+ var docevents = ['keypress', 'keydown', 'keyup',
+ 'pointerlockchange', 'mozpointerlockchange', 'webkitpointerlockchange'];
docevents.forEach(function(event)
{
document.addEventListener(event, FTEC.handleevent, true);
@@ -354,11 +408,6 @@ mergeInto(LibraryManager.library,
};
window.onresize();
- if (evmouse)
- Module.canvas.style.cursor = "none"; //hide the cursor, we'll do a soft-cursor when one is needed.
- else
- Module.canvas.style.cursor = "default"; //restore the cursor
-
if (FTEC.evcb.hashchange)
{
window.onhashchange = function()
@@ -366,6 +415,8 @@ mergeInto(LibraryManager.library,
FTEC.loadurl(location.hash.substring(1), "", undefined);
};
}
+
+ _emscriptenfte_updatepointerlock(false, false);
return 1;
},
diff --git a/engine/web/gl_vidweb.c b/engine/web/gl_vidweb.c
index e1e3c8196..d09129bca 100644
--- a/engine/web/gl_vidweb.c
+++ b/engine/web/gl_vidweb.c
@@ -193,8 +193,12 @@ void DOM_LoadFile(char *loc, char *mime, int handle)
}
int VID_ShouldSwitchToFullscreen(void)
{ //if false, mouse grabs won't work and we'll be forced to touchscreen mode.
+ //we can only go fullscreen when the user clicks something.
+ //this means that the user will get pissed off at the fullscreen state changing when they first click on the menus after it loading up.
+ //this is confounded by escape bringing up the menu. GRR IT CHANGED MODE!WTF IT CHANGED AGAIN FUCKING PIECE OF SHIT!.
+ //annoying, but that's web browsers for you. the best thing we can do is to not regrab until they next click while actually back in the game.
extern cvar_t vid_fullscreen;
- return !!vid_fullscreen.value;
+ return !!vid_fullscreen.value && (!Key_Dest_Has(kdm_console | kdm_cwindows | kdm_emenu) || !Key_MouseShouldBeFree());
}
qboolean GLVID_Init (rendererstate_t *info, unsigned char *palette)
{
@@ -280,6 +284,8 @@ void GLVID_SetCaption(char *text)
void Sys_SendKeyEvents(void)
{
/*most callbacks happen outside our code, we don't need to poll for events - except for joysticks*/
+ qboolean shouldbefree = Key_MouseShouldBeFree();
+ emscriptenfte_updatepointerlock(_windowed_mouse.ival && !shouldbefree, shouldbefree);
emscriptenfte_polljoyevents();
}
/*various stuff for joysticks, which we don't support in this port*/