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