fix playdemo not working straight after record.

make a guess at the currently applied preset. fix issues with vid_reload on preset changes being buggy.
fix erroneous weapon flashes due to limited precision.
try to fix rogue finale.
fix issue with vid_restart crashing with directinput active.
work around emscripten rand entropy, at least as far as particles are concerned.
properly implement te_explosion2 (for rogue mission pack).
drastically reduce submodel polyoffsets in the webgl port, because firefox's depth precision is 16 bit or something stupid.
fix rogue ibar background not being used with qw hud.
added terrain editor feature to edit the bsp's entity lump. this also works with .bsps. added console command to reload the map/apply those ent changes but leaving the players how they were (lets hope enemy etc stuff doesn't bug out...)
force-enabled airstep when travelling fast. this is to partly replicate nq player physics in order to fix windtunnels on r1m5 iirc.
added some more effects to the 'high' particle effect, in order to make rogue feel a little more loved.
fix for rogue buzzsaws.
rework webgl pointer locks.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4965 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2015-08-20 03:17:47 +00:00
parent c7b1e544ee
commit e99354bd20
42 changed files with 1388 additions and 372 deletions

View File

@ -922,6 +922,8 @@ void CL_Stop_f (void)
cls.demooutfile = NULL;
cls.demorecording = false;
Con_Printf ("Completed demo\n");
FS_FlushFSHash();
}

View File

@ -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,

View File

@ -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);

View File

@ -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:

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -770,6 +770,7 @@ void INS_CloseDInput (void)
FreeLibrary(hInstDI);
hInstDI = NULL;
pDirectInputCreate = NULL;
pDirectInputCreateEx = NULL;
}
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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)
{

View File

@ -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);

View File

@ -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;

View File

@ -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"

View File

@ -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;

View File

@ -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);
}
}
}

View File

@ -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

View File

@ -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))
{

View File

@ -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);

View File

@ -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

View File

@ -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);
}

View File

@ -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)

View File

@ -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 ();

View File

@ -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.

View File

@ -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

View File

@ -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,

View File

@ -59,6 +59,30 @@
<References>
</References>
<Files>
<File
RelativePath="..\web\fs_web.c"
>
</File>
<File
RelativePath="..\web\ftejslib.h"
>
</File>
<File
RelativePath="..\web\ftejslib.js"
>
</File>
<File
RelativePath="..\web\gl_vidweb.c"
>
</File>
<File
RelativePath="..\web\prejs.js"
>
</File>
<File
RelativePath="..\web\sys_web.c"
>
</File>
</Files>
<Globals>
</Globals>

View File

@ -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;

View File

@ -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__));

View File

@ -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)

View File

@ -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

View File

@ -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;

View File

@ -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},

View File

@ -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.*/

View File

@ -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 ; i<svs.allocated_client_slots ; i++) //we need to drop all q2 clients. We don't mix q1w with q2.
oldtime = sv.time;
if (preserveplayers && svprogfuncs)
{
if (svs.clients[i].state>cs_connected) //so that we don't send a datagram
svs.clients[i].state=cs_connected;
for (i=0 ; i<svs.allocated_client_slots ; i++) //we need to drop all q2 clients. We don't mix q1w with q2.
{
char buffer[8192], *buf;
size_t bufsize = 0;
if (svs.clients[i].state>cs_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 ; i<svs.allocated_client_slots ; i++) //we need to drop all q2 clients. We don't mix q1w with q2.
{
if (svs.clients[i].state>cs_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 ; i<svs.allocated_client_slots ; i++, host_client++)
// if (!preserveplayers)
{
/*pass the new map's name as an extension, so appropriate loading screens can be shown*/
if (host_client->controller == NULL)
for (i=0, host_client = svs.clients ; i<svs.allocated_client_slots ; i++, host_client++)
{
if (ISNQCLIENT(host_client))
/*pass the new map's name as an extension, so appropriate loading screens can be shown*/
if (host_client->controller == 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)

View File

@ -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

View File

@ -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)

View File

@ -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)
{

View File

@ -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(

View File

@ -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;
},

View File

@ -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. <ESC>GRR IT CHANGED MODE!<options>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*/