diff --git a/engine/Makefile b/engine/Makefile index 67b4979b4..172822d29 100644 --- a/engine/Makefile +++ b/engine/Makefile @@ -322,7 +322,11 @@ PROGS_DIR=$(BASE_DIR)/qclib NACL_DIR=$(BASE_DIR)/nacl BOTLIB_DIR=$(BASE_DIR)/botlib -ALL_CFLAGS=$(HAVECONFIG) $(VISIBILITY_FLAGS) $(BRANDFLAGS) $(CFLAGS) $(BASE_CFLAGS) $(WCFLAGS) $(ARCH_CFLAGS) +ifeq ($(NOCOMPAT),1) + NCCFLAGS=-DNOLEGACY -DOMIT_QCC + NCDIRPREFIX=nc +endif +ALL_CFLAGS=$(HAVECONFIG) $(VISIBILITY_FLAGS) $(BRANDFLAGS) $(CFLAGS) $(BASE_CFLAGS) $(WCFLAGS) $(ARCH_CFLAGS) $(NCCFLAGS) #cheap compile-everything-in-one-unit (compile becomes preprocess only) ifneq ($(WPO),) @@ -1192,6 +1196,14 @@ ifneq (,$(findstring linux,$(FTE_TARGET))) MINGL_EXE_NAME=../fteqw-mingl$(BITS) MINGL_DIR=mingl_linux$(BITS) + ifeq ($(NOCOMPAT),1) + SV_EXE_NAME=../engine-sv$(BITS)$(EXEPOSTFIX) + GL_EXE_NAME=../engine-gl$(BITS)$(EXEPOSTFIX) + VK_EXE_NAME=../engine-vk$(BITS)$(EXEPOSTFIX) + M_EXE_NAME=../engine$(BITS)$(EXEPOSTFIX) + D3D_EXE_NAME=../engine-d3d$(BITS)$(EXEPOSTFIX) + MINGL_EXE_NAME=../engine-mingl$(BITS)$(EXEPOSTFIX) + endif endif ifneq (,$(findstring rpi,$(FTE_TARGET))) #These next two lines enable cross compiling. If you're compiling natively you can just kill the two. @@ -1533,11 +1545,11 @@ _clsv-profile: reldir sv-tmp: reldir debugdir @$(MAKE) $(TYPE) OUT_DIR="$(OUT_DIR)" EXE_NAME="$(SV_EXE_NAME)" WCFLAGS="$(SV_CFLAGS)" LDFLAGS="$(SV_LDFLAGS) $(LDFLAGS)" OBJS="SV_OBJS" sv-rel: - @$(MAKE) sv-tmp TYPE=_out-rel OUT_DIR="$(RELEASE_DIR)/$(SV_DIR)" + @$(MAKE) sv-tmp TYPE=_out-rel OUT_DIR="$(RELEASE_DIR)/$(NCDIRPREFIX)$(SV_DIR)" sv-dbg: - @$(MAKE) sv-tmp TYPE=_out-dbg OUT_DIR="$(DEBUG_DIR)/$(SV_DIR)" + @$(MAKE) sv-tmp TYPE=_out-dbg OUT_DIR="$(DEBUG_DIR)/$(NCDIRPREFIX)$(SV_DIR)" sv-profile: - @$(MAKE) sv-tmp TYPE=_out-profile OUT_DIR="$(PROFILE_DIR)/$(SV_DIR)" + @$(MAKE) sv-tmp TYPE=_out-profile OUT_DIR="$(PROFILE_DIR)/$(NCDIRPREFIX)$(SV_DIR)" d3dcl-tmp: @$(MAKE) $(TYPE) OUT_DIR="$(OUT_DIR)" EXE_NAME="$(D3DCL_EXE_NAME)" WCFLAGS="$(D3D_CFLAGS)" LDFLAGS="$(D3D_LDFLAGS) $(LDFLAGS)" SOBJS="$(D3DCL_OBJS)" @@ -1545,18 +1557,18 @@ d3d-tmp: @$(MAKE) $(TYPE) OUT_DIR="$(OUT_DIR)" EXE_NAME="$(D3D_EXE_NAME)" WCFLAGS="$(D3D_CFLAGS)" LDFLAGS="$(D3D_LDFLAGS) $(LDFLAGS)" SOBJS="$(D3DCL_OBJS)" d3dcl-rel: - @$(MAKE) d3dcl-tmp TYPE=_cl-rel OUT_DIR="$(RELEASE_DIR)/$(D3DCL_DIR)" + @$(MAKE) d3dcl-tmp TYPE=_cl-rel OUT_DIR="$(RELEASE_DIR)/$(NCDIRPREFIX)$(D3DCL_DIR)" d3dcl-dbg: - @$(MAKE) d3dcl-tmp TYPE=_cl-dbg OUT_DIR="$(DEBUG_DIR)/$(D3DCL_DIR)" + @$(MAKE) d3dcl-tmp TYPE=_cl-dbg OUT_DIR="$(DEBUG_DIR)/$(NCDIRPREFIX)$(D3DCL_DIR)" d3dcl-profile: - @$(MAKE) d3dcl-tmp TYPE=_cl-profile OUT_DIR="$(PROFILE_DIR)/$(D3DCL_DIR)" + @$(MAKE) d3dcl-tmp TYPE=_cl-profile OUT_DIR="$(PROFILE_DIR)/$(NCDIRPREFIX)$(D3DCL_DIR)" d3d-rel: - @$(MAKE) d3d-tmp TYPE=_clsv-rel OUT_DIR="$(RELEASE_DIR)/$(D3DB_DIR)" + @$(MAKE) d3d-tmp TYPE=_clsv-rel OUT_DIR="$(RELEASE_DIR)/$(NCDIRPREFIX)$(D3DB_DIR)" d3d-dbg: - @$(MAKE) d3d-tmp TYPE=_clsv-dbg OUT_DIR="$(DEBUG_DIR)/$(D3DB_DIR)" + @$(MAKE) d3d-tmp TYPE=_clsv-dbg OUT_DIR="$(DEBUG_DIR)/$(NCDIRPREFIX)$(D3DB_DIR)" d3d-profile: - @$(MAKE) d3d-tmp TYPE=_clsv-profile OUT_DIR="$(PROFILE_DIR)/$(D3DB_DIR)" + @$(MAKE) d3d-tmp TYPE=_clsv-profile OUT_DIR="$(PROFILE_DIR)/$(NCDIRPREFIX)$(D3DB_DIR)" @@ -1566,18 +1578,18 @@ vk-tmp: @$(MAKE) $(TYPE) OUT_DIR="$(OUT_DIR)" EXE_NAME="$(VK_EXE_NAME)" WCFLAGS="$(VK_CFLAGS)" LDFLAGS="$(VK_LDFLAGS) $(LDFLAGS)" SOBJS="$(VKCL_OBJS)" vkcl-rel: - @$(MAKE) vkcl-tmp TYPE=_cl-rel OUT_DIR="$(RELEASE_DIR)/$(VKCL_DIR)" + @$(MAKE) vkcl-tmp TYPE=_cl-rel OUT_DIR="$(RELEASE_DIR)/$(NCDIRPREFIX)$(VKCL_DIR)" vkcl-dbg: - @$(MAKE) vkcl-tmp TYPE=_cl-dbg OUT_DIR="$(DEBUG_DIR)/$(VKCL_DIR)" + @$(MAKE) vkcl-tmp TYPE=_cl-dbg OUT_DIR="$(DEBUG_DIR)/$(NCDIRPREFIX)$(VKCL_DIR)" vkcl-profile: - @$(MAKE) vkcl-tmp TYPE=_cl-profile OUT_DIR="$(PROFILE_DIR)/$(VKCL_DIR)" + @$(MAKE) vkcl-tmp TYPE=_cl-profile OUT_DIR="$(PROFILE_DIR)/$(NCDIRPREFIX)$(VKCL_DIR)" vk-rel: - @$(MAKE) vk-tmp TYPE=_clsv-rel OUT_DIR="$(RELEASE_DIR)/$(VKB_DIR)" + @$(MAKE) vk-tmp TYPE=_clsv-rel OUT_DIR="$(RELEASE_DIR)/$(NCDIRPREFIX)$(VKB_DIR)" vk-dbg: - @$(MAKE) vk-tmp TYPE=_clsv-dbg OUT_DIR="$(DEBUG_DIR)/$(VKB_DIR)" + @$(MAKE) vk-tmp TYPE=_clsv-dbg OUT_DIR="$(DEBUG_DIR)/$(NCDIRPREFIX)$(VKB_DIR)" vk-profile: - @$(MAKE) vk-tmp TYPE=_clsv-profile OUT_DIR="$(PROFILE_DIR)/$(VKB_DIR)" + @$(MAKE) vk-tmp TYPE=_clsv-profile OUT_DIR="$(PROFILE_DIR)/$(NCDIRPREFIX)$(VKB_DIR)" glcl-tmp: @@ -1586,26 +1598,26 @@ gl-tmp: @$(MAKE) $(TYPE) OUT_DIR="$(OUT_DIR)" EXE_NAME="$(GL_EXE_NAME)" WCFLAGS="$(GL_CFLAGS)" LDFLAGS="$(GL_LDFLAGS) $(LDFLAGS)" SOBJS="$(GLCL_OBJS)" glcl-rel: - @$(MAKE) glcl-tmp TYPE=_cl-rel OUT_DIR="$(RELEASE_DIR)/$(GLCL_DIR)" + @$(MAKE) glcl-tmp TYPE=_cl-rel OUT_DIR="$(RELEASE_DIR)/$(NCDIRPREFIX)$(GLCL_DIR)" glcl-dbg: - @$(MAKE) glcl-tmp TYPE=_cl-dbg OUT_DIR="$(DEBUG_DIR)/$(GLCL_DIR)" + @$(MAKE) glcl-tmp TYPE=_cl-dbg OUT_DIR="$(DEBUG_DIR)/$(NCDIRPREFIX)$(GLCL_DIR)" glcl-profile: - @$(MAKE) glcl-tmp TYPE=_cl-profile OUT_DIR="$(PROFILE_DIR)/$(GLCL_DIR)" + @$(MAKE) glcl-tmp TYPE=_cl-profile OUT_DIR="$(PROFILE_DIR)/$(NCDIRPREFIX)$(GLCL_DIR)" gl-rel: - @$(MAKE) gl-tmp TYPE=_clsv-rel OUT_DIR="$(RELEASE_DIR)/$(GLB_DIR)" + @$(MAKE) gl-tmp TYPE=_clsv-rel OUT_DIR="$(RELEASE_DIR)/$(NCDIRPREFIX)$(GLB_DIR)" gl-dbg: - @$(MAKE) gl-tmp TYPE=_clsv-dbg OUT_DIR="$(DEBUG_DIR)/$(GLB_DIR)" + @$(MAKE) gl-tmp TYPE=_clsv-dbg OUT_DIR="$(DEBUG_DIR)/$(NCDIRPREFIX)$(GLB_DIR)" gl-profile: - @$(MAKE) gl-tmp TYPE=_clsv-profile OUT_DIR="$(PROFILE_DIR)/$(GLB_DIR)" + @$(MAKE) gl-tmp TYPE=_clsv-profile OUT_DIR="$(PROFILE_DIR)/$(NCDIRPREFIX)$(GLB_DIR)" mingl-tmp: reldir @$(MAKE) $(TYPE) OUT_DIR="$(OUT_DIR)" EXE_NAME="$(MINGL_EXE_NAME)" WCFLAGS="$(GL_CFLAGS) -DMINIMAL" LDFLAGS="$(GL_LDFLAGS) $(LDFLAGS)" SOBJS="$(GLCL_OBJS)" mingl-rel: - @$(MAKE) mingl-tmp TYPE=_cl-rel OUT_DIR="$(RELEASE_DIR)/$(MINGL_DIR)" + @$(MAKE) mingl-tmp TYPE=_cl-rel OUT_DIR="$(RELEASE_DIR)/$(NCDIRPREFIX)$(MINGL_DIR)" mingl-dbg: - @$(MAKE) mingl-tmp TYPE=_cl-dbg OUT_DIR="$(DEBUG_DIR)/$(MINGL_DIR)" + @$(MAKE) mingl-tmp TYPE=_cl-dbg OUT_DIR="$(DEBUG_DIR)/$(NCDIRPREFIX)$(MINGL_DIR)" mingl-profile: - @$(MAKE) mingl-tmp TYPE=_cl-profile OUT_DIR="$(PROFILE_DIR)/$(MINGL_DIR)" + @$(MAKE) mingl-tmp TYPE=_cl-profile OUT_DIR="$(PROFILE_DIR)/$(NCDIRPREFIX)$(MINGL_DIR)" mcl-tmp: @$(MAKE) $(TYPE) OUT_DIR="$(OUT_DIR)" EXE_NAME="$(MCL_EXE_NAME)" WCFLAGS="$(M_CFLAGS)" LDFLAGS="$(M_LDFLAGS) $(LDFLAGS)" SOBJS="$(MCL_OBJS)" @@ -1613,17 +1625,17 @@ m-tmp: @$(MAKE) $(TYPE) OUT_DIR="$(OUT_DIR)" EXE_NAME="$(M_EXE_NAME)" WCFLAGS="$(M_CFLAGS)" LDFLAGS="$(M_LDFLAGS) $(LDFLAGS)" SOBJS="$(MCL_OBJS)" mcl-rel: - @$(MAKE) mcl-tmp TYPE=_cl-rel OUT_DIR="$(RELEASE_DIR)/$(MCL_DIR)" + @$(MAKE) mcl-tmp TYPE=_cl-rel OUT_DIR="$(RELEASE_DIR)/$(NCDIRPREFIX)$(MCL_DIR)" mcl-dbg: - @$(MAKE) mcl-tmp TYPE=_cl-dbg OUT_DIR="$(DEBUG_DIR)/$(MCL_DIR)" + @$(MAKE) mcl-tmp TYPE=_cl-dbg OUT_DIR="$(DEBUG_DIR)/$(NCDIRPREFIX)$(MCL_DIR)" mcl-profile: - @$(MAKE) mcl-tmp TYPE=_cl-profile OUT_DIR="$(PROFILE_DIR)/$(MCL_DIR)" + @$(MAKE) mcl-tmp TYPE=_cl-profile OUT_DIR="$(PROFILE_DIR)/$(NCDIRPREFIX)$(MCL_DIR)" m-rel: - @$(MAKE) m-tmp TYPE=_clsv-rel OUT_DIR="$(RELEASE_DIR)/$(MB_DIR)" + @$(MAKE) m-tmp TYPE=_clsv-rel OUT_DIR="$(RELEASE_DIR)/$(NCDIRPREFIX)$(MB_DIR)" m-dbg: - @$(MAKE) m-tmp TYPE=_clsv-dbg OUT_DIR="$(DEBUG_DIR)/$(MB_DIR)" + @$(MAKE) m-tmp TYPE=_clsv-dbg OUT_DIR="$(DEBUG_DIR)/$(NCDIRPREFIX)$(MB_DIR)" m-profile: - @$(MAKE) m-tmp TYPE=_clsv-profile OUT_DIR="$(PROFILE_DIR)/$(MB_DIR)" + @$(MAKE) m-tmp TYPE=_clsv-profile OUT_DIR="$(PROFILE_DIR)/$(NCDIRPREFIX)$(MB_DIR)" .PHONY: m-tmp mcl-tmp mingl-tmp glcl-tmp gl-tmp sv-tmp _clsv-dbg _clsv-rel _cl-dbg _cl-rel _out-rel _out-dbg reldir debugdir makelibs @@ -1631,13 +1643,13 @@ m-profile: _qcc-tmp: $(REQDIR) @$(MAKE) $(TYPE) EXE_NAME="$(EXE_NAME)$(EXEPOSTFIX)" PRECOMPHEADERS="" OUT_DIR="$(OUT_DIR)" WCFLAGS="$(CLIENT_ONLY_CFLAGS) $(WCFLAGS)" LDFLAGS="$(LDFLAGS) $(QCC_LDFLAGS)" OBJS="QCC_OBJS SOBJS" qcc-rel: - @$(MAKE) _qcc-tmp TYPE=_out-rel REQDIR=reldir EXE_NAME="../fteqcc$(BITS)" OUT_DIR="$(RELEASE_DIR)/$(QCC_DIR)" SOBJS="qcctui.o $(if $(findstring win,$(FTE_TARGET)),fteqcc.o)" + @$(MAKE) _qcc-tmp TYPE=_out-rel REQDIR=reldir EXE_NAME="../fteqcc$(BITS)" OUT_DIR="$(RELEASE_DIR)/$(NCDIRPREFIX)$(QCC_DIR)" SOBJS="qcctui.o $(if $(findstring win,$(FTE_TARGET)),fteqcc.o)" qccgui-rel: - @$(MAKE) _qcc-tmp TYPE=_out-rel REQDIR=reldir EXE_NAME="../fteqccgui$(BITS)" OUT_DIR="$(RELEASE_DIR)/$(QCC_DIR)gui" SOBJS="qccgui.o qccguistuff.o fteqcc.o" LDFLAGS="$(LDFLAGS) -lole32 -lcomdlg32 -lcomctl32 -lshlwapi -mwindows" + @$(MAKE) _qcc-tmp TYPE=_out-rel REQDIR=reldir EXE_NAME="../fteqccgui$(BITS)" OUT_DIR="$(RELEASE_DIR)/$(NCDIRPREFIX)$(QCC_DIR)gui" SOBJS="qccgui.o qccguistuff.o fteqcc.o" LDFLAGS="$(LDFLAGS) -lole32 -lcomdlg32 -lcomctl32 -lshlwapi -mwindows" qcc-dbg: - @$(MAKE) _qcc-tmp TYPE=_out-dbg REQDIR=debugdir EXE_NAME="../fteqcc$(BITS)" OUT_DIR="$(DEBUG_DIR)/$(QCC_DIR)" SOBJS="qcctui.o $(if $(findstring win,$(FTE_TARGET)),fteqcc.o)" + @$(MAKE) _qcc-tmp TYPE=_out-dbg REQDIR=debugdir EXE_NAME="../fteqcc$(BITS)" OUT_DIR="$(DEBUG_DIR)/$(NCDIRPREFIX)$(QCC_DIR)" SOBJS="qcctui.o $(if $(findstring win,$(FTE_TARGET)),fteqcc.o)" qccgui-dbg: - @$(MAKE) _qcc-tmp TYPE=_out-dbg REQDIR=debugdir EXE_NAME="../fteqccgui$(BITS)" OUT_DIR="$(DEBUG_DIR)/$(QCC_DIR)gui" SOBJS="qccgui.o qccguistuff.o fteqcc.o" LDFLAGS="$(LDFLAGS) -lole32 -lcomdlg32 -lcomctl32 -lshlwapi -mwindows" + @$(MAKE) _qcc-tmp TYPE=_out-dbg REQDIR=debugdir EXE_NAME="../fteqccgui$(BITS)" OUT_DIR="$(DEBUG_DIR)/$(NCDIRPREFIX)$(QCC_DIR)gui" SOBJS="qccgui.o qccguistuff.o fteqcc.o" LDFLAGS="$(LDFLAGS) -lole32 -lcomdlg32 -lcomctl32 -lshlwapi -mwindows" #scintilla is messy as fuck when building statically. but at least we can strip out the lexers we don't use this way. diff --git a/engine/client/cl_demo.c b/engine/client/cl_demo.c index b5521c8e8..ff160e26b 100644 --- a/engine/client/cl_demo.c +++ b/engine/client/cl_demo.c @@ -2546,6 +2546,13 @@ void CL_QTVPoll (void) MC_AddConsoleCommand(sourcesmenu, 42, 170, (sourcenum++)*8 + 32, va("%s (p%i, v%i)", srchost, numplayers, numviewers), va("qtvplay %i@%s\n", streamid, qtvhostname)); //else // FIXME: add error message here +#else + (void)init_numviewers; + (void)numviewers; + (void)init_numplayers; + (void)numplayers; + (void)streamid; + (void)sourcenum; #endif } //end of sourcelist entry diff --git a/engine/client/cl_ents.c b/engine/client/cl_ents.c index 00e42e729..6947ebcc2 100644 --- a/engine/client/cl_ents.c +++ b/engine/client/cl_ents.c @@ -786,7 +786,19 @@ void CLFTE_ReadDelta(unsigned int entnum, entity_state_t *news, entity_state_t * news->lightpflags = MSG_ReadByte(); } if (bits & UF_TRAILEFFECT) - news->u.q1.traileffectnum = MSG_ReadShort(); + { + unsigned short s; + s = MSG_ReadShort(); + news->u.q1.traileffectnum = s & 0x3fff; + if (news->u.q1.traileffectnum >= countof(cl.particle_ssprecache)) + news->u.q1.traileffectnum = 0; + if (s & 0x8000) + news->u.q1.emiteffectnum = MSG_ReadShort() & 0x3fff; + else + news->u.q1.emiteffectnum = 0; + if (news->u.q1.emiteffectnum >= countof(cl.particle_ssprecache)) + news->u.q1.emiteffectnum = 0; + } if (bits & UF_COLORMOD) { @@ -870,7 +882,12 @@ void CLFTE_ParseEntities(void) cls.netchan.incoming_sequence++; cl.last_servermessage = realtime; if (cls.fteprotocolextensions2 & PEXT2_PREDINFO) - inputframe = MSG_ReadLong(); + { + inputframe = (unsigned short)MSG_ReadShort(); + inputframe = (cl.movesequence&0xffff0000) | inputframe; + if (inputframe > cl.movesequence) + inputframe -= 0x00010000; //err, if its in the future then cl.movesequence must have wrapped. + } else inputframe = cl.movesequence; @@ -1781,7 +1798,7 @@ void CLNQ_ParseEntity(unsigned int bits) else if (cls.protocol_nq == CPNQ_FITZ666) { if (bits & FITZU_ALPHA) - state->trans = MSG_ReadByte(); + state->trans = (MSG_ReadByte()-1)&0xff; if (bits & RMQU_SCALE) state->scale = MSG_ReadByte(); @@ -3125,9 +3142,11 @@ void CL_ClearLerpEntsParticleState(void) void CL_LinkStaticEntities(void *pvs) { int i; - entity_t *ent, *stat; + entity_t *ent; model_t *clmodel; + static_entity_t *stat; extern cvar_t r_drawflame, gl_part_flame; + vec3_t mins, maxs; if (r_drawflame.ival < 0 || r_drawentities.ival == 0) return; @@ -3139,33 +3158,83 @@ void CL_LinkStaticEntities(void *pvs) { if (cl_numvisedicts == cl_maxvisedicts) break; - stat = &cl_static_entities[i].ent; + stat = &cl_static_entities[i]; - clmodel = stat->model; - if (!clmodel || clmodel->loadstate != MLS_LOADED) - continue; + clmodel = stat->ent.model; - if ((!r_drawflame.ival) && (clmodel->engineflags & MDLF_FLAME)) - continue; + if (!clmodel) + { + if (stat->mdlidx < 0) + { + if (stat->mdlidx > -MAX_CSMODELS) + clmodel = cl.model_csqcprecache[-stat->mdlidx]; + } + else + { + if (stat->mdlidx < MAX_PRECACHE_MODELS) + clmodel = cl.model_precache[stat->mdlidx]; + } + if (!clmodel || clmodel->loadstate == MLS_LOADING) + continue; + if (!cl.worldmodel || cl.worldmodel->loadstate != MLS_LOADED) + continue; + + stat->ent.model = clmodel; + + //figure out the correct axis for the model + if (clmodel && clmodel->type == mod_alias && (cls.protocol == CP_QUAKEWORLD || cls.protocol == CP_NETQUAKE)) + { + stat->state.angles[0]*=-1; + AngleVectors(stat->state.angles, stat->ent.axis[0], stat->ent.axis[1], stat->ent.axis[2]); + stat->state.angles[0]*=-1; + } + else + AngleVectors(stat->state.angles, stat->ent.axis[0], stat->ent.axis[1], stat->ent.axis[2]); + VectorInverse(stat->ent.axis[1]); + + + if (clmodel) + { + //FIXME: wait for model to load so we know the correct size? + /*FIXME: compensate for angle*/ + VectorAdd(stat->state.origin, clmodel->mins, mins); + VectorAdd(stat->state.origin, clmodel->maxs, maxs); + } + else + { + VectorCopy(stat->state.origin, mins); + VectorCopy(stat->state.origin, maxs); + } + cl.worldmodel->funcs.FindTouchedLeafs(cl.worldmodel, &stat->pvscache, mins, maxs); + } /*pvs test*/ - if (pvs && !cl.worldmodel->funcs.EdictInFatPVS(cl.worldmodel, &cl_static_entities[i].pvscache, pvs)) + if (pvs && !cl.worldmodel->funcs.EdictInFatPVS(cl.worldmodel, &stat->pvscache, pvs)) continue; - ent = &cl_visedicts[cl_numvisedicts++]; - *ent = *stat; - ent->framestate.g[FS_REG].frametime[0] = cl.time; - ent->framestate.g[FS_REG].frametime[1] = cl.time; - - // emit particles for statics (we don't need to cheat check statics) - if (clmodel->particleeffect >= 0 && gl_part_flame.ival) + // emit particles for statics (we don't need to cheat check statics) + if (stat->state.u.q1.emiteffectnum) + P_EmitEffect (stat->ent.origin, stat->ent.axis, MDLF_EMITFORWARDS, CL_TranslateParticleFromServer(stat->state.u.q1.emiteffectnum), &(stat->emit)); + else if (clmodel && clmodel->particleeffect >= 0 && gl_part_flame.ival) { // TODO: this is ugly.. assumes ent is in static entities, and subtracts // pointer math to get an index to use in cl_static emit // there needs to be a cleaner method for this - P_EmitEffect(ent->origin, clmodel->particleeffect, &cl_static_entities[i].emit); + P_EmitEffect(stat->ent.origin, stat->ent.axis, clmodel->engineflags, clmodel->particleeffect, &stat->emit); + + if ((!r_drawflame.ival) && (clmodel->engineflags & MDLF_FLAME)) + continue; } + //prepare to draw it + if (!clmodel || clmodel->loadstate != MLS_LOADED) + continue; + + ent = &cl_visedicts[cl_numvisedicts++]; + *ent = stat->ent; + ent->framestate.g[FS_REG].frametime[0] = cl.time; + ent->framestate.g[FS_REG].frametime[1] = cl.time; + // FIXME: no effects on static ents // CLQ1_AddPowerupShell(ent, false, stat->effects); } @@ -4034,6 +4103,11 @@ void CL_LinkPacketEntities (void) if (state->u.q1.traileffectnum) trailef = CL_TranslateParticleFromServer(state->u.q1.traileffectnum); + if (state->u.q1.emiteffectnum) + P_EmitEffect (ent->origin, ent->axis, MDLF_EMITFORWARDS, CL_TranslateParticleFromServer(state->u.q1.emiteffectnum), &(le->emitstate)); + else if (model->particleeffect != P_INVALID && cls.allow_anyparticles && gl_part_flame.ival) + P_EmitEffect (ent->origin, ent->axis, model->engineflags, model->particleeffect, &(le->emitstate)); + // add automatic particle trails if (!model || (!(modelflags&~MF_ROTATE) && trailef < 0)) continue; @@ -4065,8 +4139,6 @@ void CL_LinkPacketEntities (void) if (trailef == P_INVALID || pe->ParticleTrail (old_origin, ent->origin, trailef, ent->keynum, ent->axis, &(le->trailstate))) if (model->traildefaultindex >= 0) pe->ParticleTrailIndex(old_origin, ent->origin, trailidx, 0, &(le->trailstate)); - if (model->particleeffect != P_INVALID && cls.allow_anyparticles && gl_part_flame.ival) - P_EmitEffect (ent->origin, model->particleeffect, &(le->emitstate)); //dlights are not so customisable. if (r_rocketlight.value && (modelflags & MF_ROCKET) && !(state->lightpflags & (PFLAGS_FULLDYNAMIC|PFLAGS_CORONA))) diff --git a/engine/client/cl_input.c b/engine/client/cl_input.c index 658f1499b..404e8d353 100644 --- a/engine/client/cl_input.c +++ b/engine/client/cl_input.c @@ -1035,7 +1035,7 @@ void CLNQ_SendMove (usercmd_t *cmd, int pnum, sizebuf_t *buf) MSG_WriteByte (buf, clc_move); - if (cls.protocol_nq >= CPNQ_DP7 || (cls.fteprotocolextensions2 & PEXT2_PREDINFO)) + if (cls.protocol_nq >= CPNQ_DP7) { extern cvar_t cl_nopred; if (cl_nopred.ival) @@ -1043,6 +1043,8 @@ void CLNQ_SendMove (usercmd_t *cmd, int pnum, sizebuf_t *buf) else MSG_WriteLong(buf, cl.movesequence); } + else if (cls.fteprotocolextensions2 & PEXT2_PREDINFO) + MSG_WriteShort(buf, cl.movesequence&0xffff); MSG_WriteFloat (buf, cl.gametime); // so server can get ping times cmd->msec = bound(0, cl.gametime - oldgametime, .25)*1000; diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 7978b4bf3..cfabf3229 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -741,8 +741,12 @@ void CL_CheckForResend (void) #ifndef CLIENTONLY if (!cls.state && (!connectinfo.trying || sv.state != ss_clustermode) && sv.state) { +#ifdef NQPROT qboolean proquakeangles = false; +#endif +#ifdef NETPREPARSE extern cvar_t dpcompat_nopreparse; +#endif memset(&connectinfo, 0, sizeof(connectinfo)); Q_strncpyz (cls.servername, "internalserver", sizeof(cls.servername)); Cvar_ForceSet(&cl_servername, cls.servername); @@ -918,8 +922,9 @@ void CL_CheckForResend (void) } net_message.packing = SZ_RAWBYTES; net_message.cursize = 0; + MSG_BeginReading(net_message.prim); - if (connectinfo.subprotocol == CPNQ_ID) + if (connectinfo.subprotocol == CPNQ_ID && !proquakeangles) { net_from = connectinfo.adr; Cmd_TokenizeString (va("connect %i %i %i \"\\name\\unconnected\"", NQ_NETCHAN_VERSION, 0, SV_NewChallenge()), false, false); @@ -1486,7 +1491,7 @@ void CL_ClearState (void) #ifdef HEXEN2 T_FreeInfoStrings(); #endif - SCR_ShowPic_Clear(false); + SCR_ShowPic_ClearAll(false); if (cl.playerview[0].playernum == -1) { //left over from q2 connect. @@ -2932,8 +2937,10 @@ void CL_ConnectionlessPacket (void) switch(strtoul(p, &p, 0)) { case PROTOCOL_VERSION_R1Q2: +#ifdef AVAIL_ZLIB //r1q2 will typically send us compressed data, which is a problem if we can't handle that (q2pro has a way to disable it). if (connectinfo.subprotocol < PROTOCOL_VERSION_R1Q2) connectinfo.subprotocol = PROTOCOL_VERSION_R1Q2; +#endif break; case PROTOCOL_VERSION_Q2PRO: if (connectinfo.subprotocol < PROTOCOL_VERSION_Q2PRO) @@ -5191,7 +5198,11 @@ double Host_Frame (double time) { //nq can send 'frames' without any entities before we're on the server, leading to short periods where the local player's position is not known. this is bad. so be more cautious with nq. this might break csqc. CL_TransitionEntities(); - if (cl.currentpackentities->num_entities) + if (cl.currentpackentities->num_entities +#ifdef CSQC_DAT + || (cls.fteprotocolextensions & PEXT_CSQC) +#endif + ) CL_MakeActive("Quake"); } else @@ -5502,7 +5513,7 @@ void CL_ExecInitialConfigs(char *resetcommand) int def; Cbuf_Execute (); //make sure any pending console commands are done with. mostly, anyway... - SCR_ShowPic_Clear(true); + SCR_ShowPic_ClearAll(true); Cbuf_AddText("unbindall\n", RESTRICT_LOCAL); Cbuf_AddText("bind volup \"inc volume 0.1\"\n", RESTRICT_LOCAL); diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 669a4ebcb..ddb272b63 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -36,6 +36,13 @@ int cl_dp_csqc_progssize; int cl_dp_csqc_progscrc; int cl_dp_serverextension_download; +#ifdef AVAIL_ZLIB +#ifndef ZEXPORT + #define ZEXPORT VARGS +#endif +#include +#endif + char *svc_qwstrings[] = { @@ -714,7 +721,12 @@ void CL_SendDownloadStartRequest(char *filename, char *localname, unsigned int f COM_StripExtension (localname, dl->tempname, sizeof(dl->tempname)-5); Q_strncatz (dl->tempname, ".tmp", sizeof(dl->tempname)); - CL_SendClientCommand(true, "download %s", filename); +#ifdef AVAIL_ZLIB + if (cls.protocol == CP_QUAKE2 && cls.protocol_q2 == PROTOCOL_VERSION_R1Q2) + CL_SendClientCommand(true, "download %s 0 udp-zlib", filename); + else +#endif + CL_SendClientCommand(true, "download %s", filename); dl->method = DL_QWPENDING; dl->percent = 0; @@ -1594,9 +1606,26 @@ void CL_RequestNextDownload (void) return; } + Cvar_ForceCallback(Cvar_FindVar("r_particlesdesc")); + #ifdef Q2CLIENT if (cls.protocol == CP_QUAKE2) { + if (cls.protocol_q2 == PROTOCOL_VERSION_R1Q2) + { //fixme: make dynamic... +// MSG_WriteByte (&cls.netchan.message, clcr1q2_setting); +// MSG_WriteShort (&cls.netchan.message, R1Q2_CLSET_NOGUN); +// MSG_WriteShort (&cls.netchan.message, r_drawviewmodel.value <= 0); + +// MSG_WriteByte (&cls.netchan.message, clcr1q2_setting); +// MSG_WriteShort (&cls.netchan.message, R1Q2_CLSET_PLAYERUPDATES); +// MSG_WriteShort (&cls.netchan.message, 1); + +// MSG_WriteByte (&cls.netchan.message, clcr1q2_setting); +// MSG_WriteShort (&cls.netchan.message, R1Q2_CLSET_FPS); +// MSG_WriteShort (&cls.netchan.message, 30); + } + Skin_NextDownload(); SCR_SetLoadingStage(LS_NONE); CL_SendClientCommand(true, "begin %i\n", cl.servercount); @@ -2395,7 +2424,7 @@ CL_ParseDownload A download message has been received from the server ===================== */ -void CL_ParseDownload (void) +void CL_ParseDownload (qboolean zlib) { extern cvar_t cl_dlemptyterminate; int size, percent; @@ -2474,6 +2503,47 @@ void CL_ParseDownload (void) SCR_EndLoadingPlaque(); } + if (zlib) + { +#ifdef AVAIL_ZLIB + z_stream s; + unsigned short clen = size; + unsigned short ulen = MSG_ReadShort(); + char cdata[8192]; + unsigned int done = 0; + memset(&s, 0, sizeof(s)); + s.next_in = net_message.data + msg_readcount; + s.avail_in = clen; + if (inflateInit2(&s, -15) != Z_OK) + Host_EndGame ("CL_ParseZDownload: unable to initialise zlib"); + for(;;) + { + int zerr; + s.next_out = cdata; + s.avail_out = sizeof(cdata); + zerr = inflate(&s, Z_FULL_FLUSH); + VFS_WRITE (dl->file, cdata, s.total_out - done); + done = s.total_out; + if (zerr == Z_STREAM_END) + break; + else if (zerr == Z_OK) + continue; + else + Host_EndGame ("CL_ParseZDownload: stream truncated"); + } + if (inflateEnd(&s) != Z_OK) + Host_EndGame ("CL_ParseZDownload: stream truncated"); + VFS_WRITE (dl->file, cdata, s.total_out - done); + done = s.total_out; + if (s.total_out != ulen || s.total_in != clen) + Host_EndGame ("CL_ParseZDownload: stream truncated"); + +#else + Host_EndGame("Unable to handle zlib downloads, zlib is not supported in this build"); +#endif + msg_readcount += size; + } + else #ifdef PEXT_ZLIBDL if (percent >= 101 && percent <= 201)// && cls.fteprotocolextensions & PEXT_ZLIBDL) { @@ -2534,7 +2604,7 @@ qboolean CL_ParseOOBDownload(void) if (MSG_ReadChar() != svc_download) return false; - CL_ParseDownload(); + CL_ParseDownload(false); return true; } @@ -3204,6 +3274,7 @@ void CLQ2_ParseServerData (void) cl.numq2visibleweapons = 1; //give it a default. cl.q2visibleweapons[0] = "weapon.md2"; + cl.q2svnetrate = 10; // get the full level name str = MSG_ReadString (); @@ -3217,11 +3288,16 @@ void CLQ2_ParseServerData (void) if (isenhanced) Host_EndGame ("R1Q2 server is running an unsupported mod"); r1q2ver = MSG_ReadShort(); //protocol version... limit... yeah, buggy. - if (r1q2ver < 1903 || r1q2ver > 1905) + if (r1q2ver > 1905) Host_EndGame ("R1Q2 server version %i not supported", r1q2ver); - MSG_ReadByte(); //'used to be advanced deltas' - MSG_ReadByte(); //strafejump hack + if (r1q2ver >= 1903) + { + MSG_ReadByte(); //'used to be advanced deltas' + MSG_ReadByte(); //strafejump hack + } + if (r1q2ver >= 1904) + cls.netchan.netprim.flags |= NPQ2_R1Q2_UCMD; if (r1q2ver >= 1905) cls.netchan.netprim.flags |= NPQ2_SOLID32; } @@ -3241,6 +3317,7 @@ void CLQ2_ParseServerData (void) MSG_ReadByte(); //some kind of waterjump hack enable } + cls.netchan.message.prim = cls.netchan.netprim; MSG_ChangePrimitives(cls.netchan.netprim); if (cl.playerview[0].playernum == -1) @@ -3435,6 +3512,21 @@ void CLNQ_ParseServerData(void) //Doesn't change gamedir - use with caution. CLNQ_ParseProtoVersion(); + if (cls.fteprotocolextensions2 & PEXT2_PREDINFO) + { + str = MSG_ReadString(); +#ifndef CLIENTONLY + if (!sv.state) +#endif + { + COM_FlushTempoaryPacks(); + COM_Gamedir(str, NULL); +#ifndef CLIENTONLY + Info_SetValueForStarKey (svs.info, "*gamedir", str, MAX_SERVERINFO_STRING); +#endif + } + } + cl.allocated_client_slots = MSG_ReadByte(); if (cl.allocated_client_slots > MAX_CLIENTS) { @@ -3623,9 +3715,9 @@ void CLNQ_ParseClientdata (void) CL_SetStatInt(0, STAT_VIEWHEIGHT, DEFAULT_VIEWHEIGHT); if (bits & SU_IDEALPITCH) - /*cl.idealpitch =*/ MSG_ReadChar (); - /*else - cl.idealpitch = 0;*/ + CL_SetStatInt(0, STAT_IDEALPITCH, MSG_ReadChar ()); + else + CL_SetStatInt(0, STAT_IDEALPITCH, 0); for (i=0 ; i<3 ; i++) { @@ -4325,8 +4417,14 @@ void CL_ParseStatic (int version) VectorCopy (es.origin, ent->origin); VectorCopy (es.angles, ent->angles); - es.angles[0]*=-1; - AngleVectors(es.angles, ent->axis[0], ent->axis[1], ent->axis[2]); + if (ent->model && ent->model->type == mod_alias) + { + es.angles[0]*=-1; + AngleVectors(es.angles, ent->axis[0], ent->axis[1], ent->axis[2]); + es.angles[0]*=-1; + } + else + AngleVectors(es.angles, ent->axis[0], ent->axis[1], ent->axis[2]); VectorInverse(ent->axis[1]); if (!cl.worldmodel || cl.worldmodel->loadstate != MLS_LOADED) @@ -6782,7 +6880,7 @@ void CLQW_ParseServerMessage (void) break; case svc_download: - CL_ParseDownload (); + CL_ParseDownload (false); break; case svc_playerinfo: @@ -6906,6 +7004,63 @@ void CLQW_ParseServerMessage (void) } #ifdef Q2CLIENT +void CLQ2_ParseZPacket(void) +{ +#ifndef AVAIL_ZLIB + Host_EndGame ("CLQ2_ParseZPacket: zlib not supported in this build"); +#else + z_stream s; + char *indata, *outdata; //we're hacking stuff onto the end of the current buffer, to avoid issues if something errors out and doesn't leave net_message in a clean state + unsigned short clen = MSG_ReadShort(); + unsigned short ulen = MSG_ReadShort(); + sizebuf_t restoremsg; + int restorereadcount; + if (clen > net_message.cursize-msg_readcount) + Host_EndGame ("CLQ2_ParseZPacket: svcr1q2_zpacket truncated"); + if (ulen > net_message.maxsize-net_message.cursize) + Host_EndGame ("CLQ2_ParseZPacket: svcr1q2_zpacket overflow"); + indata = net_message.data + msg_readcount; + outdata = net_message.data + net_message.cursize; + MSG_ReadSkip(clen); + restoremsg = net_message; + restorereadcount = msg_readcount; + msg_readcount = net_message.cursize; + net_message.cursize += ulen; + + memset(&s, 0, sizeof(s)); + s.next_in = indata; + s.avail_in = clen; + s.total_in = 0; + s.next_out = outdata; + s.avail_out = ulen; + s.total_out = 0; + if (inflateInit2(&s, -15) != Z_OK) + Host_EndGame ("CLQ2_ParseZPacket: unable to initialise zlib"); + if (inflate(&s, Z_FINISH) != Z_STREAM_END) + Host_EndGame ("CLQ2_ParseZPacket: stream truncated"); + if (inflateEnd(&s) != Z_OK) + Host_EndGame ("CLQ2_ParseZPacket: stream truncated"); + if (s.total_out != ulen || s.total_in != clen) + Host_EndGame ("CLQ2_ParseZPacket: stream truncated"); + + CLQ2_ParseServerMessage(); + net_message = restoremsg; + msg_readcount = restorereadcount; + msg_badread = false; +#endif +} +void CLR1Q2_ParseSetting(void) +{ + int setting = MSG_ReadLong(); + int value = MSG_ReadLong(); + + if (setting == R1Q2_SVSET_FPS) + { + cl.q2svnetrate = value; + if (cl.validsequence) + Con_Printf("warning: fps rate changed mid-game\n"); //fixme: we need to clean up lerping stuff. if its now lower, we might have a whole load of things waiting ages for a timeout. + } +} void CL_WriteDemoMessage (sizebuf_t *msg, int payloadoffset); void CLQ2_ParseServerMessage (void) { @@ -6968,6 +7123,7 @@ void CLQ2_ParseServerMessage (void) switch (cmd) { default: +isilegible: if (cls.protocol_q2 == PROTOCOL_VERSION_R1Q2 || cls.protocol_q2 == PROTOCOL_VERSION_Q2PRO) { switch(cmd & 0x1f) @@ -7064,7 +7220,7 @@ void CLQ2_ParseServerMessage (void) SCR_CenterPrint (seat, s, false); break; case svcq2_download: //16 // [short] size [size bytes] - CL_ParseDownload(); + CL_ParseDownload(false); break; case svcq2_playerinfo: //17 // variable Host_EndGame ("CL_ParseServerMessage: svcq2_playerinfo not as part of svcq2_frame"); @@ -7078,6 +7234,31 @@ void CLQ2_ParseServerMessage (void) case svcq2_frame: //20 (the bastard to implement.) CLQ2_ParseFrame(0); break; + + case svcr1q2_zpacket: //r1q2, just try to ignore it. + if (cls.protocol_q2 == PROTOCOL_VERSION_R1Q2 || cls.protocol_q2 == PROTOCOL_VERSION_Q2PRO) + CLQ2_ParseZPacket(); + else + goto isilegible; + break; + case svcr1q2_zdownload: + if (cls.protocol_q2 == PROTOCOL_VERSION_R1Q2 || cls.protocol_q2 == PROTOCOL_VERSION_Q2PRO) + CL_ParseDownload(true); + else + goto isilegible; + break; + case svcr1q2_playerupdate: + if (cls.protocol_q2 == PROTOCOL_VERSION_R1Q2) + CLR1Q2_ParsePlayerUpdate(); + else + goto isilegible; + break; + case svcr1q2_setting: + if (cls.protocol_q2 == PROTOCOL_VERSION_R1Q2) + CLR1Q2_ParseSetting(); + else + goto isilegible; + break; } } CL_SetSolidEntities (); @@ -7480,6 +7661,14 @@ void CLNQ_ParseServerMessage (void) cl.gametime = MSG_ReadFloat(); cl.gametimemark = realtime; + if (cls.fteprotocolextensions2 & PEXT2_PREDINFO) + { + unsigned int seq = (cl.ackedmovesequence&0xffff0000) | MSG_ReadShort(); + if (seq > cl.ackedmovesequence) + seq -= 0x10000; //protect against wraps. + cl.ackedmovesequence = seq; + } + { extern vec3_t demoangles; int fr = cls.netchan.incoming_sequence&UPDATE_MASK; diff --git a/engine/client/cl_pred.c b/engine/client/cl_pred.c index 3f00e91de..673b58eb5 100644 --- a/engine/client/cl_pred.c +++ b/engine/client/cl_pred.c @@ -1117,7 +1117,7 @@ void CL_PredictMovePNum (int seat) if (cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS) { #ifdef QUAKESTATS - //putting weapon frames in there was probably a stupid idea. + //putting weapon frames in there was a stupid idea. qwisms I guess. if (!(cls.fteprotocolextensions2 & PEXT2_PREDINFO)) { pv->stats[STAT_WEAPONFRAME] = cl.players[pv->playernum].stats[STAT_WEAPONFRAME] = pe->entities[i].u.q1.weaponframe; diff --git a/engine/client/cl_screen.c b/engine/client/cl_screen.c index e1f16b71b..74e3b2a6c 100644 --- a/engine/client/cl_screen.c +++ b/engine/client/cl_screen.c @@ -695,7 +695,9 @@ void SCR_DrawCenterString (vrect_t *rect, cprint_t *p, struct font_s *font) void SCR_CheckDrawCenterString (void) { +#ifdef QUAKEHUD extern qboolean sb_showscores; +#endif int pnum; cprint_t *p; @@ -1035,7 +1037,7 @@ char *SCR_ShowPics_ClickCommand(int cx, int cy) } //all=false clears only server pics, not ones from configs. -void SCR_ShowPic_Clear(qboolean persistflag) +void SCR_ShowPic_ClearAll(qboolean persistflag) { showpic_t **link, *sp; int pnum; @@ -1048,7 +1050,7 @@ void SCR_ShowPic_Clear(qboolean persistflag) for (link = &showpics; (sp=*link); ) { - if (sp->persist == persistflag) + if (sp->persist != persistflag) { link = &sp->next; continue; @@ -1252,7 +1254,7 @@ void SCR_ShowPic_Script_f(void) void SCR_ShowPic_Remove_f(void) { - SCR_ShowPic_Clear(!Cmd_FromGamecode()); + SCR_ShowPic_ClearAll(!Cmd_FromGamecode()); } //============================================================================= @@ -2355,7 +2357,7 @@ void *SCR_ScreenShot_Capture(int fbwidth, int fbheight, enum uploadfmt *fmt) qboolean okay = false; Q_strncpyz(r_refdef.rt_destcolour[0].texname, "megascreeny", sizeof(r_refdef.rt_destcolour[0].texname)); - R2D_RT_Configure(r_refdef.rt_destcolour[0].texname, fbwidth, fbheight, 1); + R2D_RT_Configure(r_refdef.rt_destcolour[0].texname, fbwidth, fbheight, 1, RT_IMAGEFLAGS); BE_RenderToTextureUpdate2d(true); R2D_FillBlock(0, 0, vid.fbvwidth, vid.fbvheight); @@ -2388,7 +2390,7 @@ void *SCR_ScreenShot_Capture(int fbwidth, int fbheight, enum uploadfmt *fmt) else buf = VID_GetRGBInfo(&width, &height, fmt); - R2D_RT_Configure(r_refdef.rt_destcolour[0].texname, 0, 0, 0); + R2D_RT_Configure(r_refdef.rt_destcolour[0].texname, 0, 0, 0, RT_IMAGEFLAGS); Q_strncpyz(r_refdef.rt_destcolour[0].texname, "", sizeof(r_refdef.rt_destcolour[0].texname)); BE_RenderToTextureUpdate2d(true); @@ -3007,8 +3009,7 @@ void SCR_DeInit (void) for (i = 0; i < countof(scr_centerprint); i++) { Z_Free(scr_centerprint[i].string); - scr_centerprint[i].string = NULL; - scr_centerprint[i].stringbytes = 0; + memset(&scr_centerprint[i], 0, sizeof(scr_centerprint[i])); } if (scr_initialized) { diff --git a/engine/client/cl_tent.c b/engine/client/cl_tent.c index ffd50cebb..847f5241a 100644 --- a/engine/client/cl_tent.c +++ b/engine/client/cl_tent.c @@ -311,24 +311,35 @@ typedef struct associatedeffect_s { AE_TRAIL, AE_EMIT, - AE_REPLACE } type; + unsigned int meflags; } 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)); + int type, i; + unsigned int flags = 0; struct associatedeffect_s *ae; if (!strcmp(Cmd_Argv(0), "r_trail")) type = AE_TRAIL; else { - if (type) - type = AE_REPLACE; - else - type = AE_EMIT; + type = AE_EMIT; + + for (i = 3; i < Cmd_Argc(); i++) + { + const char *fn = Cmd_Argv(i); + if (!strcmp(fn, "replace") || !strcmp(fn, "1")) + flags |= MDLF_EMITREPLACE; + else if (!strcmp(fn, "forwards") || !strcmp(fn, "forward")) + flags |= MDLF_EMITFORWARDS; + else if (!strcmp(fn, "0")) + ; //1 or 0 are legacy, meaning replace or not + else + Con_DPrintf("%s %s: unknown flag %s\n", Cmd_Argv(0), modelname, fn); + } } if ( @@ -351,20 +362,18 @@ void CL_AssociateEffect_f(void) { if (!strcmp(ae->mname, modelname)) if ((ae->type==AE_TRAIL) == (type==AE_TRAIL)) - { - strcpy(ae->pname, effectname); break; - } } if (!ae) { ae = Z_Malloc(sizeof(*ae)); - ae->type = type; strcpy(ae->mname, modelname); - strcpy(ae->pname, effectname); ae->next = associatedeffect; associatedeffect = ae; } + ae->type = type; + ae->meflags = flags; + strcpy(ae->pname, effectname); if (pe) CL_RegisterParticles(); @@ -433,7 +442,8 @@ void P_LoadedModel(model_t *mod) mod->particleeffect = P_INVALID; mod->particletrail = P_INVALID; - mod->engineflags &= ~MDLF_ENGULPHS; + mod->engineflags &= ~(MDLF_EMITREPLACE|MDLF_EMITFORWARDS); + mod->engineflags |= MDLF_RECALCULATERAIN; for(ae = associatedeffect; ae; ae = ae->next) { if (!strcmp(ae->mname, mod->name)) @@ -445,11 +455,7 @@ void P_LoadedModel(model_t *mod) break; case AE_EMIT: mod->particleeffect = P_FindParticleType(ae->pname); - mod->engineflags &= ~MDLF_ENGULPHS; - break; - case AE_REPLACE: - mod->particleeffect = P_FindParticleType(ae->pname); - mod->engineflags |= MDLF_ENGULPHS; + mod->engineflags |= ae->meflags; break; } } diff --git a/engine/client/cl_ui.c b/engine/client/cl_ui.c index d5a738ad7..a44c5fbba 100644 --- a/engine/client/cl_ui.c +++ b/engine/client/cl_ui.c @@ -600,6 +600,8 @@ void VQ3_RenderView(const q3refdef_t *ref) r_refdef.vrect.height = ref->height; r_refdef.time = ref->time/1000.0f; r_refdef.useperspective = true; + r_refdef.mindist = bound(0.1, gl_mindist.value, 4); + r_refdef.maxdist = gl_maxdist.value; r_refdef.playerview = &cl.playerview[0]; if (r_torch.ival) diff --git a/engine/client/client.h b/engine/client/client.h index 0a0c6c701..a00375d19 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -874,6 +874,7 @@ typedef struct char q2layout[MAX_SPLITS][1024]; int parse_entities; float lerpfrac; + float q2svnetrate; //number of frames we expect to receive per second (required to calculate the server time correctly). #endif char lastcenterprint[1024]; //prevents too much spam with console centerprint logging. @@ -1511,6 +1512,7 @@ void CLQ2_ParseTEnt (void); void CLQ2_AddEntities (void); void CLQ2_ParseBaseline (void); void CLQ2_ClearParticleState(void); +void CLR1Q2_ParsePlayerUpdate(void); void CLQ2_ParseFrame (int extrabits); void CLQ2_RunMuzzleFlash2 (int ent, int flash_number); int CLQ2_RegisterTEntModels (void); diff --git a/engine/client/clq2_ents.c b/engine/client/clq2_ents.c index b4b2a3001..f6b1b4b22 100644 --- a/engine/client/clq2_ents.c +++ b/engine/client/clq2_ents.c @@ -159,11 +159,11 @@ void CLQ2_TeleporterParticles(entity_state_t *es){}; /*these are emissive effects (ie: emitted each frame), but they're also mutually exclusive, so sharing emitstate is fine*/ void CLQ2_Tracker_Shell(q2centity_t *ent, vec3_t org) { - P_EmitEffect (org, pt_q2[Q2PT_TRACKERSHELL], &(ent->emitstate)); + P_EmitEffect (org, NULL, 0, pt_q2[Q2PT_TRACKERSHELL], &(ent->emitstate)); }; void CLQ2_BfgParticles(q2centity_t *ent, vec3_t org) { - P_EmitEffect (org, pt_q2[Q2PT_BFGPARTICLES], &(ent->emitstate)); + P_EmitEffect (org, NULL, 0, pt_q2[Q2PT_BFGPARTICLES], &(ent->emitstate)); }; void CLQ2_FlyEffect(q2centity_t *ent, vec3_t org) { @@ -1230,6 +1230,51 @@ void CLQ2_FireEntityEvents (q2frame_t *frame) } } +void CLR1Q2_ParsePlayerUpdate(void) +{ + unsigned int framenum = MSG_ReadLong(); + q2frame_t *frame = &cl.q2frames[framenum & Q2UPDATE_MASK]; + int seat; + if (frame->serverframe != framenum) + Con_DPrintf("svcr1q2_playerupdate: stale frame\n"); + else if (!frame->valid) + Con_DPrintf("svcr1q2_playerupdate: invalid frame\n"); //excrement happens. + else + { + int pnum; + vec3_t neworg; + entity_state_t *st; + for (pnum = 0; pnum < frame->num_entities; pnum++) + { + st = &clq2_parse_entities[(frame->parse_entities+pnum) & (MAX_PARSE_ENTITIES-1)]; + + //I don't like how r1q2 does its maxclients, so I'm just going to go on message size instead + if (msg_readcount == net_message.cursize) + break; + + //the local client(s) is not included, thanks to prediction covering that. + for (seat = 0; seat < cl.splitclients; seat++) + { + if (st->number == cl.playerview[0].playernum+1) + break; + } + if (seat != cl.splitclients) + continue; + + if (st->number != 1) + continue; + + //FIXME: handle this, with lerping and stuff. + MSG_ReadPos(neworg); + } + + //just for sanity's sake + if (msg_readcount != net_message.cursize) + msg_badread = true; + } + //this should be the only/last thing in these packets, because if it isn't then we're screwed when a packet got lost + msg_readcount = net_message.cursize; +} /* ================ @@ -1274,7 +1319,7 @@ void CLQ2_ParseFrame (int extrabits) extrabits = Q2PSX_OLD; } - cl.q2frame.servertime = cl.q2frame.serverframe*100; + cl.q2frame.servertime = cl.q2frame.serverframe*(1000/cl.q2svnetrate); cl.oldgametime = cl.gametime; cl.oldgametimemark = cl.gametimemark; @@ -2151,6 +2196,8 @@ void CLQ2_CalcViewValues (int seat) memcpy(r_refdef.areabits, cl.q2frame.areabits, sizeof(r_refdef.areabits)); r_refdef.useperspective = true; + r_refdef.mindist = bound(0.1, gl_mindist.value, 4); + r_refdef.maxdist = gl_maxdist.value; // find the previous frame to interpolate from ps = &cl.q2frame.playerstate[seat]; @@ -2246,27 +2293,10 @@ void CLQ2_AddEntities (void) if (cls.state != ca_active) return; - cl.lerpfrac = 1.0 - (cl.q2frame.servertime - cl.time*1000) * 0.01; + cl.lerpfrac = 1.0 - (cl.q2frame.servertime/1000.0 - cl.time) * cl.q2svnetrate; // Con_Printf("%g: %g\n", cl.q2frame.servertime - (cl.time*1000), cl.lerpfrac); cl.lerpfrac = bound(0, cl.lerpfrac, 1); -/* if (cl.time*1000 > cl.q2frame.servertime) - { -// if (cl_showclamp.value) -// Con_Printf ("high clamp %f\n", cl.time - cl.q2frame.servertime); - cl.time = (cl.q2frame.servertime)/1000.0; - cl.lerpfrac = 1.0; - } - else if (cl.time*1000 < cl.q2frame.servertime - 100) - { -// if (cl_showclamp.value) -// Con_Printf ("low clamp %f\n", cl.q2frame.servertime-100 - cl.time); - cl.time = (cl.q2frame.servertime - 100)/1000.0; - cl.lerpfrac = 0; - } - else - cl.lerpfrac = 1.0 - (cl.q2frame.servertime - cl.time*1000) * 0.01; -*/ for (seat = 0; seat < cl.splitclients; seat++) CLQ2_CalcViewValues (seat); CLQ2_AddPacketEntities (&cl.q2frame); diff --git a/engine/client/m_mp3.c b/engine/client/m_mp3.c index 3abc4463a..2053fb681 100644 --- a/engine/client/m_mp3.c +++ b/engine/client/m_mp3.c @@ -3662,7 +3662,7 @@ static void Media_RecordFilm (char *recordingname, qboolean demo) if (demo && capturewidth.ival && captureheight.ival && qrenderer == QR_OPENGL && gl_config.ext_framebuffer_objects) { capturingfbo = true; - capturetexture = R2D_RT_Configure("$democapture", capturewidth.ival, captureheight.ival, TF_BGRA32); + capturetexture = R2D_RT_Configure("$democapture", capturewidth.ival, captureheight.ival, TF_BGRA32, RT_IMAGEFLAGS); captureoldfbo = GLBE_FBO_Update(&capturefbo, FBO_RB_DEPTH|(Sh_StencilShadowsActive()?FBO_RB_STENCIL:0), &capturetexture, 1, r_nulltex, capturewidth.ival, captureheight.ival, 0); vid.fbpwidth = capturewidth.ival; vid.fbpheight = captureheight.ival; diff --git a/engine/client/m_script.c b/engine/client/m_script.c index 749a7e1a4..238672d8d 100644 --- a/engine/client/m_script.c +++ b/engine/client/m_script.c @@ -8,17 +8,27 @@ int selectitem; menu_t *menu_script; -void M_Script_Option (menu_t *menu, char *optionvalue) +void M_Script_Option (menu_t *menu, char *optionvalue, qboolean isexplicit) { menuoption_t *mo; + char *scriptname = menu->data; char buf[8192]; - int level; + //FIXME: not sure about these, as the user typically can't see what they'll do. + int expandlevel = RESTRICT_SERVER; + int execlevel = RESTRICT_LOCAL; - Cbuf_AddText("wait\n", RESTRICT_LOCAL); + Cbuf_AddText("wait\n", execlevel); + + if (!*scriptname) + { + if (isexplicit) + Cbuf_AddText(va("%s\n", optionvalue), execlevel); + return; + } //update the option - Cbuf_AddText(va("set option %s\n", COM_QuotedString(optionvalue, buf, sizeof(buf), false)), RESTRICT_LOCAL); + Cbuf_AddText(va("set option %s\n", COM_QuotedString(optionvalue, buf, sizeof(buf), false)), execlevel); //expand private arguments for (mo = menu->options, *buf = 0; mo; mo = mo->common.next) @@ -33,12 +43,11 @@ void M_Script_Option (menu_t *menu, char *optionvalue) } } Cmd_TokenizeString(buf, false, false); - level = RESTRICT_SERVER; - Cmd_ExpandString(menu->data, buf, sizeof(buf), &level, true, true); + Cmd_ExpandString(scriptname, buf, sizeof(buf), &expandlevel, true, true); //and execute it as-is - Cbuf_AddText(buf, RESTRICT_LOCAL); - Cbuf_AddText("\n", RESTRICT_LOCAL); + Cbuf_AddText(buf, execlevel); + Cbuf_AddText("\n", execlevel); } void M_Script_Remove (menu_t *menu) @@ -46,7 +55,7 @@ void M_Script_Remove (menu_t *menu) if (menu == menu_script) menu_script = NULL; - M_Script_Option(menu, "cancel"); + M_Script_Option(menu, "cancel", false); } qboolean M_Script_Key (int key, menu_t *menu) { @@ -55,9 +64,9 @@ qboolean M_Script_Key (int key, menu_t *menu) if (key >= '0' && key <= '9' && menu->data) { if (key == '0') //specal case so that "hello" < "0"... (plus matches common impulses) - M_Script_Option(menu, "10"); + M_Script_Option(menu, "10", false); else - M_Script_Option(menu, va("%i", key-'0')); + M_Script_Option(menu, va("%i", key-'0'), false); return true; } return false; @@ -67,7 +76,7 @@ void M_MenuS_Callback_f (void) { if (menu_script) { - M_Script_Option(menu_script, Cmd_Argv(1)); + M_Script_Option(menu_script, Cmd_Argv(1), true); } } void M_MenuS_Clear_f (void) @@ -253,7 +262,7 @@ void M_MenuS_Text_f (void) MC_AddBufferedText(menu_script, x, 0, y, text, false, false); else { - option = (menuoption_t *)MC_AddConsoleCommand(menu_script, x, 0, y, text, va("menucallback %s\n", command)); + option = (menuoption_t *)MC_AddConsoleCommand(menu_script, x, 0, y, text, va("menucallback \"%s\"\n", command)); if (selectitem-- == 0) menu_script->selecteditem = option; } diff --git a/engine/client/merged.h b/engine/client/merged.h index 5be6db7d7..bf55a40d4 100644 --- a/engine/client/merged.h +++ b/engine/client/merged.h @@ -478,7 +478,8 @@ typedef struct rendererinfo_s { #define BE_RenderToTextureUpdate2d rf->BE_RenderToTextureUpdate2d -texid_t R2D_RT_Configure(const char *id, int width, int height, uploadfmt_t rtfmt); +#define RT_IMAGEFLAGS IF_NOMIPMAP|IF_CLAMP|IF_LINEAR|IF_RENDERTARGET +texid_t R2D_RT_Configure(const char *id, int width, int height, uploadfmt_t rtfmt, unsigned int imageflags); texid_t R2D_RT_GetTexture(const char *id, unsigned int *width, unsigned int *height); diff --git a/engine/client/p_classic.c b/engine/client/p_classic.c index 585c0346c..5d59a375b 100644 --- a/engine/client/p_classic.c +++ b/engine/client/p_classic.c @@ -261,11 +261,6 @@ static void PClassic_ParticleTrailIndex (vec3_t start, vec3_t end, int color, in { } -//this function is called to tell the particle system about surfaces that might emit particles at map startup. -static void PClassic_EmitSkyEffectTris(model_t *mod, msurface_t *fa, int ptype) -{ -} - //the one-time initialisation function, called no mater which renderer is active. static qboolean PClassic_InitParticles (void) { @@ -1154,7 +1149,6 @@ particleengine_t pe_classic = PClassic_RunParticleEffectPalette, PClassic_ParticleTrailIndex, - PClassic_EmitSkyEffectTris, PClassic_InitParticles, PClassic_ShutdownParticles, PClassic_DelinkTrailstate, diff --git a/engine/client/p_null.c b/engine/client/p_null.c index b53590e37..b9bafd6d3 100644 --- a/engine/client/p_null.c +++ b/engine/client/p_null.c @@ -23,7 +23,6 @@ static void PNULL_RunParticleEffect4 (vec3_t org, float radius, int color, int e static void PNULL_RunParticleEffectPalette (const char *nameprefix, vec3_t org, vec3_t dir, int color, int count){} static void PNULL_ParticleTrailIndex (vec3_t start, vec3_t end, int color, int crnd, trailstate_t **tsk){} -static void PNULL_EmitSkyEffectTris(model_t *mod, msurface_t *fa, int ptype){} static qboolean PNULL_InitParticles (void) { @@ -69,7 +68,6 @@ particleengine_t pe_null = PNULL_RunParticleEffectPalette, PNULL_ParticleTrailIndex, - PNULL_EmitSkyEffectTris, PNULL_InitParticles, PNULL_ShutdownParticles, PNULL_DelinkTrailstate, diff --git a/engine/client/p_script.c b/engine/client/p_script.c index 26a7bf9b3..cdbc66dd5 100644 --- a/engine/client/p_script.c +++ b/engine/client/p_script.c @@ -82,7 +82,7 @@ static qboolean pe_script_enabled; static float psintable[256]; -static qboolean P_LoadParticleSet(char *name, qboolean implicit); +static qboolean P_LoadParticleSet(char *name, qboolean implicit, qboolean showwarning); static void R_Particles_KillAllEffects(void); static void buildsintable(void) @@ -149,13 +149,14 @@ typedef struct beamseg_s typedef struct skytris_s { - struct skytris_s *next; - vec3_t org; - vec3_t x; - vec3_t y; - float area; - float nexttime; - struct msurface_s *face; + struct skytris_s *next; + vec3_t org; + vec3_t x; + vec3_t y; + float area; + float nexttime; + int ptype; + struct msurface_s *face; } skytris_t; typedef struct skytriblock_s @@ -262,6 +263,7 @@ typedef struct part_type_s { int countextra; float count; float countrand; + float rainfrequency; int assoc; int cliptype; @@ -318,8 +320,8 @@ typedef struct part_type_s { particle_t *particles; clippeddecal_t *clippeddecals; beamseg_t *beams; - skytris_t *skytris; struct part_type_s *nexttorun; + struct part_type_s **runlink; unsigned int flags; #define PT_VELOCITY 0x0001 // has velocity modifiers @@ -357,7 +359,9 @@ static pcfg_t *loadedconfigs; #define crand() (rand()%32767/16383.5f-1) static void P_ReadPointFile_f (void); +#ifndef NOLEGACY static void P_ExportBuiltinSet_f(void); +#endif #define MAX_BEAMSEGS (1<<11) // default max # of beam segments #define MAX_PARTICLES (1<<18) // max # of particles at one time @@ -377,8 +381,6 @@ beamseg_t *free_beams; beamseg_t *beams; int r_numbeams; -skytriblock_t *skytrimem; - clippeddecal_t *free_decals; clippeddecal_t *decals; int r_numdecals; @@ -396,6 +398,7 @@ extern cvar_t r_bloodstains; extern cvar_t gl_part_flame; extern cvar_t r_decal_noperpendicular; +static void PScript_EmitSkyEffectTris(model_t *mod, msurface_t *fa, int ptype); static void FinishParticleType(part_type_t *ptype); // callbacks static void QDECL R_ParticleDesc_Callback(struct cvar_s *var, char *oldvalue); @@ -489,12 +492,21 @@ static part_type_t *P_GetParticleType(const char *config, const char *name) if (oldlist) { + part_type_t **link; if (part_run_list) part_run_list = (part_type_t*)((char*)part_run_list - (char*)oldlist + (char*)part_type); for (i = 0; i < numparticletypes; i++) + { if (part_type[i].nexttorun) part_type[i].nexttorun = (part_type_t*)((char*)part_type[i].nexttorun - (char*)oldlist + (char*)part_type); + part_type[i].runlink = NULL; + } + for (link = &part_run_list; *link;) + { + (*link)->runlink = link; + link = &(*link)->nexttorun; + } } ptype->loaded = 0; @@ -546,10 +558,10 @@ static void PScript_RetintEffect(part_type_t *to, part_type_t *from, const char //'from' might still have some links so we need to clear those out. to->nexttorun = NULL; + to->runlink = NULL; to->particles = NULL; to->clippeddecals = NULL; to->beams = NULL; - to->skytris = NULL; to->slooks = &to->looks; r_plooksdirty = true; @@ -620,13 +632,13 @@ static int PScript_FindParticleType(const char *fullname) int from = PScript_FindParticleType(va("%s.te_explosion2", cfg)); if (from != P_INVALID) { - int to = P_AllocateParticleType(cfg, name); + int to = P_AllocateParticleType(part_type[from].config, name); PScript_RetintEffect(&part_type[to], &part_type[from], name+14); return to; } } if (*cfg) - P_LoadParticleSet(cfg, true); + P_LoadParticleSet(cfg, true, true); if (fallback) { @@ -935,8 +947,6 @@ static void P_LoadTexture(part_type_t *ptype, qboolean warn) static void P_ResetToDefaults(part_type_t *ptype) { particle_t *parts; - skytris_t *st; - part_type_t *torun; char tnamebuf[sizeof(ptype->name)]; char tconfbuf[sizeof(ptype->config)]; @@ -961,21 +971,13 @@ static void P_ResetToDefaults(part_type_t *ptype) // if we're in the runstate loop through and remove from linked list if (ptype->state & PS_INRUNLIST) { - if (part_run_list == ptype) - part_run_list = part_run_list->nexttorun; - else - { - for (torun = part_run_list; torun != NULL; torun = torun->nexttorun) - { - if (torun->nexttorun == ptype) - torun->nexttorun = torun->nexttorun->nexttorun; - } - } + *ptype->runlink = ptype->nexttorun; + if (ptype->nexttorun) + ptype->nexttorun->runlink = ptype->runlink; } //some things need to be preserved before we clear everything. beamsegs = ptype->beams; - st = ptype->skytris; strcpy(tnamebuf, ptype->name); strcpy(tconfbuf, ptype->config); @@ -993,7 +995,7 @@ static void P_ResetToDefaults(part_type_t *ptype) //now set any non-0 defaults. ptype->beams = beamsegs; - ptype->skytris = st; + ptype->rainfrequency = 1; strcpy(ptype->name, tnamebuf); strcpy(ptype->config, tconfbuf); ptype->assoc=P_INVALID; @@ -1336,6 +1338,10 @@ void P_ParticleEffect_f(void) if (Cmd_Argc()>3) ptype->countextra = atof(Cmd_Argv(3)); } + else if (!strcmp(var, "rainfrequency")) + { //multiplier to ramp up the effect or whatever (without affecting spawn patterns). + ptype->rainfrequency = atof(value); + } else if (!strcmp(var, "alpha")) ptype->alpha = atof(value); @@ -1377,7 +1383,7 @@ void P_ParticleEffect_f(void) #endif else if (!strcmp(var, "randomvel")) - { + { //shortcut for velwrand (and velbias for z bias) ptype->velbias[0] = ptype->velbias[1] = 0; ptype->velwrand[0] = ptype->velwrand[1] = atof(value); if (Cmd_Argc()>3) @@ -1825,7 +1831,10 @@ parsefluid: ptype->looks.blendmode = BM_PREMUL; } else + { + Con_DPrintf("%s.%s: uses unknown blend type '%s', assuming legacy 'blendalpha'\n", ptype->config, ptype->name, value); ptype->looks.blendmode = BM_BLEND; //fallback + } } else if (!strcmp(var, "spawnmode")) { @@ -1853,8 +1862,13 @@ parsefluid: } else if (!strcmp(value, "distball")) ptype->spawnmode = SM_DISTBALL; - else + else if (!strcmp(value, "box")) ptype->spawnmode = SM_BOX; + else + { + Con_DPrintf("%s.%s: uses unknown spawn type '%s', assuming 'box'\n", ptype->config, ptype->name, value); + ptype->spawnmode = SM_BOX; //fallback + } if (Cmd_Argc()>2) { @@ -1877,8 +1891,13 @@ parsefluid: ptype->looks.type = PT_CDECAL; else if (!strcmp(value, "udecal")) ptype->looks.type = PT_UDECAL; - else + else if (!strcmp(value, "normal")) ptype->looks.type = PT_NORMAL; + else + { + Con_DPrintf("%s.%s: uses unknown (render) type '%s', assuming 'normal'\n", ptype->config, ptype->name, value); + ptype->looks.type = PT_NORMAL; //fallback + } settype = true; } else if (!strcmp(var, "clippeddecal")) //mask, match @@ -2010,8 +2029,13 @@ parsefluid: ptype->rampmode = RAMP_NEAREST; else if (!strcmp(value, "lerp")) //don't use the name 'linear'. ramps are there to avoid linear... ptype->rampmode = RAMP_LERP; - else //if (!strcmp(value, "delta")) + else if (!strcmp(value, "delta")) ptype->rampmode = RAMP_DELTA; + else + { + Con_DPrintf("%s.%s: uses unknown ramp mode '%s', assuming 'delta'\n", ptype->config, ptype->name, value); + ptype->rampmode = RAMP_DELTA; + } } else if (!strcmp(var, "rampindexlist")) { // better not use this with delta ramps... @@ -2153,7 +2177,7 @@ parsefluid: ptype->stain_rgb[1] = atof(Cmd_Argv(3)); ptype->stain_rgb[2] = atof(Cmd_Argv(4)); } - else + else if (Cmd_Argc()) Con_DPrintf("%s.%s: %s is not a recognised particle type field\n", ptype->config, ptype->name, var); } ptype->loaded = part_parseweak?1:2; @@ -2314,7 +2338,9 @@ qboolean PScript_Query(int typenum, int body, char *outstr, int outstrlen) } if (ptype->count || all) - Q_strncatz(outstr, va("count %g\n", ptype->count), outstrlen); + Q_strncatz(outstr, va("count %g %g %i\n", ptype->count, ptype->countrand, ptype->countextra), outstrlen); + if (ptype->rainfrequency != 1 || all) + Q_strncatz(outstr, va("rainfrequency %g\n", ptype->rainfrequency), outstrlen); if (ptype->rgb[0] || ptype->rgb[1] || ptype->rgb[2] || all) Q_strncatz(outstr, va("rgbf %g %g %g\n", ptype->rgb[0], ptype->rgb[1], ptype->rgb[2]), outstrlen); @@ -2432,7 +2458,7 @@ qboolean PScript_Query(int typenum, int body, char *outstr, int outstrlen) Q_strncatz(outstr, va("orgbias %g %g %g\n", ptype->orgbias[0], ptype->orgbias[1], ptype->orgbias[2]), outstrlen); if (DotProduct(ptype->orgwrand,ptype->orgwrand) || all) Q_strncatz(outstr, va("orgwrand %g %g %g\n", ptype->orgwrand[0], ptype->orgwrand[1], ptype->orgwrand[2]), outstrlen); - if (ptype->velbias[0] == 0 && ptype->velbias[1] == 0 && ptype->velwrand[0] == ptype->velwrand[1]) + if (ptype->velbias[0] == 0 && ptype->velbias[1] == 0 && ptype->velwrand[0] == ptype->velwrand[1] && !all) Q_strncatz(outstr, va("randomvel %g %g %g\n", ptype->velwrand[0], ptype->velbias[2] - ptype->velwrand[2], ptype->velbias[2]*2-(ptype->velbias[2]- ptype->velwrand[2])), outstrlen); else { @@ -2499,74 +2525,6 @@ qboolean PScript_Query(int typenum, int body, char *outstr, int outstrlen) Q_strncatz(outstr, va("stains %g\n", ptype->stainonimpact), outstrlen); return true; - -#if 0 - plooks_t *slooks; //shared looks, so state switches don't apply between particles so much - plooks_t looks; - - float spawntime; //time limit for trails - float spawnchance; //if < 0, particles might not spawn so many - - float scaledelta; - int countextra; - float count; - float countrand; - - int cliptype; - int inwater; - float clipcount; - int emit; - float emittime; - float emitrand; - float emitstart; - - float areaspread; - float areaspreadvert; - - float spawnparam1; - float spawnparam2; -/* float spawnparam3; */ - - enum { - SM_BOX, //box = even spread within the area - SM_CIRCLE, //circle = around edge of a circle - SM_BALL, //ball = filled sphere - SM_SPIRAL, //spiral = spiral trail - SM_TRACER, //tracer = tracer trail - SM_TELEBOX, //telebox = q1-style telebox - SM_LAVASPLASH, //lavasplash = q1-style lavasplash - SM_UNICIRCLE, //unicircle = uniform circle - SM_FIELD, //field = synced field (brightfield, etc) - SM_DISTBALL // uneven distributed ball - } spawnmode; - - float gravity; - vec3_t friction; - float clipbounce; - int stainonimpact; - - vec3_t stain_rgb; - float stain_radius; - - enum {RAMP_NONE, RAMP_DELTA, RAMP_ABSOLUTE} rampmode; - int rampindexes; - ramp_t *ramp; - - int loaded; - particle_t *particles; - clippeddecal_t *clippeddecals; - beamseg_t *beams; - skytris_t *skytris; - struct part_type_s *nexttorun; - - unsigned int flags; -#define PT_CITRACER 0x008 // Q1-style tracer behavior for colorindex -#define PT_INVFRAMETIME 0x010 // apply inverse frametime to count (causes emits to be per frame) -#define PT_AVERAGETRAIL 0x020 // average trail points from start to end, useful with t_lightning, etc -#define PT_NOSTATE 0x040 // don't use trailstate for this emitter (careful with assoc...) -#define PT_NOSPREADFIRST 0x080 // don't randomize org/vel for first generated particle -#define PT_NOSPREADLAST 0x100 // don't randomize org/vel for last generated particle -#endif } return false; @@ -2788,12 +2746,24 @@ static void FinishEffectinfoParticleType(part_type_t *ptype, qboolean blooddecal ptype->die = 20; ptype->alphachange = -(ptype->alpha / ptype->die); } + else if (ptype->looks.type == PT_UDECAL) + { + //dp's decals have a size as a radius. fte's udecals are 'just' quads. + //also, dp uses 'stretch'. + ptype->looks.stretch *= 1/1.414213562373095; + ptype->scale *= ptype->looks.stretch; + ptype->scalerand *= ptype->looks.stretch; + ptype->scaledelta *= ptype->looks.stretch; + ptype->looks.stretch = 1; + } else if (ptype->looks.type == PT_NORMAL) { //fte's textured particles are *0.25 for some reason. - ptype->scale *= 4; - ptype->scalerand *= 4; - ptype->scaledelta *= 4; + //but fte also uses radiuses, while dp uses total size so we only need to double it here.. + ptype->scale *= 2*ptype->looks.stretch; + ptype->scalerand *= 2*ptype->looks.stretch; + ptype->scaledelta *= 2*2*ptype->looks.stretch; //fixme: this feels wrong, the results look correct though. hrmph. + ptype->looks.stretch = 1; } if (blooddecalonimpact) //DP blood particles generate decals unconditionally (and prevent blood from bouncing) ptype->clipbounce = -2; @@ -2833,8 +2803,8 @@ static void P_ImportEffectInfo(char *config, char *line) { teximages[i][0] = 1/8.0 * (i & 7); teximages[i][1] = 1/8.0 * (1+(i & 7)); - teximages[i][2] = 1/8.0 * (i>>3); - teximages[i][3] = 1/8.0 * (1+(i>>3)); + teximages[i][2] = 1/8.0 * (1+(i>>3)); + teximages[i][3] = 1/8.0 * (i>>3); } file = FS_OpenVFS("particles/particlefont.txt", "rb", FS_GAME); @@ -2853,8 +2823,8 @@ static void P_ImportEffectInfo(char *config, char *line) i = atoi(arg[0]); teximages[i][0] = atof(arg[1]); teximages[i][1] = atof(arg[3]); - teximages[i][2] = atof(arg[2]); - teximages[i][3] = atof(arg[4]); + teximages[i][2] = atof(arg[4]); + teximages[i][3] = atof(arg[2]); } } VFS_CLOSE(file); @@ -3059,8 +3029,16 @@ static void P_ImportEffectInfo(char *config, char *line) else if (!strcmp(arg[0], "alpha") && args == 4) { float a1 = atof(arg[1]), a2 = atof(arg[2]), f = atof(arg[3]); - ptype->alpha = a1/256; - ptype->alpharand = (a2-a1)/256; + if (a1 > a2) + { //backwards + ptype->alpha = a2/256; + ptype->alpharand = (a1-a2)/256; + } + else + { + ptype->alpha = a1/256; + ptype->alpharand = (a2-a1)/256; + } ptype->alphachange = -f/256; } else if (!strcmp(arg[0], "velocityoffset") && args == 4) @@ -3203,6 +3181,7 @@ static void P_ImportEffectInfo(char *config, char *line) ptype->rotationstartrand *= M_PI/180; ptype->rotationmin *= M_PI/180; ptype->rotationrand *= M_PI/180; + ptype->rotationstartmin += M_PI/4; } else Con_Printf("Particle effect token not recognised, or invalid args: %s %s %s %s %s %s\n", arg[0], args<2?"":arg[1], args<3?"":arg[2], args<4?"":arg[3], args<5?"":arg[4], args<6?"":arg[5]); @@ -3374,6 +3353,9 @@ static void PScript_Shutdown (void) Cmd_RemoveCommand("pointfile"); //load the leak info produced from qbsp into the particle system to show a line. :) + + Cmd_RemoveCommand("r_converteffectinfo"); + Cmd_RemoveCommand("r_exportalleffects"); Cmd_RemoveCommand("r_exportbuiltinparticles"); Cmd_RemoveCommand("r_importeffectinfo"); @@ -3415,13 +3397,6 @@ static void PScript_Shutdown (void) BZ_Free (decals); BZ_Free (trailstates); - while(skytrimem) - { - void *f = skytrimem; - skytrimem = skytrimem->next; - BZ_Free(f); - } - r_numparticles = 0; } @@ -3469,7 +3444,6 @@ static void PScript_ClearParticles (void) part_type[i].clippeddecals = NULL; part_type[i].particles = NULL; part_type[i].beams = NULL; - part_type[i].skytris = NULL; } } @@ -3506,7 +3480,7 @@ static void P_ExportBuiltinSet_f(void) } #endif -static qboolean P_LoadParticleSet(char *name, qboolean implicit) +static qboolean P_LoadParticleSet(char *name, qboolean implicit, qboolean showwarning) { char *file; int i; @@ -3527,6 +3501,7 @@ static qboolean P_LoadParticleSet(char *name, qboolean implicit) strcpy(cfg->name, name); cfg->next = loadedconfigs; loadedconfigs = cfg; + name = cfg->name; #ifdef PSET_CLASSIC if (!strcmp(name, "classic")) @@ -3577,15 +3552,10 @@ static qboolean P_LoadParticleSet(char *name, qboolean implicit) P_ImportEffectInfo_Name(name); return true; } - - if (P_LoadParticleSet("high", true)) - Con_Printf(CON_WARNING "Couldn't find particle description %s, loading 'high' instead\n", name); - else #endif - { + if (showwarning) Con_Printf(CON_WARNING "Couldn't find particle description %s\n", name); - return false; - } + return false; } return true; } @@ -3627,7 +3597,8 @@ static void R_Particles_KillAllEffects(void) static void QDECL R_ParticleDesc_Callback(struct cvar_s *var, char *oldvalue) { char token[256]; - qboolean first; + int count; + qboolean failure; char *c; @@ -3636,14 +3607,33 @@ static void QDECL R_ParticleDesc_Callback(struct cvar_s *var, char *oldvalue) R_Particles_KillAllEffects(); - first = true; - for (c = COM_ParseStringSet(var->string, token, sizeof(token)); token[0]; c = COM_ParseStringSet(c, token, sizeof(token))) + if (cls.state != ca_connected) { - /*set up a default*/ - if (first && !*token) - strcpy(token, "classic"); - P_LoadParticleSet(token, false); - first = false; + failure = false; + count = 0; + for (c = COM_ParseStringSet(var->string, token, sizeof(token)); token[0]; c = COM_ParseStringSet(c, token, sizeof(token))) + { + if (*token) + { + if (!P_LoadParticleSet(token, false, false)) + failure = true; + count++; + } + } + + //if we didn't manage to load any, make sure SOMETHING got loaded... + if (!count) + P_LoadParticleSet("classic", true, true); + else if (failure) + P_LoadParticleSet("high", true, true); + + if (cls.state) + { + //per-map configs. because we can. + memcpy(token, "map_", 4); + COM_FileBase(cl.model_name[1], token+4, sizeof(token)-4); + P_LoadParticleSet(token, false, false); + } } // Cbuf_AddText("r_effect\n", RESTRICT_LOCAL); @@ -3734,95 +3724,21 @@ static void P_ReadPointFile_f (void) Con_Printf ("spawned %i of %i points\n", spawned, c); } -static void P_AddRainParticles(void) +void PScript_ClearSurfaceParticles(model_t *mod) { - float x; - float y; - static float skipped; - static float lastrendered; - int ptype; - - vec3_t org, vdist; - - skytris_t *st; - - if (!r_part_rain.ival || !r_part_rain_quantity.ival) + mod->skytime = 0; + mod->skytris = NULL; + while(mod->skytrimem) { - skipped = true; - return; - } - - if (lastrendered < particletime - 0.5) - skipped = true; //we've gone for half a sec without any new rain. This would cause some strange effects, so reset times. - - if (skipped) - { - for (ptype = 0; ptypenext) - { - st->nexttime = particletime; - } - } - } - skipped = false; - - lastrendered = particletime; - - for (ptype = 0; ptypenext) - { - if (st->face->visframe != r_framecount) - { - st->nexttime = particletime; - continue; - } - - while (st->nexttime < particletime) - { - if (!free_particles) - return; - - st->nexttime += 10000/(st->area*r_part_rain_quantity.value); - - x = frandom()*frandom(); - y = frandom() * (1-x); - VectorMA(st->org, x, st->x, org); - VectorMA(org, y, st->y, org); - - - VectorSubtract(org, r_refdef.vieworg, vdist); - - if (Length(vdist) > (1024+512)*frandom()) - continue; - - if (st->face->flags & SURF_PLANEBACK) - VectorMA(org, -0.5, st->face->plane->normal, org); - else - VectorMA(org, 0.5, st->face->plane->normal, org); - - if (!(cl.worldmodel->funcs.PointContents(cl.worldmodel, NULL, org) & FTECONTENTS_SOLID)) - { - if (st->face->flags & SURF_PLANEBACK) - { - vdist[0] = -st->face->plane->normal[0]; - vdist[1] = -st->face->plane->normal[1]; - vdist[2] = -st->face->plane->normal[2]; - P_RunParticleEffectType(org, vdist, 1, ptype); - } - else - P_RunParticleEffectType(org, st->face->plane->normal, 1, ptype); - } - } - } + void *f = mod->skytrimem; + mod->skytrimem = mod->skytrimem->next; + Z_Free(f); } + mod->skytime = 0; + mod->engineflags |= MDLF_RECALCULATERAIN; } -static void R_Part_SkyTri(float *v1, float *v2, float *v3, msurface_t *surf, int ptype) +static void R_Part_SkyTri(model_t *mod, float *v1, float *v2, float *v3, msurface_t *surf, int ptype) { float dot; float xm; @@ -3833,17 +3749,16 @@ static void R_Part_SkyTri(float *v1, float *v2, float *v3, msurface_t *surf, int skytris_t *st; - skytriblock_t *mem = skytrimem; + skytriblock_t *mem = mod->skytrimem; if (!mem || mem->count >= sizeof(mem->tris)/sizeof(mem->tris[0])) { - skytrimem = BZ_Malloc(sizeof(*skytrimem)); - skytrimem->next = mem; - skytrimem->count = 0; - mem = skytrimem; + mod->skytrimem = BZ_Malloc(sizeof(*mod->skytrimem)); + mod->skytrimem->next = mem; + mod->skytrimem->count = 0; + mem = mod->skytrimem; } - st = &mem->tris[mem->count++]; - st->next = part_type[ptype].skytris; + st = &mem->tris[mem->count]; VectorCopy(v1, st->org); VectorSubtract(v2, st->org, st->x); VectorSubtract(v3, st->org, st->y); @@ -3862,11 +3777,14 @@ static void R_Part_SkyTri(float *v1, float *v2, float *v3, msurface_t *surf, int st->area = sin(theta)*xm*ym; st->nexttime = particletime; st->face = surf; + st->ptype = ptype; if (st->area<=0) return;//bummer. - part_type[ptype].skytris = st; + mem->count++; + st->next = mod->skytris; + mod->skytris = st; } @@ -3910,11 +3828,131 @@ static void PScript_EmitSkyEffectTris(model_t *mod, msurface_t *fa, int ptype) v2 = 1; for (v3 = 2; v3 < numverts; v3++) { - R_Part_SkyTri(verts[v1], verts[v2], verts[v3], fa, ptype); + R_Part_SkyTri(mod, verts[v1], verts[v2], verts[v3], fa, ptype); v2 = v3; } } +static void P_AddRainParticles(model_t *mod, vec3_t axis[3], vec3_t eorg, int visframe, float contribution) +{ + float x; + float y; + part_type_t *type; + + vec3_t org, vdist, worg, wnorm; + + skytris_t *st; + size_t nc,oc; + float ot; + + if (mod->engineflags & MDLF_RECALCULATERAIN) + { + PScript_ClearSurfaceParticles(mod); + mod->engineflags &= ~MDLF_RECALCULATERAIN; + + if (mod->type == mod_brush) + { + int t, i; + int ptype; + msurface_t *surf; + + /*particle emision based upon texture. this is lazy code*/ + for (t = mod->numtextures-1; t >= 0; t--) + { + /*FIXME: we should read the shader for the particle names here*/ + /*FIXME: if the paticle system changes mid-map, we should be prepared to reload things*/ + char *pn; + char *h; + if (mod->textures[t]->partname) + pn = mod->textures[t]->partname; + else + { + pn = va("tex_%s", mod->textures[t]->name); + //strip any trailing data after #, so textures can have shader args + h = strchr(pn, '#'); + if (h) + *h = 0; + } + ptype = P_FindParticleType(pn); + + if (ptype != P_INVALID) + { + for (i=0; inummodelsurfaces; i++) + { + surf = mod->surfaces + i + mod->firstmodelsurface; + if (surf->texinfo->texture == mod->textures[t]) + { + /*FIXME: it would be a good idea to determine the surface's (midpoint) pvs cluster so that we're not spamming for the entire map*/ + PScript_EmitSkyEffectTris(mod, surf, ptype); + } + } + } + } + } + } + + if (!mod->skytris) + return; + + ot = mod->skytime; + mod->skytime += contribution; + + for (st = mod->skytris; st; st = st->next) + { + if (st->face->visframe != visframe) + { + st->nexttime = mod->skytime; + continue; + } + + if ((unsigned int)st->ptype >= (unsigned int)numparticletypes) + continue; + type = &part_type[st->ptype]; + if (!type->loaded) //woo, batch skipping. + continue; + + nc = (mod->skytime*st->area*r_part_rain_quantity.value*type->rainfrequency)/10000.0; + oc = (ot*st->area*r_part_rain_quantity.value*type->rainfrequency)/10000.0; + + while (oc < nc) + { + oc++; + if (!free_particles) + return; + +// st->nexttime += 10000.0/(st->area*r_part_rain_quantity.value*type->rainfrequency); + + x = frandom()*frandom(); + y = frandom() * (1-x); + VectorMA(st->org, x, st->x, org); + VectorMA(org, y, st->y, org); + + worg[0] = DotProduct(org, axis[0]) + eorg[0]; + worg[1] = DotProduct(org, axis[1]) + eorg[1]; + worg[2] = DotProduct(org, axis[2]) + eorg[2]; + + //ignore it if its too far away + VectorSubtract(worg, r_refdef.vieworg, vdist); + if (VectorLength(vdist) > (1024+512)*frandom()) + continue; + + if (st->face->flags & SURF_PLANEBACK) + VectorScale(st->face->plane->normal, -1, vdist); + else + VectorCopy(st->face->plane->normal, vdist); + + wnorm[0] = DotProduct(vdist, axis[0]); + wnorm[1] = DotProduct(vdist, axis[1]); + wnorm[2] = DotProduct(vdist, axis[2]); + + VectorMA(worg, 0.5, wnorm, worg); + if (!(cl.worldmodel->funcs.PointContents(cl.worldmodel, NULL, worg) & FTECONTENTS_SOLID)) //should be paranoia, at least for the world. + { + P_RunParticleEffectType(worg, wnorm, 1, st->ptype); + } + } + } +} // Trailstate functions static void P_CleanTrailstate(trailstate_t *ts) @@ -4425,8 +4463,11 @@ static void PScript_AddDecals(void *vctx, vec3_t *fte_restrict points, size_t nu // maintain run list if (!(ptype->state & PS_INRUNLIST)) { + ptype->runlink = &part_run_list; ptype->nexttorun = part_run_list; - part_run_list = ptype; + if (part_run_list) + part_run_list->runlink = &ptype->nexttorun; + *ptype->runlink = ptype; ptype->state |= PS_INRUNLIST; } } @@ -5038,8 +5079,11 @@ static int PScript_RunParticleEffectState (vec3_t org, vec3_t dir, float count, // maintain run list if (!(ptype->state & PS_INRUNLIST) && (ptype->particles || ptype->clippeddecals)) { + ptype->runlink = &part_run_list; ptype->nexttorun = part_run_list; - part_run_list = ptype; + if (part_run_list) + part_run_list->runlink = &ptype->nexttorun; + *ptype->runlink = ptype; ptype->state |= PS_INRUNLIST; } @@ -5859,8 +5903,11 @@ static void P_ParticleTrailDraw (vec3_t startpos, vec3_t end, part_type_t *ptype // maintain run list if (!(ptype->state & PS_INRUNLIST)) { + ptype->runlink = &part_run_list; ptype->nexttorun = part_run_list; - part_run_list = ptype; + if (part_run_list) + part_run_list->runlink = &ptype->nexttorun; + *ptype->runlink = ptype; ptype->state |= PS_INRUNLIST; } @@ -6625,7 +6672,7 @@ static void PScript_DrawParticleTypes (void) vec3_t oldorg; vec3_t stop, normal; - part_type_t *type, *lastvalidtype; + part_type_t *type; particle_t *p, *kill; clippeddecal_t *d, *dkill; ramp_t *ramp; @@ -6725,7 +6772,7 @@ static void PScript_DrawParticleTypes (void) memcpy(lastviewmatrix, r_refdef.m_view, sizeof(tmp)); } - for (type = part_run_list, lastvalidtype = NULL; type != NULL; type = type->nexttorun) + for (type = part_run_list; type != NULL; type = type->nexttorun) { if (type->clippeddecals) { @@ -7215,7 +7262,7 @@ static void PScript_DrawParticleTypes (void) ctx.bias1 = type->s1 + ctx.scale1/2; ctx.scale2 = type->t2 - type->t1; ctx.bias2 = type->t1 + ctx.scale2/2; - m = p->scale*(1.5+frandom()*0.5); //decals should be a little bigger, for some reason. + m = p->scale*(0.5+frandom()*0.5); //decals should be a little bigger, for some reason. ctx.scale0 = 2.0 / m; ctx.scale1 /= m; ctx.scale2 /= m; @@ -7389,14 +7436,12 @@ endtype: // delete from run list if necessary if (!type->particles && !type->beams && !type->clippeddecals) { - if (!lastvalidtype) - part_run_list = type->nexttorun; - else - lastvalidtype->nexttorun = type->nexttorun; + if (type->nexttorun) + type->nexttorun->runlink = type->runlink; + *type->runlink = type->nexttorun; + type->runlink = NULL; type->state &= ~PS_INRUNLIST; } - else - lastvalidtype = type; } RSpeedEnd(RSPEED_PARTICLES); @@ -7418,7 +7463,24 @@ R_DrawParticles */ static void PScript_DrawParticles (void) { - P_AddRainParticles(); + if (r_part_rain.value) + { + entity_t *ent; + int i; + + P_AddRainParticles(cl.worldmodel, r_worldentity.axis, r_worldentity.origin, r_framecount, pframetime); + + for (i = 0; i < cl_numvisedicts ; i++) + { + ent = &cl_visedicts[i]; + if (!ent->model || ent->model->loadstate != MLS_LOADED) + continue; + + //this timer, as well as the per-tri timer, are unable to deal with certain rates+sizes. it would be good to fix that... + //it would also be nice to do mdls too... + P_AddRainParticles(ent->model, ent->axis, ent->origin, 0, pframetime); + } + } PScript_DrawParticleTypes(); @@ -7447,7 +7509,6 @@ particleengine_t pe_script = PScript_RunParticleEffectPalette, PScript_ParticleTrailIndex, - PScript_EmitSkyEffectTris, PScript_InitParticles, PScript_Shutdown, PScript_DelinkTrailstate, diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index d868a13d3..d2cdfc8ee 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -1614,7 +1614,6 @@ void buildmatricies(void) float modelview[16]; float proj[16]; float ofovx = r_refdef.fov_x,ofovy=r_refdef.fov_y; - extern cvar_t gl_mindist, gl_maxdist; V_ApplyAFov(csqc_playerview); @@ -1623,7 +1622,7 @@ void buildmatricies(void) if (r_refdef.useperspective) Matrix4x4_CM_Projection2(proj, r_refdef.fov_x, r_refdef.fov_y, 4); else - Matrix4x4_CM_Orthographic(proj, -r_refdef.fov_x/2, r_refdef.fov_x/2, -r_refdef.fov_y/2, r_refdef.fov_y/2, gl_mindist.value, gl_maxdist.value>=1?gl_maxdist.value:9999); + Matrix4x4_CM_Orthographic(proj, -r_refdef.fov_x/2, r_refdef.fov_x/2, -r_refdef.fov_y/2, r_refdef.fov_y/2, r_refdef.mindist, r_refdef.maxdist>=1?r_refdef.maxdist:9999); /*build the vp matrix*/ Matrix4_Multiply(proj, modelview, csqc_proj_matrix); @@ -1830,6 +1829,13 @@ void QCBUILTIN PF_R_GetViewFlag(pubprogfuncs_t *prinst, struct globalvars_s *pr_ r[1] = r_refdef.grect.y; break; + case VF_MINDIST: + *r = r_refdef.mindist; + break; + case VF_MAXDIST: + *r = r_refdef.maxdist; + break; + case VF_DRAWWORLD: *r = !(r_refdef.flags&RDF_NOWORLDMODEL); break; @@ -2005,6 +2011,12 @@ void QCBUILTIN PF_R_SetViewFlag(pubprogfuncs_t *prinst, struct globalvars_s *pr_ r_refdef.grect.x = p[0]; r_refdef.grect.y = p[1]; break; + case VF_MINDIST: + r_refdef.mindist = *p; + break; + case VF_MAXDIST: + r_refdef.maxdist = *p; + break; case VF_DRAWWORLD: r_refdef.flags = (r_refdef.flags&~RDF_NOWORLDMODEL) | (*p?0:RDF_NOWORLDMODEL); @@ -2035,7 +2047,7 @@ void QCBUILTIN PF_R_SetViewFlag(pubprogfuncs_t *prinst, struct globalvars_s *pr_ { float fmt = G_FLOAT(OFS_PARM2); float *size = G_VECTOR(OFS_PARM3); - R2D_RT_Configure(r_refdef.rt_destcolour[i].texname, size[0], size[1], PR_TranslateTextureFormat(fmt)); + R2D_RT_Configure(r_refdef.rt_destcolour[i].texname, size[0], size[1], PR_TranslateTextureFormat(fmt), RT_IMAGEFLAGS); } BE_RenderToTextureUpdate2d(true); } @@ -2046,7 +2058,7 @@ void QCBUILTIN PF_R_SetViewFlag(pubprogfuncs_t *prinst, struct globalvars_s *pr_ { float fmt = G_FLOAT(OFS_PARM2); float *size = G_VECTOR(OFS_PARM3); - R2D_RT_Configure(r_refdef.rt_sourcecolour.texname, size[0], size[1], PR_TranslateTextureFormat(fmt)); + R2D_RT_Configure(r_refdef.rt_sourcecolour.texname, size[0], size[1], PR_TranslateTextureFormat(fmt), RT_IMAGEFLAGS); } BE_RenderToTextureUpdate2d(false); break; @@ -2056,7 +2068,7 @@ void QCBUILTIN PF_R_SetViewFlag(pubprogfuncs_t *prinst, struct globalvars_s *pr_ { float fmt = G_FLOAT(OFS_PARM2); float *size = G_VECTOR(OFS_PARM3); - R2D_RT_Configure(r_refdef.rt_depth.texname, size[0], size[1], PR_TranslateTextureFormat(fmt)); + R2D_RT_Configure(r_refdef.rt_depth.texname, size[0], size[1], PR_TranslateTextureFormat(fmt), RT_IMAGEFLAGS); } BE_RenderToTextureUpdate2d(false); break; @@ -2066,7 +2078,7 @@ void QCBUILTIN PF_R_SetViewFlag(pubprogfuncs_t *prinst, struct globalvars_s *pr_ { float fmt = G_FLOAT(OFS_PARM2); float *size = G_VECTOR(OFS_PARM3); - R2D_RT_Configure(r_refdef.rt_ripplemap.texname, size[0], size[1], PR_TranslateTextureFormat(fmt)); + R2D_RT_Configure(r_refdef.rt_ripplemap.texname, size[0], size[1], PR_TranslateTextureFormat(fmt), RT_IMAGEFLAGS); } BE_RenderToTextureUpdate2d(false); break; @@ -3267,11 +3279,10 @@ static void CheckSendPings(void) } } -static void QCBUILTIN PF_cs_serverkey (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +static const char *PF_cs_serverkey_internal(const char *keyname) { - char *keyname = PF_VarString(prinst, 0, pr_globals); char *ret; - char adr[MAX_ADR_SIZE]; + static char adr[MAX_ADR_SIZE]; if (!strcmp(keyname, "ip")) { @@ -3399,32 +3410,12 @@ static void QCBUILTIN PF_cs_serverkey (pubprogfuncs_t *prinst, struct globalvars #endif ret = Info_ValueForKey(cl.serverinfo, keyname); } - - if (*ret) - RETURN_TSTRING(ret); - else - G_INT(OFS_RETURN) = 0; + return ret; } - -//string(float pnum, string keyname) -static void QCBUILTIN PF_cs_getplayerkey (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +static const char *PF_cs_getplayerkey_internal (unsigned int pnum, const char *keyname) { - char buffer[64]; + static char buffer[64]; char *ret; - int pnum = G_FLOAT(OFS_PARM0); - const char *keyname = PR_GetStringOfs(prinst, OFS_PARM1); - if (pnum < 0) - { - if (csqc_resortfrags) - { - Sbar_SortFrags(true, false); - csqc_resortfrags = false; - } - if (pnum >= -scoreboardlines) - {//sort by - pnum = fragsort[-(pnum+1)]; - } - } if (pnum < 0 || pnum >= cl.allocated_client_slots) ret = ""; @@ -3522,6 +3513,68 @@ static void QCBUILTIN PF_cs_getplayerkey (pubprogfuncs_t *prinst, struct globalv { ret = Info_ValueForKey(cl.players[pnum].userinfo, keyname); } + return ret; +} + +//string(string keyname) +static void QCBUILTIN PF_cs_serverkey (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + char *keyname = PF_VarString(prinst, 0, pr_globals); + const char *ret = PF_cs_serverkey_internal(keyname); + if (*ret) + RETURN_TSTRING(ret); + else + G_INT(OFS_RETURN) = 0; +} +//string(float pnum, string keyname) +static void QCBUILTIN PF_cs_getplayerkey (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + int pnum = G_FLOAT(OFS_PARM0); + const char *keyname = PR_GetStringOfs(prinst, OFS_PARM1); + const char *ret; + if (pnum < 0) + { + if (csqc_resortfrags) + { + Sbar_SortFrags(true, false); + csqc_resortfrags = false; + } + if (pnum >= -scoreboardlines) + {//sort by + pnum = fragsort[-(pnum+1)]; + } + } + + ret = PF_cs_getplayerkey_internal(pnum, keyname); + if (*ret) + RETURN_TSTRING(ret); + else + G_INT(OFS_RETURN) = 0; +} + +static void QCBUILTIN PF_cs_infokey (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + csqcedict_t *ent = (csqcedict_t*)G_EDICT(prinst, OFS_PARM0); + const char *keyname = PR_GetStringOfs(prinst, OFS_PARM1); + const char *ret; + if (!ent->entnum) + ret = PF_cs_serverkey_internal(keyname); + else + { + int pnum = ent->xv->entnum-1; //figure out which ssqc entity this is meant to be + if (pnum < 0) + { + csqc_deprecated("infokey: entity does not correlate to an ssqc entity\n"); + ret = ""; + } + else if (pnum >= cl.allocated_client_slots) + { + csqc_deprecated("infokey: entity does not correlate to an ssqc player entity\n"); + ret = ""; + } + else + ret = PF_cs_getplayerkey_internal(pnum, keyname); + } if (*ret) RETURN_TSTRING(ret); else @@ -4581,7 +4634,7 @@ void CSQC_EntStateToCSQC(unsigned int flags, float lerptime, entity_state_t *src // ent->xv->glow_size = src->glowsize*4; // ent->xv->glow_color = src->glowcolour; // ent->xv->glow_trail = !!(state->dpflags & RENDER_GLOWTRAIL); - ent->xv->alpha = src->trans/255.0f; + ent->xv->alpha = (src->trans==255)?0:src->trans/254.0f; // ent->v->solid = src->solid; // ent->v->color[0] = src->light[0]/255.0; // ent->v->color[1] = src->light[1]/255.0; @@ -4643,7 +4696,7 @@ void CSQC_PlayerStateToCSQC(int pnum, player_state_t *srcp, csqcedict_t *ent) ent->v->colormap = pnum+1; ent->xv->scale = srcp->scale; //ent->v->fatness = srcp->fatness; - ent->xv->alpha = srcp->alpha/255.0f; + ent->xv->alpha = (srcp->alpha==255)?0:(srcp->alpha/254.0f); // ent->v->colormod[0] = srcp->colormod[0]*(8/256.0f); // ent->v->colormod[1] = srcp->colormod[1]*(8/256.0f); @@ -5516,7 +5569,7 @@ static struct { {"logfrag", PF_NoCSQC, 79}, // #79 void(entity killer, entity killee) logfrag (QW_ENGINE) (don't support) //80 - {"infokey", PF_NoCSQC, 80}, // #80 string(entity e, string keyname) infokey (QW_ENGINE) (don't support) + {"infokey", PF_cs_infokey, 80}, // #80 string(entity e, string keyname) infokey (QW_ENGINE) (don't support) {"stof", PF_stof, 81}, // #81 float(string s) stof (FRIK_FILE or QW_ENGINE) {"multicast", PF_NoCSQC, 82}, // #82 void(vector where, float set) multicast (QW_ENGINE) (don't support) @@ -6830,6 +6883,7 @@ qboolean CSQC_Init (qboolean anycsqc, qboolean csdatenabled, unsigned int checks csqc_world.physicstime = 0.1; + CSQC_RendererRestarted(); if (cls.state == ca_disconnected) CSQC_WorldLoaded(); @@ -6855,7 +6909,11 @@ void CSQC_RendererRestarted(void) //let the csqc know that its rendertargets got purged if (csqcg.rendererrestarted) + { + void *pr_globals = PR_globals(csqcprogs, PR_CURRENT); + (((string_t *)pr_globals)[OFS_PARM1] = PR_TempString(csqcprogs, rf->description)); PR_ExecuteProgram(csqcprogs, csqcg.rendererrestarted); + } //in case it drew to any render targets. if (R2D_Flush) R2D_Flush(); diff --git a/engine/client/r_2d.c b/engine/client/r_2d.c index 3f06102f1..ca493090b 100644 --- a/engine/client/r_2d.c +++ b/engine/client/r_2d.c @@ -1578,27 +1578,26 @@ void R2D_DrawCrosshair(void) R2D_ImageColours(1, 1, 1, 1); } -#define RT_IMAGEFLAGS IF_NOMIPMAP|IF_CLAMP|IF_LINEAR|IF_RENDERTARGET static texid_t internalrt; //resize a texture for a render target and specify the format of it. //pass TF_INVALID and sizes=0 to get without configuring (shaders that hardcode an $rt1 etc). -texid_t R2D_RT_Configure(const char *id, int width, int height, uploadfmt_t rtfmt) +texid_t R2D_RT_Configure(const char *id, int width, int height, uploadfmt_t rtfmt, unsigned int imageflags) { texid_t tid; if (!strcmp(id, "-")) { - internalrt = tid = Image_CreateTexture("", NULL, RT_IMAGEFLAGS); + internalrt = tid = Image_CreateTexture("", NULL, imageflags); } else { - tid = Image_FindTexture(id, NULL, RT_IMAGEFLAGS); + tid = Image_FindTexture(id, NULL, imageflags); if (!TEXVALID(tid)) - tid = Image_CreateTexture(id, NULL, RT_IMAGEFLAGS); + tid = Image_CreateTexture(id, NULL, imageflags); } if (rtfmt) { - Image_Upload(tid, rtfmt, NULL, NULL, width, height, RT_IMAGEFLAGS); + Image_Upload(tid, rtfmt, NULL, NULL, width, height, imageflags); tid->width = width; tid->height = height; } diff --git a/engine/client/r_part.c b/engine/client/r_part.c index b18dc6cd3..35890de46 100644 --- a/engine/client/r_part.c +++ b/engine/client/r_part.c @@ -81,9 +81,9 @@ typedef struct //to make things repeatable so that people can depend upon placement. unsigned int R_Clutter_Random(clutter_build_ctx_t *ctx) { //ripped from wikipedia (originally called xorshift128) - unsigned int t = ctx->x ^ (ctx->x << 11); - ctx->x = ctx->y; ctx->y = ctx->z; ctx->z = ctx->w; - return ctx->w = ctx->w ^ (ctx->w >> 19) ^ t ^ (t >> 8); + unsigned int t = ctx->x ^ (ctx->x << 11); + ctx->x = ctx->y; ctx->y = ctx->z; ctx->z = ctx->w; + return ctx->w = ctx->w ^ (ctx->w >> 19) ^ t ^ (t >> 8); } float R_Clutter_FRandom(clutter_build_ctx_t *ctx) { @@ -621,7 +621,7 @@ cvar_t r_part_density = CVARF("r_part_density", "1", CVAR_ARCHIVE); cvar_t r_part_classic_expgrav = CVARFD("r_part_classic_expgrav", "10", CVAR_ARCHIVE, "Scaler for how fast classic explosion particles should accelerate due to gravity. 1 for like vanilla, 10 for like zquake."); cvar_t r_part_classic_opaque = CVARFD("r_part_classic_opaque", "0", CVAR_ARCHIVE, "Disables transparency on classic particles, for the oldskool look."); -cvar_t r_part_maxparticles = CVAR("r_part_maxparticles", "8192"); +cvar_t r_part_maxparticles = CVAR("r_part_maxparticles", "65536"); cvar_t r_part_maxdecals = CVAR("r_part_maxdecals", "8192"); @@ -951,12 +951,26 @@ float CL_TraceLine (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal, int } //handy utility... -void P_EmitEffect (vec3_t pos, int type, trailstate_t **tsk) +void P_EmitEffect (vec3_t pos, vec3_t orientation[3], unsigned int modeleflags, int type, trailstate_t **tsk) { + float count; if (cl.paused) return; - pe->RunParticleEffectState(pos, NULL, ((host_frametime>0.1)?0.1:host_frametime), type, tsk); + count = ((host_frametime>0.1)?0.1:host_frametime); + if (orientation) + { + if (modeleflags & MDLF_EMITFORWARDS) + pe->RunParticleEffectState(pos, orientation[0], count, type, tsk); + else + { + vec3_t down; + VectorNegate(orientation[2], down); + pe->RunParticleEffectState(pos, down, count, type, tsk); + } + } + else + pe->RunParticleEffectState(pos, NULL, count, type, tsk); } diff --git a/engine/client/r_partset.c b/engine/client/r_partset.c index 54b68430f..453d05b8b 100644 --- a/engine/client/r_partset.c +++ b/engine/client/r_partset.c @@ -3771,7 +3771,6 @@ char *particle_set_q2part = "r_part teq2_bubbletrail\n" "{\n" -/*blue spiral*/ "texture \"classicparticle\"\n" "tcoords 0 0 16 16 32\n" "scale 0.5\n" @@ -3786,7 +3785,7 @@ char *particle_set_q2part = "r_part TEQ2_RAILTRAIL\n" "{\n" -/*blue spiral*/ +//blue spiral "texture \"classicparticle\"\n" "tcoords 0 0 16 16 32\n" "scale 0.5\n" @@ -3803,7 +3802,7 @@ char *particle_set_q2part = "}\n" "r_part +TEQ2_RAILTRAIL\n" "{\n" -/*grey filler*/ +//grey filler "texture \"classicparticle\"\n" "tcoords 0 0 16 16 32\n" "scale 0.5\n" diff --git a/engine/client/r_surf.c b/engine/client/r_surf.c index e7631d352..a362b689b 100644 --- a/engine/client/r_surf.c +++ b/engine/client/r_surf.c @@ -3430,12 +3430,11 @@ int Surf_NewExternalLightmaps(int count, char *filepattern, qboolean deluxe) void Surf_BuildModelLightmaps (model_t *m) { - int i, t; + int i; int shift; msurface_t *surf; batch_t *batch; int sortid; - int ptype; int newfirst; if (m->loadstate != MLS_LOADED) @@ -3491,36 +3490,6 @@ void Surf_BuildModelLightmaps (model_t *m) } } - - /*particle emision based upon texture. this is lazy code*/ - if (m == cl.worldmodel) - { - for (t = m->numtextures-1; t >= 0; t--) - { - /*FIXME: we should read the shader for the particle names here*/ - /*FIXME: if the paticle system changes mid-map, we should be prepared to reload things*/ - char *pn = va("tex_%s", m->textures[t]->name); - char *h = strchr(pn, '#'); - if (h) - *h = 0; - ptype = P_FindParticleType(pn); - - if (ptype != P_INVALID) - { - for (i=0; inummodelsurfaces; i++) - { - surf = m->surfaces + i + m->firstmodelsurface; - if (surf->texinfo->texture == m->textures[t]) - { - /*FIXME: it would be a good idea to determine the surface's (midpoint) pvs cluster so that we're not spamming for the entire map*/ - P_EmitSkyEffectTris(m, surf, ptype); - } - } - } - } - } - - if (m->fromgame == fg_quake3) { int j; diff --git a/engine/client/render.h b/engine/client/render.h index da21eaf1f..dac9d3217 100644 --- a/engine/client/render.h +++ b/engine/client/render.h @@ -252,6 +252,7 @@ typedef struct vec3_t eyeoffset; /*world space, for vr screenies*/ float fov_x, fov_y, afov; + float mindist, maxdist; //maxdist may be 0, for 'infinite', in which case mindist probably isn't valid either. qboolean drawsbar; qboolean drawcrosshair; @@ -600,7 +601,7 @@ extern cvar_t r_xflip; #endif extern cvar_t r_lightprepass; -extern cvar_t gl_maxdist; +extern cvar_t gl_mindist, gl_maxdist; extern cvar_t r_clear; extern cvar_t gl_poly; extern cvar_t gl_affinemodels; diff --git a/engine/client/renderer.c b/engine/client/renderer.c index 001c58f8b..338474159 100644 --- a/engine/client/renderer.c +++ b/engine/client/renderer.c @@ -1576,19 +1576,7 @@ TRACE(("dbg: R_ApplyRenderer: checking any wad textures\n")); Mod_NowLoadExternal(cl.worldmodel); for (i = 0; i < cl.num_statics; i++) //make the static entities reappear. - { cl_static_entities[i].ent.model = NULL; - if (cl_static_entities[i].mdlidx < 0) - { - if (cl_static_entities[i].mdlidx > -MAX_CSMODELS) - cl_static_entities[i].ent.model = cl.model_csqcprecache[-cl_static_entities[i].mdlidx]; - } - else - { - if (cl_static_entities[i].mdlidx < MAX_PRECACHE_MODELS) - cl_static_entities[i].ent.model = cl.model_precache[cl_static_entities[i].mdlidx]; - } - } TRACE(("dbg: R_ApplyRenderer: Surf_NewMap\n")); Surf_NewMap(); diff --git a/engine/client/screen.h b/engine/client/screen.h index e15bdd934..0873d0a0c 100644 --- a/engine/client/screen.h +++ b/engine/client/screen.h @@ -72,7 +72,7 @@ void SCR_ShowPic_Create(void); void SCR_ShowPic_Hide(void); void SCR_ShowPic_Move(void); void SCR_ShowPic_Update(void); -void SCR_ShowPic_Clear(qboolean persistflag); +void SCR_ShowPic_ClearAll(qboolean persistflag); char *SCR_ShowPics_ClickCommand(int cx, int cy); void SCR_ShowPic_Script_f(void); void SCR_ShowPic_Remove_f(void); diff --git a/engine/client/snd_dma.c b/engine/client/snd_dma.c index 8c4e462c2..0b216c953 100644 --- a/engine/client/snd_dma.c +++ b/engine/client/snd_dma.c @@ -2421,7 +2421,7 @@ static void SND_Spatialize(soundcardinfo_t *sc, channel_t *ch) #ifdef CSQC_DAT if (ch->entnum < 0 && -ch->entnum < csqc_world.num_edicts) { - wedict_t *ed = &csqc_world.edicts[-ch->entnum]; + wedict_t *ed = WEDICT_NUM(csqc_world.progs, -ch->entnum); if (ed->ereftype == ER_ENTITY) { VectorCopy(ed->v->origin, ch->origin); @@ -3350,6 +3350,8 @@ static void S_UpdateCard(soundcardinfo_t *sc) if (sc->ChannelUpdate) { + if (ch->flags & CF_FOLLOW) + SND_Spatialize(sc, ch); //update it a little sc->ChannelUpdate(sc, ch, false); continue; } diff --git a/engine/client/view.c b/engine/client/view.c index 0e21f9217..0e0809fe5 100644 --- a/engine/client/view.c +++ b/engine/client/view.c @@ -1193,6 +1193,13 @@ void V_ApplyAFov(playerview_t *pv) r_refdef.fov_x = CalcFov(r_refdef.fov_y, r_refdef.vrect.height, r_refdef.vrect.width*ws); } } + if (r_refdef.useperspective) + { + if (r_refdef.mindist < 1) + r_refdef.mindist = 1; + if (r_refdef.maxdist && r_refdef.maxdist < 100) + r_refdef.maxdist = 0; //small values should just use infinite. + } } /* ================= @@ -1206,8 +1213,8 @@ void V_ApplyRefdef (void) #ifdef QUAKEHUD float size; int h; -#endif qboolean full = false; +#endif // force the status bar to redraw Sbar_Changed (); @@ -1441,6 +1448,8 @@ void V_CalcRefdef (playerview_t *pv) VectorCopy (pv->simorg, r_refdef.vieworg); r_refdef.useperspective = true; + r_refdef.mindist = bound(0.1, gl_mindist.value, 4); + r_refdef.maxdist = gl_maxdist.value; // never let it sit exactly on a node line, because a water plane can // dissapear when viewed with the eye exactly on it. @@ -1646,8 +1655,6 @@ static void SCR_DrawAutoID(vec3_t org, player_info_t *pl, qboolean isteam) vec3_t tagcenter; float alpha; qboolean obscured; - int health, armour; - unsigned int items; int x, y; int r; float barwidth; @@ -1656,6 +1663,9 @@ static void SCR_DrawAutoID(vec3_t org, player_info_t *pl, qboolean isteam) int h; char *pname; + int health; + int armour; + unsigned int items; static vec4_t healthcolours[] = { {0.7, 0.45, 0.45, 1}, @@ -1664,6 +1674,7 @@ static void SCR_DrawAutoID(vec3_t org, player_info_t *pl, qboolean isteam) {1, 0.4, 0, 1}, {1, 1, 1, 1} }; +#ifdef QUAKESTATS static vec4_t armourcolours[] = { {25, 170, 0, 0.2}, @@ -1674,7 +1685,6 @@ static void SCR_DrawAutoID(vec3_t org, player_info_t *pl, qboolean isteam) {255, 0, 0, 1} }; -#ifdef QUAKESTATS extern cvar_t tp_name_sg,tp_name_ssg,tp_name_ng,tp_name_sng,tp_name_gl,tp_name_rl,tp_name_lg; static cvar_t *wbitnames[] = { @@ -1827,6 +1837,9 @@ static void SCR_DrawAutoID(vec3_t org, player_info_t *pl, qboolean isteam) Draw_ExpandedString(x + barwidth*0.5 + 4, y, buffer); } } +#else + (void)items; + (void)armour; #endif } diff --git a/engine/client/zqtp.c b/engine/client/zqtp.c index 40e3255bf..12284c997 100644 --- a/engine/client/zqtp.c +++ b/engine/client/zqtp.c @@ -1246,7 +1246,9 @@ static char *TP_ParseMacroString (char *s) // check %[P], etc if (*s == '%' && s[1]=='[' && s[2] && s[3]==']') { +#ifdef QUAKESTATS static char mbuf[MAX_MACRO_VALUE]; +#endif switch (s[2]) { #ifdef QUAKESTATS case 'a': diff --git a/engine/common/bothdefs.h b/engine/common/bothdefs.h index 7fd9789df..0ecd2381c 100644 --- a/engine/common/bothdefs.h +++ b/engine/common/bothdefs.h @@ -770,6 +770,7 @@ STAT_ITEMS = 15, STAT_VIEWHEIGHT = 16, //same as zquake STAT_TIME = 17, //zquake STAT_MATCHSTARTTIME = 18, +STAT_IDEALPITCH = 19, #ifdef SIDEVIEWS STAT_VIEW2 = 20, #endif diff --git a/engine/common/cmd.c b/engine/common/cmd.c index 4865e4e00..86ab6b7bd 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -2173,6 +2173,12 @@ void Cmd_ForwardToServer_f (void) { //don't send any extension flags this if we're using cl_loopbackprotocol nqid, purely for a compat test. //if you want to record compat-demos, disable extensions instead. unsigned int fp1 = Net_PextMask(1, cls.protocol == CP_NETQUAKE), fp2 = Net_PextMask(2, cls.protocol == CP_NETQUAKE); + extern cvar_t cl_nopext; + if (cl_nopext.ival) + { + fp1 = 0; + fp2 = 0; + } CL_SendClientCommand(true, "pext %#x %#x %#x %#x", PROTOCOL_VERSION_FTE, fp1, PROTOCOL_VERSION_FTE2, fp2); return; } diff --git a/engine/common/com_mesh.c b/engine/common/com_mesh.c index 7cce784f4..09c365031 100644 --- a/engine/common/com_mesh.c +++ b/engine/common/com_mesh.c @@ -225,6 +225,7 @@ static void GenMatrixPosQuat3Scale(vec3_t pos, vec3_t quat3, vec3_t scale, float GenMatrixPosQuat4Scale(pos, quat4, scale, result); } +#ifdef MD5MODELS static void GenMatrix(float x, float y, float z, float qx, float qy, float qz, float result[12]) { float qw; @@ -288,7 +289,9 @@ static void GenMatrix(float x, float y, float z, float qx, float qy, float qz, f result[2*4+3] = z; } } +#endif +#ifdef PSKMODELS static void PSKGenMatrix(float x, float y, float z, float qx, float qy, float qz, float qw, float result[12]) { float xx, xy, xz, xw, yy, yz, yw, zz, zw; @@ -317,6 +320,7 @@ static void PSKGenMatrix(float x, float y, float z, float qx, float qy, float qz result[1*4+3] = y; result[2*4+3] = z; } +#endif /*transforms some skeletal vecV_t values*/ static void Alias_TransformVerticies_V(const float *bonepose, int vertcount, qbyte *bidx, float *weights, float *xyzin, float *fte_restrict xyzout) @@ -659,6 +663,7 @@ void QDECL Alias_ForceConvertBoneData(skeltype_t sourcetype, const float *source } } +#ifndef NOLEGACY static float Alias_CalculateSkeletalNormals(galiasinfo_t *model) { #ifndef SERVERONLY @@ -846,6 +851,7 @@ static float Alias_CalculateSkeletalNormals(galiasinfo_t *model) #endif } #endif +#endif @@ -3567,7 +3573,7 @@ qboolean QDECL Mod_LoadQ1Model (model_t *mod, void *buffer, size_t fsize) { if (pinstverts[i].onseam != 0x20 && !galias->warned) { - Con_Printf(CON_WARNING "Model %s has an invalid seam flag, which may crash software-rendered engines\n", mod->name); + Con_DPrintf(CON_WARNING "Model %s has an invalid seam flag, which may crash software-rendered engines\n", mod->name); //1 == ALIAS_LEFT_CLIP galias->warned = true; } @@ -3599,7 +3605,7 @@ qboolean QDECL Mod_LoadQ1Model (model_t *mod, void *buffer, size_t fsize) unsigned int v3 = LittleLong(pinq1triangles[i].vertindex[2]); if (v1 >= pq1inmodel->numverts || v2 >= pq1inmodel->numverts || v3 >= pq1inmodel->numverts) { - Con_Printf(CON_ERROR"%s has invalid triangle (%u %u %u > %u)\n", mod->name, v1, v2, v3, pq1inmodel->numverts); + Con_DPrintf(CON_ERROR"%s has invalid triangle (%u %u %u > %u)\n", mod->name, v1, v2, v3, pq1inmodel->numverts); v1 = v2 = v3 = 0; } if (!pinq1triangles[i].facesfront) diff --git a/engine/common/common.c b/engine/common/common.c index c7563b448..3be87f02f 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -1190,6 +1190,7 @@ void MSG_WriteDeltaUsercmd (sizebuf_t *buf, usercmd_t *from, usercmd_t *cmd) #ifdef Q2CLIENT if (cls.protocol == CP_QUAKE2) { + unsigned char buttons = 0; if (cmd->angles[0] != from->angles[0]) bits |= Q2CM_ANGLE1; if (cmd->angles[1] != from->angles[1]) @@ -1207,27 +1208,79 @@ void MSG_WriteDeltaUsercmd (sizebuf_t *buf, usercmd_t *from, usercmd_t *cmd) if (cmd->impulse != from->impulse) bits |= Q2CM_IMPULSE; + + if (buf->prim.flags & NPQ2_R1Q2_UCMD) + { + if (bits & Q2CM_ANGLE1) + buttons = cmd->buttons & (1|2|128); //attack, jump, any. + if ((bits & Q2CM_FORWARD) && !(cmd->forwardmove % 5) && abs(cmd->forwardmove/5) < 128) + buttons |= R1Q2_BUTTON_BYTE_FORWARD; + if ((bits & Q2CM_SIDE) && !(cmd->sidemove % 5) && abs(cmd->sidemove/5) < 128) + buttons |= R1Q2_BUTTON_BYTE_SIDE; + if ((bits & Q2CM_UP) && !(cmd->upmove % 5) && abs(cmd->upmove/5) < 128) + buttons |= R1Q2_BUTTON_BYTE_UP; + if ((bits & Q2CM_ANGLE1) && !(cmd->angles[0] % 64) && abs(cmd->angles[0] / 64) < 128) + buttons |= R1Q2_BUTTON_BYTE_ANGLE1; + if ((bits & Q2CM_ANGLE2) && !(cmd->angles[1] % 256)) + buttons |= R1Q2_BUTTON_BYTE_ANGLE2; + if (buttons & (R1Q2_BUTTON_BYTE_FORWARD|R1Q2_BUTTON_BYTE_SIDE|R1Q2_BUTTON_BYTE_UP|R1Q2_BUTTON_BYTE_ANGLE1|R1Q2_BUTTON_BYTE_ANGLE2)) + bits |= Q2CM_BUTTONS; + } + MSG_WriteByte (buf, bits); + if (buf->prim.flags & NPQ2_R1Q2_UCMD) + { + if (bits & Q2CM_BUTTONS) + MSG_WriteByte (buf, buttons); + } if (bits & Q2CM_ANGLE1) - MSG_WriteShort (buf, cmd->angles[0]); + { + if (buttons & R1Q2_BUTTON_BYTE_ANGLE1) + MSG_WriteChar (buf, cmd->angles[0] / 64); + else + MSG_WriteShort (buf, cmd->angles[0]); + } if (bits & Q2CM_ANGLE2) - MSG_WriteShort (buf, cmd->angles[1]); + { + if (buttons & R1Q2_BUTTON_BYTE_ANGLE2) + MSG_WriteChar (buf, cmd->angles[1] / 256); + else + MSG_WriteShort (buf, cmd->angles[1]); + } if (bits & Q2CM_ANGLE3) MSG_WriteShort (buf, cmd->angles[2]); if (bits & Q2CM_FORWARD) - MSG_WriteShort (buf, cmd->forwardmove); + { + if (buttons & R1Q2_BUTTON_BYTE_FORWARD) + MSG_WriteChar (buf, cmd->forwardmove/5); + else + MSG_WriteShort (buf, cmd->forwardmove); + } if (bits & Q2CM_SIDE) - MSG_WriteShort (buf, cmd->sidemove); + { + if (buttons & R1Q2_BUTTON_BYTE_SIDE) + MSG_WriteChar (buf, cmd->sidemove/5); + else + MSG_WriteShort (buf, cmd->sidemove); + } if (bits & Q2CM_UP) - MSG_WriteShort (buf, cmd->upmove); + { + if (buttons & R1Q2_BUTTON_BYTE_UP) + MSG_WriteChar (buf, cmd->upmove/5); + else + MSG_WriteShort (buf, cmd->upmove); + } - if (bits & Q2CM_BUTTONS) - MSG_WriteByte (buf, cmd->buttons); - if (bits & Q2CM_IMPULSE) + if (!(buf->prim.flags & NPQ2_R1Q2_UCMD)) + { + if (bits & Q2CM_BUTTONS) + MSG_WriteByte (buf, cmd->buttons); + } + if (bits & Q2CM_IMPULSE) MSG_WriteByte (buf, cmd->impulse); - MSG_WriteByte (buf, bound(0, cmd->msec, 255)); + MSG_WriteByte (buf, bound(0, cmd->msec, 250)); //clamp msecs to 250, because r1q2 likes kicking us if we stall for any reason MSG_WriteByte (buf, cmd->lightlevel); @@ -1264,13 +1317,13 @@ void MSG_WriteDeltaUsercmd (sizebuf_t *buf, usercmd_t *from, usercmd_t *cmd) if (bits & CM_FORWARD) MSG_WriteShort (buf, cmd->forwardmove); if (bits & CM_SIDE) - MSG_WriteShort (buf, cmd->sidemove); + MSG_WriteShort (buf, cmd->sidemove); if (bits & CM_UP) MSG_WriteShort (buf, cmd->upmove); - if (bits & CM_BUTTONS) - MSG_WriteByte (buf, cmd->buttons); - if (bits & CM_IMPULSE) + if (bits & CM_BUTTONS) + MSG_WriteByte (buf, cmd->buttons); + if (bits & CM_IMPULSE) MSG_WriteByte (buf, cmd->impulse); MSG_WriteByte (buf, bound(0, cmd->msec, 255)); } @@ -1764,30 +1817,64 @@ void MSG_ReadDeltaUsercmd (usercmd_t *from, usercmd_t *move) void MSGQ2_ReadDeltaUsercmd (usercmd_t *from, usercmd_t *move) { int bits; + unsigned int buttons = 0; memcpy (move, from, sizeof(*move)); bits = MSG_ReadByte (); + if (net_message.prim.flags & NPQ2_R1Q2_UCMD) + buttons = MSG_ReadByte(); + // read current angles if (bits & Q2CM_ANGLE1) - move->angles[0] = MSG_ReadShort (); + { + if (buttons & R1Q2_BUTTON_BYTE_ANGLE1) + move->angles[0] = MSG_ReadChar ()*64; + else + move->angles[0] = MSG_ReadShort (); + } if (bits & Q2CM_ANGLE2) - move->angles[1] = MSG_ReadShort (); + { + if (buttons & R1Q2_BUTTON_BYTE_ANGLE2) + move->angles[1] = MSG_ReadChar ()*256; + else + move->angles[1] = MSG_ReadShort (); + } if (bits & Q2CM_ANGLE3) move->angles[2] = MSG_ReadShort (); // read movement if (bits & Q2CM_FORWARD) - move->forwardmove = MSG_ReadShort (); + { + if (buttons & R1Q2_BUTTON_BYTE_FORWARD) + move->forwardmove = MSG_ReadChar ()*5; + else + move->forwardmove = MSG_ReadShort (); + } if (bits & Q2CM_SIDE) - move->sidemove = MSG_ReadShort (); + { + if (buttons & R1Q2_BUTTON_BYTE_SIDE) + move->sidemove = MSG_ReadChar ()*5; + else + move->sidemove = MSG_ReadShort (); + } if (bits & Q2CM_UP) - move->upmove = MSG_ReadShort (); + { + if (buttons & R1Q2_BUTTON_BYTE_UP) + move->upmove = MSG_ReadChar ()*5; + else + move->upmove = MSG_ReadShort (); + } // read buttons if (bits & Q2CM_BUTTONS) - move->buttons = MSG_ReadByte (); + { + if (net_message.prim.flags & NPQ2_R1Q2_UCMD) + move->buttons = buttons & (1|2|128); //only use the bits that are actually buttons, so gamecode can't get excited despite being crippled by this. + else + move->buttons = MSG_ReadByte (); + } move->buttons_compat = move->buttons & 0xff; if (bits & Q2CM_IMPULSE) @@ -2795,6 +2882,7 @@ char *COM_DeFunString(conchar_t *str, conchar_t *stop, char *out, int outsize, q return out; } +#ifndef NOLEGACY static unsigned int koi2wc (unsigned char uc) { static const char koi2wc_table[64] = @@ -2831,6 +2919,7 @@ static unsigned int koi2wc (unsigned char uc) else return uc; } +#endif enum { diff --git a/engine/common/common.h b/engine/common/common.h index 6a8e3d3e4..24526c6d0 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -141,6 +141,7 @@ struct netprim_s qbyte anglesize; #define NPQ2_ANG16 (1u<<0) #define NPQ2_SOLID32 (1u<<1) +#define NPQ2_R1Q2_UCMD (1u<<2) qbyte flags; qbyte pad; diff --git a/engine/common/fs.c b/engine/common/fs.c index 0ef567885..849f566cd 100644 --- a/engine/common/fs.c +++ b/engine/common/fs.c @@ -3885,11 +3885,13 @@ qboolean Sys_FindGameData(const char *poshname, const char *gamename, char *base { #if defined(__linux__) || defined(__unix__) || defined(__apple__) struct stat sb; + char *s; + if (!*gamename) + gamename = "quake"; //just a paranoia fallback, shouldn't be needed. if (!strcmp(gamename, "quake")) { if (stat("/usr/share/quake/", &sb) == 0) { - // /usr/share/quake if (S_ISDIR(sb.st_mode)) { Q_strncpyz(basepath, "/usr/share/quake/", basepathlen); @@ -3897,6 +3899,15 @@ qboolean Sys_FindGameData(const char *poshname, const char *gamename, char *base } } } + s = va("/usr/share/games/%s/", gamename); + if (stat(s, &sb) == 0) + { + if (S_ISDIR(sb.st_mode)) + { + Q_strncpyz(basepath, s, basepathlen); + return true; + } + } #endif return false; } diff --git a/engine/common/gl_q2bsp.c b/engine/common/gl_q2bsp.c index 512f27905..0642b4b5b 100644 --- a/engine/common/gl_q2bsp.c +++ b/engine/common/gl_q2bsp.c @@ -1308,7 +1308,7 @@ qboolean CModQ2_LoadTexInfo (model_t *mod, qbyte *mod_base, lump_t *l, char *map q2texinfo_t *in; mtexinfo_t *out; int i, j, count; - char name[MAX_QPATH], *lwr; + char *lwr; char sname[MAX_QPATH]; int texcount; @@ -1393,7 +1393,7 @@ qboolean CModQ2_LoadTexInfo (model_t *mod, qbyte *mod_base, lump_t *l, char *map { out->texture = ZG_Malloc(&mod->memgroup, sizeof(texture_t) + 16*16+8*8+4*4+2*2); - Con_Printf (CON_WARNING "Couldn't load %s\n", name); + Con_Printf (CON_WARNING "Couldn't load \"%s.wal\"\n", in->texture); memcpy(out->texture, r_notexture_mip, sizeof(texture_t) + 16*16+8*8+4*4+2*2); } diff --git a/engine/common/net_chan.c b/engine/common/net_chan.c index 88eba1961..a41278467 100644 --- a/engine/common/net_chan.c +++ b/engine/common/net_chan.c @@ -87,8 +87,8 @@ cvar_t qport = CVARF("qport_", "0", CVAR_NOSAVE); cvar_t net_mtu = CVARD("net_mtu", "1440", "Specifies a maximum udp payload size, above which packets will be fragmented. If routers all worked properly this could be some massive value, and some massive value may work really nicely for lans. Use smaller values than the default if you're connecting through nested tunnels through routers that fail with IP fragmentation."); cvar_t net_compress = CVARD("net_compress", "0", "Enables huffman compression of network packets."); -cvar_t pext_replacementdeltas = CVAR("pext_replacementdeltas", "1"); -cvar_t pext_predinfo = CVAR("debug_pext_predinfo", "0"); +cvar_t pext_replacementdeltas = CVARD("pext_replacementdeltas", "1", "Enables the use of alternative nack-based entity deltas"); +cvar_t pext_predinfo = CVARD("pext_predinfo", "1", "Enables some extra things to support prediction over NQ protocols."); /*returns the entire bitmask of supported+enabled extensions*/ unsigned int Net_PextMask(int maskset, qboolean fornq) @@ -211,7 +211,7 @@ unsigned int Net_PextMask(int maskset, qboolean fornq) mask |= PEXT2_REPLACEMENTDELTAS; if (mask & PEXT2_REPLACEMENTDELTAS) - mask |= PEXT2_NEWSIZEENCODING; + mask |= PEXT2_NEWSIZEENCODING; //use if we can if (fornq) { @@ -339,6 +339,7 @@ void Netchan_Setup (netsrc_t sock, netchan_t *chan, netadr_t *adr, int qport) #ifdef NQPROT chan->nqreliable_allowed = true; #endif + chan->incoming_unreliable = -1; chan->message.data = chan->message_buf; chan->message.allowoverflow = true; diff --git a/engine/common/particles.h b/engine/common/particles.h index 48210ea73..b39c28c8e 100644 --- a/engine/common/particles.h +++ b/engine/common/particles.h @@ -205,8 +205,11 @@ void P_ShutdownParticleSystem(void); void P_Shutdown(void); void P_LoadedModel(struct model_s *mod); /*checks a model's various effects*/ void P_DefaultTrail (unsigned int entityeffects, unsigned int modelflags, int *trailid, int *trailpalidx); -void P_EmitEffect (vec3_t pos, int type, trailstate_t **tsk);//this is just a wrapper +void P_EmitEffect (vec3_t pos, vec3_t orientation[3], unsigned int modeleflags, int type, trailstate_t **tsk);//this is just a wrapper int P_FindParticleType(const char *efname); +#ifdef PSET_SCRIPT +void PScript_ClearSurfaceParticles(struct model_s *mod); +#endif #define P_RunParticleEffectTypeString pe->RunParticleEffectTypeString #define P_ParticleTrail pe->ParticleTrail @@ -220,7 +223,6 @@ int P_FindParticleType(const char *efname); #define P_RunParticleEffectPalette pe->RunParticleEffectPalette #define P_ParticleTrailIndex pe->ParticleTrailIndex -#define P_EmitSkyEffectTris pe->EmitSkyEffectTris #define P_InitParticles pe->InitParticles #define P_DelinkTrailstate pe->DelinkTrailstate #define P_ClearParticles pe->ClearParticles @@ -245,7 +247,6 @@ typedef struct { void (*RunParticleEffectPalette) (const char *nameprefix, vec3_t org, vec3_t dir, int color, int count); void (*ParticleTrailIndex) (vec3_t start, vec3_t end, int color, int crnd, trailstate_t **tsk); - void (*EmitSkyEffectTris) (struct model_s *mod, struct msurface_s *fa, int ptype); qboolean (*InitParticles) (void); void (*ShutdownParticles) (void); void (*DelinkTrailstate) (trailstate_t **tsk); diff --git a/engine/common/pr_common.h b/engine/common/pr_common.h index f8e437867..990ff04bd 100644 --- a/engine/common/pr_common.h +++ b/engine/common/pr_common.h @@ -636,6 +636,8 @@ typedef enum VF_ENGINESBAR = 20, VF_DRAWCROSSHAIR = 21, VF_CARTESIAN_ANGLES = 22, + VF_MINDIST = 23, + VF_MAXDIST = 24, //this is a DP-compatibility hack. VF_CL_VIEWANGLES_V = 33, diff --git a/engine/common/protocol.h b/engine/common/protocol.h index 0db0cbb08..79473c222 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -401,22 +401,38 @@ enum svcq2_ops_e svcr1q2_zpacket = 21, svcr1q2_zdownload = 22, - svcq2pro_gamestate = 23, // q2pro specific, means svc_playerupdate in r1q2 + svcr1q2_playerupdate = 23, + svcr1q2_setting = 24, + svcq2pro_gamestate = 23, svcq2pro_setting = 24, }; enum clcq2_ops_e { - clcq2_bad, - clcq2_nop, - clcq2_move, // [[usercmd_t] - clcq2_userinfo, // [[userinfo string] - clcq2_stringcmd, // [string] message + clcq2_bad = 0, + clcq2_nop = 1, + clcq2_move = 2, // [[usercmd_t] + clcq2_userinfo = 3, // [[userinfo string] + clcq2_stringcmd = 4, // [string] message + + clcr1q2_setting = 5, // [setting][value] R1Q2 settings support. + clcr1q2_multimoves = 6, // for crappy clients that can't lerp //fte-extended - clcq2_voicechat + clcq2_voicechat = 31 }; +enum { + R1Q2_CLSET_NOGUN = 0, + R1Q2_CLSET_NOBLEND = 1, + R1Q2_CLSET_RECORDING = 2, + R1Q2_CLSET_PLAYERUPDATES = 3, + R1Q2_CLSET_FPS = 4 +}; +enum { + R1Q2_SVSET_PLAYERUPDATES = 0, + R1Q2_SVSET_FPS = 1 +}; //============================================== @@ -541,6 +557,12 @@ enum clcq2_ops_e #define Q2CM_BUTTONS (1<<6) #define Q2CM_IMPULSE (1<<7) +#define R1Q2_BUTTON_BYTE_FORWARD 4 +#define R1Q2_BUTTON_BYTE_SIDE 8 +#define R1Q2_BUTTON_BYTE_UP 16 +#define R1Q2_BUTTON_BYTE_ANGLE1 32 +#define R1Q2_BUTTON_BYTE_ANGLE2 64 + //============================================== // the first 16 bits of a packetentities update holds 9 bits @@ -1030,17 +1052,16 @@ typedef struct entity_state_s /*info to predict other players, so I don't get yelled at if fte were to stop supporting it*/ qbyte pmovetype; qbyte msec; - qbyte pad2; - qbyte pad1; - short vangle[3]; - unsigned short weaponframe; short movement[3]; short velocity[3]; // 1/8th + unsigned short weaponframe; unsigned char gravitydir[2]; //pitch/yaw, no roll + unsigned short traileffectnum; + unsigned short emiteffectnum; vec3_t predorg; } q1; @@ -1070,9 +1091,12 @@ typedef struct entity_state_s qbyte glowmod[3]; qbyte trans; + unsigned short light[4]; + qbyte lightstyle; qbyte lightpflags; unsigned short tagindex; + unsigned int tagentity; unsigned int solidsize; @@ -1081,8 +1105,6 @@ typedef struct entity_state_s #define ES_SOLID_HULL1 0x80201810 #define ES_SOLID_HULL2 0x80401820 #define ES_SOLID_HAS_EXTRA_BITS(solid) ((solid&0x0707) || (((solid>>16)-32768+32) & 7)) - - unsigned short light[4]; } entity_state_t; extern entity_state_t nullentitystate; diff --git a/engine/d3d/vid_d3d.c b/engine/d3d/vid_d3d.c index 70fae879b..3143dcb32 100644 --- a/engine/d3d/vid_d3d.c +++ b/engine/d3d/vid_d3d.c @@ -707,7 +707,7 @@ static qboolean D3D9_VID_Init(rendererstate_t *info, unsigned char *palette) return false; } - + CL_UpdateWindowTitle(); while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { @@ -1123,7 +1123,6 @@ static void (D3D9_R_DeInit) (void) static void D3D9_SetupViewPortProjection(void) { - extern cvar_t gl_mindist; int x, x2, y2, y, w, h; float fov_x, fov_y; @@ -1175,11 +1174,22 @@ static void D3D9_SetupViewPortProjection(void) Matrix4x4_CM_ModelViewMatrixFromAxis(r_refdef.m_view, vpn, vright, vup, r_refdef.vieworg); d3d9error(IDirect3DDevice9_SetTransform(pD3DDev9, D3DTS_VIEW, (D3DMATRIX*)r_refdef.m_view)); - /*d3d projection matricies scale depth to 0 to 1*/ - Matrix4x4_CM_Projection_Inf(d3d_trueprojection, fov_x, fov_y, bound(0.1, gl_mindist.value, 4)/2); + if (r_refdef.maxdist) + { + /*d3d projection matricies scale depth to 0 to 1*/ + Matrix4x4_CM_Projection_Far(d3d_trueprojection, fov_x, fov_y, r_refdef.mindist/2, r_refdef.maxdist); + /*ogl projection matricies scale depth to -1 to 1, and I would rather my code used consistant culling*/ + Matrix4x4_CM_Projection_Far(r_refdef.m_projection, fov_x, fov_y, r_refdef.mindist, r_refdef.maxdist); + } + else + { + /*d3d projection matricies scale depth to 0 to 1*/ + Matrix4x4_CM_Projection_Inf(d3d_trueprojection, fov_x, fov_y, r_refdef.mindist/2); + /*ogl projection matricies scale depth to -1 to 1, and I would rather my code used consistant culling*/ + Matrix4x4_CM_Projection_Inf(r_refdef.m_projection, fov_x, fov_y, r_refdef.mindist); + } + d3d9error(IDirect3DDevice9_SetTransform(pD3DDev9, D3DTS_PROJECTION, (D3DMATRIX*)d3d_trueprojection)); - /*ogl projection matricies scale depth to -1 to 1, and I would rather my code used consistant culling*/ - Matrix4x4_CM_Projection_Inf(r_refdef.m_projection, fov_x, fov_y, bound(0.1, gl_mindist.value, 4)); } static void (D3D9_R_RenderView) (void) diff --git a/engine/d3d/vid_d3d11.c b/engine/d3d/vid_d3d11.c index aa4456600..cfa32a0f5 100644 --- a/engine/d3d/vid_d3d11.c +++ b/engine/d3d/vid_d3d11.c @@ -180,7 +180,7 @@ void D3D11_ApplyRenderTargets(qboolean usedepth) if (*r_refdef.rt_depth.texname) depth = R2D_RT_GetTexture(r_refdef.rt_depth.texname, &width, &height); else - depth = R2D_RT_Configure("depth", width, height, TF_DEPTH24); + depth = R2D_RT_Configure("depth", width, height, TF_DEPTH24, RT_IMAGEFLAGS); } else depth = NULL; @@ -1386,7 +1386,6 @@ static void D3D11_R_DeInit (void) static void D3D11_SetupViewPort(void) { - extern cvar_t gl_mindist; int x, x2, y2, y; float fov_x, fov_y; @@ -1425,7 +1424,10 @@ static void D3D11_SetupViewPort(void) /*view matrix*/ Matrix4x4_CM_ModelViewMatrixFromAxis(r_refdef.m_view, vpn, vright, vup, r_refdef.vieworg); - Matrix4x4_CM_Projection_Inf(r_refdef.m_projection, fov_x, fov_y, bound(0.1, gl_mindist.value, 4)); + if (r_refdef.maxdist) + Matrix4x4_CM_Projection_Far(r_refdef.m_projection, fov_x, fov_y, r_refdef.mindist, r_refdef.maxdist); + else + Matrix4x4_CM_Projection_Inf(r_refdef.m_projection, fov_x, fov_y, r_refdef.mindist); } static void (D3D11_R_RenderView) (void) diff --git a/engine/gl/gl_alias.c b/engine/gl/gl_alias.c index 1100f221f..81638db32 100644 --- a/engine/gl/gl_alias.c +++ b/engine/gl/gl_alias.c @@ -215,7 +215,7 @@ static void Mod_ComposeSkin(char *texture, struct cctx_s *cctx) strcpy(r_refdef.rt_destcolour[0].texname, "-"); cctx->width = x+w; cctx->height = y+h; - cctx->diffuse = R2D_RT_Configure(r_refdef.rt_destcolour[0].texname, cctx->width, cctx->height, TF_RGBA32); + cctx->diffuse = R2D_RT_Configure(r_refdef.rt_destcolour[0].texname, cctx->width, cctx->height, TF_RGBA32, RT_IMAGEFLAGS); BE_RenderToTextureUpdate2d(true); } @@ -2607,7 +2607,7 @@ void BE_GenModelBatches(batch_t **batches, const dlight_t *dl, unsigned int bemo { if (gl_part_flame.value) { - if (ent->model->engineflags & MDLF_ENGULPHS) + if (ent->model->engineflags & MDLF_EMITREPLACE) continue; } } diff --git a/engine/gl/gl_heightmap.c b/engine/gl/gl_heightmap.c index 315333cea..06854bfeb 100644 --- a/engine/gl/gl_heightmap.c +++ b/engine/gl/gl_heightmap.c @@ -2982,7 +2982,7 @@ void Terr_DrawTerrainModel (batch_t **batches, entity_t *e) Terr_Brush_Draw(hm, batches, e); - if (r_refdef.globalfog.density || gl_maxdist.value>0) + if (r_refdef.globalfog.density || r_refdef.maxdist>0) { float culldist; extern cvar_t r_fog_exp2; @@ -3001,8 +3001,8 @@ void Terr_DrawTerrainModel (batch_t **batches, entity_t *e) else culldist = 999999999999999.f; - if (culldist > gl_maxdist.value && gl_maxdist.value>0) - culldist = gl_maxdist.value; + if (culldist > r_refdef.maxdist && r_refdef.maxdist>0) + culldist = r_refdef.maxdist; bounds[0] = bound(hm->firstsegx, (r_refdef.vieworg[0] + (CHUNKBIAS + 0)*hm->sectionsize - culldist) / hm->sectionsize, hm->maxsegx); bounds[1] = bound(hm->firstsegx, (r_refdef.vieworg[0] + (CHUNKBIAS + 1)*hm->sectionsize + culldist) / hm->sectionsize, hm->maxsegx); @@ -5538,7 +5538,7 @@ void Terr_Brush_Draw(heightmap_t *hm, batch_t **batches, entity_t *e) { qbyte *mips[4] = {(qbyte*)tx + tx->offsets[0], (qbyte*)tx + tx->offsets[1], (qbyte*)tx + tx->offsets[2], (qbyte*)tx + tx->offsets[3]}; unsigned int mapflags = SHADER_HASPALETTED | SHADER_HASDIFFUSE | SHADER_HASFULLBRIGHT | SHADER_HASNORMALMAP | SHADER_HASGLOSS; - R_BuildLegacyTexnums(bt->shader, tx->name, NULL, mapflags, 0, TF_BGRA32, tx->width, tx->height, mips, NULL); + R_BuildLegacyTexnums(bt->shader, tx->name, NULL, mapflags, 0, TF_MIP4_SOLID8, tx->width, tx->height, mips, NULL); } else R_BuildDefaultTexnums(NULL, bt->shader); @@ -5915,12 +5915,12 @@ static void Terr_Brush_DeleteIdx(heightmap_t *hm, size_t idx) if (idx < hm->numbrushes) hm->wbrushes[idx] = hm->wbrushes[hm->numbrushes]; } -static void Terr_Brush_DeleteId(heightmap_t *hm, unsigned int brushid) +static qboolean Terr_Brush_DeleteId(heightmap_t *hm, unsigned int brushid) { size_t i; brushes_t *br; if (!hm) - return; + return false; for (i = 0; i < hm->numbrushes; i++) { @@ -5928,9 +5928,10 @@ static void Terr_Brush_DeleteId(heightmap_t *hm, unsigned int brushid) if (br->id == brushid) { Terr_Brush_DeleteIdx(hm, i); - break; + return true; } } + return false; } @@ -6033,7 +6034,12 @@ void CL_Parse_BrushEdit(void) heightmap_t *hm = mod?mod->terrain:NULL; if (cmd == 0) - Terr_Brush_DeleteId(hm, MSG_ReadLong()); + { + int id = MSG_ReadLong(); + if (sv.state >= ss_loading) + return; //ignore if we're the server, we should already have it anyway. + Terr_Brush_DeleteId(hm, id); + } else if (cmd == 1) { brushes_t brush; @@ -6046,6 +6052,8 @@ void CL_Parse_BrushEdit(void) brush.faces = alloca(sizeof(*brush.faces) * brush.numplanes); if (!Brush_Deserialise(hm, &brush)) Host_EndGame("CL_Parse_BrushEdit: unparsable brush\n"); + if (sv.state >= ss_loading) + return; //ignore if we're the server, we should already have it anyway (but might need it for demos, hence why its still sent). if (brush.id) { int i; @@ -6065,6 +6073,9 @@ void CL_Parse_BrushEdit(void) } else if (cmd == 2) { + if (sv.state >= ss_loading) + return; //ignore if we're the server, we should already have it anyway. + hm = CL_BrushEdit_ForceContext(mod); //make sure we don't end up with any loaded brushes. if (hm) { @@ -6303,6 +6314,7 @@ void QCBUILTIN PF_brush_create(pubprogfuncs_t *prinst, struct globalvars_s *pr_g unsigned int numfaces = G_INT(OFS_PARM2); qcbrushface_t *in_faces = validateqcpointer(prinst, G_INT(OFS_PARM1), sizeof(*in_faces), numfaces, false); unsigned int contents = G_INT(OFS_PARM3); + unsigned int brushid = (prinst->callargc > 4)?G_INT(OFS_PARM4):0; //to simplify edits unsigned int i; brushes_t brush, *nb; @@ -6329,6 +6341,34 @@ void QCBUILTIN PF_brush_create(pubprogfuncs_t *prinst, struct globalvars_s *pr_g return; } + //if we're creating one that already exists, then assume that its a move. + if (brushid && Terr_Brush_DeleteId(hm, brushid)) + { +#ifndef CLIENTONLY + if (sv.state && modelindex > 0) + { + MSG_WriteByte(&sv.multicast, svcfte_brushedit); + MSG_WriteShort(&sv.multicast, modelindex); + MSG_WriteByte(&sv.multicast, 0); + MSG_WriteLong(&sv.multicast, brushid); + SV_MulticastProtExt(vec3_origin, MULTICAST_ALL_R, ~0, 0, 0); + } + else +#endif +#ifndef SERVERONLY + if (cls.state && modelindex > 0) + { + MSG_WriteByte(&cls.netchan.message, clcfte_brushedit); + MSG_WriteShort(&cls.netchan.message, modelindex); + MSG_WriteByte(&cls.netchan.message, 0); + MSG_WriteLong(&cls.netchan.message, brushid); + } +#else + { + } +#endif + } + planes = alloca(sizeof(*planes) * numfaces); faces = alloca(sizeof(*faces) * numfaces); for (i = 0; i < numfaces; i++) diff --git a/engine/gl/gl_model.c b/engine/gl/gl_model.c index 69364ff65..c65df7416 100644 --- a/engine/gl/gl_model.c +++ b/engine/gl/gl_model.c @@ -541,6 +541,10 @@ void Mod_Purge(enum mod_purge_e ptype) Z_Free(mod->entities); mod->entities = NULL; +#ifdef PSET_SCRIPT + PScript_ClearSurfaceParticles(mod); +#endif + //and obliterate anything else remaining in memory. ZG_FreeGroup(&mod->memgroup); mod->meshinfo = NULL; @@ -1056,6 +1060,8 @@ void Mod_LoadModelWorker (void *ctx, void *data, size_t a, size_t b) !strncmp(mod->name, "progs/v_", 8)) mod->engineflags &= ~MDLF_EZQUAKEFBCHEAT; + mod->engineflags |= MDLF_RECALCULATERAIN; + // get string used for replacement tokens COM_FileExtension(mod->name, ext, sizeof(ext)); if (!Q_strcasecmp(ext, "spr") || !Q_strcasecmp(ext, "sp2")) @@ -2081,6 +2087,7 @@ void Mod_LoadVisibility (model_t *loadmodel, qbyte *mod_base, lump_t *l, qbyte * memcpy (loadmodel->visdata, ptr, len); } +//scans through the worldspawn for a single specific key. char *Mod_ParseWorldspawnKey(const char *ents, const char *key, char *buffer, size_t sizeofbuffer) { char keyname[64]; @@ -2160,6 +2167,11 @@ void Mod_LoadEntities (model_t *loadmodel, qbyte *mod_base, lump_t *l) { char fname[MAX_QPATH]; size_t sz; + char keyname[64]; + char value[1024]; + char *ents, *k; + int t; + loadmodel->entitiescrc = 0; loadmodel->entities = NULL; if (!l->filelen) @@ -2195,6 +2207,37 @@ void Mod_LoadEntities (model_t *loadmodel, qbyte *mod_base, lump_t *l) } else loadmodel->entitiescrc = QCRC_Block(loadmodel->entities, strlen(loadmodel->entities)); + + ents = loadmodel->entities; + while(ents && *ents) + { + ents = COM_ParseOut(ents, keyname, sizeof(keyname)); + if (*keyname == '{') //an entity + { + while (ents && *ents) + { + ents = COM_ParseOut(ents, keyname, sizeof(keyname)); + if (*keyname == '}') + break; + ents = COM_ParseOut(ents, value, sizeof(value)); + if (!strncmp(keyname, "_texpart_", 9) || !strncmp(keyname, "texpart_", 8)) + { + k = keyname + ((*keyname=='_')?9:8); + for (t = 0; t < loadmodel->numtextures; t++) + { + if (!strcmp(k, loadmodel->textures[t]->name)) + { + loadmodel->textures[t]->partname = ZG_Malloc(&loadmodel->memgroup, strlen(value)+1); + strcpy(loadmodel->textures[t]->partname, value); + break; + } + } + if (t == loadmodel->numtextures) + Con_Printf("\"%s\" is not valid for %s\n", keyname, loadmodel->name); + } + } + } + } } diff --git a/engine/gl/gl_model.h b/engine/gl/gl_model.h index 865217762..91af342a4 100644 --- a/engine/gl/gl_model.h +++ b/engine/gl/gl_model.h @@ -301,6 +301,7 @@ typedef struct texture_s unsigned width, height; struct shader_s *shader; + char *partname; //parsed from the worldspawn entity int anim_total; // total tenths in sequence ( 0 = no) int anim_min, anim_max; // time for this frame min <=time< max @@ -870,7 +871,7 @@ typedef struct model_s modtype_t type; fromgame_t fromgame; - + int numframes; synctype_t synctype; @@ -879,6 +880,9 @@ typedef struct model_s int particleeffect; int particletrail; int traildefaultindex; + struct skytris_s *skytris; //for surface emittance + float skytime; //for surface emittance + struct skytriblock_s *skytrimem; // // volume occupied by the model graphics @@ -989,17 +993,19 @@ typedef struct model_s zonegroup_t memgroup; } model_t; -#define MDLF_ENGULPHS 0x001 // particle effect engulphs model (don't draw) -#define MDLF_NODEFAULTTRAIL 0x002 -#define MDLF_RGBLIGHTING 0x004 -#define MDLF_PLAYER 0x008 // players have specific lighting values -#define MDLF_FLAME 0x010 // can be excluded with r_drawflame, fullbright render hack -#define MDLF_DOCRC 0x020 // model needs CRC built -#define MDLF_NEEDOVERBRIGHT 0x040 // only overbright these models with gl_overbright_all set -#define MDLF_BOLT 0x080 // doesn't produce shadows -#define MDLF_NOTREPLACEMENTS 0x100 // can be considered a cheat, disable texture replacements -#define MDLF_EZQUAKEFBCHEAT 0x200 // this is a blatent cheat, one that can disadvantage us fairly significantly if we don't support it. -#define MDLF_HASBRUSHES 0x400 // q1bsp has brush info for more precise traceboxes +#define MDLF_EMITREPLACE 0x0001 // particle effect engulphs model (don't draw) +#define MDLF_EMITFORWARDS 0x0002 +#define MDLF_NODEFAULTTRAIL 0x0004 +#define MDLF_RGBLIGHTING 0x0008 +#define MDLF_PLAYER 0x0010 // players have specific lighting values +#define MDLF_FLAME 0x0020 // can be excluded with r_drawflame, fullbright render hack +#define MDLF_DOCRC 0x0040 // model needs CRC built +#define MDLF_NEEDOVERBRIGHT 0x0080 // only overbright these models with gl_overbright_all set +#define MDLF_BOLT 0x0100 // doesn't produce shadows +#define MDLF_NOTREPLACEMENTS 0x0200 // can be considered a cheat, disable texture replacements +#define MDLF_EZQUAKEFBCHEAT 0x0400 // this is a blatent cheat, one that can disadvantage us fairly significantly if we don't support it. +#define MDLF_HASBRUSHES 0x0800 // q1bsp has brush info for more precise traceboxes +#define MDLF_RECALCULATERAIN 0x1000 // particles changed, recalculate any sky polys //============================================================================ #endif // __MODEL__ diff --git a/engine/gl/gl_rlight.c b/engine/gl/gl_rlight.c index ad1230c40..db714e82d 100644 --- a/engine/gl/gl_rlight.c +++ b/engine/gl/gl_rlight.c @@ -286,7 +286,6 @@ static qboolean R_BuildDlightMesh(dlight_t *light, float colscale, float radscal float rad; float *bub_sin, *bub_cos; vec3_t colour; - extern cvar_t gl_mindist; bub_sin = bubble_sintable; bub_cos = bubble_costable; @@ -309,7 +308,7 @@ static qboolean R_BuildDlightMesh(dlight_t *light, float colscale, float radscal } VectorSubtract (light->origin, r_origin, v); - if (dtype != 1 && Length (v) < rad + gl_mindist.value*2) + if (dtype != 1 && Length (v) < rad + r_refdef.mindist*2) { // view is inside the dlight return false; } diff --git a/engine/gl/gl_rmain.c b/engine/gl/gl_rmain.c index 699fc7414..690f356c7 100644 --- a/engine/gl/gl_rmain.c +++ b/engine/gl/gl_rmain.c @@ -542,23 +542,23 @@ void R_SetupGL (float stereooffset) { int stencilshadows = Sh_StencilShadowsActive(); - if ((!stencilshadows || !gl_stencilbits) && gl_maxdist.value>=100)//gl_nv_range_clamp) + if ((!stencilshadows || !gl_stencilbits) && r_refdef.maxdist)//gl_nv_range_clamp) { // yfov = 2*atan((float)r_refdef.vrect.height/r_refdef.vrect.width)*180/M_PI; // yfov = (2.0 * tan (scr_fov.value/360*M_PI)) / screenaspect; // yfov = 2*atan((float)r_refdef.vrect.height/r_refdef.vrect.width)*(scr_fov.value*2)/M_PI; // MYgluPerspective (yfov, screenaspect, 4, 4096); - Matrix4x4_CM_Projection_Far(r_refdef.m_projection, fov_x, fov_y, bound(0.1, gl_mindist.value, 4), gl_maxdist.value); + Matrix4x4_CM_Projection_Far(r_refdef.m_projection, fov_x, fov_y, r_refdef.mindist, r_refdef.maxdist); } else { - Matrix4x4_CM_Projection_Inf(r_refdef.m_projection, fov_x, fov_y, bound(0.1, gl_mindist.value, 4)); + Matrix4x4_CM_Projection_Inf(r_refdef.m_projection, fov_x, fov_y, r_refdef.mindist); } } else { - Matrix4x4_CM_Orthographic(r_refdef.m_projection, -fov_x/2, fov_x/2, -fov_y/2, fov_y/2, gl_mindist.value, gl_maxdist.value>=1?gl_maxdist.value:9999); + Matrix4x4_CM_Orthographic(r_refdef.m_projection, -fov_x/2, fov_x/2, -fov_y/2, fov_y/2, r_refdef.mindist, r_refdef.maxdist?r_refdef.maxdist:9999); } Matrix4x4_CM_ModelViewMatrixFromAxis(r_refdef.m_view, vpn, vright, vup, r_origin); @@ -944,7 +944,7 @@ void GLR_DrawPortal(batch_t *batch, batch_t **blist, batch_t *depthmasklist[2], return; } //if we're behind it, then also don't draw anything. for our purposes, behind is when the entire near clipplane is behind. - if (DotProduct(r_refdef.vieworg, plane.normal)-plane.dist < -gl_mindist.value) + if (DotProduct(r_refdef.vieworg, plane.normal)-plane.dist < -r_refdef.mindist) return; TRACE(("GLR_DrawPortal: portal type %i\n", portaltype)); @@ -1727,7 +1727,7 @@ texid_t R_RenderPostProcess (texid_t sourcetex, int type, shader_t *shader, char if (R2D_Flush) R2D_Flush(); GLBE_FBO_Sources(sourcetex, r_nulltex); - sourcetex = R2D_RT_Configure(restexname, w, h, TF_RGBA32); + sourcetex = R2D_RT_Configure(restexname, w, h, TF_RGBA32, RT_IMAGEFLAGS); GLBE_FBO_Update(&fbo_postproc, 0, &sourcetex, 1, r_nulltex, w, h, 0); R2D_ScalePic(0, 0, r_refdef.vrect.width, r_refdef.vrect.height, shader); if (R2D_Flush) @@ -1906,7 +1906,7 @@ void GLR_RenderView (void) vid.fbvwidth = vid.fbpwidth; vid.fbvheight = vid.fbpheight; - sourcetex = R2D_RT_Configure("rt/$lastgameview", vid.fbpwidth, vid.fbpheight, /*(r_refdef.flags&RDF_BLOOM)?TF_RGBA16F:*/TF_RGBA32); + sourcetex = R2D_RT_Configure("rt/$lastgameview", vid.fbpwidth, vid.fbpheight, /*(r_refdef.flags&RDF_BLOOM)?TF_RGBA16F:*/TF_RGBA32, RT_IMAGEFLAGS); oldfbo = GLBE_FBO_Update(&fbo_gameview, FBO_RB_DEPTH, &sourcetex, 1, r_nulltex, vid.fbpwidth, vid.fbpheight, 0); dofbo = true; diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c index 28ebe6e61..69c85c382 100644 --- a/engine/gl/gl_shader.c +++ b/engine/gl/gl_shader.c @@ -617,17 +617,17 @@ static void Shader_ParseFunc (shader_t *shader, char **ptr, shaderfunc_t *func) token = Shader_ParseString (ptr); if (!Q_stricmp (token, "sin")) - func->type = SHADER_FUNC_SIN; + func->type = SHADER_FUNC_SIN; else if (!Q_stricmp (token, "triangle")) - func->type = SHADER_FUNC_TRIANGLE; + func->type = SHADER_FUNC_TRIANGLE; else if (!Q_stricmp (token, "square")) - func->type = SHADER_FUNC_SQUARE; + func->type = SHADER_FUNC_SQUARE; else if (!Q_stricmp (token, "sawtooth")) - func->type = SHADER_FUNC_SAWTOOTH; + func->type = SHADER_FUNC_SAWTOOTH; else if (!Q_stricmp (token, "inversesawtooth")) - func->type = SHADER_FUNC_INVERSESAWTOOTH; + func->type = SHADER_FUNC_INVERSESAWTOOTH; else if (!Q_stricmp (token, "noise")) - func->type = SHADER_FUNC_NOISE; + func->type = SHADER_FUNC_NOISE; func->args[0] = Shader_ParseFloat (shader, ptr, 0); func->args[1] = Shader_ParseFloat (shader, ptr, 0); @@ -639,6 +639,7 @@ static void Shader_ParseFunc (shader_t *shader, char **ptr, shaderfunc_t *func) static int Shader_SetImageFlags(shader_t *shader, shaderpass_t *pass, char **name) { + //fixme: pass flags should be handled elsewhere. int flags = 0; for(;name;) @@ -646,7 +647,13 @@ static int Shader_SetImageFlags(shader_t *shader, shaderpass_t *pass, char **nam if (!Q_strnicmp(*name, "$rt:", 4)) { *name += 4; - flags |= IF_NOMIPMAP|IF_CLAMP|IF_LINEAR|IF_RENDERTARGET; + flags |= IF_NOMIPMAP|IF_CLAMP|IF_RENDERTARGET; + if (!(flags & (IF_NEAREST|IF_LINEAR))) + { + flags |= IF_LINEAR; + if (pass) + pass->flags |= SHADER_PASS_LINEAR; + } } else if (!Q_strnicmp(*name, "$clamp:", 7)) { @@ -666,16 +673,24 @@ static int Shader_SetImageFlags(shader_t *shader, shaderpass_t *pass, char **nam else if (!Q_strnicmp(*name, "$nearest:", 9)) { *name+=9; - flags|= IF_NEAREST; + flags &= ~IF_LINEAR; + flags |= IF_NEAREST; if (pass) + { + pass->flags &= ~SHADER_PASS_LINEAR; pass->flags |= SHADER_PASS_NEAREST; + } } else if (!Q_strnicmp(*name, "$linear:", 8)) { *name+=8; - flags|= IF_LINEAR; + flags &= ~IF_NEAREST; + flags |= IF_LINEAR; if (pass) + { + pass->flags &= ~SHADER_PASS_NEAREST; pass->flags |= SHADER_PASS_LINEAR; + } } else break; @@ -751,7 +766,7 @@ static texid_t Shader_FindImage ( char *name, int flags ) return R_LoadColourmapImage(); } if (flags & IF_RENDERTARGET) - return R2D_RT_Configure(name, 0, 0, TF_INVALID); + return R2D_RT_Configure(name, 0, 0, TF_INVALID, flags); return R_LoadHiResTexture(name, NULL, flags); } @@ -5481,15 +5496,19 @@ void Shader_DefaultBSPQ1(const char *shortname, shader_t *s, const void *args) "{\n" "map $diffuse\n" "tcgen base\n" - "alphamask\n" + "alphamask\n" "}\n" - "if $lightmap\n" +// "if $lightmap\n" "{\n" "map $lightmap\n" + "if gl_overbright > 1\n" + "blendfunc gl_dst_color gl_src_color\n" //scale it up twice. will probably still get clamped, but what can you do + "else\n" "blendfunc gl_dst_color gl_zero\n" + "endif\n" "depthfunc equal\n" "}\n" - "endif\n" +// "endif\n" "{\n" "map $fullbright\n" "blendfunc add\n" diff --git a/engine/gl/gl_shadow.c b/engine/gl/gl_shadow.c index 3922579dc..41ff4af9c 100644 --- a/engine/gl/gl_shadow.c +++ b/engine/gl/gl_shadow.c @@ -1730,7 +1730,6 @@ static qboolean Sh_ScissorForBox(vec3_t mins, vec3_t maxs, srect_t *r) float frac; float x,x1,x2,y,y1,y2; double z, z1, z2; - extern cvar_t gl_mindist; r->x = 0; r->y = 0; @@ -1752,7 +1751,7 @@ static qboolean Sh_ScissorForBox(vec3_t mins, vec3_t maxs, srect_t *r) // return false; // } - ncpdist = DotProduct(r_refdef.vieworg, vpn) + gl_mindist.value; + ncpdist = DotProduct(r_refdef.vieworg, vpn) + r_refdef.mindist; for (i = 0; i < 8; i++) { @@ -2889,7 +2888,7 @@ static void Sh_DrawStencilLightShadows(dlight_t *dl, qbyte *lvis, qbyte *vvis, q if (cls.allow_anyparticles) //allowed or static { - if (emodel->engineflags & MDLF_ENGULPHS) + if (emodel->engineflags & MDLF_EMITREPLACE) { if (gl_part_flame.value) continue; @@ -3024,7 +3023,7 @@ static qboolean Sh_DrawStencilLight(dlight_t *dl, vec3_t colour, vec3_t axis[3], } #endif //our stencil writes. - if (gl_config.arb_depth_clamp && gl_maxdist.value != 0) + if (gl_config.arb_depth_clamp && r_refdef.maxdist != 0) qglEnable(GL_DEPTH_CLAMP_ARB); #if 0 //def _DEBUG diff --git a/engine/gl/gl_vidnt.c b/engine/gl/gl_vidnt.c index 12cad6c90..0f998fb8c 100644 --- a/engine/gl/gl_vidnt.c +++ b/engine/gl/gl_vidnt.c @@ -1398,6 +1398,11 @@ void VID_UnSetMode (void) mainwindow = NULL; } + if (WinNT) + UnregisterClassW(WINDOW_CLASS_NAME_W, global_hInstance); + else + UnregisterClassA(WINDOW_CLASS_NAME_A, global_hInstance); + #if 0 //Logically this code should be active. However... //1: vid_restarts are slightly slower if we don't reuse the old dll @@ -2689,11 +2694,6 @@ void GLVID_DeInit (void) Cvar_Unhook(&vid_vsync); Cvar_Unhook(&vid_wndalpha); Cmd_RemoveCommand("vid_recenter"); - - if (WinNT) - UnregisterClassW(WINDOW_CLASS_NAME_W, global_hInstance); - else - UnregisterClassA(WINDOW_CLASS_NAME_A, global_hInstance); } /* diff --git a/engine/gl/gl_warp.c b/engine/gl/gl_warp.c index 6de534535..c5189a572 100644 --- a/engine/gl/gl_warp.c +++ b/engine/gl/gl_warp.c @@ -498,7 +498,7 @@ static void R_DrawSkyMesh(batch_t *batch, mesh_t *m, shader_t *shader) float skydist = gl_skyboxdist.value; if (skydist<1) - skydist=gl_maxdist.value * 0.577; + skydist=r_refdef.maxdist * 0.577; if (skydist<1) skydist = 10000000; @@ -524,6 +524,7 @@ static void R_DrawSkyMesh(batch_t *batch, mesh_t *m, shader_t *shader) b.skin = NULL; b.texture = NULL; b.vbo = NULL; + Vector4Set(skyent.shaderRGBAf, 1, 1, 1, 1); BE_SubmitBatch(&b); } @@ -601,7 +602,7 @@ static void MakeSkyGridVec2 (float s, float t, int axis, vec3_t v, vec2_t tc1, v float skydist = gl_skyboxdist.value; if (skydist<1) - skydist=gl_maxdist.value * 0.577; + skydist=r_refdef.maxdist * 0.577; if (skydist<1) skydist = 10000000; @@ -835,19 +836,19 @@ void R_InitSky (shader_t *shader, const char *skyname, qbyte *src, unsigned int temp = BZF_Malloc(imagewidth*imageheight*sizeof(*temp)); if (temp) { - for (i=0 ; idefaulttextures->base = R_LoadReplacementTexture(name, NULL, IF_NOALPHA, temp, imagewidth, imageheight, TF_RGBX32); - for (i=0 ; ilightstyles[i] = surf_styles[i]; memset(&l->lightmaps[i], 0, sizeof(l->lightmaps[i][0])*l->numsurfpt); diff --git a/engine/partcfgs/q2part.cfg b/engine/partcfgs/q2part.cfg index 506bad4fa..2b0b184bd 100644 --- a/engine/partcfgs/q2part.cfg +++ b/engine/partcfgs/q2part.cfg @@ -348,7 +348,6 @@ r_part TR_BLASTERTRAIL2 r_part teq2_bubbletrail { - /*blue spiral*/ texture "classicparticle" tcoords 0 0 16 16 32 scale 0.5 @@ -363,7 +362,7 @@ r_part teq2_bubbletrail r_part TEQ2_RAILTRAIL { - /*blue spiral*/ + //blue spiral texture "classicparticle" tcoords 0 0 16 16 32 scale 0.5 @@ -380,7 +379,7 @@ r_part TEQ2_RAILTRAIL } r_part +TEQ2_RAILTRAIL { - /*grey filler*/ + //grey filler texture "classicparticle" tcoords 0 0 16 16 32 scale 0.5 diff --git a/engine/qclib/pr_edict.c b/engine/qclib/pr_edict.c index e5220f784..01948d120 100644 --- a/engine/qclib/pr_edict.c +++ b/engine/qclib/pr_edict.c @@ -45,6 +45,8 @@ edictrun_t *ED_AllocIntoTable (progfuncs_t *progfuncs, int num, pbool object, un if (e->fields) progfuncs->funcs.AddressableFree(&progfuncs->funcs, e->fields); e->fields = progfuncs->funcs.AddressableAlloc(&progfuncs->funcs, fields_size); + if (!e->fields) + Sys_Error ("ED_Alloc: Unable to allocate more field space"); e->fieldsize = fields_size; // e->fields = PRAddressableExtend(progfuncs, NULL, fields_size, 0); @@ -1870,22 +1872,20 @@ char *PDECL PR_SaveEnts(pubprogfuncs_t *ppf, char *buf, size_t *bufofs, size_t b int header_crc; //if 'general' block is found, this is a compleate state, otherwise, we should spawn entities like -int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, float killonspawnflags) +int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PDECL *callback) (pubprogfuncs_t *progfuncs, struct edict_s *ed, void *ctx, const char *entstart, const char *entend)) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; - eval_t *fulldata; //this is part of FTE_FULLSPAWNDATA const char *datastart; - eval_t *selfvar = NULL; - eval_t *var; - const char *spawnwarned[20] = {NULL}; +// eval_t *selfvar = NULL; +// eval_t *var; +// const char *spawnwarned[20] = {NULL}; char filename[128]; int num; edictrun_t *ed=NULL; ddef16_t *d16; ddef32_t *d32; - func_t CheckSpawn=0; void *oldglobals = NULL; int oldglobalssize = 0; @@ -1902,15 +1902,10 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, float killonspawnfl isloadgame = true; numents = -1; file+=8; - fulldata = NULL; } else - { isloadgame = false; - fulldata = PR_FindGlobal(&progfuncs->funcs, "__fullspawndata", PR_ANY, NULL); - } - while(1) { datastart = file; @@ -1945,6 +1940,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, float killonspawnfl file = QCC_COM_Parse(file); num = atoi(qcc_token); + datastart = file; file = QCC_COM_Parse(file); if (qcc_token[0] != '{') Sys_Error("Progs loading found %s, not '{'", qcc_token); @@ -1965,33 +1961,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, float killonspawnfl externs->entspawn((struct edict_s *) ed, true); file = ED_ParseEdict(progfuncs, file, ed); - if (killonspawnflags) - { - var = QC_GetEdictFieldValue (&progfuncs->funcs, (struct edict_s *)&ed, "spawnflags", ev_float, &prinst.spawnflagscache); - if (var) - { - if ((int)var->_float & (int)killonspawnflags) - { - ed->ereftype = ER_FREE; - continue; - } - } - } - - if (!resethunk) - { - mfunction_t *f; - if ((var = QC_GetEdictFieldValue (&progfuncs->funcs, (struct edict_s *)ed, "classname", ev_string, NULL))) - { - f = ED_FindFunction(progfuncs, PR_StringToNative(&progfuncs->funcs, var->string), NULL, -1); - if (f) - { - var = (eval_t *)((int *)pr_globals + ED_FindGlobalOfs(progfuncs, "self")); - var->edict = EDICT_TO_PROG(progfuncs, ed); - PR_ExecuteProgram(&progfuncs->funcs, f-pr_cp_functions); - } - } - } + callback(ppf, (struct edict_s *)ed, ctx, datastart, file); } else if (!strcmp(qcc_token, "progs")) { @@ -2263,103 +2233,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, float killonspawnfl externs->entspawn((struct edict_s *) ed, true); file = ED_ParseEdict(progfuncs, file, ed); - if (killonspawnflags) - { - var = QC_GetEdictFieldValue (&progfuncs->funcs, (struct edict_s *)ed, "spawnflags", ev_float, &prinst.spawnflagscache); - if (var) - { - if ((int)var->_float & (int)killonspawnflags) - { - ed->ereftype = ER_FREE; - ed->freetime = 0; - continue; - } - } - } - - if (!resethunk) - { - const char *eclassname; - func_t f; - if (!CheckSpawn) - CheckSpawn = PR_FindFunc(&progfuncs->funcs, "CheckSpawn", -2); - - var = QC_GetEdictFieldValue (&progfuncs->funcs, (struct edict_s *)ed, "classname", ev_string, NULL); - if (!var || !var->string || !*PR_StringToNative(&progfuncs->funcs, var->string)) - { - printf("No classname\n"); - ED_Free(&progfuncs->funcs, (struct edict_s *)ed); - } - else - { - //added by request of Mercury. - if (fulldata) //this is a vital part of HL map support!!! - { //essentually, it passes the ent's spawn info to the ent. - char *nl; //otherwise it sees only the named fields of - char *spawndata;//a standard quake ent. - spawndata = PRHunkAlloc(progfuncs, file - datastart +1, "fullspawndata"); - strncpy(spawndata, datastart, file - datastart); - spawndata[file - datastart] = '\0'; - for (nl = spawndata; *nl; nl++) - if (*nl == '\n') - *nl = '\t'; - fulldata->string = PR_StringToProgs(&progfuncs->funcs, spawndata); - } - - if (!selfvar) - selfvar = PR_FindGlobal(&progfuncs->funcs, "self", PR_ANY, NULL); - if (selfvar) - selfvar->edict = EDICT_TO_PROG(progfuncs, ed); - - //DP_SV_SPAWNFUNC_PREFIX support - eclassname = PR_StringToNative(&progfuncs->funcs, var->string); -#ifdef _WIN32 - _snprintf(filename, sizeof(filename), "spawnfunc_%s", eclassname); - filename[sizeof(filename)-1] = 0; -#else - snprintf(filename, sizeof(filename), "spawnfunc_%s", eclassname); -#endif - f = PR_FindFunc(&progfuncs->funcs, filename, PR_ANYBACK); - if (!f) - f = PR_FindFunc(&progfuncs->funcs, eclassname, PR_ANYBACK); - if (f) - { - if (CheckSpawn) - { - G_INT(OFS_PARM0) = f; - PR_ExecuteProgram(&progfuncs->funcs, CheckSpawn); - //call the spawn func or remove. - } - else - PR_ExecuteProgram(&progfuncs->funcs, f); - } - else if (CheckSpawn) - { - G_INT(OFS_PARM0) = 0; - PR_ExecuteProgram(&progfuncs->funcs, CheckSpawn); - //the mod is responsible for freeing unrecognised ents. - } - else - { - //only warn on the first occurence of the classname, don't spam. - int i; - const char *fnc = PR_StringToNative(&progfuncs->funcs, var->string); - if (prinst.pr_typecurrent >= 0) - for (i = 0; i < sizeof(spawnwarned)/sizeof(spawnwarned[0]); i++) - { - if (!spawnwarned[i]) - { - printf("Couldn't find spawn function %s\n", fnc); - spawnwarned[i] = fnc; - break; - } - else if (!strcmp(spawnwarned[i], fnc)) - break; - } - ED_Free(&progfuncs->funcs, (struct edict_s *)ed); - } - } - } + callback(ppf, (struct edict_s *)ed, ctx, datastart, file); } else Sys_Error("Bad entity lump: '%s' not recognised (last ent was %i)", qcc_token, ed?ed->entnum:0); diff --git a/engine/qclib/progsint.h b/engine/qclib/progsint.h index e74b0f35a..259dd161d 100644 --- a/engine/qclib/progsint.h +++ b/engine/qclib/progsint.h @@ -283,7 +283,7 @@ int PDECL Comp_Continue(pubprogfuncs_t *progfuncs); pbool PDECL PR_SetWatchPoint(pubprogfuncs_t *progfuncs, char *key); char *PDECL PR_EvaluateDebugString(pubprogfuncs_t *progfuncs, char *key); char *PDECL PR_SaveEnts(pubprogfuncs_t *progfuncs, char *mem, size_t *size, size_t maxsize, int mode); -int PDECL PR_LoadEnts(pubprogfuncs_t *progfuncs, const char *file, float killonspawnflags); +int PDECL PR_LoadEnts(pubprogfuncs_t *progfuncs, const char *file, void *ctx, void (PDECL *callback) (pubprogfuncs_t *progfuncs, struct edict_s *ed, void *ctx, const char *entstart, const char *entend)); char *PDECL PR_SaveEnt (pubprogfuncs_t *progfuncs, char *buf, size_t *size, size_t maxsize, struct edict_s *ed); struct edict_s *PDECL PR_RestoreEnt (pubprogfuncs_t *progfuncs, const char *buf, size_t *size, struct edict_s *ed); void PDECL PR_StackTrace (pubprogfuncs_t *progfuncs, int showlocals); diff --git a/engine/qclib/progslib.h b/engine/qclib/progslib.h index 011948bfd..dc1f76819 100644 --- a/engine/qclib/progslib.h +++ b/engine/qclib/progslib.h @@ -123,7 +123,7 @@ struct pubprogfuncs_s void (PDECL *ED_Print) (pubprogfuncs_t *prinst, struct edict_s *ed); char *(PDECL *save_ents) (pubprogfuncs_t *prinst, char *buf, size_t *size, size_t maxsize, int mode); //dump the entire progs info into one big self allocated string - int (PDECL *load_ents) (pubprogfuncs_t *prinst, const char *s, float killonspawnflags); //restore the entire progs state (or just add some more ents) (returns edicts ize) + int (PDECL *load_ents) (pubprogfuncs_t *prinst, const char *s, void *ctx, void (PDECL *callback) (pubprogfuncs_t *progfuncs, struct edict_s *ed, void *ctx, const char *entstart, const char *entend)); //restore the entire progs state (or just add some more ents) (returns edicts ize) char *(PDECL *saveent) (pubprogfuncs_t *prinst, char *buf, size_t *size, size_t maxsize, struct edict_s *ed); //will save just one entities vars struct edict_s *(PDECL *restoreent) (pubprogfuncs_t *prinst, const char *buf, size_t *size, struct edict_s *ed); //will restore the entity that had it's values saved (can use NULL for ed) @@ -279,7 +279,7 @@ typedef union eval_s #define ED_Free(pf, ed) (*pf->EntFree) (pf, ed) #define ED_Clear(pf, ed) (*pf->EntClear) (pf, ed) -#define PR_LoadEnts(pf, s, kf) (*pf->load_ents) (pf, s, kf) +#define PR_LoadEnts(pf, s, ctx, cb) (*pf->load_ents) (pf, s, ctx, cb) #define PR_SaveEnts(pf, buf, size, maxsize, mode) (*pf->save_ents) (pf, buf, size, maxsize, mode) #if 0//def _DEBUG diff --git a/engine/qclib/qcc.h b/engine/qclib/qcc.h index ecf2d615f..097b23cc6 100644 --- a/engine/qclib/qcc.h +++ b/engine/qclib/qcc.h @@ -957,6 +957,10 @@ void QCC_PR_EmitArrayGetFunction(QCC_def_t *defscope, QCC_def_t *thearray, char void QCC_PR_EmitArraySetFunction(QCC_def_t *defscope, QCC_def_t *thearray, char *arrayname); void QCC_PR_EmitClassFromFunction(QCC_def_t *defscope, QCC_type_t *basetype); +QCC_def_t *QCC_PR_DummyDef(QCC_type_t *type, char *name, QCC_function_t *scope, int arraysize, QCC_def_t *rootsymbol, unsigned int ofs, int referable, unsigned int flags); +void QCC_PR_ParseInitializerDef(QCC_def_t *def, unsigned int flags); + + void PostCompile(void); pbool PreCompile(void); void QCC_Cleanup(void); diff --git a/engine/qclib/qcc_pr_comp.c b/engine/qclib/qcc_pr_comp.c index bdc3d0e51..67d601137 100644 --- a/engine/qclib/qcc_pr_comp.c +++ b/engine/qclib/qcc_pr_comp.c @@ -2661,14 +2661,35 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ } break; case OP_BITAND_F: - case OP_AND_F: - if (QCC_PR_RoundFloatConst(eval_a) != 0) + case OP_BITAND_FI: + if (QCC_PR_RoundFloatConst(eval_a) == 0) { - optres_constantarithmatic++; - QCC_FreeTemp(var_a); - return var_b; + QCC_FreeTemp(var_a); QCC_FreeTemp(var_b); + return QCC_MakeFloatConst(0); } break; + case OP_BITAND_I: + case OP_BITAND_IF: + if (eval_a->_int == 0) + { + QCC_FreeTemp(var_a); QCC_FreeTemp(var_b); + return QCC_MakeIntConst(0); + } + break; + case OP_AND_F: + optres_constantarithmatic++; + QCC_FreeTemp(var_a); + if (eval_a->_float != 0) + return var_b; + QCC_FreeTemp(var_b); + return QCC_MakeFloatConst(0); + case OP_AND_I: + optres_constantarithmatic++; + QCC_FreeTemp(var_a); + if (eval_a->_int != 0) + return var_b; + QCC_FreeTemp(var_b); + return QCC_MakeIntConst(0); case OP_BITOR_I: case OP_OR_I: @@ -2688,15 +2709,6 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ return var_b; } break; - case OP_BITAND_I: - case OP_AND_I: - if (eval_a->_int != 0) - { - optres_constantarithmatic++; - QCC_FreeTemp(var_a); - return var_b; - } - break; } } } @@ -5209,15 +5221,15 @@ QCC_sref_t QCC_PR_GenerateFunctionCallRef (QCC_sref_t newself, QCC_sref_t func, { //gah, this is messy if (arglist[i]->base.ofs == oself.ofs && arglist[i]->base.sym == oself.sym) { + QCC_UnFreeTemp(self); QCC_FreeTemp(arglist[i]->base); arglist[i]->base = self; - QCC_UnFreeTemp(arglist[i]->base); } if (arglist[i]->index.ofs == oself.ofs && arglist[i]->index.sym == oself.sym) { + QCC_UnFreeTemp(self); QCC_FreeTemp(arglist[i]->index); arglist[i]->index = self; - QCC_UnFreeTemp(arglist[i]->index); } } @@ -5250,7 +5262,7 @@ QCC_sref_t QCC_PR_GenerateFunctionCallRef (QCC_sref_t newself, QCC_sref_t func, //FIXME: this may need to generate function calls, which can potentially clobber parms. This would be bad. we may need to copy them all out first THEN do the assignments. //FIXME: this can't cope with splitting return values over different extra_parms. - QCC_StoreSRefToRef(arglist[i], d, true, false); + QCC_StoreSRefToRef(arglist[i], d, false, false); } parm += (func.cast->params[i].type->size+2)/3; } @@ -9548,6 +9560,7 @@ void PR_GenerateReturnOuts(void) p.ofs = 0; p.cast = type_vector; } + QCC_ForceUnFreeDef(p.sym); QCC_StoreToSRef(p, QCC_MakeSRefForce(local, 0, local->type), local->type, false, false); } parm += (pr_scope->type->params[i].type->size+2)/3; diff --git a/engine/qclib/qcc_pr_lex.c b/engine/qclib/qcc_pr_lex.c index 680bd6f4c..7dc93eca1 100644 --- a/engine/qclib/qcc_pr_lex.c +++ b/engine/qclib/qcc_pr_lex.c @@ -48,7 +48,7 @@ extern pbool expandedemptymacro; //really these should not be in here extern unsigned int locals_end, locals_start; extern QCC_type_t *pr_classtype; -QCC_function_t *QCC_PR_ParseImmediateStatements (QCC_def_t *def, QCC_type_t *type); +QCC_function_t *QCC_PR_ParseImmediateStatements (QCC_def_t *def, QCC_type_t *type, pbool dowrap); static void Q_strlcpy(char *dest, const char *src, int sizeofdest) @@ -4605,10 +4605,6 @@ QCC_type_t *QCC_PR_GenFunctionType (QCC_type_t *rettype, struct QCC_typeparam_s extern char *basictypenames[]; extern QCC_type_t **basictypes[]; -QCC_def_t *QCC_PR_DummyDef(QCC_type_t *type, char *name, QCC_function_t *scope, int arraysize, QCC_def_t *rootsymbol, unsigned int ofs, int referable, unsigned int flags); - -void QCC_PR_ParseInitializerDef(QCC_def_t *def); - pbool type_inlinefunction; /*newtype=true: creates a new type always silentfail=true: function is permitted to return NULL if it was not given a type, otherwise never returns NULL @@ -4831,7 +4827,7 @@ QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail) def = QCC_PR_GetSRef(functype, funcname, NULL, true, 0, GDF_CONST | (isinline?GDF_INLINE:0)); //pr_classtype = newt; - f = QCC_PR_ParseImmediateStatements (def.sym, functype); + f = QCC_PR_ParseImmediateStatements (def.sym, functype, false); pr_classtype = NULL; pr_scope = NULL; def.sym->symboldata[def.ofs].function = f - functions; @@ -5114,7 +5110,7 @@ QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail) else { pr_classtype = newt; - QCC_PR_ParseInitializerDef(def); + QCC_PR_ParseInitializerDef(def, 0); QCC_FreeDef(def); pr_classtype = NULL; /* diff --git a/engine/qclib/qccgui.c b/engine/qclib/qccgui.c index 38b3c1b15..1d2156146 100644 --- a/engine/qclib/qccgui.c +++ b/engine/qclib/qccgui.c @@ -793,7 +793,7 @@ LRESULT CALLBACK MySubclassWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l if (editor->editpane == hWnd) break; } - if (editor->scintilla) + if (editor && editor->scintilla) { FindNextScintilla(editor, buffer); } @@ -2795,7 +2795,7 @@ unsigned char *GUIReadFile(const char *fname, void *buffer, int blen, size_t *sz else { *(wchar_t*)buffer = 0xfeff; - GetWindowTextW(e->editpane, (wchar_t*)buffer+1, blen); + GetWindowTextW(e->editpane, (wchar_t*)buffer+1, blen-sizeof(wchar_t)); } if (e->modified) @@ -3083,6 +3083,7 @@ static pbool EngineCommandWndf(HWND wnd, char *message, ...) DWORD WINAPI threadwrapper(void *args) { + pbool hadstatus = false; enginewindow_t *ctx = args; { char workingdir[MAX_PATH+10]; @@ -3156,6 +3157,7 @@ DWORD WINAPI threadwrapper(void *args) MessageBox(mainwindow, qcva("gla: %x", hr), "Cannot Start Engine", 0); break; } + hadstatus = true; //don't warn about other stuff } //these ends of the pipes were inherited by now, so we can discard them in the caller. @@ -3192,10 +3194,12 @@ DWORD WINAPI threadwrapper(void *args) if (!strncmp(buffer, "status ", 7)) { //SetWindowText(ctx->window, buffer+7); + hadstatus = true; } else if (!strcmp(buffer, "status")) { //SetWindowText(ctx->window, "Engine"); + hadstatus = true; } else if (!strcmp(buffer, "curserver")) { @@ -3243,6 +3247,7 @@ DWORD WINAPI threadwrapper(void *args) SetWindowText(ctx->window, caption); } PostMessage(ctx->window, WM_USER+1, 0, 0); //and tell the owning window to try to close it again + hadstatus = true; } else if (!strncmp(buffer, "refocuswindow", 13) && (buffer[13] == ' ' || !buffer[13])) { @@ -3251,6 +3256,7 @@ DWORD WINAPI threadwrapper(void *args) l++; ctx->refocuswindow = (HWND)(size_t)strtoull(l, &l, 0); ShowWindow(ctx->window, SW_HIDE); + hadstatus = true; } else { @@ -3269,6 +3275,9 @@ DWORD WINAPI threadwrapper(void *args) ctx->pipefromengine = NULL; CloseHandle(ctx->pipetoengine); ctx->pipetoengine = NULL; + + if (!hadstatus) + MessageBox(mainwindow, "Engine terminated without acknowledging debug session.\nCurrently only FTE supports debugging.", "Debugging Failed", MB_OK); } ctx->pipeclosed = true; diff --git a/engine/qclib/qccguistuff.c b/engine/qclib/qccguistuff.c index f517867f9..3d284b7f8 100644 --- a/engine/qclib/qccguistuff.c +++ b/engine/qclib/qccguistuff.c @@ -28,15 +28,19 @@ int Grep(char *filename, char *string) char *last, *found, *linestart; int line = 1; int sz; - char *buf; + char *raw, *buf; + pbool dofree; + int origfmt; if (!filename) return foundcount; sz = GUIFileSize(filename); if (sz <= 0) return foundcount; - buf = malloc(sz+1); - buf[sz] = 0; - GUIReadFile(filename, buf, sz, NULL); + raw = malloc(sz+2); + raw[sz] = 0; + GUIReadFile(filename, raw, sz, NULL); + + buf = QCC_SanitizeCharSet(raw, &sz, &dofree, &origfmt); linestart = last = found = buf; while ((found = QC_strcasestr(found, string))) @@ -60,7 +64,9 @@ int Grep(char *filename, char *string) linestart = found; foundcount++; } - free(buf); + if (dofree) + free(buf); + free(raw); return foundcount; } diff --git a/engine/qclib/qccmain.c b/engine/qclib/qccmain.c index 4b1019785..2b2b33d0f 100644 --- a/engine/qclib/qccmain.c +++ b/engine/qclib/qccmain.c @@ -2924,6 +2924,9 @@ int QCC_PR_FinishCompilation (void) errors = false; + if (pr_error_count) + return false; + if (qcc_targetformat == QCF_FTE || qcc_targetformat == QCF_FTEDEBUG || qcc_targetformat == QCF_FTEH2) externokay = true; @@ -4738,7 +4741,14 @@ void QCC_ContinueCompile(void) return; } - QCC_FindBestInclude(qcc_token, compilingrootfile, 2); + if(setjmp(pr_parse_abort)) + { + if (++pr_error_count > MAX_ERRORS) + QCC_Error (ERR_PARSEERRORS, "Errors have occured\n"); + return; //just try move onto the next file, gather errors. + } + else + QCC_FindBestInclude(qcc_token, compilingrootfile, 2); /* { int includepath = 0; diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index d6f6cddd8..0361ce1a0 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -398,6 +398,7 @@ static void ASMCALL ThinkTimeOp (pubprogfuncs_t *prinst, edict_t *ed, float var) } #endif +static int SV_ParticlePrecache_Add(const char *pname); static pbool PDECL SV_BadField(pubprogfuncs_t *inst, edict_t *foo, const char *keyname, const char *value) { #ifdef HEXEN2 @@ -419,6 +420,20 @@ static pbool PDECL SV_BadField(pubprogfuncs_t *inst, edict_t *foo, const char *k } #endif + if (!strcmp(keyname, "traileffect")) + { + foo->xv->traileffectnum = SV_ParticlePrecache_Add(value); + return true; + } + if (!strcmp(keyname, "emiteffect")) + { + foo->xv->emiteffectnum = SV_ParticlePrecache_Add(value); + return true; + } + + if (!strcmp(keyname, "sky") || !strcmp(keyname, "fog")) + return true; //these things are handled in the client, so don't warn if they're used. + //don't spam warnings about missing fields if we failed to load the progs. if (!svs.numprogs) return true; @@ -1189,7 +1204,7 @@ void PR_ApplyCompilation_f (void) PR_RegisterFields(); sv.world.edict_size=PR_InitEnts(svprogfuncs, sv.world.max_edicts); - sv.world.edict_size=svprogfuncs->load_ents(svprogfuncs, s, 0); + sv.world.edict_size=svprogfuncs->load_ents(svprogfuncs, s, NULL, NULL); PR_LoadGlabalStruct(false); @@ -1412,6 +1427,207 @@ void SVQ1_CvarChanged(cvar_t *var) } } +static void QCBUILTIN PF_precache_model (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); +static void QCBUILTIN PF_setmodel (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); +void QCBUILTIN PF_makestatic (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); +static void PR_FallbackSpawn_Misc_Model(pubprogfuncs_t *progfuncs, edict_t *self) +{ + void *pr_globals; + eval_t *val; + + if (!self->v->model && (val = progfuncs->GetEdictFieldValue(progfuncs, self, "mdl", ev_string, NULL))) + self->v->model = val->string; + if (!*PR_GetString(progfuncs, self->v->model)) //must have a model, because otherwise various things will assume its not valid at all. + progfuncs->SetStringField(progfuncs, self, &self->v->model, "*null", true); + + if (self->v->angles[1] < 0) //mimic AD. shame there's no avelocity clientside. + self->v->angles[1] = (rand()*(360.0f/RAND_MAX)); + + //make sure the model is precached, to avoid errors. + pr_globals = PR_globals(progfuncs, PR_CURRENT); + G_INT(OFS_PARM0) = self->v->model; + PF_precache_model(progfuncs, pr_globals); + + pr_globals = PR_globals(progfuncs, PR_CURRENT); + G_INT(OFS_PARM0) = EDICT_TO_PROG(progfuncs, self); + G_INT(OFS_PARM1) = self->v->model; + PF_setmodel(progfuncs, pr_globals); + + //and lets just call makestatic instead of worrying if it'll interfere with the rest of the qc. + pr_globals = PR_globals(progfuncs, PR_CURRENT); + G_INT(OFS_PARM0) = EDICT_TO_PROG(progfuncs, self); + PF_makestatic(progfuncs, pr_globals); +} +struct spawnents_s +{ + int killonspawnflags; + eval_t *fulldata; + func_t CheckSpawn; + const char *spawnwarned[32]; +}; +static void PDECL PR_DoSpawnInitialEntity(pubprogfuncs_t *progfuncs, struct edict_s *ed, void *vctx, const char *start, const char *end) +{ + struct spawnents_s *ctx = vctx; + const char *eclassname; + func_t f; + char spawnfuncname[256]; + + if ((int)ed->v->spawnflags & ctx->killonspawnflags) + { + ED_Free(progfuncs, (struct edict_s *)ed); + return; + } + + if (!ctx->CheckSpawn) + ctx->CheckSpawn = PR_FindFunction(progfuncs, "CheckSpawn", -2); + + eclassname = PR_GetString(progfuncs, ed->v->classname); + if (!*eclassname) + { + printf("No classname\n"); + ED_Free(progfuncs, ed); + } + else + { + //added by request of Mercury. + if (ctx->fulldata) //this is a vital part of HL map support!!! + { //essentually, it passes the ent's spawn info to the ent. + char *spawndata;//a standard quake ent. +#ifdef QCGC + const char *in; + ctx->fulldata->string = progfuncs->AllocTempString(progfuncs, &spawndata, end-start+1); + for (in = start; in < end; in++) + { + if (*in == '\n') + { + in++; + *spawndata++ = '\t'; + } + else + *spawndata++ = *in++; + } + *spawndata = 0; +#else + char *nl; //otherwise it sees only the named fields of + spawndata = PRHunkAlloc(progfuncs, file - datastart +1, "fullspawndata"); + strncpy(spawndata, datastart, file - datastart); + spawndata[file - datastart] = '\0'; + for (nl = spawndata; *nl; nl++) + if (*nl == '\n') + *nl = '\t'; + ctx->fulldata->string = PR_StringToProgs(&progfuncs->funcs, spawndata); +#endif + } + + *sv.world.g.self = EDICT_TO_PROG(progfuncs, ed); + + //DP_SV_SPAWNFUNC_PREFIX support + Q_snprintfz(spawnfuncname, sizeof(spawnfuncname), "spawnfunc_%s", eclassname); + f = PR_FindFunction(progfuncs, spawnfuncname, PR_ANYBACK); + if (!f) + f = PR_FindFunction(progfuncs, eclassname, PR_ANYBACK); + if (f) + { + if (ctx->CheckSpawn) + { + void *pr_globals = PR_globals(progfuncs, PR_CURRENT); + G_INT(OFS_PARM0) = f; + PR_ExecuteProgram(progfuncs, ctx->CheckSpawn); + //call the spawn func or remove. + } + else + PR_ExecuteProgram(progfuncs, f); + } + else if (ctx->CheckSpawn) + { + void *pr_globals = PR_globals(progfuncs, PR_CURRENT); + G_INT(OFS_PARM0) = 0; + PR_ExecuteProgram(progfuncs, ctx->CheckSpawn); + //the mod is responsible for freeing unrecognised ents. + } + else if (!strcmp(eclassname, "misc_model")) + PR_FallbackSpawn_Misc_Model(progfuncs, ed); + else + { + //only warn on the first occurence of the classname, don't spam. + int i; + if (svs.numprogs) + for (i = 0; i < countof(ctx->spawnwarned); i++) + { + if (!ctx->spawnwarned[i]) + { + printf("Couldn't find spawn function for %s\n", eclassname); + ctx->spawnwarned[i] = eclassname; + break; + } + else if (!strcmp(ctx->spawnwarned[i], eclassname)) + break; + } + ED_Free(progfuncs, ed); + } + } +} +void PR_SpawnInitialEntities(const char *file) +{ + extern cvar_t skill; + struct spawnents_s ctx; + memset(&ctx, 0, sizeof(ctx)); + +#ifdef HEXEN2 + if (progstype == PROG_H2) + { + extern cvar_t coop; + if (deathmatch.value) + ctx.killonspawnflags = SPAWNFLAG_NOT_H2DEATHMATCH; + else if (coop.value) + ctx.killonspawnflags = SPAWNFLAG_NOT_H2COOP; + else + { + cvar_t *cl_playerclass = Cvar_Get("cl_playerclass", "0", CVAR_USERINFO, 0); + ctx.killonspawnflags = SPAWNFLAG_NOT_H2SINGLE; + + if (cl_playerclass && cl_playerclass->ival == 1) + ctx.killonspawnflags |= SPAWNFLAG_NOT_H2PALADIN; + else if (cl_playerclass && cl_playerclass->ival == 2) + ctx.killonspawnflags |= SPAWNFLAG_NOT_H2CLERIC; + else if (cl_playerclass && cl_playerclass->ival == 3) + ctx.killonspawnflags |= SPAWNFLAG_NOT_H2NECROMANCER; + else if (cl_playerclass && cl_playerclass->ival == 4) + ctx.killonspawnflags |= SPAWNFLAG_NOT_H2THEIF; + else if (cl_playerclass && cl_playerclass->ival == 5) + ctx.killonspawnflags |= SPAWNFLAG_NOT_H2NECROMANCER; /*yes, I know.,. makes no sense*/ + } + if (skill.value < 0.5) + ctx.killonspawnflags |= SPAWNFLAG_NOT_H2EASY; + else if (skill.value > 1.5) + ctx.killonspawnflags |= SPAWNFLAG_NOT_H2HARD; + else + ctx.killonspawnflags |= SPAWNFLAG_NOT_H2MEDIUM; + + //don't filter based on player class. we're lame and don't have any real concept of player classes. + } + else +#endif + if (!deathmatch.value) //decide if we are to inhibit single player game ents instead + { + if (skill.value < 0.5) + ctx.killonspawnflags = SPAWNFLAG_NOT_EASY; + else if (skill.value > 1.5) + ctx.killonspawnflags = SPAWNFLAG_NOT_HARD; + else + ctx.killonspawnflags = SPAWNFLAG_NOT_MEDIUM; + } + else + ctx.killonspawnflags = SPAWNFLAG_NOT_DEATHMATCH; + + ctx.fulldata = PR_FindGlobal(svprogfuncs, "__fullspawndata", PR_ANY, NULL); + + if (svprogfuncs) + sv.world.edict_size = PR_LoadEnts(svprogfuncs, file, &ctx, PR_DoSpawnInitialEntity); + else + sv.world.edict_size = 0; +} + void SV_RegisterH2CustomTents(void); void Q_InitProgs(void) { @@ -2888,6 +3104,7 @@ static void QCBUILTIN PF_te_blooddp (pubprogfuncs_t *prinst, globalvars_t *pr_gl MSG_WriteByte (&sv.nqmulticast, 73); #endif + (void)dir; //FIXME: sould be sending TEDP_BLOOD MSG_WriteByte (&sv.multicast, svc_temp_entity); MSG_WriteByte (&sv.multicast, TEQW_BLOOD); MSG_WriteByte (&sv.multicast, count<10?1:(count+10)/20); @@ -3450,7 +3667,6 @@ static void QCBUILTIN PF_checkclient (pubprogfuncs_t *prinst, struct globalvars_ void PF_stuffcmd_Internal(int entnum, const char *str, unsigned int flags) { client_t *cl; - static qboolean expectingcolour; int slen; if (entnum < 1 || entnum > sv.allocated_client_slots) @@ -3472,6 +3688,7 @@ void PF_stuffcmd_Internal(int entnum, const char *str, unsigned int flags) //FIXME: should buffer the entire command instead. if (progstype != PROG_QW) { + static qboolean expectingcolour; if (!strncmp(str, "color ", 6)||!strncmp(str, ";color ", 7)) //okay, so this is a hack, but it fixes the qw scoreboard { expectingcolour = true; @@ -9531,7 +9748,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"vectoyaw", PF_Fixme, 0, 0, 0, 10, "float(vector)"}, {"vectoangles", PF_Fixme, 0, 0, 0, 11, "vector(vector)"}, {"random", PF_Fixme, 0, 0, 0, 12, "float()"}, - {"localcmd", PF_Fixme, 0, 0, 0, 13, "void(string)"}, + {"localcmd", PF_Fixme, 0, 0, 0, 13, "void(string,...)"}, {"cvar", PF_Fixme, 0, 0, 0, 14, "float(string name)"}, {"cvar_set", PF_Fixme, 0, 0, 0, 15, "void(string name, string value)"}, {"dprint", PF_Fixme, 0, 0, 0, 16, "void(string text)"}, @@ -9630,7 +9847,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"vectoyaw", PF_vectoyaw, 13, 13, 13, 0, D("float(vector v, optional entity reference)", "Given a direction vector, returns the yaw angle in which that direction vector points. If an entity is passed, the yaw angle will be relative to that entity's gravity direction.")}, {"spawn", PF_Spawn, 14, 14, 14, 0, D("entity()", "Adds a brand new entity into the world! Hurrah, you're now a parent!")}, {"remove", PF_Remove, 15, 15, 15, 0, D("void(entity e)", "Destroys the given entity and clears some limited fields (including model, modelindex, solid, classname). Any references to the entity following the call are an error. After two seconds, the entity will be reused, in the interim you can unfortunatly still read its fields to see if the reference is no longer valid.")}, - {"traceline", PF_svtraceline, 16, 16, 16, 0, D("void(vector v1, vector v2, float flags, entity ent)", "Traces an infinitely thin line through the world from v1 towards v2.\nWill not collide with ent, ent.owner, or any entity who's owner field refers to ent.\nThe passed entity will also be used to determine whether to use a capsule trace, the contents that the trace should impact, and a couple of other extra fields that define the trace.\nThere are no side effects beyond the trace_* globals being written.\nflags&MOVE_NOMONSTERS will not impact on non-bsp entities.\nflags&MOVE_MISSILE will impact with increased size.\nflags&MOVE_HITMODEL will impact upon model meshes, instead of their bounding boxes.\nflags&MOVE_TRIGGERS will also stop on triggers\nflags&MOVE_EVERYTHING will stop if it hits anything, even non-solid entities.\nflags&MOVE_LAGGED will backdate entity positions for the purposes of this builtin according to the indicated player ent's latency, to provide lag compensation.")}, + {"traceline", PF_svtraceline, 16, 16, 16, 0, D("void(vector v1, vector v2, float flags, entity ent)", "Traces a thin line through the world from v1 towards v2.\nWill not collide with ent, ent.owner, or any entity who's owner field refers to ent.\nThe passed entity will also be used to determine whether to use a capsule trace, the contents that the trace should impact, and a couple of other extra fields that define the trace.\nThere are no side effects beyond the trace_* globals being written.\nflags&MOVE_NOMONSTERS will not impact on non-bsp entities.\nflags&MOVE_MISSILE will impact with increased size.\nflags&MOVE_HITMODEL will impact upon model meshes, instead of their bounding boxes.\nflags&MOVE_TRIGGERS will also stop on triggers\nflags&MOVE_EVERYTHING will stop if it hits anything, even non-solid entities.\nflags&MOVE_LAGGED will backdate entity positions for the purposes of this builtin according to the indicated player ent's latency, to provide lag compensation.")}, {"checkclient", PF_checkclient, 17, 17, 17, 0, D("entity()", "Returns one of the player entities. The returned player will change periodically.")}, {"find", PF_FindString, 18, 18, 18, 0, D("entity(entity start, .string fld, string match)", "Scan for the next entity with a given field set to the given 'match' value. start should be either world, or the previous entity that was found. Returns world on failure/if there are no more.")}, {"precache_sound", PF_precache_sound, 19, 19, 19, 0, D("string(string s)", "Precaches a sound, making it known to clients and loading it from disk. This builtin (strongly) should be called during spawn functions. This builtin must be called for the sound before the sound builtin is called, or it might not even be heard.")}, @@ -10022,7 +10239,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs "\tfloat\ttbias;\n" \ "} brushface_t;\n" {"brush_get", PF_brush_get, 0, 0, 0, 0, D(qcbrushface "int(float modelidx, int brushid, brushface_t *out_faces, int maxfaces, int *out_contents)", "Queries a brush's information. You must pre-allocate the face array for the builtin to write to. Return value is the number of faces retrieved, 0 on error.")}, - {"brush_create", PF_brush_create, 0, 0, 0, 0, D("int(float modelidx, brushface_t *in_faces, int numfaces, int contents)", "Inserts a new brush into the model. Return value is the new brush's id.")}, + {"brush_create", PF_brush_create, 0, 0, 0, 0, D("int(float modelidx, brushface_t *in_faces, int numfaces, int contents, optional int brushid)", "Inserts a new brush into the model. Return value is the new brush's id.")}, {"brush_delete", PF_brush_delete, 0, 0, 0, 0, D("void(float modelidx, int brushid)", "Destroys the specified brush.")}, {"brush_selected", PF_brush_selected, 0, 0, 0, 0, D("float(float modelid, int brushid, int faceid, float selectedstate)", "Allows you to easily set transient visual properties of a brush. returns old value. selectedstate=-1 changes nothing (called for its return value).")}, {"brush_getfacepoints",PF_brush_getfacepoints,0,0, 0, 0, D("int(float modelid, int brushid, int faceid, vector *points, int maxpoints)", "Returns the list of verticies surrounding the given face. If face is 0, returns the center of the brush (if space for 1 point) or the mins+maxs (if space for 2 points).")}, @@ -10692,33 +10909,39 @@ void PR_ResetBuiltins(progstype_t type) //fix all nulls to PF_FIXME and add any *QSG_Extensions[i].queried = false; } - if (type == PROG_QW && pr_imitatemvdsv.value>0) //pretend to be mvdsv for a bit. + if (type == PROG_QW) { - if ( - PR_EnableEBFSBuiltin("executecommand", 83) != 83 || - PR_EnableEBFSBuiltin("mvdtokenize", 84) != 84 || - PR_EnableEBFSBuiltin("mvdargc", 85) != 85 || - PR_EnableEBFSBuiltin("mvdargv", 86) != 86 || - PR_EnableEBFSBuiltin("teamfield", 87) != 87 || - PR_EnableEBFSBuiltin("substr", 88) != 88 || - PR_EnableEBFSBuiltin("mvdstrcat", 89) != 89 || - PR_EnableEBFSBuiltin("mvdstrlen", 90) != 90 || - PR_EnableEBFSBuiltin("str2byte", 91) != 91 || - PR_EnableEBFSBuiltin("str2short", 92) != 92 || - PR_EnableEBFSBuiltin("mvdnewstr", 93) != 93 || - PR_EnableEBFSBuiltin("mvdfreestr", 94) != 94 || - PR_EnableEBFSBuiltin("conprint", 95) != 95 || - PR_EnableEBFSBuiltin("readcmd", 96) != 96 || - PR_EnableEBFSBuiltin("mvdstrcpy", 97) != 97 || - PR_EnableEBFSBuiltin("strstr", 98) != 98 || - PR_EnableEBFSBuiltin("mvdstrncpy", 99) != 99 || - PR_EnableEBFSBuiltin("logtext", 100)!= 100 || -// PR_EnableEBFSBuiltin("redirectcmd", 101)!= 101 || - PR_EnableEBFSBuiltin("mvdcalltimeofday",102)!= 102 || - PR_EnableEBFSBuiltin("forcedemoframe", 103)!= 103) - Con_Printf("Failed to register all MVDSV builtins\n"); - else - Con_Printf("Be aware that MVDSV does not follow standards. Please encourage mod developers to not require pr_imitatemvdsv to be set.\n"); + //this conflicts with dp's logarithm builtin. + PR_EnableEBFSBuiltin("precache_vwep_model", 532); + + if (pr_imitatemvdsv.value>0) //pretend to be mvdsv for a bit. + { + if ( + PR_EnableEBFSBuiltin("executecommand", 83) != 83 || + PR_EnableEBFSBuiltin("mvdtokenize", 84) != 84 || + PR_EnableEBFSBuiltin("mvdargc", 85) != 85 || + PR_EnableEBFSBuiltin("mvdargv", 86) != 86 || + PR_EnableEBFSBuiltin("teamfield", 87) != 87 || + PR_EnableEBFSBuiltin("substr", 88) != 88 || + PR_EnableEBFSBuiltin("mvdstrcat", 89) != 89 || + PR_EnableEBFSBuiltin("mvdstrlen", 90) != 90 || + PR_EnableEBFSBuiltin("str2byte", 91) != 91 || + PR_EnableEBFSBuiltin("str2short", 92) != 92 || + PR_EnableEBFSBuiltin("mvdnewstr", 93) != 93 || + PR_EnableEBFSBuiltin("mvdfreestr", 94) != 94 || + PR_EnableEBFSBuiltin("conprint", 95) != 95 || + PR_EnableEBFSBuiltin("readcmd", 96) != 96 || + PR_EnableEBFSBuiltin("mvdstrcpy", 97) != 97 || + PR_EnableEBFSBuiltin("strstr", 98) != 98 || + PR_EnableEBFSBuiltin("mvdstrncpy", 99) != 99 || + PR_EnableEBFSBuiltin("logtext", 100)!= 100 || + // PR_EnableEBFSBuiltin("redirectcmd", 101)!= 101 || + PR_EnableEBFSBuiltin("mvdcalltimeofday",102)!= 102 || + PR_EnableEBFSBuiltin("forcedemoframe", 103)!= 103) + Con_Printf("Failed to register all MVDSV builtins\n"); + else + Con_Printf("Be aware that MVDSV does not follow standards. Please encourage mod developers to not require pr_imitatemvdsv to be set.\n"); + } } } @@ -11191,7 +11414,7 @@ void PR_DumpPlatform_f(void) {"CSQC_Parse_Event", "void()", CS, "Called when the client receives an SVC_CGAMEPACKET. The csqc should read the data or call the error builtin if it does not recognise the message."}, {"CSQC_InputEvent", "float(float evtype, float scanx, float chary, float devid)", CS, "Called whenever a key is pressed, the mouse is moved, etc. evtype will be one of the IE_* constants. The other arguments vary depending on the evtype. Key presses are not guarenteed to have both scan and unichar values set at the same time."}, {"CSQC_Input_Frame", "__used void()", CS, "Called just before each time clientcommandframe is updated. You can edit the input_* globals in order to apply your own player inputs within csqc, which may allow you a convienient way to pass certain info to ssqc."}, - {"CSQC_RendererRestarted", "void(void)", CS, "Called by the engine after the video was restarted. This serves to notify the CSQC that any render targets that it may have cached were purged, and will need to be regenerated."}, + {"CSQC_RendererRestarted", "void(string rendererdescription)", CS, "Called by the engine after the video was restarted. This serves to notify the CSQC that any render targets that it may have cached were purged, and will need to be regenerated."}, {"CSQC_ConsoleCommand", "float(string cmd)", CS, "Called if the user uses any console command registed via registercommand."}, {"CSQC_ConsoleLink", "float(string text, string info)", CS, "Called if the user clicks a ^[text\\infokey\\infovalue^] link. Use infoget to read/check each supported key. Return true if you wish the engine to not attempt to handle the link itself."}, {"CSQC_Ent_Update", "void(float isnew)", CS}, @@ -11349,6 +11572,14 @@ void PR_DumpPlatform_f(void) {"CONTENTBITS_BOXSOLID", "const int", QW|NQ|CS, "Bits that tracebox would normally consider solid", 0,"CONTENTBIT_SOLID|"STRINGIFY(Q2CONTENTS_WINDOW)"|CONTENTBIT_BODY|CONTENTBIT_PLAYERCLIP"}, {"CONTENTBITS_FLUID", "const int", QW|NQ|CS, NULL, 0,"CONTENTBIT_WATER|CONTENTBIT_SLIME|CONTENTBIT_LAVA|CONTENTBIT_SKY"}, + {"SPA_POSITION", "const int", QW|NQ|CS, "These SPA_* constants are to specify which attribute is returned by the getsurfacepointattribute builtin", 0}, + {"SPA_S_AXIS", "const int", QW|NQ|CS, NULL, 1}, + {"SPA_T_AXIS", "const int", QW|NQ|CS, NULL, 2}, + {"SPA_R_AXIS", "const int", QW|NQ|CS, "aka: SPA_NORMAL", 3}, + {"SPA_TEXCOORDS0", "const int", QW|NQ|CS, NULL, 4}, + {"SPA_LIGHTMAP0_TEXCOORDS", "const int", QW|NQ|CS, NULL, 5}, + {"SPA_LIGHTMAP0_COLOR", "const int", QW|NQ|CS, NULL, 6}, + {"CHAN_AUTO", "const float", QW|NQ|CS, "The automatic channel, play as many sounds on this channel as you want, and they'll all play, however the other channels will replace each other.", CHAN_AUTO}, {"CHAN_WEAPON", "const float", QW|NQ|CS, NULL, CHAN_WEAPON}, {"CHAN_VOICE", "const float", QW|NQ|CS, NULL, CHAN_VOICE}, @@ -11595,12 +11826,15 @@ void PR_DumpPlatform_f(void) {"VF_DRAWENGINESBAR", "const float", CS, "boolean. If set to 1, the sbar will be drawn, and viewsize will be honoured automatically.", VF_ENGINESBAR}, {"VF_DRAWCROSSHAIR", "const float", CS, "boolean. If set to 1, the engine will draw its default crosshair.", VF_DRAWCROSSHAIR}, + {"VF_MINDIST", "const float", CS|MENU, "The distance of the near clip plane from the view position. Should generally not be <=0, as this would introduce NANs.", VF_MINDIST}, + {"VF_MAXDIST", "const float", CS|MENU, "The distance of the far clip plane from the view position. If 0, will be considered infinite.", VF_MAXDIST}, + {"VF_CL_VIEWANGLES", "const float", CS, NULL, VF_CL_VIEWANGLES_V}, {"VF_CL_VIEWANGLES_X", "const float", CS, NULL, VF_CL_VIEWANGLES_X}, {"VF_CL_VIEWANGLES_Y", "const float", CS, NULL, VF_CL_VIEWANGLES_Y}, {"VF_CL_VIEWANGLES_Z", "const float", CS, NULL, VF_CL_VIEWANGLES_Z}, - {"VF_PERSPECTIVE", "const float", CS|MENU, "1: regular rendering. Fov specifies the angle. 0: isometric-style. Fov specifies the number of Quake Units each side of the viewport.", VF_PERSPECTIVE}, + {"VF_PERSPECTIVE", "const float", CS|MENU, "1: regular rendering. Fov specifies the angle. 0: isometric-style. Fov specifies the number of Quake Units each side of the viewport, and mindist restrictions are removed, pvs culling should be disabled.", VF_PERSPECTIVE}, {"VF_ACTIVESEAT", "#define VF_LPLAYER VF_ACTIVESEAT\nconst float", CS, "The 'seat' number, used when running splitscreen.", VF_ACTIVESEAT}, {"VF_AFOV", "const float", CS|MENU, "Aproximate fov. Matches the 'fov' cvar. The engine handles the aspect ratio for you.", VF_AFOV}, {"VF_SCREENVSIZE", "const float", CS|MENU, "Provides a reliable way to retrieve the current virtual screen size (even if the screen is automatically scaled to retain aspect).", VF_SCREENVSIZE}, diff --git a/engine/server/pr_q1qvm.c b/engine/server/pr_q1qvm.c index 9c0cf353c..e07b7a3f3 100755 --- a/engine/server/pr_q1qvm.c +++ b/engine/server/pr_q1qvm.c @@ -496,8 +496,10 @@ static edict_t *QDECL Q1QVMPF_EntAlloc(pubprogfuncs_t *pf, pbool object, size_t return (struct edict_s *)e; } -static int QDECL Q1QVMPF_LoadEnts(pubprogfuncs_t *pf, const char *mapstring, float spawnflags) +static int QDECL Q1QVMPF_LoadEnts(pubprogfuncs_t *pf, const char *mapstring, void *ctx, void (PDECL *callback) (pubprogfuncs_t *progfuncs, struct edict_s *ed, void *ctx, const char *entstart, const char *entend)) { + //the qvm calls the spawn functions itself. + //no saved-games. q1qvmentstring = mapstring; VM_Call(q1qvm, GAME_LOADENTS, 0, 0, 0); q1qvmentstring = NULL; diff --git a/engine/server/progdefs.h b/engine/server/progdefs.h index 240c10a0a..344bdb00a 100644 --- a/engine/server/progdefs.h +++ b/engine/server/progdefs.h @@ -274,6 +274,7 @@ and the extension fields are added on the end and can have extra vm-specific stu comfieldfloat(glow_color,NULL)\ comfieldfloat(glow_trail,NULL)\ comfieldfloat(traileffectnum,"This should be set to the result of particleeffectnum, in order to attach a custom trail effect to an entity as it moves.")/*DP_ENT_TRAILEFFECTNUM*/\ + comfieldfloat(emiteffectnum,"This should be set to the result of particleeffectnum, in order to continually spawn particles in the direction that this entity faces.")/*DP_ENT_TRAILEFFECTNUM*/\ /*comfieldfloat(baseframe,"Specifies the current frame(group) to use for the lower (numerically) bones of a skeletal model. The basebone field specifies the bone where the regular frame field takes over.")*/ /*FTESS_QC_BASEFRAME*/\ /*comfieldfloat(basebone,"Specifies the bone at which the baseframe* fields stop being effective.")*/ /*FTE_SSQC_BASEFRAME*/\ comfieldfloat(dimension_see,"This is the dimension mask (bitfield) that the client is allowed to see. Entities and events not in this dimension mask will be invisible.")/*EXT_DIMENSION_VISIBLE*/\ diff --git a/engine/server/progs.h b/engine/server/progs.h index ff3caae93..7188a8986 100644 --- a/engine/server/progs.h +++ b/engine/server/progs.h @@ -33,6 +33,7 @@ void PR_Deinit(void); //server shutting down void PR_Shutdown(void); //server quitting void PR_LoadGlabalStruct(qboolean muted); void Q_InitProgs(void); +void PR_SpawnInitialEntities(const char *file); void PR_RegisterFields(void); void PR_Init(void); void QDECL ED_Spawned (struct edict_s *ent, int loading); diff --git a/engine/server/savegame.c b/engine/server/savegame.c index aaef861c9..b9e833540 100644 --- a/engine/server/savegame.c +++ b/engine/server/savegame.c @@ -317,7 +317,7 @@ void SV_Loadgame_Legacy(char *filename, vfsfile_t *f, int version) strcpy(file, "loadgame"); clnum=VFS_READ(f, file+8, filelen); file[filelen+8]='\0'; - sv.world.edict_size=svprogfuncs->load_ents(svprogfuncs, file, 0); + sv.world.edict_size=svprogfuncs->load_ents(svprogfuncs, file, NULL, NULL); BZ_Free(file); PR_LoadGlabalStruct(false); @@ -757,7 +757,7 @@ qboolean SV_LoadLevelCache(const char *savename, const char *level, const char * memset(file, 0, filelen+1); VFS_READ(f, file, filelen); file[filelen]='\0'; - sv.world.edict_size=svprogfuncs->load_ents(svprogfuncs, file, 0); + sv.world.edict_size=svprogfuncs->load_ents(svprogfuncs, file, NULL, NULL); BZ_Free(file); progstype = pt; @@ -1319,7 +1319,7 @@ void SV_Savegame_f (void) Con_Printf("%s: invalid number of arguments\n", Cmd_Argv(0)); } -cvar_t sv_autosave = CVARD("sv_autosave", "5", "Interval for autosaves, in minutes. Set to 0 to disable autosave."); +cvar_t sv_autosave = CVARFD("sv_autosave", "5", CVAR_SAVE, "Interval for autosaves, in minutes. Set to 0 to disable autosave."); void SV_AutoSave(void) { #ifndef NOBUILTINMENUS diff --git a/engine/server/sv_ccmds.c b/engine/server/sv_ccmds.c index 06453407c..ab415a637 100644 --- a/engine/server/sv_ccmds.c +++ b/engine/server/sv_ccmds.c @@ -439,7 +439,9 @@ void SV_Map_f (void) qboolean flushparms = false; //flush parms+serverflags qboolean cinematic = false; //new map is .cin / .roq or something qboolean q2savetos0 = false; +#ifdef Q3SERVER qboolean q3singleplayer = false; //forces g_gametype to 2 (otherwise clears if it was 2). +#endif qboolean waschangelevel = false; int i; @@ -471,7 +473,9 @@ void SV_Map_f (void) startspot = ((Cmd_Argc() == 2)?NULL:Cmd_Argv(2)); q2savetos0 = !strcmp(Cmd_Argv(0), "gamemap") && !isDedicated; //q2 +#ifdef Q3SERVER q3singleplayer = !strcmp(Cmd_Argv(0), "spmap"); +#endif flushparms = !strcmp(Cmd_Argv(0), "map") || !strcmp(Cmd_Argv(0), "spmap"); newunit = flushparms || (!strcmp(Cmd_Argv(0), "changelevel") && !startspot); diff --git a/engine/server/sv_ents.c b/engine/server/sv_ents.c index ea46997ae..9b06668af 100644 --- a/engine/server/sv_ents.c +++ b/engine/server/sv_ents.c @@ -550,6 +550,9 @@ void SV_EmitCSQCUpdate(client_t *client, sizebuf_t *msg, qbyte svcnumber) void SV_CSQC_DroppedPacket(client_t *client, int sequence) { int i; + if (!ISQWCLIENT(client) && !ISNQCLIENT(client)) + return; + if (!client->frameunion.frames) { Con_Printf("Server bug: No frames!\n"); @@ -1044,7 +1047,7 @@ static unsigned int SVFTE_DeltaCalcBits(entity_state_t *from, qbyte *frombonedat if (to->light[0] != from->light[0] || to->light[1] != from->light[1] || to->light[2] != from->light[2] || to->light[3] != from->light[3] || to->lightstyle != from->lightstyle || to->lightpflags != from->lightpflags) bits |= UF_LIGHT; - if (to->u.q1.traileffectnum != from->u.q1.traileffectnum) + if (to->u.q1.traileffectnum != from->u.q1.traileffectnum || to->u.q1.emiteffectnum != from->u.q1.emiteffectnum) bits |= UF_TRAILEFFECT; if (to->modelindex2 != from->modelindex2) @@ -1311,7 +1314,15 @@ static void SVFTE_WriteUpdate(unsigned int bits, entity_state_t *state, sizebuf_ MSG_WriteByte (msg, state->lightpflags); } if (bits & UF_TRAILEFFECT) - MSG_WriteShort(msg, state->u.q1.traileffectnum); + { + if (state->u.q1.emiteffectnum) + { + MSG_WriteShort(msg, (state->u.q1.traileffectnum & 0x3fff) | 0x8000); + MSG_WriteShort(msg, (state->u.q1.emiteffectnum & 0x3fff)); + } + else + MSG_WriteShort(msg, (state->u.q1.traileffectnum & 0x3fff)); + } if (bits & UF_COLORMOD) { @@ -1359,7 +1370,7 @@ Writes changed entities to the client. Changed ent states will be tracked, even if they're not sent just yet, dropped packets will also re-flag dropped delta bits Only what changed is tracked, via bitmask, its previous value is never tracked. */ -void SVFTE_EmitPacketEntities(client_t *client, packet_entities_t *to, sizebuf_t *msg) +qboolean SVFTE_EmitPacketEntities(client_t *client, packet_entities_t *to, sizebuf_t *msg) { edict_t *e; entity_state_t *o, *n; @@ -1371,14 +1382,10 @@ void SVFTE_EmitPacketEntities(client_t *client, packet_entities_t *to, sizebuf_t int sequence; qbyte *oldbonedata; unsigned int maxbonedatasize; - - if (ISNQCLIENT(client)) - sequence = client->netchan.outgoing_unreliable; - else - sequence = client->netchan.incoming_sequence; + qboolean overflow = false; if (!client->pendingdeltabits) - return; + return false; if (client->delta_sequence < 0) client->pendingdeltabits[0] = UF_REMOVE; @@ -1514,6 +1521,13 @@ void SVFTE_EmitPacketEntities(client_t *client, packet_entities_t *to, sizebuf_t Z_Free(oldbonedata); + + + if (ISNQCLIENT(client)) + sequence = client->netchan.outgoing_unreliable; + else + sequence = client->netchan.incoming_sequence; + /*cache frame info*/ resend = client->frameunion.frames[sequence & UPDATE_MASK].resend; outno = 0; @@ -1523,7 +1537,7 @@ void SVFTE_EmitPacketEntities(client_t *client, packet_entities_t *to, sizebuf_t MSG_WriteByte (msg, svcfte_updateentities); if (ISNQCLIENT(client) && (client->fteprotocolextensions2 & PEXT2_PREDINFO)) { - MSG_WriteLong(msg, client->last_sequence); + MSG_WriteShort(msg, client->last_sequence); } // Con_Printf("Gen sequence %i\n", sequence); MSG_WriteFloat(msg, sv.world.physicstime); @@ -1543,7 +1557,10 @@ void SVFTE_EmitPacketEntities(client_t *client, packet_entities_t *to, sizebuf_t if (!(bits & ~UF_RESET2)) //skip while there's nothing to send (skip reset2 if there's no other changes, its only to reduce chances of the client getting 'new' entities containing just an origin)*/ continue; if (msg->cursize + 50 > msg->maxsize) + { + overflow = true; break; /*give up if it gets full. FIXME: bone data is HUGE.*/ + } if (outno >= outmax) { //expand the frames. may need some copying... SVFTE_ExpandFrames(client, outno+1); @@ -1587,6 +1604,7 @@ void SVFTE_EmitPacketEntities(client_t *client, packet_entities_t *to, sizebuf_t client->frameunion.frames[sequence & UPDATE_MASK].entities.num_entities = outno; client->frameunion.frames[sequence & UPDATE_MASK].sequence = sequence; + return overflow; } /* @@ -2883,7 +2901,7 @@ int glowsize=0, glowcolor=0, colourmod=0; if (host_client->protocol == SCP_FITZ666) { - if (bits & FITZU_ALPHA) MSG_WriteByte(msg, ent->trans); + if (bits & FITZU_ALPHA) MSG_WriteByte(msg, (ent->trans+1)&0xff); if (bits & RMQU_SCALE) MSG_WriteByte(msg, ent->scale); if (bits & FITZU_FRAME2) MSG_WriteByte(msg, ent->frame>>8); if (bits & FITZU_MODEL2) MSG_WriteByte(msg, ent->modelindex>>8); @@ -3215,7 +3233,7 @@ void SV_Snapshot_BuildStateQ1(entity_state_t *state, edict_t *ent, client_t *cli //everyone else sees it normally. } - if (0)//ent->xv->basebone < 0) + if (ent->xv->basebone < 0) { if (ent->xv->skeletonindex && pack) { @@ -3226,7 +3244,7 @@ void SV_Snapshot_BuildStateQ1(entity_state_t *state, edict_t *ent, client_t *cli if (fs.skeltype == SKEL_RELATIVE && fs.bonecount) { Bones_To_PosQuat4(fs.bonecount, fs.bonestate, AllocateBoneSpace(pack, state->bonecount = fs.bonecount, &state->boneoffset)); - state->dpflags |= RENDER_COMPLEXANIMATION; + //state->dpflags |= RENDER_COMPLEXANIMATION; } } } @@ -3257,6 +3275,7 @@ void SV_Snapshot_BuildStateQ1(entity_state_t *state, edict_t *ent, client_t *cli state->lightstyle = ent->xv->style; state->lightpflags = ent->xv->pflags; state->u.q1.traileffectnum = ent->xv->traileffectnum; + state->u.q1.emiteffectnum = ent->xv->emiteffectnum; if (ent->xv->gravitydir[2] == -1) { @@ -3426,7 +3445,7 @@ void SV_Snapshot_BuildStateQ1(entity_state_t *state, edict_t *ent, client_t *cli if (!ent->xv->alpha) state->trans = 255; else - state->trans = bound(1, ent->xv->alpha*255, 255); + state->trans = bound(1, ent->xv->alpha*254, 254); //QSG_DIMENSION_PLANES - if the only shared dimensions are ghost dimensions, Set half alpha. if (client && client->edict) @@ -3841,7 +3860,9 @@ svc_playerinfo messages */ void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qboolean ignorepvs) { +#ifdef NQPROT int e; +#endif packet_entities_t *pack; edict_t *clent; client_frame_t *frame; @@ -3911,8 +3932,24 @@ void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qboolean ignore { if (client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS) { - SVFTE_EmitPacketEntities(client, pack, msg); - client->netchan.incoming_sequence++; + qboolean overflow; + for (;;) + { + overflow = SVFTE_EmitPacketEntities(client, pack, msg); + client->netchan.incoming_sequence++; + + if (overflow && pack == &svs.entstatebuffer) + { + if (!Netchan_CanPacket(&client->netchan, SV_RateForClient(client)/2)) + break; + Netchan_Transmit (&client->netchan, msg->cursize, msg->data, SV_RateForClient(client)); + SZ_Clear(msg); + if (!Netchan_CanPacket(&client->netchan, SV_RateForClient(client)/2)) + break; + } + else + break; + } } else if (client->protocol == SCP_DARKPLACES6 || client->protocol == SCP_DARKPLACES7) SVDP_EmitEntitiesUpdate(client, pack, msg); diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index f1d702405..b1e5150b5 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -791,7 +791,6 @@ void SV_SpawnServer (const char *server, const char *startspot, qboolean noents, q2edict_t *q2ent; #endif int i, j; - int spawnflagmask; extern int sv_allow_cheats; size_t fsz; @@ -1442,53 +1441,6 @@ void SV_SpawnServer (const char *server, const char *startspot, qboolean noents, SCR_SetLoadingFile("entities"); if (!deathmatch.value && !*skill.string) //skill was left blank so it doesn't polute serverinfo on deathmatch servers. in single player, we ensure that it gets a proper value. Cvar_Set(&skill, "1"); -#ifdef HEXEN2 - if (progstype == PROG_H2) - { - extern cvar_t coop; - spawnflagmask = 0; - if (deathmatch.value) - spawnflagmask |= SPAWNFLAG_NOT_H2DEATHMATCH; - else if (coop.value) - spawnflagmask |= SPAWNFLAG_NOT_H2COOP; - else - { - cvar_t *cl_playerclass = Cvar_Get("cl_playerclass", "0", CVAR_USERINFO, 0); - spawnflagmask |= SPAWNFLAG_NOT_H2SINGLE; - - if (cl_playerclass && cl_playerclass->ival == 1) - spawnflagmask |= SPAWNFLAG_NOT_H2PALADIN; - else if (cl_playerclass && cl_playerclass->ival == 2) - spawnflagmask |= SPAWNFLAG_NOT_H2CLERIC; - else if (cl_playerclass && cl_playerclass->ival == 3) - spawnflagmask |= SPAWNFLAG_NOT_H2NECROMANCER; - else if (cl_playerclass && cl_playerclass->ival == 4) - spawnflagmask |= SPAWNFLAG_NOT_H2THEIF; - else if (cl_playerclass && cl_playerclass->ival == 5) - spawnflagmask |= SPAWNFLAG_NOT_H2NECROMANCER; /*yes, I know.,. makes no sense*/ - } - if (skill.value < 0.5) - spawnflagmask |= SPAWNFLAG_NOT_H2EASY; - else if (skill.value > 1.5) - spawnflagmask |= SPAWNFLAG_NOT_H2HARD; - else - spawnflagmask |= SPAWNFLAG_NOT_H2MEDIUM; - - //don't filter based on player class. we're lame and don't have any real concept of player classes. - } - else -#endif - if (!deathmatch.value) //decide if we are to inhibit single player game ents instead - { - if (skill.value < 0.5) - spawnflagmask = SPAWNFLAG_NOT_EASY; - else if (skill.value > 1.5) - spawnflagmask = SPAWNFLAG_NOT_HARD; - else - spawnflagmask = SPAWNFLAG_NOT_MEDIUM; - } - else - spawnflagmask = SPAWNFLAG_NOT_DEATHMATCH; //do this and get the precaches/start up the game if (sv.world.worldmodel->entitiescrc) { @@ -1506,10 +1458,7 @@ void SV_SpawnServer (const char *server, const char *startspot, qboolean noents, switch(svs.gametype) { default: - if (svprogfuncs) - sv.world.edict_size = PR_LoadEnts(svprogfuncs, file, spawnflagmask); - else - sv.world.edict_size = 0; + PR_SpawnInitialEntities(file); break; #ifdef Q2SERVER case GT_QUAKE2: diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index bff3d5ef9..b2755b16b 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -1904,6 +1904,8 @@ void SV_ClientProtocolExtensionsChanged(client_t *client) seat->max_net_ents = client->max_net_ents; seat->maxmodels = client->maxmodels; } + + client->lastsequence_acknowledged = -2000000000; } @@ -2089,7 +2091,10 @@ client_t *SVC_DirectConnect(void) int qport; int version; int challenge; +#ifdef HUFFNETWORK int huffcrc = 0; + extern cvar_t net_compress; +#endif int mtu = 0; char guid[128] = ""; char basic[80]; @@ -2104,7 +2109,9 @@ client_t *SVC_DirectConnect(void) unsigned int protextsupported=0; unsigned int protextsupported2=0; - extern cvar_t sv_protocol_nq, net_compress; +#ifdef NQPROT + extern cvar_t sv_protocol_nq; +#endif char *name; @@ -4248,7 +4255,7 @@ void SV_GetConsoleCommands (void) } } -#define MINDRATE 500 +#define MINDRATE 4096 #define MINRATE 500 int SV_RateForClient(client_t *cl) { @@ -4263,7 +4270,7 @@ int SV_RateForClient(client_t *cl) else if (rate < MINDRATE) rate = MINDRATE; } - else if (rate >= 1 && rate < MINDRATE) + else if (rate != 0 && rate < MINDRATE) rate = MINDRATE; } else @@ -4276,7 +4283,7 @@ int SV_RateForClient(client_t *cl) else if (rate < MINRATE) rate = MINRATE; } - else if (rate >= 1 && rate < MINRATE) + else if (rate != 0 && rate < MINRATE) rate = MINRATE; } @@ -5044,7 +5051,9 @@ void SV_ExtractFromUserinfo (client_t *cl, qboolean verbose) client_t *client; int dupc = 1; char newname[80], basic[80]; +#ifdef SVRANKING extern cvar_t rank_filename; +#endif int bottom = atoi(Info_ValueForKey(cl->userinfo, "bottomcolor")); @@ -5114,7 +5123,7 @@ void SV_ExtractFromUserinfo (client_t *cl, qboolean verbose) break; } - if (strncmp(newname, cl->name, sizeof(cl->namebuf)-1)) + if (!cl->drop && strncmp(newname, cl->name, sizeof(cl->namebuf)-1)) { if ((cl->penalties & BAN_MUTE) && *cl->name && verbose) //!verbose is a gamecode-forced update, where the gamecode is expected to know what its doing. { @@ -5150,7 +5159,7 @@ void SV_ExtractFromUserinfo (client_t *cl, qboolean verbose) } } - if (strncmp(val, cl->name, sizeof(cl->namebuf)-1)) + if (!cl->drop && strncmp(val, cl->name, sizeof(cl->namebuf)-1)) { if (*cl->name && cl->state >= cs_spawned && !cl->spectator && verbose) { @@ -5191,7 +5200,7 @@ void SV_ExtractFromUserinfo (client_t *cl, qboolean verbose) if (strlen(val)) cl->drate = atoi(val); else - cl->drate = cl->rate; //0 disables the downloading check + cl->drate = 0; //0 disables rate limiting while downloading #ifdef HEXEN2 val = Info_ValueForKey (cl->userinfo, "cl_playerclass"); diff --git a/engine/server/sv_send.c b/engine/server/sv_send.c index 7a7128dbf..e4eb32fd4 100644 --- a/engine/server/sv_send.c +++ b/engine/server/sv_send.c @@ -195,6 +195,16 @@ void Con_TPrintf (translation_t stringnum, ...) char msg[MAXPRINTMSG]; const char *fmt; + if (!Sys_IsMainThread()) + { //shouldn't be redirected anyway... + fmt = langtext(stringnum,svs.language); + va_start (argptr,stringnum); + vsnprintf (msg,sizeof(msg)-1, fmt,argptr); + va_end (argptr); + COM_AddWork(WG_MAIN, Con_PrintFromThread, NULL, Z_StrDup(msg), 0, 0); + return; + } + // add to redirected message if (sv_redirected) { @@ -225,6 +235,11 @@ Con_DPrintf A Con_Printf that only shows up if the "developer" cvar is set ================ */ +static void Con_DPrintFromThread (void *ctx, void *data, size_t a, size_t b) +{ + Con_DPrintf("%s", (char*)data); + BZ_Free(data); +} void Con_DPrintf (const char *fmt, ...) { va_list argptr; @@ -238,6 +253,12 @@ void Con_DPrintf (const char *fmt, ...) vsnprintf (msg,sizeof(msg)-1, fmt,argptr); va_end (argptr); + if (!Sys_IsMainThread()) + { + COM_AddWork(WG_MAIN, Con_DPrintFromThread, NULL, Z_StrDup(msg), 0, 0); + return; + } + // add to redirected message if (sv_redirected) { @@ -1392,7 +1413,7 @@ void SV_StartSound (int ent, vec3_t origin, float *velocity, int seenmask, int c } } - if (reliable || !sv_phs.value) // no PHS flag + if (reliable || !sv_phs.value || !attenuation) // no PHS flag use_phs = false; else use_phs = attenuation!=0; @@ -1598,11 +1619,11 @@ void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg) int i; int bits, items; edict_t *ent; + qboolean nqjunk = true; + int weaponmodelindex = 0; #endif client_t *split; int pnum=0; - int weaponmodelindex = 0; - qboolean nqjunk = true; // send the chokecount for r_netgraph if (ISQWCLIENT(client)) @@ -1685,7 +1706,7 @@ void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg) MSG_WriteFloat(msg, sv.world.physicstime); if (client->fteprotocolextensions2 & PEXT2_PREDINFO) - MSG_WriteLong(msg, client->last_sequence); + MSG_WriteShort(msg, client->last_sequence); // Con_Printf("%f\n", sv.world.physicstime); } @@ -1695,10 +1716,12 @@ void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg) return; +#ifdef NQPROT if (client->protocol == SCP_DARKPLACES6 || client->protocol == SCP_DARKPLACES7) nqjunk = false; else nqjunk = true; +#endif bits = 0; @@ -1810,7 +1833,7 @@ void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg) if (client->protocol == SCP_DARKPLACES6 || client->protocol == SCP_DARKPLACES7) MSG_WriteCoord(msg, ent->v->velocity[i]); else - MSG_WriteChar (msg, ent->v->velocity[i]/16); + MSG_WriteChar (msg, bound(-128, ent->v->velocity[i]/16, 127)); } } @@ -2481,7 +2504,7 @@ qboolean SV_SendClientDatagram (client_t *client) client->edict->v->goalentity = 0; } - if (client->protocol != SCP_FITZ666 && !client->netchan.fragmentsize) +// if (client->protocol != SCP_FITZ666 && !client->netchan.fragmentsize) msg.maxsize = MAX_DATAGRAM; if (sv.world.worldmodel && !client->controller) @@ -2525,11 +2548,11 @@ qboolean SV_SendClientDatagram (client_t *client) // copy the accumulated multicast datagram // for this client out to the message - if (client->datagram.overflowed || msg.cursize + client->datagram.cursize > msg.maxsize) - Con_Printf ("WARNING: datagram overflowed for %s\n", client->name); - else + if (!client->datagram.overflowed && msg.cursize + client->datagram.cursize <= msg.maxsize) + { SZ_Write (&msg, client->datagram.data, client->datagram.cursize); - SZ_Clear (&client->datagram); + SZ_Clear (&client->datagram); + } if (msg.overflowed) { @@ -2541,12 +2564,34 @@ qboolean SV_SendClientDatagram (client_t *client) // send the datagram sentbytes = Netchan_Transmit (&client->netchan, msg.cursize, buf, SV_RateForClient(client)); - if (ISQWCLIENT(client) || ISNQCLIENT(client)) { client_frame_t *frame = &client->frameunion.frames[client->netchan.outgoing_sequence & UPDATE_MASK]; frame->packetsizeout += sentbytes; } + + if (ISNQCLIENT(client) && (client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)) + { + if (!client->datagram.overflowed && client->datagram.cursize) + { + SZ_Clear (&msg); + SZ_Write (&msg, client->datagram.data, client->datagram.cursize); + SZ_Clear (&client->datagram); + + sentbytes = Netchan_Transmit (&client->netchan, msg.cursize, buf, SV_RateForClient(client)); + if (ISQWCLIENT(client) || ISNQCLIENT(client)) + { + client_frame_t *frame = &client->frameunion.frames[client->netchan.outgoing_sequence & UPDATE_MASK]; + frame->packetsizeout += sentbytes; + } + } + } + + if (client->datagram.cursize) + { + Con_Printf ("WARNING: datagram overflowed for %s\n", client->name); + SZ_Clear (&client->datagram); + } return true; } @@ -2893,7 +2938,9 @@ void SV_SendClientMessages (void) int i, j; client_t *c; int sentbytes, fnum; +#ifdef NQPROT float pt = sv.paused?realtime:sv.world.physicstime; +#endif #ifdef NEWSPEEDCHEATPROT static unsigned int lasttime; diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index 7afad7819..e2d07d4ed 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -482,8 +482,8 @@ void SVNQ_New_f (void) if (!host_client->pextknown && sv_listen_nq.ival != 1) //1 acts as a legacy mode, used for clients that can't cope with cmd before serverdata (either because they crash out or because they refuse to send reliables until after they got the first serverdata) { - if (!host_client->supportedprotocols) - { + if (!host_client->supportedprotocols && host_client->netchan.remote_address.type != NA_LOOPBACK) + { //don't override cl_loopbackprotocol's choice char *msg = "cmd protocols\n"; ClientReliableWrite_Begin (host_client, svc_stufftext, 2+strlen(msg)); ClientReliableWrite_String (host_client, msg); @@ -696,6 +696,10 @@ void SVNQ_New_f (void) MSG_WriteLong (&host_client->netchan.message, protmain); if (protmain == PROTOCOL_VERSION_RMQ) MSG_WriteLong (&host_client->netchan.message, protfl); + + if (protext2 & PEXT2_PREDINFO) + MSG_WriteString(&host_client->netchan.message, gamedir); + MSG_WriteByte (&host_client->netchan.message, (sv.allocated_client_slots>host_client->max_net_clients)?host_client->max_net_clients:sv.allocated_client_slots); if (!coop.value && deathmatch.value) @@ -4424,9 +4428,6 @@ void Cmd_God_f (void) void Cmd_Give_f (void) { - char *t; - int v; - #ifdef HLSERVER if (svs.gametype == GT_HALFLIFE) { @@ -4444,48 +4445,49 @@ void Cmd_Give_f (void) if (!svprogfuncs) return; - t = Cmd_Argv(1); - v = atoi (Cmd_Argv(2)); - SV_LogPlayer(host_client, "give cheat"); #ifdef QUAKESTATS - if (strlen(t) == 1 && (Cmd_Argc() == 3 || (*t>='0' && *t <= '9'))) { - switch (t[0]) + char *t = Cmd_Argv(1); + if (strlen(t) == 1 && (Cmd_Argc() == 3 || (*t>='0' && *t <= '9'))) { - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - sv_player->v->items = (int) sv_player->v->items | IT_SHOTGUN<< (t[0] - '2'); - break; + int v = atoi (Cmd_Argv(2)); + switch (t[0]) + { + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + sv_player->v->items = (int) sv_player->v->items | IT_SHOTGUN<< (t[0] - '2'); + break; - case 's': - sv_player->v->ammo_shells = v; - break; - case 'n': - sv_player->v->ammo_nails = v; - break; - case 'r': - sv_player->v->ammo_rockets = v; - break; - case 'h': - sv_player->v->health = v; - break; - case 'c': - sv_player->v->ammo_cells = v; - break; - default: - SV_TPrintToClient(host_client, PRINT_HIGH, "give: unknown item\n"); + case 's': + sv_player->v->ammo_shells = v; + break; + case 'n': + sv_player->v->ammo_nails = v; + break; + case 'r': + sv_player->v->ammo_rockets = v; + break; + case 'h': + sv_player->v->health = v; + break; + case 'c': + sv_player->v->ammo_cells = v; + break; + default: + SV_TPrintToClient(host_client, PRINT_HIGH, "give: unknown item\n"); + } + return; } } - else #endif - if (svprogfuncs->EvaluateDebugString) + if (svprogfuncs->EvaluateDebugString) { if (developer.value < 2 && host_client->netchan.remote_address.type != NA_LOOPBACK) //we don't want clients doing nasty things... like setting movetype 3123 { @@ -5486,7 +5488,7 @@ void SVNQ_PreSpawn_f (void) prot = " (dpp7)"; break; } - Con_Printf("Warning: %s cannot be enforced on player %s%s.\n", sv_mapcheck.name, host_client->name, prot); //as you can fake it in a client anyway, this is hardly a significant issue. + Con_DPrintf("Warning: %s cannot be enforced on player %s%s.\n", sv_mapcheck.name, host_client->name, prot); //as you can fake it in a client anyway, this is hardly a significant issue. } } @@ -7737,8 +7739,15 @@ void SVNQ_ReadClientMove (usercmd_t *move) frame = &host_client->frameunion.frames[host_client->netchan.incoming_acknowledged & UPDATE_MASK]; - if (host_client->protocol == SCP_DARKPLACES7 || (host_client->fteprotocolextensions2 & PEXT2_PREDINFO)) + if (host_client->protocol == SCP_DARKPLACES7) host_client->last_sequence = MSG_ReadLong (); + else if (host_client->fteprotocolextensions2 & PEXT2_PREDINFO) + { + int seq = (unsigned short)MSG_ReadShort (); + if (seq < (host_client->last_sequence&0xffff)) + host_client->last_sequence += 0x10000; //wrapped + host_client->last_sequence = (host_client->last_sequence&0xffff0000) | seq; + } else host_client->last_sequence = 0; cltime = MSG_ReadFloat (); @@ -7805,9 +7814,9 @@ void SVNQ_ReadClientMove (usercmd_t *move) SV_ReadPrydonCursor(); } - host_client->msecs -= move->msec; if (SV_RunFullQCMovement(host_client, move)) { + host_client->msecs -= move->msec; pr_global_struct->time = sv.world.physicstime; pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player); #ifdef VM_Q1 @@ -7946,6 +7955,8 @@ void SVNQ_ExecuteClientMessage (client_t *cl) case clcdp_ackframe: cl->delta_sequence = MSG_ReadLong(); + if (cl->delta_sequence == -1 && cl->pendingdeltabits) + cl->pendingdeltabits[0] = UF_REMOVE; SV_AckEntityFrame(cl, cl->delta_sequence); break; case clcdp_ackdownloaddata: diff --git a/engine/sw/sw_rast.c b/engine/sw/sw_rast.c index 0c50be965..df2f649d5 100644 --- a/engine/sw/sw_rast.c +++ b/engine/sw/sw_rast.c @@ -911,14 +911,9 @@ void SW_R_RenderView(void) AngleVectors (r_refdef.viewangles, vpn, vright, vup); VectorCopy (r_refdef.vieworg, r_origin); if (r_refdef.useperspective) - Matrix4x4_CM_Projection_Inf(r_refdef.m_projection, r_refdef.fov_x, r_refdef.fov_y, gl_mindist.value); + Matrix4x4_CM_Projection_Inf(r_refdef.m_projection, r_refdef.fov_x, r_refdef.fov_y, r_refdef.mindist); else - { - if (gl_maxdist.value>=1) - Matrix4x4_CM_Orthographic(r_refdef.m_projection, -r_refdef.fov_x/2, r_refdef.fov_x/2, -r_refdef.fov_y/2, r_refdef.fov_y/2, -gl_maxdist.value, gl_maxdist.value); - else - Matrix4x4_CM_Orthographic(r_refdef.m_projection, 0, r_refdef.vrect.width, 0, r_refdef.vrect.height, -9999, 9999); - } + Matrix4x4_CM_Orthographic(r_refdef.m_projection, -r_refdef.fov_x/2, r_refdef.fov_x/2, -r_refdef.fov_y/2, r_refdef.fov_y/2, r_refdef.mindist, r_refdef.maxdist>=1?r_refdef.maxdist:9999); VectorCopy(r_refdef.viewangles, newa); newa[0] = r_refdef.viewangles[0]; newa[1] = r_refdef.viewangles[1]; diff --git a/engine/vk/vk_backend.c b/engine/vk/vk_backend.c index d126b4cba..e4e38e01e 100644 --- a/engine/vk/vk_backend.c +++ b/engine/vk/vk_backend.c @@ -4267,6 +4267,8 @@ static void BE_RotateForEntity (const entity_t *e, const model_t *mod) for (i = 0; i < MAXRLIGHTMAPS ; i++) { + //FIXME: this is fucked + /* extern cvar_t gl_overbright; unsigned char s = shaderstate.curbatch?shaderstate.curbatch->lmlightstyle[i]:0; float sc; @@ -4298,6 +4300,8 @@ static void BE_RotateForEntity (const entity_t *e, const model_t *mod) else sc = shaderstate.identitylighting; sc *= d_lightstylevalue[s]/256.0f; + */ + float sc = 1; Vector4Set(cbe->e_lmscale[i], sc, sc, sc, 1); } @@ -5128,9 +5132,8 @@ static void TransformDir(vec3_t in, vec3_t planea[3], vec3_t viewa[3], vec3_t re void R_ObliqueNearClip(float *viewmat, mplane_t *wplane); void CL_DrawDebugPlane(float *normal, float dist, float r, float g, float b, qboolean enqueue); -void R_DrawPortal(batch_t *batch, batch_t **blist, batch_t *depthmasklist[2], int portaltype) +static void R_DrawPortal(batch_t *batch, batch_t **blist, batch_t *depthmasklist[2], int portaltype) { - extern cvar_t gl_mindist; entity_t *view; plane_t plane, oplane; float vmat[16]; @@ -5186,7 +5189,7 @@ void R_DrawPortal(batch_t *batch, batch_t **blist, batch_t *depthmasklist[2], in return; } //if we're behind it, then also don't draw anything. for our purposes, behind is when the entire near clipplane is behind. - if (DotProduct(r_refdef.vieworg, plane.normal)-plane.dist < -gl_mindist.value) + if (DotProduct(r_refdef.vieworg, plane.normal)-plane.dist < -r_refdef.mindist) return; TRACE(("R_DrawPortal: portal type %i\n", portaltype)); diff --git a/engine/vk/vk_init.c b/engine/vk/vk_init.c index a6d028fef..5e0860210 100644 --- a/engine/vk/vk_init.c +++ b/engine/vk/vk_init.c @@ -1099,8 +1099,6 @@ void VK_R_DeInit (void) void VK_SetupViewPortProjection(qboolean flipy) { - extern cvar_t gl_mindist; - float fov_x, fov_y; AngleVectors (r_refdef.viewangles, vpn, vright, vup); @@ -1131,7 +1129,10 @@ void VK_SetupViewPortProjection(qboolean flipy) Matrix4x4_CM_ModelViewMatrixFromAxis(r_refdef.m_view, vpn, vright, vup, r_refdef.vieworg); r_refdef.flipcull = 0; } - Matrix4x4_CM_Projection_Inf(r_refdef.m_projection, fov_x, fov_y, bound(0.1, gl_mindist.value, 4)); + if (r_refdef.maxdist) + Matrix4x4_CM_Projection_Far(r_refdef.m_projection, fov_x, fov_y, r_refdef.mindist, r_refdef.maxdist); + else + Matrix4x4_CM_Projection_Inf(r_refdef.m_projection, fov_x, fov_y, r_refdef.mindist); } void VK_Set2D(void) @@ -3052,6 +3053,11 @@ qboolean VK_Init(rendererstate_t *info, const char *sysextname, qboolean (*creat nvglsl = !!vk_loadglsl.ival; } free(ext); + + if (nvglsl) + Con_Printf("Using %s.\n", VK_NV_GLSL_SHADER_EXTENSION_NAME); + else if (vk_loadglsl.ival) + Con_Printf("unable to enable %s extension. direct use of glsl is not supported.\n", VK_NV_GLSL_SHADER_EXTENSION_NAME); } { const char *devextensions[8]; diff --git a/quakec/csaddon/src/cam.qc b/quakec/csaddon/src/cam.qc index e686f0fb4..833470b99 100644 --- a/quakec/csaddon/src/cam.qc +++ b/quakec/csaddon/src/cam.qc @@ -479,7 +479,7 @@ void(float attime) spline_overrides = setviewprop(VF_ANGLES, vectoangles(view - position)); }; -void() editor_spline_add = +void() editor_spline_addentities = { local int i; local float ctime; diff --git a/quakec/csaddon/src/csaddon.qc b/quakec/csaddon/src/csaddon.qc index 78bc0cefb..688e550b4 100644 --- a/quakec/csaddon/src/csaddon.qc +++ b/quakec/csaddon/src/csaddon.qc @@ -1,4 +1,4 @@ -float autocvar_ca_show; +var float autocvar_ca_show = 0; var float autocvar_ca_editormode = MODE_LIGHTEDIT; string autocvar_ca_colourtint; @@ -24,19 +24,27 @@ float editorrsd[editornames.length]; float(float key, float unic, vector mouse) editor_options_key = { if (key == K_MOUSE1) - if (mouse_y >= 16 && mouse_y < 16+8) + { + int sel = (mouse_y - 16) / 8; + if (sel == 0) { cvar_set("ca_show", "0"); return TRUE; } + else if (sel == 1) + { + localcmd("brushedit_binds\n"); + return TRUE; + } + } return FALSE; }; void(vector mouse) editor_options_overlay = { - if (mouse_y >= 16 && mouse_y < 16+8) - drawstring('0 16', "Close", '8 8', '0 0 1', 1, 0); - else - drawstring('0 16', "Close", '8 8', '1 1 1', 1, 0); + vector pos = '0 16'; + int sel = (mouse_y - pos_y) / 8; + drawstring(pos, "Close", '8 8', (sel==0)?'0 0 1':'1 1 1', 1, 0); pos_y += 8; + drawstring(pos, "Brush Editor Bindings", '8 8', (sel==1)?'0 0 1':'1 1 1', 1, 0); }; /*the renderscene builtin in the parent progs is redirected to here*/ @@ -169,17 +177,17 @@ void() wrap_renderscene = } if (autocvar_ca_editormode == MODE_LIGHTEDIT) - editor_lights_add(); + editor_lights_addentities(); else if (autocvar_ca_editormode == MODE_ENTSEDIT) - editor_ents_add(); + editor_ents_addentities(); #ifdef CAMQUAKE else if (autocvar_ca_editormode == MODE_SPLINEEDIT) - editor_spline_add(); + editor_spline_addentities(); #endif else if (autocvar_ca_editormode == MODE_TERRAINEDIT) - editor_terrain_add(curmousepos); + editor_terrain_addentities(curmousepos); else if (autocvar_ca_editormode == MODE_BRUSHEDIT) - editor_brushes_add(curmousepos); + editor_brushes_addentities(curmousepos); renderscene(); diff --git a/quakec/csaddon/src/csfixups.qc b/quakec/csaddon/src/csfixups.qc index 919be318b..0d838bb54 100644 --- a/quakec/csaddon/src/csfixups.qc +++ b/quakec/csaddon/src/csfixups.qc @@ -34,6 +34,7 @@ float mousedown; float shiftdown; float ctrldown; float altdown; +vector curmousepos; diff --git a/quakec/csaddon/src/csplat.qc b/quakec/csaddon/src/csplat.qc index d9a66bd02..6e95a6b60 100644 --- a/quakec/csaddon/src/csplat.qc +++ b/quakec/csaddon/src/csplat.qc @@ -1,5 +1,5 @@ /* -This file was automatically generated by FTE QuakeWorld v1.03 +This file was automatically generated by FTE QuakeWorld v1.05 This file can be regenerated by issuing the following command: pr_dumpplatform -FFTE -Fdefines -TCS -O csplat -Faccessors Available options: @@ -16,6 +16,7 @@ Available options: //#pragma flag enable logicops #pragma warning error Q101 /*too many parms*/ #pragma warning error Q105 /*too few parms*/ +#pragma warning error Q106 /*assignment to constant/lvalue*/ #pragma warning error Q208 /*system crc unknown*/ #pragma warning enable F301 /*non-utf-8 strings*/ #pragma warning enable F302 /*uninitialised locals*/ @@ -56,18 +57,24 @@ Available options: #define BX_COLOREDTEXT #define DP_CON_SET /* The 'set' console command exists, and can be used to create/set cvars. */ #define DP_CON_SETA /* The 'seta' console command exists, like the 'set' command, but also marks the cvar for archiving, allowing it to be written into the user's config. Use this command in your default.cfg file. */ +#define DP_EF_ADDITIVE #define DP_EF_BLUE #define DP_EF_FULLBRIGHT +#define DP_EF_NODEPTHTEST #define DP_EF_NODRAW +#define DP_EF_NOGUNBOB +#define DP_EF_NOSHADOW #define DP_EF_RED #define DP_ENT_CUSTOMCOLORMAP #define DP_ENT_EXTERIORMODELTOCLIENT +#define DP_ENT_TRAILEFFECTNUM /* self.traileffectnum=particleeffectnum("myeffectname"); can be used to attach a particle trail to the given server entity. This is equivelent to calling trailparticles each frame. */ #define DP_ENT_VIEWMODEL #define DP_GECKO_SUPPORT #define DP_GFX_SKINFILES #define DP_GFX_SKYBOX #define DP_HALFLIFE_MAP_CVAR #define DP_INPUTBUTTONS +#define DP_LIGHTSTYLE_STATICVALUE #define DP_LITSUPPORT #define DP_MD3_TAGSINFO #define DP_MONSTERWALK /* MOVETYPE_WALK is valid on non-player entities. Note that only players receive acceleration etc in line with none/bounce/fly/noclip movetypes on the player, thus you will have to provide your own accelerations (incluing gravity) yourself. */ @@ -94,7 +101,7 @@ Available options: #define DP_QC_MINMAXBOUND #define DP_QC_MULTIPLETEMPSTRINGS /* Superseded by DP_QC_UNLIMITEDTEMPSTRINGS. Functions that return a temporary string will not overwrite/destroy previous temporary strings until at least 16 strings are returned (or control returns to the engine). */ #define DP_QC_RANDOMVEC -#define DP_QC_RENDER_SCENE +#define DP_QC_RENDER_SCENE /* clearscene+addentity+setviewprop+renderscene+setmodel are available to menuqc. WARNING: DP advertises this extension without actually supporting it, FTE does actually support it. */ #define DP_QC_SINCOSSQRTPOW #define DP_QC_STRFTIME #define DP_QC_STRING_CASE_FUNCTIONS @@ -107,7 +114,7 @@ Available options: #define DP_QC_TRACE_MOVETYPE_HITMODEL #define DP_QC_TRACE_MOVETYPE_WORLDONLY #define DP_QC_TRACE_MOVETYPES -#define DP_QC_UNLIMITEDTEMPSTRINGS /* Supersedes DP_QC_MULTIPLETEMPSTRINGS, superseded by FTE_QC_PERSISTENTTEMPSTRINGS. All temp strings will be valid at least until the QCVM returns. */ +#define DP_QC_UNLIMITEDTEMPSTRINGS /* Supersedes DP_QC_MULTIPLETEMPSTRINGS, superseded by FTE_QC_PERSISTENTTEMPSTRINGS. Specifies that all temp strings will be valid at least until the QCVM returns. */ #define DP_QC_URI_ESCAPE #define DP_QC_URI_GET #define DP_QC_URI_POST @@ -123,22 +130,21 @@ Available options: #define DP_SOLIDCORPSE #define DP_SPRITE32 #define DP_SV_BOTCLIENT -#define DP_SV_CLIENTCOLORS -#define DP_SV_CLIENTNAME +#define DP_SV_CLIENTCOLORS /* Provided only for compatibility with DP. */ +#define DP_SV_CLIENTNAME /* Provided only for compatibility with DP. */ #define DP_SV_DRAWONLYTOCLIENT -#define DP_SV_DROPCLIENT +#define DP_SV_DROPCLIENT /* Equivelent to quakeworld's stuffcmd(self,"disconnect\n"); hack */ #define DP_SV_EFFECT #define DP_SV_EXTERIORMODELFORCLIENT #define DP_SV_NODRAWTOCLIENT -#define DP_SV_PLAYERPHYSICS +#define DP_SV_PLAYERPHYSICS /* Allows reworking parts of NQ player physics. USE AT OWN RISK - this necessitates NQ physics and is thus guarenteed to break prediction. */ #define DP_SV_POINTSOUND -#define DP_SV_PRECACHEANYTIME +#define DP_SV_PRECACHEANYTIME /* Specifies that the various precache builtins can be called at any time. WARNING: precaches are sent reliably while sound events, modelindexes, and particle events are not. This can mean sounds and particles might not work the first time around, or models may take a while to appear (after the reliables are received and the model is loaded from disk). Always attempt to precache a little in advance in order to reduce these issues (preferably at the start of the map...) */ #define DP_SV_SETCOLOR #define DP_SV_SPAWNFUNC_PREFIX #define DP_SV_WRITEPICTURE #define DP_SV_WRITEUNTERMINATEDSTRING #define DP_TE_BLOOD -#define DP_TE_BLOODSHOWER #define DP_TE_CUSTOMFLASH #define DP_TE_EXPLOSIONRGB #define DP_TE_PARTICLECUBE @@ -151,60 +157,86 @@ Available options: #define EXT_DIMENSION_PHYSICS #define EXT_DIMENSION_GHOST #define FRIK_FILE -#define FTE_CALLTIMEOFDAY -#define FTE_CSQC_ALTCONSOLES_WIP -#define FTE_CSQC_BASEFRAME /* Specifies that .basebone, .baseframe, .baselerpfrac, etc exist. These fields affect all bones in the entity's model with a lower index than the .basebone field, allowing you to give separate control to the legs of a skeletal model, without affecting the torso animations. */ -#define FTE_CSQC_HALFLIFE_MODELS +#define FTE_CALLTIMEOFDAY /* Replication of mvdsv functionality (call calltimeofday to cause 'timeofday' to be called, with arguments that can be saved off to a global). Generally strftime is simpler to use. */ +#define FTE_CSQC_ALTCONSOLES /* The engine tracks multiple consoles. These may or may not be directly visible to the user. */ +#define FTE_CSQC_BASEFRAME /* Specifies that .basebone, .baseframe2, .baselerpfrac, baseframe1time, etc exist in csqc. These fields affect all bones in the entity's model with a lower index than the .basebone field, allowing you to give separate control to the legs of a skeletal model, without affecting the torso animations. */ +#define FTE_QC_BASEFRAME /* Specifies that .basebone and .baseframe exist in ssqc. 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, from ssqc. */ #define FTE_CSQC_SERVERBROWSER -#define FTE_CSQC_SKELETONOBJECTS -#define FTE_CSQC_RENDERTARGETS_WIP /* VF_DESTCOLOUR etc exist and are supported */ -#define FTE_ENT_SKIN_CONTENTS +#define FTE_CSQC_SKELETONOBJECTS /* Provides container objects for skeletal bone data, which can be modified on a per bone basis if needed. This allows you to dynamically generate animations (or just blend them with greater customisation) instead of being limited to a single animation or two. */ +#define FTE_CSQC_RENDERTARGETS /* VF_RT_DESTCOLOUR exists and can be used to redirect any rendering to a texture instead of the screen. */ +#define FTE_ENT_SKIN_CONTENTS /* self.skin = CONTENTS_WATER; makes a brush entity into water. use -16 for a ladder. */ #define FTE_ENT_UNIQUESPAWNID #define FTE_EXTENDEDTEXTCODES -#define FTE_FORCESHADER -#define FTE_FORCEINFOKEY -#define FTE_GFX_QUAKE3SHADERS +#define FTE_FORCESHADER /* Allows csqc to override shaders on models with an explicitly named replacement. Also allows you to define shaders with a fallback if it does not exist on disk. */ +#define FTE_FORCEINFOKEY /* Provides an easy way to change a user's userinfo from the server. */ +#define FTE_GFX_QUAKE3SHADERS /* specifies that the engine has full support for vanilla quake3 shaders */ +#define FTE_GFX_REMAPSHADER /* With the raw power of stuffcmds, the r_remapshader console command is exposed! This mystical command can be used to remap any shader to another. Remapped shaders that specify $diffuse etc in some form will inherit the textures implied by the surface. */ #define FTE_ISBACKBUFFERED /* Allows you to check if a client has too many reliable messages pending. */ #define FTE_MEMALLOC /* Allows dynamically allocating memory. Use pointers to access this memory. Memory will not be saved into saved games. */ -#define FTE_MEDIA_AVI -#define FTE_MEDIA_CIN -#define FTE_MEDIA_ROQ -#define FTE_MULTIPROGS /* Multiple progs.dat files can be loaded inside the same qcvm. */ -#define FTE_MULTITHREADED +#define FTE_MEDIA_AVI /* playfilm command supports avi files. */ +#define FTE_MEDIA_CIN /* playfilm command supports q2 cin files. */ +#define FTE_MEDIA_ROQ /* playfilm command supports q3 roq files. */ +#define FTE_MULTIPROGS /* Multiple progs.dat files can be loaded inside the same qcvm. Insert new ones with addprogs inside the 'init' function, and use externvalue+externset to rewrite globals (and hook functions) to link them together. Note that the result is generally not very clean unless you carefully design for it beforehand. */ +#define FTE_MULTITHREADED /* Faux multithreading, allowing multiple contexts to run in sequence. */ +#define FTE_MVD_PLAYERSTATS /* In csqc, getplayerstat can be used to query any player's stats when playing back MVDs. isdemo will return 2 in this case. */ #define FTE_NPCCHAT -#define FTE_QC_CHECKCOMMAND +#define FTE_PART_SCRIPT /* Specifies that the r_particledesc cvar can be used to select a list of particle effects to load from particles/*.cfg, the format of which is documented elsewhere. */ +#define FTE_PART_NAMESPACES /* Specifies that the engine can use foo.bar to load effect foo from particle description bar. When used via ssqc, this should cause the client to download whatever effects as needed. */ +#define FTE_PART_NAMESPACE_EFFECTINFO /* Specifies that effectinfo.bar can load effects from effectinfo.txt for DP compatibility. */ +#define FTE_QC_CHECKCOMMAND /* Provides a way to test if a console command exists, and whether its a command/alias/cvar. Does not say anything about the expected meanings of any arguments or values. */ #define FTE_QC_CHECKPVS #define FTE_QC_HARDWARECURSORS /* setcursormode exists in both csqc+menuqc, and accepts additional arguments to specify a cursor image to use when this module has focus. If the image exceeds hardware limits, it will be emulated using regular draws - this at least still avoids conflicting cursors. */ -#define FTE_QC_HASHTABLES -#define FTE_QC_INTCONV +#define FTE_QC_HASHTABLES /* Provides efficient string-based lookups. */ +#define FTE_QC_INTCONV /* Provides string<>int conversions, including hex representations. */ #define FTE_QC_MATCHCLIENTNAME #define FTE_QC_PAUSED #define FTE_QC_PERSISTENTTEMPSTRINGS /* Supersedes DP_QC_MULTIPLETEMPSTRINGS. Temp strings are garbage collected automatically, and do not expire while they're still in use. This makes strzone redundant. */ #define FTE_QC_RAGDOLL_WIP -#define FTE_QC_SENDPACKET +#define FTE_QC_SENDPACKET /* Allows the use of out-of-band udp packets to/from other hosts. Includes the SV_ParseConnectionlessPacket event. */ #define FTE_QC_TRACETRIGGER -#define FTE_SOLID_LADDER -#define FTE_SQL -#define FTE_STRINGS +#define FTE_QUAKE2_CLIENT /* This engine is able to act as a quake2 client */ +#define FTE_QUAKE2_SERVER /* This engine is able to act as a quake2 server */ +#define FTE_QUAKE3_CLIENT /* This engine is able to act as a quake3 client */ +#define FTE_QUAKE3_SERVER /* This engine is able to act as a quake3 server */ +#define FTE_SOLID_LADDER /* Allows a simple trigger to remove effects of gravity (solid 20). obsolete. will prolly be removed at some point as it is not networked properly. Use FTE_ENT_SKIN_CONTENTS */ +#define FTE_SQL /* Provides sql* builtins which can be used for sql database access */ +#define FTE_SQL_SQLITE /* SQL functionality is able to utilise sqlite databases */ +#define FTE_STRINGS /* Extra builtins (and additional behaviour) to make string manipulation easier */ +#define FTE_SV_POINTPARTICLES /* Specifies that particleeffectnum, pointparticles, and trailparticles exist in ssqc as well as csqc. particleeffectnum acts as a precache, allowing ssqc values to be networked up with csqc for use. Use in combination with FTE_PART_SCRIPT+FTE_PART_NAMESPACES to use custom effects. This extension is functionally identical to the DP version, but avoids any misplaced assumptions about the format of the client's particle descriptions. */ #define FTE_SV_REENTER -#define FTE_TE_STANDARDEFFECTBUILTINS -#define KRIMZON_SV_PARSECLIENTCOMMAND +#define FTE_TE_STANDARDEFFECTBUILTINS /* Provides builtins to replace writebytes, with a QW compatible twist. */ +#define FTE_TERRAIN_MAP /* This engine supports .hmp files, as well as terrain embedded within bsp files. */ +#define FTE_RAW_MAP /* This engine supports directly loading .map files, as well as realtime editing of the various brushes. */ +#define KRIMZON_SV_PARSECLIENTCOMMAND /* SSQC's SV_ParseClientCommand function is able to handle client 'cmd' commands. The tokenizing parts also work in csqc. */ #define NEH_CMD_PLAY2 #define NEH_RESTOREGAME #define QSG_CVARSTRING #define QW_ENGINE -#define QWE_MVD_RECORD +#define QWE_MVD_RECORD /* You can use the easyrecord command to record MVD demos serverside. */ #define TEI_MD3_MODEL -#define ZQ_MOVETYPE_FLY -#define ZQ_MOVETYPE_NOCLIP -#define ZQ_MOVETYPE_NONE +#define TENEBRAE_GFX_DLIGHTS /* Allows ssqc to attach rtlights to entities with various special properties. */ +#define ZQ_MOVETYPE_FLY /* MOVETYPE_FLY works on players. */ +#define ZQ_MOVETYPE_NOCLIP /* MOVETYPE_NOCLIP works on players. */ +#define ZQ_MOVETYPE_NONE /* MOVETYPE_NONE works on players. */ #define ZQ_VWEP -#define ZQ_QC_STRINGS +#define ZQ_QC_STRINGS /* The strings-only subset of FRIK_FILE is supported. */ + +#define _ACCESSORS; +#ifdef _ACCESSORS accessor strbuf : float; accessor searchhandle : float; accessor hashtable : float; accessor infostring : string; accessor filestream : float; +accessor filestream : float; +#else +#define strbuf float +#define searchhandle float +#define hashtable float +#define infostring string +#define filestream float +#endif + entity self; /* The magic me */ entity other; /* Valid in touch functions, this is the entity that we touched. */ entity world; /* The null entity. Hurrah. Readonly after map spawn time. */ @@ -273,8 +305,15 @@ vector input_cursor_screen; vector input_cursor_trace_start; vector input_cursor_trace_endpos; float input_cursor_trace_entnum; +int trace_endcontents; +int trace_surfaceflags; int trace_brush_id; int trace_brush_faceid; +int trace_surface_id; /* 1-based. 0 if not known. */ +int trace_bone_id; /* 1-based. 0 if not known. typically needs MOVE_HITMODEL. */ +int trace_triangle_id; /* 1-based. 0 if not known. */ +vector global_gravitydir = '0 0 -1'; /* The direction gravity should act in if not otherwise specified per entity. */ +int serverid; /* The unique id of this server within the server cluster. */ .vector punchangle; .float gravity; .float hull; /* Overrides the hull used by the entity for walkmove/movetogoal and not traceline/tracebox. */ @@ -283,11 +322,13 @@ int trace_brush_faceid; .void(float old, float new) contentstransition; /* This function is called when the entity moves between water and air. If specified, default splash sounds will be disabled allowing you to provide your own. */ .float dimension_solid; /* This is the bitmask of dimensions which the entity is solid within. */ .float dimension_hit; /* This is the bitmask of dimensions which the entity will be blocked by. If other.dimension_solid & self.dimension_hit, our traces will impact and not proceed. If its false, the traces will NOT impact, allowing self to pass straight through. */ -.float hitcontentsmask; +.int hitcontentsmaski; /* Traces performed for this entity will impact against surfaces that match this contents mask. */ .float scale; /* Multiplier that resizes the entity. 1 is normal sized, 2 is double sized. scale 0 is remapped to 1. In SSQC, this is limited to 1/16th precision, with a maximum just shy of 16. */ .float fatness; /* How many QuakeUnits to push the entity's verticies along their normals by. */ .float alpha; /* The transparency of the entity. 1 means opaque, 0.0001 means virtually invisible. 0 is remapped to 1, for compatibility. */ .float modelflags; /* Used to override the flags set in the entity's model. Should be set according to the MF_ constants. Use effects|=EF_NOMODELFLAGS to ignore the model's flags completely. The traileffectnum field is more versatile. */ +.float 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. */ +.float baseframe; /* See basebone */ .void() customphysics; /* Called once each physics frame, overriding the entity's .movetype field and associated logic. You'll probably want to use tracebox to move it through the world. Be sure to call .think as appropriate. */ .entity tag_entity; .float tag_index; @@ -305,24 +346,19 @@ int trace_brush_faceid; .float bouncestop; .float idealpitch; .float pitch_speed; +.vector color; /* This affects the colour of realtime lights that were enabled via the pflags field. */ +.float light_lev; /* This is the radius of an entity's light. This is not normally used by the engine, but is used for realtime lights (ones that are enabled with the pflags field). */ +.float style; /* Used by the light util to decide how an entity's light should animate. On an entity with pflags set, this also affects realtime lights. */ +.float pflags; /* Realtime lighting flags */ .float frame3; /* Some people just don't understand how to use framegroups... */ .float frame4; .float lerpfrac3; .float lerpfrac4; .float forceshader; /* Contains a shader handle used to replace all surfaces upon the entity. */ -.float baseframe; /* See basebone */ .float baseframe2; /* See basebone */ .float baseframe1time; /* See basebone */ .float baseframe2time; /* See basebone */ .float baselerpfrac; /* See basebone */ -.float 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. */ -.float bonecontrol1; /* Halflife model format bone controller. On player models, this typically affects the spine's yaw. */ -.float bonecontrol2; /* Halflife model format bone controller. On player models, this typically affects the spine's yaw. */ -.float bonecontrol3; /* Halflife model format bone controller. On player models, this typically affects the spine's yaw. */ -.float bonecontrol4; /* Halflife model format bone controller. On player models, this typically affects the spine's yaw. */ -.float bonecontrol5; /* Halflife model format bone controller. This typically affects the mouth. */ -.float subblendfrac; /* Weird animation value specific to halflife models. On player models, this typically affects the spine's pitch. */ -.float basesubblendfrac; /* See basebone */ void(float reqid, float responsecode, string resourcebody) URI_Get_Callback; /* Called as an eventual result of the uri_get builtin. */ void(float apilevel, string enginename, float engineversion) CSQC_Init; /* Called at startup. enginename and engineversion are arbitary hints and can take any form. enginename should be consistant between revisions, but this cannot truely be relied upon. */ void() CSQC_WorldLoaded; /* Called after model+sound precaches have been executed. Gives a chance for the qc to read the entity lump from the bsp. */ @@ -338,7 +374,7 @@ void(string printmsg, float printlvl) CSQC_Parse_Print; /* Gives the CSQC a chan void() CSQC_Parse_Event; /* Called when the client receives an SVC_CGAMEPACKET. The csqc should read the data or call the error builtin if it does not recognise the message. */ float(float evtype, float scanx, float chary, float devid) CSQC_InputEvent; /* Called whenever a key is pressed, the mouse is moved, etc. evtype will be one of the IE_* constants. The other arguments vary depending on the evtype. Key presses are not guarenteed to have both scan and unichar values set at the same time. */ __used void() CSQC_Input_Frame; /* Called just before each time clientcommandframe is updated. You can edit the input_* globals in order to apply your own player inputs within csqc, which may allow you a convienient way to pass certain info to ssqc. */ -void(void) CSQC_RendererRestarted; /* Called by the engine after the video was restarted. This serves to notify the CSQC that any render targets that it may have cached were purged, and will need to be regenerated. */ +void(string rendererdescription) CSQC_RendererRestarted; /* Called by the engine after the video was restarted. This serves to notify the CSQC that any render targets that it may have cached were purged, and will need to be regenerated. */ float(string cmd) CSQC_ConsoleCommand; /* Called if the user uses any console command registed via registercommand. */ float(string text, string info) CSQC_ConsoleLink; /* Called if the user clicks a ^[text\infokey\infovalue^] link. Use infoget to read/check each supported key. Return true if you wish the engine to not attempt to handle the link itself. */ void(float isnew) CSQC_Ent_Update; @@ -371,15 +407,17 @@ float drawfont; /* Allows you to choose exactly which font is to be used to draw #define MOVETYPE_BOUNCE 10 #define MOVETYPE_BOUNCEMISSILE 11 #define MOVETYPE_FOLLOW 12 +#define MOVETYPE_6DOF 30 /* A glorified MOVETYPE_FLY. Players using this movetype will get some flightsim-like physics, with fully independant rotations (order-dependant transforms). */ #define MOVETYPE_WALLWALK 31 /* Players using this movetype will be able to orient themselves to walls, and then run up them. */ #define MOVETYPE_PHYSICS 32 /* Enable the use of ODE physics upon this entity. */ #define SOLID_NOT 0 #define SOLID_TRIGGER 1 #define SOLID_BBOX 2 #define SOLID_SLIDEBOX 3 -#define SOLID_BSP 4 -#define SOLID_CORPSE 5 +#define SOLID_BSP 4 /* Does not collide against other SOLID_BSP entities. Normally paired with MOVETYPE_PUSH. */ +#define SOLID_CORPSE 5 /* Non-solid to SOLID_SLIDEBOX or other SOLID_CORPSE entities. For hitscan weapons to hit corpses, change the player's .solid value to SOLID_BBOX or so, perform the traceline, then revert the player's .solid value. */ #define SOLID_LADDER 20 /* Obsolete and may be removed at some point. Use skin=CONTENT_LADDER and solid_bsp or solid_trigger instead. */ +#define SOLID_PORTAL 21 /* CSG subtraction volume combined with entity transformations on impact. */ #define SOLID_PHYSICS_BOX 32 #define SOLID_PHYSICS_SPHERE 33 #define SOLID_PHYSICS_CAPSULE 34 @@ -421,6 +459,25 @@ float drawfont; /* Allows you to choose exactly which font is to be used to draw #define GE_MAXS 13 /* Valid for getentity. Guesses the entity's .max vector. */ #define GE_ABSMIN 14 /* Valid for getentity. Guesses the entity's .absmin vector. */ #define GE_ABSMAX 15 /* Valid for getentity. Guesses the entity's .absmax vector. */ +#define GE_MODELINDEX 200 /* Valid for getentity. Guesses the entity's .modelindex float. */ +#define GE_MODELINDEX2 201 /* Valid for getentity. Guesses the entity's .vw_index float. */ +#define GE_EFFECTS 202 /* Valid for getentity. Guesses the entity's .effects float. */ +#define GE_FRAME 203 /* Valid for getentity. Guesses the entity's .frame float. */ +#define GE_ANGLES 204 /* Valid for getentity. Guesses the entity's .angles vector. */ +#define GE_FATNESS 205 /* Valid for getentity. Guesses the entity's .fatness float. */ +#define GE_DRAWFLAGS 206 /* Valid for getentity. Guesses the entity's .drawflags float. */ +#define GE_ABSLIGHT 207 /* Valid for getentity. Guesses the entity's .abslight float. */ +#define GE_GLOWMOD 208 /* Valid for getentity. Guesses the entity's .glowmod vector. */ +#define GE_GLOWSIZE 209 /* Valid for getentity. Guesses the entity's .glowsize float. */ +#define GE_GLOWCOLOUR 210 /* Valid for getentity. Guesses the entity's .glowcolor float. */ +#define GE_RTSTYLE 211 /* Valid for getentity. Guesses the entity's .style float. */ +#define GE_RTPFLAGS 212 /* Valid for getentity. Guesses the entity's .pflags float. */ +#define GE_RTCOLOUR 213 /* Valid for getentity. Guesses the entity's .color vector. */ +#define GE_RTRADIUS 214 /* Valid for getentity. Guesses the entity's .light_lev float. */ +#define GE_TAGENTITY 215 /* Valid for getentity. Guesses the entity's .tag_entity float. */ +#define GE_TAGINDEX 216 /* Valid for getentity. Guesses the entity's .tag_index float. */ +#define GE_GRAVITYDIR 217 /* Valid for getentity. Guesses the entity's .gravitydir vector. */ +#define GE_TRAILEFFECTNUM 218 /* Valid for getentity. Guesses the entity's .traileffectnum float. */ #define CONTENT_EMPTY -1 #define CONTENT_SOLID -2 #define CONTENT_WATER -3 @@ -428,20 +485,38 @@ float drawfont; /* Allows you to choose exactly which font is to be used to draw #define CONTENT_LAVA -5 #define CONTENT_SKY -6 #define CONTENT_LADDER -16 /* If this value is assigned to a solid_bsp's .skin field, the entity will become a ladder volume. */ +const int CONTENTBIT_NONE = 0x00000000; +const int CONTENTBIT_SOLID = 0x00000001; +const int CONTENTBIT_LAVA = 0x00000008; +const int CONTENTBIT_SLIME = 0x00000010; +const int CONTENTBIT_WATER = 0x00000020; +const int CONTENTBIT_FTELADDER = 0x00004000; +const int CONTENTBIT_PLAYERCLIP = 0x00010000; +const int CONTENTBIT_MONSTERCLIP = 0x00020000; +const int CONTENTBIT_BODY = 0x02000000; +const int CONTENTBIT_CORPSE = 0x04000000; +const int CONTENTBIT_Q2LADDER = 0x20000000; /* Content bit specific to q2bsp */ +const int CONTENTBIT_SKY = 0x80000000i; +const int CONTENTBITS_POINTSOLID = CONTENTBIT_SOLID|0x00000002|CONTENTBIT_BODY; /* Bits that traceline would normally consider solid */ +const int CONTENTBITS_BOXSOLID = CONTENTBIT_SOLID|0x00000002|CONTENTBIT_BODY|CONTENTBIT_PLAYERCLIP; /* Bits that tracebox would normally consider solid */ +const int CONTENTBITS_FLUID = CONTENTBIT_WATER|CONTENTBIT_SLIME|CONTENTBIT_LAVA|CONTENTBIT_SKY; #define CHAN_AUTO 0 /* The automatic channel, play as many sounds on this channel as you want, and they'll all play, however the other channels will replace each other. */ #define CHAN_WEAPON 1 #define CHAN_VOICE 2 #define CHAN_ITEM 3 #define CHAN_BODY 4 -#define SOUNDFLAG_ABSVOLUME 16 -#define SOUNDFLAG_FORCELOOP 2 -#define SOUNDFLAG_NOSPACIALISE 4 +#define SOUNDFLAG_ABSVOLUME 16 /* The sample's volume is not scaled by the volume cvar. Use with caution */ +#define SOUNDFLAG_FORCELOOP 2 /* The sound will restart once it reaches the end of the sample. */ +#define SOUNDFLAG_NOSPACIALISE 4 /* The different audio channels are played at the same volume regardless of which way the player is facing, without needing to use 0 attenuation. */ +#define SOUNDFLAG_NOREVERB 32 /* Disables the use of underwater/reverb effects on this sound effect. */ +#define SOUNDFLAG_FOLLOW 64 /* The sound's origin will updated to follow the emitting entity. */ #define ATTN_NONE 0 /* Sounds with this attenuation can be heard throughout the map */ #define ATTN_NORM 1 /* Standard attenuation */ #define ATTN_IDLE 2 /* Extra attenuation so that sounds don't travel too far. */ #define ATTN_STATIC 3 /* Even more attenuation to avoid torches drowing out everything else throughout the map. */ #define INFOKEY_P_PING "ping" /* The player's ping time, in milliseconds. */ #define INFOKEY_P_NAME "name" /* The player's name. */ +#define INFOKEY_P_SPECTATOR "*spectator" /* Whether the player is a spectator or not. */ #define INFOKEY_P_TOPCOLOR "topcolor" /* The player's upper/shirt colour (palette index). */ #define INFOKEY_P_BOTTOMCOLOR "bottomcolor" /* The player's lower/pants/trouser colour (palette index). */ #define INFOKEY_P_TOPCOLOR_RGB "topcolor_rgb" /* The player's upper/shirt colour as an rgb value in a format usable with stov. */ @@ -484,6 +559,7 @@ If the current file info is omitted, then we are waiting for a download to start #define MOVE_TRIGGERS 16 /* This trace type will impact only triggers. It will ignore non-solid entities. */ #define MOVE_EVERYTHING 32 /* This type of trace will hit solids and triggers alike. Even non-solid entities. */ #define MOVE_ENTCHAIN 128 /* Returns a list of entities impacted via the trace_ent.chain field */ +#define MOVE_OTHERONLY 256 /* Traces that use this trace type will collide against *only* the entity specified via the 'other' global, and will ignore all owner/solid_not/dimension etc rules, they will still adhere to contents and bsp/bbox rules though. */ #define RESTYPE_MODEL 0 /* RESTYPE_* constants are used as arguments with the resourcestatus builtin. */ #define RESTYPE_SOUND 1 /* precache_sound */ #define RESTYPE_PARTICLE 2 /* particleeffectnum */ @@ -499,20 +575,22 @@ If the current file info is omitted, then we are waiting for a download to start #define EF_MUZZLEFLASH 2 #define EF_BRIGHTLIGHT 4 #define EF_DIMLIGHT 8 -#define EF_ADDITIVE 32 -#define EF_BLUE 64 -#define EF_RED 128 -#define EF_GREEN 262144 -#define EF_FULLBRIGHT 512 -#define EF_NODEPTHTEST 8192 +#define EF_NODRAW 16 +#define EF_ADDITIVE 32 /* The entity will be drawn with an additive blend. */ +#define EF_BLUE 64 /* A blue glow */ +#define EF_RED 128 /* A red glow */ +#define EF_GREEN 262144 /* A green glow */ +#define EF_FULLBRIGHT 512 /* This entity will ignore lighting */ +#define EF_NOSHADOW 4096 /* This entity will not cast shadows */ +#define EF_NODEPTHTEST 8192 /* This entity will be drawn over the top of other things that are closer. */ #define MF_ROCKET 1 #define MF_GRENADE 2 -#define MF_GIB 4 +#define MF_GIB 4 /* Regular blood trail */ #define MF_ROTATE 8 -#define MF_TRACER 16 -#define MF_ZOMGIB 32 -#define MF_TRACER2 64 -#define MF_TRACER3 128 +#define MF_TRACER 16 /* AKA: green scrag trail */ +#define MF_ZOMGIB 32 /* Dark blood trail */ +#define MF_TRACER2 64 /* AKA: hellknight projectile trail */ +#define MF_TRACER3 128 /* AKA: purple vore trail */ #define PFLAGS_NOSHADOW 1 /* Associated RT lights attached will not cast shadows, making them significantly faster to draw. */ #define PFLAGS_CORONA 2 /* Enables support of coronas on the associated rtlights. */ #define EV_STRING 1 @@ -567,11 +645,13 @@ hashtable gamestate; /* Special hash table index for hash_add and hash_get. Entr #define VF_DRAWWORLD 19 /* boolean. If set to 1, the engine will draw the world and static/persistant rtlights. If 0, the world will be skipped and everything will be fullbright. */ #define VF_DRAWENGINESBAR 20 /* boolean. If set to 1, the sbar will be drawn, and viewsize will be honoured automatically. */ #define VF_DRAWCROSSHAIR 21 /* boolean. If set to 1, the engine will draw its default crosshair. */ +#define VF_MINDIST 23 /* The distance of the near clip plane from the view position. Should generally not be <=0, as this would introduce NANs. */ +#define VF_MAXDIST 24 /* The distance of the far clip plane from the view position. If 0, will be considered infinite. */ #define VF_CL_VIEWANGLES 33 #define VF_CL_VIEWANGLES_X 34 #define VF_CL_VIEWANGLES_Y 35 #define VF_CL_VIEWANGLES_Z 36 -#define VF_PERSPECTIVE 200 /* 1: regular rendering. Fov specifies the angle. 0: isometric-style. Fov specifies the number of Quake Units each side of the viewport. */ +#define VF_PERSPECTIVE 200 /* 1: regular rendering. Fov specifies the angle. 0: isometric-style. Fov specifies the number of Quake Units each side of the viewport, and mindist restrictions are removed, pvs culling should be disabled. */ #define VF_LPLAYER VF_ACTIVESEAT const float VF_ACTIVESEAT = 202; /* The 'seat' number, used when running splitscreen. */ #define VF_AFOV 203 /* Aproximate fov. Matches the 'fov' cvar. The engine handles the aspect ratio for you. */ @@ -585,6 +665,7 @@ Note that any rendertarget textures may be destroyed on video mode changes or so #define VF_RT_SOURCECOLOUR 209 /* The texture name to use with shaders that specify a $sourcecolour map. */ #define VF_RT_DEPTH 210 /* The texture name to use as a depth buffer. Also used for shaders that specify $sourcedepth. 1-based. Additional arguments are: format (16bit=4,24bit=5,32bit=6), sizexy. */ #define VF_RT_RIPPLE 211 /* The texture name to use as a ripplemap (target for shaders with 'sort ripple'). Also used for shaders that specify $ripplemap. 1-based. Additional arguments are: format, sizexy. */ +#define VF_ENVMAP 220 /* The cubemap name to use as a fallback for $reflectcube, if a shader was unable to load one. Note that this doesn't automatically change shader permutations or anything. */ #define RF_VIEWMODEL 1 /* Specifies that the entity is a view model, and that its origin is relative to the current view position. These entities are also subject to viewweapon bob. */ #define RF_EXTERNALMODEL 2 /* Specifies that this entity should be displayed in mirrors (and may still cast shadows), but will not otherwise be visible. */ #define RF_DEPTHHACK 4 /* Hacks the depth values such that the entity uses depth values as if it were closer to the screen. This is useful when combined with viewmodels to avoid weapons poking in to walls. */ @@ -599,6 +680,7 @@ Note that any rendertarget textures may be destroyed on video mode changes or so #define IE_ACCELEROMETER 4 #define IE_FOCUS 5 /* Specifies that input focus was given. parama says mouse focus, paramb says keyboard focus. If either are -1, then it is unchanged. */ #define IE_JOYAXIS 6 /* Specifies that what value a joystick/controller axis currently specifies. x=axis, y=value. Will be called multiple times, once for each axis of each active controller. */ +#define IE_GYROSCOPE 7 #define FILE_READ 0 /* The file may be read via fgets to read a single line at a time. */ #define FILE_APPEND 1 /* Like FILE_WRITE, but writing starts at the end of the file. */ #define FILE_WRITE 2 /* fputs will be used to write to the file. */ @@ -724,6 +806,7 @@ void(entity e) remove = #15; /* void(vector v1, vector v2, float flags, entity ent) traceline = #16; /* Traces an infinitely thin line through the world from v1 towards v2. Will not collide with ent, ent.owner, or any entity who's owner field refers to ent. + The passed entity will also be used to determine whether to use a capsule trace, the contents that the trace should impact, and a couple of other extra fields that define the trace. There are no side effects beyond the trace_* globals being written. flags&MOVE_NOMONSTERS will not impact on non-bsp entities. flags&MOVE_MISSILE will impact with increased size. @@ -866,26 +949,45 @@ float(float minimum, float val, float maximum) bound = #96; /* Part of DP_QC_MIN Returns val, unless minimum is higher, or maximum is less. */ float(float value, float exp) pow = #97; /* Part of DP_QC_SINCOSSQRTPOW*/ -float(float v, optional float base) logarithm = #0/*:logarithm*/; /* +float(float v, optional float base) logarithm = #0:logarithm; /* Determines the logarithm of the input value according to the specified base. This can be used to calculate how much something was shifted by. */ -entity(entity start, .float fld, float match) findfloat = #98; /* Part of DP_QC_FINDFLOAT - Equivelent to the find builtin, but instead of comparing strings, this builtin compares floats. This builtin requires multiple calls in order to scan all entities - set start to the previous call's return value. - world is returned when there are no more entities. */ - -entity(entity start, .entity fld, entity match) findentity = #98; /* - Equivelent to the find builtin, but instead of comparing strings, this builtin compares entities. This builtin requires multiple calls in order to scan all entities - set start to the previous call's return value. +#define findentity findfloat +entity(entity start, .__variant fld, __variant match) findfloat = #98; /* Part of DP_QC_FINDFLOAT + Equivelent to the find builtin, but instead of comparing strings contents, this builtin compares the raw values. This builtin requires multiple calls in order to scan all entities - set start to the previous call's return value. world is returned when there are no more entities. */ float(string extname) checkextension = #99; /* Checks for an extension by its name (eg: checkextension("FRIK_FILE") says that its okay to go ahead and use strcat). Use cvar("pr_checkextension") to see if this builtin exists. */ +float(__variant funcref) checkbuiltin = #0:checkbuiltin; /* + Checks to see if the specified builtin is supported/mapped. This is intended as a way to check for #0 functions, allowing for simple single-builtin functions. */ + float(float value) anglemod = #102; -filestream(string filename, float mode, optional float mmapminsize) fopen = #110; /* Part of FRIK_FILE*/ +filestream(string filename, float mode, optional float mmapminsize) fopen = #110; /* Part of FRIK_FILE + Opens a file, typically prefixed with "data/", for either read or write access. */ + void(filestream fhandle) fclose = #111; /* Part of FRIK_FILE*/ -string(filestream fhandle) fgets = #112; /* Part of FRIK_FILE*/ -void(filestream fhandle, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7) fputs = #113; /* Part of FRIK_FILE*/ +string(filestream fhandle) fgets = #112; /* Part of FRIK_FILE + Reads a single line out of the file. The new line character is not returned as part of the string. Returns the null string on EOF (use if not(string) to easily test for this, which distinguishes it from the empty string which is returned if the line being read is blank */ + +void(filestream fhandle, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7) fputs = #113; /* Part of FRIK_FILE + Writes the given string(s) into the file. For compatibility with fgets, you should ensure that the string is terminated with a \n - this will not otherwise be done for you. It is up to the engine whether dos or unix line endings are actually written. */ + +int(filestream fhandle, void *ptr, int size) fread = #0:fread; /* + Reads binary data out of the file. Returns truncated lengths if the read exceeds the length of the file. */ + +int(filestream fhandle, void *ptr, int size) fwrite = #0:fwrite; /* + Writes binary data out of the file. */ + +#define ftell fseek //c compat +int(filestream fhandle, optional int newoffset) fseek = #0:fseek; /* + Changes the current position of the file, if specified. Returns prior position, in bytes. */ + +int(filestream fhandle, optional int newsize) fsize = #0:fsize; /* + Reports the total size of the file, in bytes. Can also be used to truncate/extend the file */ + float(string s) strlen = #114; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/ string(string s1, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7, optional string s8) strcat = #115; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/ string(string s, float start, float length) substring = #116; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/ @@ -896,6 +998,9 @@ string(string s, ...) strzone = #118; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_S void(string s) strunzone = #119; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS Destroys a string that was allocated by strunzone. Further references to the string MAY crash the game. In FTE, this function became redundant and now does nothing. */ +void(string soundname, optional float channel, optional float volume) localsound = #177; /* + Plays a sound... locally... probably best not to call this from ssqc. Also disables reverb. */ + float(string modelname, optional float queryonly) getmodelindex = #200; /* Acts as an alternative to precache_model(foo);setmodel(bar, foo); return bar.modelindex; If queryonly is set and the model was not previously precached, the builtin will return 0 without needlessly precaching the model. */ @@ -980,7 +1085,7 @@ float(string s1, string s2, float len, optional float s1ofs, optional float s2of Compares up to 'len' chars in the two strings without case sensitivity. s1ofs allows you to treat s2 as a substring to compare against, or should be 0. Returns 0 if they are equal. The sign of the return value may be significant, but should not be depended upon. */ -string(string s) strtrim = #0/*:strtrim*/; /* +string(string s) strtrim = #0:strtrim; /* Trims the whitespace from the start+end of the string. */ void() calltimeofday = #231; /* Part of FTE_CALLTIMEOFDAY @@ -1011,10 +1116,10 @@ int(string) stoh = #261; /* Part of FTE_QC_INTCONV string(int) htos = #262; /* Part of FTE_QC_INTCONV Formats an integer as a base16 string, with leading 0s and no prefix. Always returns 8 characters. */ -int(float) ftoi = #0/*:ftoi*/; /* +int(float) ftoi = #0:ftoi; /* Converts the given float into a true integer without depending on extended qcvm instructions. */ -float(int) itof = #0/*:itof*/; /* +float(int) itof = #0:itof; /* Converts the given true integer into a float without depending on extended qcvm instructions. */ float(float modlindex, optional float useabstransforms) skel_create = #263; /* Part of FTE_CSQC_SKELETONOBJECTS @@ -1065,6 +1170,10 @@ float(float modidx, string framename) frameforname = #276; /* Part of FTE_CSQC_S float(float modidx, float framenum) frameduration = #277; /* Part of FTE_CSQC_SKELETONOBJECTS Retrieves the duration (in seconds) of the specified framegroup. */ +#define dotproduct(v1,v2) ((vector)(v1)*(vector)(v2)) +vector(vector v1, vector v2) crossproduct = #0:crossproduct; /* + Small helper function to calculate the crossproduct of two vectors. */ + void(float action, optional vector pos, optional float radius, optional float quant, ...) terrain_edit = #278; /* Realtime terrain editing. Actions are the TEREDIT_ constants. */ @@ -1078,25 +1187,25 @@ typedef struct vector tdir; float tbias; } brushface_t; -int(float modelidx, int brushid, brushface_t *out_faces, int maxfaces, int *out_contents) brush_get = #0/*:brush_get*/; /* +int(float modelidx, int brushid, brushface_t *out_faces, int maxfaces, int *out_contents) brush_get = #0:brush_get; /* Queries a brush's information. You must pre-allocate the face array for the builtin to write to. Return value is the number of faces retrieved, 0 on error. */ -int(float modelidx, brushface_t *in_faces, int numfaces, int contents) brush_create = #0/*:brush_create*/; /* +int(float modelidx, brushface_t *in_faces, int numfaces, int contents, optional int brushid) brush_create = #0:brush_create; /* Inserts a new brush into the model. Return value is the new brush's id. */ -void(float modelidx, int brushid) brush_delete = #0/*:brush_delete*/; /* +void(float modelidx, int brushid) brush_delete = #0:brush_delete; /* Destroys the specified brush. */ -float(float modelid, int brushid, int faceid, float selectedstate) brush_selected = #0/*:brush_selected*/; /* +float(float modelid, int brushid, int faceid, float selectedstate) brush_selected = #0:brush_selected; /* Allows you to easily set transient visual properties of a brush. returns old value. selectedstate=-1 changes nothing (called for its return value). */ -int(float modelid, int brushid, int faceid, vector *points, int maxpoints) brush_getfacepoints = #0/*:brush_getfacepoints*/; /* +int(float modelid, int brushid, int faceid, vector *points, int maxpoints) brush_getfacepoints = #0:brush_getfacepoints; /* Returns the list of verticies surrounding the given face. If face is 0, returns the center of the brush (if space for 1 point) or the mins+maxs (if space for 2 points). */ -int(int faceid, brushface_t *in_faces, int numfaces, vector *points, int maxpoints) brush_calcfacepoints = #0/*:brush_calcfacepoints*/; /* +int(int faceid, brushface_t *in_faces, int numfaces, vector *points, int maxpoints) brush_calcfacepoints = #0:brush_calcfacepoints; /* Determines the points of the specified face, if the specified brush were to actually be created. */ -int(float modelid, vector *planes, float *dists, int numplanes, int *out_brushes, int *out_faces, int maxresults) brush_findinvolume = #0/*:brush_findinvolume*/; /* +int(float modelid, vector *planes, float *dists, int numplanes, int *out_brushes, int *out_faces, int maxresults) brush_findinvolume = #0:brush_findinvolume; /* Allows you to easily obtain a list of brushes+faces within the given bounding region. If out_faces is not null, the same brush might be listed twice. */ void(optional entity ent, optional vector neworigin) touchtriggers = #279; /* @@ -1157,13 +1266,16 @@ void() clearscene = #300; /* void(float mask) addentities = #301; /* Walks through all entities effectively doing this: - if (ent.drawmask&mask){ ent.predaw(); if (wasremoved(ent)||(ent.renderflags&RF_NOAUTOADD))continue; addentity(ent); } + if (ent.drawmask&mask){ if (!ent.predaw()) addentity(ent); } If mask&MASK_DELTA, non-csqc entities, particles, and related effects will also be added to the rentity list. If mask&MASK_STDVIEWMODEL then the default view model will also be added. */ void(entity ent) addentity = #302; /* Copies the entity fields into a new rentity for later rendering via addscene. */ +void(string texturename, float flags, void *verts, int *indexes, int numindexes) addtrisoup_1 = #0:addtrisoup_1; /* + Adds the specified trisoup into the scene as additional geometry. This permits caching geometry to reduce builtin spam. Indexes are a triangle list (so eg quads will need 6 indicies to form two triangles). NOTE: this is not going to be a speedup over polygons if you're still generating lots of new data every frame. */ + #define setviewprop setproperty float(float property, ...) setproperty = #303; /* Allows you to override default view properties like viewport, fov, and whether the engine hud will be drawn. Different VF_ values have slightly different arguments, some are vectors, some floats. */ @@ -1196,7 +1308,7 @@ vector (vector v) unproject = #310; /* vector (vector v) project = #311; /* Transform a 3d world-space point into a 2d screen-space point, according the various origin+angle+fov etc settings set via setproperty. */ -void(vector pos, vector size, float alignflags, string text) drawtextfield = #0/*:drawtextfield*/; /* +void(vector pos, vector size, float alignflags, string text) drawtextfield = #0:drawtextfield; /* Draws a multi-line block of text, including word wrapping and alignment. alignflags bits are RTLB, typically 3. */ void(float width, vector pos1, vector pos2, vector rgb, float alpha, optional float drawflag) drawline = #315; /* @@ -1208,6 +1320,12 @@ float(string name) iscachedpic = #316; /* string(string name, optional float trywad) precache_pic = #317; /* Forces the engine to load the named image. If trywad is specified, the specified name must any lack path and extension. */ +void(string imagename, int width, int height, int *pixeldata) r_uploadimage = #0:r_uploadimage; /* + Updates a texture with the specified rgba data. Will be created if needed. */ + +int*(string filename, __out int width, __out int height) r_readimage = #0:r_readimage; /* + Reads and decodes an image from disk, providing raw pixel data. Returns __NULL__ if the image could not be read for any reason. Use memfree to free the data once you're done with it. */ + #define draw_getimagesize drawgetimagesize vector(string picname) drawgetimagesize = #318; /* Returns the dimensions of the named image. Images specified with .lmp should give the original .lmp's dimensions even if texture replacements use a different resolution. */ @@ -1253,10 +1371,10 @@ float(string text, float usecolours, optional vector fontsize) stringwidth = #32 void(vector pos, vector sz, string pic, vector srcpos, vector srcsz, vector rgb, float alpha, optional float drawflag) drawsubpic = #328; /* Draws a rescaled subsection of an image to the screen. */ -void(vector pivot, vector mins, vector maxs, string pic, vector rgb, float alpha, float angle) drawrotpic = #0/*:drawrotpic*/; /* +void(vector pivot, vector mins, vector maxs, string pic, vector rgb, float alpha, float angle) drawrotpic = #0:drawrotpic; /* Draws an image rotating at the pivot. To rotate in the center, use mins+maxs of half the size with mins negated. Angle is in degrees. */ -void(vector pivot, vector mins, vector maxs, string pic, vector txmin, vector txsize, vector rgb, vector alphaandangles) drawrotsubpic = #0/*:drawrotsubpic*/; /* +void(vector pivot, vector mins, vector maxs, string pic, vector txmin, vector txsize, vector rgb, vector alphaandangles) drawrotsubpic = #0:drawrotsubpic; /* Overcomplicated draw function for over complicated people. Positions follow drawrotpic, while texture coords follow drawsubpic. Due to argument count limitations in builtins, the alpha value and angles are combined into separate fields of a vector (tip: use fteqcc's [alpha, angle] feature. */ float(float stnum) getstati = #330; /* @@ -1266,24 +1384,27 @@ float(float stnum) getstati = #330; /* float(float stnum, optional float firstbit, optional float bitcount) getstatf = #331; /* Retrieves the numerical value of the given EV_FLOAT stat. If firstbit and bitcount are specified, retrieves the upper bits of the STAT_ITEMS stat. */ -string(float firststnum) getstats = #332; /* +string(float stnum) getstats = #332; /* Retrieves the value of the given EV_STRING stat, as a tempstring. Older engines may use 4 consecutive integer stats, with a limit of 15 chars (yes, really. 15.), but FTE QuakeWorld uses a separate namespace for string stats and has a much higher length limit. */ +__variant(float playernum, float statnum, float stattype) getplayerstat = #0:getplayerstat; /* + Retrieves a specific player's stat, matching the type specified on the server. This builtin is primarily intended for mvd playback where ALL players are known. For EV_ENTITY, world will be returned if the entity is not in the pvs, use type-punning with EV_INTEGER to get the entity number if you just want to see if its set. STAT_ITEMS should be queried as an EV_INTEGER on account of runes and items2 being packed into the upper bits. */ + void(entity e, float mdlindex) setmodelindex = #333; /* Sets a model by precache index instead of by name. Otherwise identical to setmodel. */ string(float mdlindex) modelnameforindex = #334; /* Retrieves the name of the model based upon a precache index. This can be used to reduce csqc network traffic by enabling model matching. */ -float(string effectname) particleeffectnum = #335; /* +float(string effectname) particleeffectnum = #335; /* Part of DP_ENT_TRAILEFFECTNUM, FTE_SV_POINTPARTICLES Precaches the named particle effect. If your effect name is of the form 'foo.bar' then particles/foo.cfg will be loaded by the client if foo.bar was not already defined. Different engines will have different particle systems, this specifies the QC API only. */ -void(float effectnum, entity ent, vector start, vector end) trailparticles = #336; /* +void(float effectnum, entity ent, vector start, vector end) trailparticles = #336; /* Part of FTE_SV_POINTPARTICLES Draws the given effect between the two named points. If ent is not world, distances will be cached in the entity in order to avoid framerate dependancies. The entity is not otherwise used. */ -void(float effectnum, vector origin, optional vector dir, optional float count) pointparticles = #337; /* +void(float effectnum, vector origin, optional vector dir, optional float count) pointparticles = #337; /* Part of FTE_SV_POINTPARTICLES Spawn a load of particles from the given effect at the given point traveling or aiming along the direction specified. The number of particles are scaled by the count argument. */ void(string s, ...) cprint = #338; /* @@ -1304,7 +1425,7 @@ string(float keynum) getkeybind = #342; /* void(float usecursor, optional string cursorimage, optional vector hotspot, optional float scale) setcursormode = #343; /* Pass TRUE if you want the engine to release the mouse cursor (absolute input events + touchscreen mode). Pass FALSE if you want the engine to grab the cursor (relative input events + standard looking). If the image name is specified, the engine will use that image for a cursor (use an empty string to clear it again), in a way that will not conflict with the console. Images specified this way will be hardware accelerated, if supported by the platform/port. */ -float(float effective) getcursormode = #0/*:getcursormode*/; /* +float(float effective) getcursormode = #0:getcursormode; /* Reports the cursor mode this module previously attempted to use. If 'effective' is true, reports the cursor mode currently active (if was overriden by a different module which has precidence, for instance, or if there is only a touchscreen and no mouse). */ vector() getmousepos = #344; /* @@ -1331,8 +1452,36 @@ float() isdemo = #349; /* float() isserver = #350; /* Returns if the client is acting as the server (aka: listen server) */ -void(vector origin, vector forward, vector right, vector up, optional float inwater) SetListener = #351; /* - Sets the position of the view, as far as the audio subsystem is concerned. This should be called once per CSQC_UpdateView as it will otherwise revert to default. */ +void(vector origin, vector forward, vector right, vector up, optional float reverbtype) SetListener = #351; /* + Sets the position of the view, as far as the audio subsystem is concerned. This should be called once per CSQC_UpdateView as it will otherwise revert to default. For reverbtype, see setup_reverb or treat as 'underwater'. */ + +typedef struct { + float flDensity; + float flDiffusion; + float flGain; + float flGainHF; + float flGainLF; + float flDecayTime; + float flDecayHFRatio; + float flDecayLFRatio; + float flReflectionsGain; + float flReflectionsDelay; + vector flReflectionsPan; + float flLateReverbGain; + float flLateReverbDelay; + vector flLateReverbPan; + float flEchoTime; + float flEchoDepth; + float flModulationTime; + float flModulationDepth; + float flAirAbsorptionGainHF; + float flHFReference; + float flLFReference; + float flRoomRolloffFactor; + int iDecayHFLimit; +} reverbinfo_t; +void(float reverbslot, reverbinfo_t *reverbinfo, int sizeofreverinfo_t) setup_reverb = #0:setup_reverb; /* + Reconfigures a reverb slot for weird effects. Slot 0 is reserved for no effects. Slot 1 is reserved for underwater effects. Reserved slots will be reinitialised on snd_restart, but can otherwise be changed. These reverb slots can be activated with SetListener. Note that reverb will currently only work when using OpenAL. */ void(string cmdname) registercommand = #352; /* Register the given console command, for easy console use. @@ -1355,7 +1504,7 @@ float(string s) findfont = #356; /* Looks up a named font slot. Matches the actual font name as a last resort. */ float(string fontname, string fontmaps, string sizes, float slot, optional float fix_scale, optional float fix_voffset) loadfont = #357; /* - too convoluted for me to even try to explain correct usage. Try drawfont = loadfont("foo", "cour", "16", 0, 0, 0); to switch to the courier font, if you have the freetype2 library in windows.. */ + too convoluted for me to even try to explain correct usage. Try drawfont = loadfont("", "cour", "16", -1, 0, 0); to switch to the courier font (optimised for 16 virtual pixels high), if you have the freetype2 library in windows.. */ void(string evname, string evargs, ...) sendevent = #359; /* Invoke Cmd_evname_evargs in ssqc. evargs must be a string of initials refering to the types of the arguments to pass. v=vector, e=entity(.entnum field is sent), f=float, i=int. 6 arguments max - you can get more if you pack your floats into vectors. */ @@ -1392,7 +1541,7 @@ void(entity e, string skinfilename, optional string skindata) setcustomskin = #3 qwskin "foo" - use an unmodified quakeworld player skin (including crop+repalette rules) q1lower 0xff0000 - specify an override for the entity's lower colour, in this case to red q1upper 0x0000ff - specify an override for the entity's lower colour, in this case to blue - compose "surfacename" "shader" "imagename@x,y:w,h?r,g,b,a" - compose a skin texture from multiple images. + compose "surfacename" "shader" "imagename@x,y:w,h$s,t,s2,t2?r,g,b,a" - compose a skin texture from multiple images. The texture is determined to be sufficient to hold the first named image, additional images can be named as extra tokens on the same line. Use a + at the end of the line to continue reading image tokens from the next line also, the named shader must use 'map $diffuse' to read the composed texture (compatible with the defaultskin shader). */ @@ -1417,32 +1566,40 @@ void(__variant *dst, float ofs, __variant val) memsetval = #389; /* __variant*(__variant *base, float ofs) memptradd = #390; /* Perform some pointer maths. Woo. */ -float(string s) memstrsize = #0/*:memstrsize*/; /* +float(string s) memstrsize = #0:memstrsize; /* strlen, except ignores utf-8 */ -string(string conname, string field, optional string newvalue) con_getset = #391; /* Part of FTE_CSQC_ALTCONSOLES_WIP +string(string conname, string field, optional string newvalue) con_getset = #391; /* Part of FTE_CSQC_ALTCONSOLES Reads or sets a property from a console object. The old value is returned. Iterrate through consoles with the 'next' field. Valid properties: title, name, next, unseen, markup, forceutf8, close, clear, hidden, linecount */ -void(string conname, string messagefmt, ...) con_printf = #392; /* Part of FTE_CSQC_ALTCONSOLES_WIP +void(string conname, string messagefmt, ...) con_printf = #392; /* Part of FTE_CSQC_ALTCONSOLES Prints onto a named console. */ -void(string conname, vector pos, vector size, float fontsize) con_draw = #393; /* Part of FTE_CSQC_ALTCONSOLES_WIP +void(string conname, vector pos, vector size, float fontsize) con_draw = #393; /* Part of FTE_CSQC_ALTCONSOLES Draws the named console. */ -float(string conname, float inevtype, float parama, float paramb, float paramc) con_input = #394; /* Part of FTE_CSQC_ALTCONSOLES_WIP +float(string conname, float inevtype, float parama, float paramb, float paramc) con_input = #394; /* Part of FTE_CSQC_ALTCONSOLES Forwards input events to the named console. Mouse updates should be absolute only. */ -float() cvars_haveunsaved = #0/*:cvars_haveunsaved*/; /* +void(string newcaption) setwindowcaption = #0:setwindowcaption; /* + Replaces the title of the game window, as seen when task switching or just running in windowed mode. */ + +float() cvars_haveunsaved = #0:cvars_haveunsaved; /* Returns true if any archived cvar has an unsaved value. */ -void(entity from, entity to) copyentity = #400; /* Part of DP_QC_COPYENTITY*/ +float(entity e, float nowreadonly) entityprotection = #0:entityprotection; /* + Changes the protection on the specified entity to protect it from further edits from QC. The return value is the previous setting. Note that this can be used to unprotect the world, but doing so long term is not advised as you will no longer be able to detect invalid entity references. Also, world is not networked, so results might not be seen by clients (or in other words, world.avelocity_y=64 is a bad idea). */ + +entity(entity from, optional entity to) copyentity = #400; /* Part of DP_QC_COPYENTITY + Copies all fields from one entity to another. */ + entity(.string field, string match) findchain = #402; /* Part of DP_QC_FINDCHAIN*/ entity(.float fld, float match) findchainfloat = #403; /* Part of DP_QC_FINDCHAINFLOAT*/ void(vector org, string modelname, float startframe, float endframe, float framerate) effect = #404; /* Part of DP_SV_EFFECT Spawns a self-animating sprite */ void(vector org, vector dir, float count) te_blood = #405; /* Part of DP_TE_BLOOD*/ -void(vector mincorner, vector maxcorner, float explosionspeed, float howmany) te_bloodshower = #406; /* Part of DP_TE_BLOODSHOWER*/ +void(vector mincorner, vector maxcorner, float explosionspeed, float howmany) te_bloodshower = #406; /* Part of _DP_TE_BLOODSHOWER*/ void(vector org, vector color) te_explosionrgb = #407; /* Part of DP_TE_EXPLOSIONRGB*/ void(vector mincorner, vector maxcorner, vector vel, float howmany, float color, float gravityflag, float randomveljitter) te_particlecube = #408; /* Part of DP_TE_PARTICLECUBE*/ void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlerain = #409; /* Part of _DP_TE_PARTICLERAIN*/ @@ -1489,10 +1646,10 @@ float(searchhandle handle) search_getsize = #446; /* Part of DP_QC_FS_SEARCH string(searchhandle handle, float num) search_getfilename = #447; /* Part of DP_QC_FS_SEARCH Retrieves name of one of the files that was found by the initial search. */ -float(searchhandle handle, float num) search_getfilesize = #0/*:search_getfilesize*/; /* +float(searchhandle handle, float num) search_getfilesize = #0:search_getfilesize; /* Retrieves the size of one of the files that was found by the initial search. */ -string(searchhandle handle, float num) search_getfilemtime = #0/*:search_getfilemtime*/; /* +string(searchhandle handle, float num) search_getfilemtime = #0:search_getfilemtime; /* Retrieves modification time of one of the files. */ string(string cvarname) cvar_string = #448; /* Part of DP_QC_CVAR_STRING*/ @@ -1502,6 +1659,7 @@ float(entity ent, string tagname) gettagindex = #451; /* Part of DP_MD3_TAGSINFO vector(entity ent, float tagindex) gettaginfo = #452; /* Part of DP_MD3_TAGSINFO Obtains the current worldspace position+orientation of the bone or tag from the given entity. The return value is the world coord, v_forward, v_right, v_up are also set according to the bone/tag's orientation. */ +void(vector org, vector vel, float howmany) te_flamejet = #457; /* Part of _DP_TE_FLAMEJET*/ entity(float entnum) edict_num = #459; /* Part of DP_QC_EDICT_NUM*/ strbuf() buf_create = #460; /* Part of DP_QC_STRINGBUFFERS*/ void(strbuf bufhandle) buf_del = #461; /* Part of DP_QC_STRINGBUFFERS*/ @@ -1536,7 +1694,7 @@ string(string search, string replace, string subject) strreplace = #484; /* Part string(string search, string replace, string subject) strireplace = #485; /* Part of DP_QC_STRREPLACE*/ vector(entity e, float s, float n, float a) getsurfacepointattribute = #486; /* Part of DP_QC_GETSURFACEPOINTATTRIBUTE*/ float(string name) gecko_create = #487; /* Part of DP_GECKO_SUPPORT - Create a new 'browser tab' shader with the specified name that can then be drawn via drawpic. In order to function correctly, this builtin depends upon external plugins being available. Use gecko_navigate to navigate it to a page of your choosing. */ + Create a new 'browser tab' shader with the specified name that can then be drawn via drawpic (shader should not already exist - including from map/model textures or disk). In order to function correctly, this builtin depends upon external plugins being available. Use gecko_navigate to navigate it to a page of your choosing. */ void(string name) gecko_destroy = #488; /* Part of DP_GECKO_SUPPORT Destroy a shader. */ @@ -1554,18 +1712,26 @@ void(string name, float w, float h) gecko_resize = #492; /* Part of DP_GECKO_SUP Request to resize a media decoder. */ vector(string name) gecko_get_texture_extent = #493; /* Part of DP_GECKO_SUPPORT - Query a media decoder for its current pixel size. */ + Retrieves a media decoder current image pixel sizes. */ +string(string shadname, string propname) gecko_getproperty = #0:gecko_getproperty; /* + Queries the media decoder (especially browser ones) for decoder-specific properties. The cef plugin recognises url, title, status. */ + +float(string file, string id) cin_open = #0:cin_open; +void(string id) cin_close = #0:cin_close; +void(string id, float newstate) cin_setstate = #0:cin_setstate; +float(string id) cin_getstate = #0:cin_getstate; +void(string file) cin_restart = #0:cin_restart; float(float caseinsensitive, string s, ...) crc16 = #494; /* Part of DP_QC_CRC16*/ float(string name) cvar_type = #495; /* Part of DP_QC_CVAR_TYPE*/ float() numentityfields = #496; /* Part of DP_QC_ENTITYDATA Gives the number of named entity fields. Note that this is not the size of an entity, but rather just the number of unique names (ie: vectors use 4 names rather than 3). */ -float(string fieldname) findentityfield = #0/*:findentityfield*/; /* +float(string fieldname) findentityfield = #0:findentityfield; /* Find a field index by name. */ typedef .__variant field_t; -field_t(float fieldnum) entityfieldref = #0/*:entityfieldref*/; /* +field_t(float fieldnum) entityfieldref = #0:entityfieldref; /* Returns a field value that can be directly used to read entity fields. Be sure to validate the type with entityfieldtype before using. */ string(float fieldnum) entityfieldname = #497; /* Part of DP_QC_ENTITYDATA @@ -1576,6 +1742,9 @@ float(float fieldnum) entityfieldtype = #498; /* Part of DP_QC_ENTITYDATA string(float fieldnum, entity ent) getentityfieldstring = #499; /* Part of DP_QC_ENTITYDATA*/ float(float fieldnum, entity ent, string s) putentityfieldstring = #500; /* Part of DP_QC_ENTITYDATA*/ +string() ReadPicture = #501; /* + Reads a picture that was written by ReadPicture, and returns a name that can be used in drawpic and other 2d drawing functions. In FTE, this acts as a readstring-with-downloadcheck - the image will appear normally once it has been downloaded, but its size may be incorrect until then. */ + void(float effectindex, entity own, vector org_from, vector org_to, vector dir_from, vector dir_to, float countmultiplier, optional float flags) boxparticles = #502; string(string filename, optional float makereferenced) whichpack = #503; /* Part of DP_QC_WHICHPACK Returns the pak file name that contains the file specified. progs/player.mdl will generally return something like 'pak0.pak'. If makereferenced is true, clients will automatically be told that the returned package should be pre-downloaded and used, even if allow_download_refpackages is not set. */ @@ -1612,7 +1781,7 @@ string(float keynum) keynumtostring_omgwtf = #520; string(string command, optional float bindmap) findkeysforcommand = #521; /* Returns a list of keycodes that perform the given console command in a format that can only be parsed via tokenize (NOT tokenize_console). This only and always returns two values - if only one key is actually bound, -1 will be returned. The bindmap argument is listed for compatibility with dp-specific defs, but is ignored in FTE. */ -string(string command, optional float bindmap) findkeysforcommandex = #0/*:findkeysforcommandex*/; /* +string(string command, optional float bindmap) findkeysforcommandex = #0:findkeysforcommandex; /* Returns a list of key bindings in keyname format instead of keynums. Use tokenize to parse. This list may contain modifiers. May return large numbers of keys. */ void(string s) loadfromdata = #529; /* @@ -1621,7 +1790,10 @@ void(string s) loadfromdata = #529; /* void(string s) loadfromfile = #530; /* Reads a set of entities from the named file. This file should have the same format as a .ent file or a saved game. Entities will be spawned as required. If you need to see the entities that were created, you should use parseentitydata instead. */ -float(entity e, float channel, string newsample, float volume, float attenuation, float pitchpct, float flags, float timeoffset) soundupdate = #0/*:soundupdate*/; /* +float(float v, optional float base) log = #532; /* Part of ??MVDSV_BUILTINS + Determines the logarithm of the input value according to the specified base. This can be used to calculate how much something was shifted by. */ + +float(entity e, float channel, string newsample, float volume, float attenuation, float pitchpct, float flags, float timeoffset) soundupdate = #0:soundupdate; /* Changes the properties of the current sound being played on the given entity channel. newsample may be empty, and will be ignored in this case. timeoffset is relative to the current position (subtract the result of getsoundtime for absolute positions). Negative volume can be used to stop the sound. Return value is a fractional value based upon the number of audio devices that could be updated - test against TRUE rather than non-zero. */ float(entity e, float channel) getsoundtime = #533; /* @@ -1662,8 +1834,11 @@ vector(float vidmode, optional float forfullscreen) getresolution = #608; /* string(float keynum) keynumtostring_menu = #609; float(float type) gethostcachevalue = #611; /* Part of FTE_CSQC_SERVERBROWSER*/ string(float type, float hostnr) gethostcachestring = #612; /* Part of FTE_CSQC_SERVERBROWSER*/ -void(entity e, string s) parseentitydata = #613; /* - Reads a single entity's fields into an already-spawned entity. s should contain field pairs like in a saved game: {"foo1" "bar" "foo2" "5"} */ +float(entity e, string s, optional float offset) parseentitydata = #613; /* + Reads a single entity's fields into an already-spawned entity. s should contain field pairs like in a saved game: {"foo1" "bar" "foo2" "5"}. Returns <=0 on failure, otherwise returns the offset in the string that was read to. */ + +string(entity e) generateentitydata = #0:generateentitydata; /* + Dumps the entities fields into a string which can later be parsed with parseentitydata. */ float(string key) stringtokeynum_menu = #614; void() resethostcachemasks = #615; /* Part of FTE_CSQC_SERVERBROWSER*/ @@ -1800,6 +1975,7 @@ string(string digest, string data, ...) digest_hex = #639; #define K_BACKQUOTE 96 #define K_BACKSLASH 92 #endif +#ifdef _ACCESSORS accessor strbuf : float { inline get float asfloat[float idx] = {return stof(bufstr_get(this, idx));}; @@ -1834,4 +2010,5 @@ accessor filestream : float get string = fgets; inline set string = {fputs(this,value);}; }; +#endif #pragma noref 0 diff --git a/quakec/csaddon/src/editor_brushes.qc b/quakec/csaddon/src/editor_brushes.qc index 74e81b911..7ab4f94c2 100644 --- a/quakec/csaddon/src/editor_brushes.qc +++ b/quakec/csaddon/src/editor_brushes.qc @@ -1,26 +1,3 @@ -#if 0 -typedef struct -{ - string shadername; //aka: texture name - vector planenormal; - float planedist; - vector sdir; - float sbias; - vector tdir; - float tbias; -} brushface_t; -#define out //for annotation only -#define in //for annotation only -#define inout //for annotation only -int brush_get(float modelid, int brushid, out brushface_t *faces, int maxfaces, int *contents) = #0; //returns number of faces, or 0 on error. -int brush_create(float modelid, in brushface_t *faces, int numfaces, int contents) = #0; //returns newly created brushid. -void brush_delete(float modelid, int brushid) = #0; //destroys brush -//float brush_editplane(float modelid, int brushid, int faceid, in brushface_t *face) = #0; //allows changing shader/s/t info, but ignores plane changes. returns success -float brush_selected(float modelid, int brushid, int faceid, float selectedstate) = #0; //allows you to easily set visual properties of a brush. if brush/face is -1, applies to all. returns old value. selectedstate=-1 changes nothing (called for its return value). -int(float modelid, int brushid, int faceid, vector *points, int maxpoints) brush_getfacepoints = #0; -int(float modelid, vector *planes, float *dists, int numplanes, int *out_brushes, int *out_faces, int maxresults) brush_findinvolume = #0; -#endif - /*traceline/tracebox returns trace_brush_id and trace_brush_faceid @@ -29,11 +6,17 @@ to move 50 brushes, mod needs to get+delete+transform+create brush ids are ints. this allows different clients to use different ranges without float problems. */ +int *selectedbrushes; +int selectedbrushcount; + int selectedbrush; int selectedbrushface; int selectedbrushmodel; vector facepoints[64]; +var float autocvar_ca_brush_view = 0; //0=normal, 1=x, 2=y, 3=z +var float autocvar_ca_brush_viewsize = 1024; //for different views. + #define EPSILON (1.0 / 32) //inprecision sucks. //history is implemented using a ringbuffer @@ -52,6 +35,58 @@ int history_min; //oldest we can go int history; //updated on each change int history_max; //max value for redo +int(int brushid) brush_isselected = +{ + for (int i = 0; i < selectedbrushcount; i++) + if (selectedbrushes[i] == brushid) + return i+1; + return 0; +}; +int(int brushid) brush_deselect = +{ + int i = brush_isselected(brushid); + if (!i) + return FALSE; + brush_selected(selectedbrushmodel, brushid, -1, FALSE); + memcpy(&selectedbrushes[i-1], &selectedbrushes[i], sizeof(int)*(selectedbrushcount-i)); + selectedbrushcount--; + return TRUE; +}; +void() brush_deselectall = +{ + brush_selected(selectedbrushmodel, selectedbrush, -1, FALSE); + selectedbrush = 0; + selectedbrushface = 0; + selectedbrushcount = 0; +}; +void(int brushid) brush_select = +{ + if (!brush_isselected(brushid)) + { + int *n = memalloc(sizeof(int) * (selectedbrushcount+1)); + memcpy(n, selectedbrushes, sizeof(int)*selectedbrushcount); + memfree(selectedbrushes); + selectedbrushes = n; + n[selectedbrushcount++] = brushid; + brush_selected(selectedbrushmodel, brushid, -1, TRUE); + } + if (!selectedbrush) + selectedbrush = brushid; +}; + +//when replaying history, ids get changed, which makes +static void(int old, int new) history_remap = +{ + if (selectedbrush == old) + selectedbrush = new; + for (int i = history_min; i < history_max; i++) + { + history_t *h = &historyring[i & (historyring.length-1)]; + if (h->id == old) + h->id = new; + } +}; + void() brush_undo = { do @@ -64,11 +99,10 @@ void() brush_undo = //we're undoing, so deletes create and creates delete. weird, yes. if (h->wasdelete) - h->id = brush_create(h->brushmodel, h->brushdata, h->numfaces, h->contents); + history_remap(h->id, brush_create(h->brushmodel, h->brushdata, h->numfaces, h->contents)); else { brush_delete(h->brushmodel, h->id); - h->id = 0; } } while (historyring[(history-1) & (historyring.length-1)].timestamp == h->timestamp); }; @@ -81,15 +115,14 @@ void() brush_redo = history_t *h = &historyring[history & (historyring.length-1)]; - //we're redoing stuff that has previously been doing. yay. + //we're redoing stuff that has previously been done. yay. if (h->wasdelete) { brush_delete(h->brushmodel, h->id); - h->id = 0; } else { - h->id = brush_create(h->brushmodel, h->brushdata, h->numfaces, h->contents); + history_remap(h->id, brush_create(h->brushmodel, h->brushdata, h->numfaces, h->contents)); } history++; } while (historyring[history & (historyring.length-1)].timestamp == h->timestamp); @@ -127,7 +160,7 @@ void(int mod, int id) brush_history_delete = h->timestamp = time; h->brushmodel = mod; - h->id = 0; + h->id = id; h->contents = 0; h->wasdelete = TRUE; @@ -144,20 +177,18 @@ void(int mod, int id) brush_history_delete = int(int mod, int id, brushface_t *faces, int numfaces, int contents) brush_history_edit = { + int wasselected = brush_isselected(id); + int wasmainselected = selectedbrush == id; + brush_history_delete(mod, id); - return brush_history_create(mod, faces, numfaces, contents); + id = brush_history_create(mod, faces, numfaces, contents); + if (wasselected) + selectedbrushes[wasselected-1] = id; + if (wasmainselected) + selectedbrush = id; + return id; }; - - -vector(vector a, vector b) cross = -{ - return [ a_y * b_z - a_z * b_y, - a_z * b_x - a_x * b_z, - a_x * b_y - a_y * b_x]; -}; -#define dot(a,b) ((a)*(b)) - typedef struct { int numverts; @@ -174,7 +205,7 @@ int tmp_numfaces; int tmp_contents; //int selected; -int brushlist[64]; +int brushlist[2048]; var string autocvar_ca_newbrushtexture = "metal4_2"; var float autocvar_ca_newbrushheight = 64; @@ -236,13 +267,13 @@ static int(brushface_t *fa, int famax, vector *points, int numpoints, float heig { int count = 0; - fa->planenormal = normalize(cross(points[2] - points[0], points[1] - points[0])); + fa->planenormal = normalize(crossproduct(points[2] - points[0], points[1] - points[0])); fa->planedist = bt_point[0] * fa->planenormal + height; fa->shadername = autocvar_ca_newbrushtexture; reset_texturecoords(fa); fa++; - fa->planenormal = -normalize(cross(points[2] - points[0], points[1] - points[0])); + fa->planenormal = -normalize(crossproduct(points[2] - points[0], points[1] - points[0])); fa->planedist = (bt_point[0] * fa->planenormal); fa->shadername = autocvar_ca_newbrushtexture; reset_texturecoords(fa); @@ -254,7 +285,7 @@ static int(brushface_t *fa, int famax, vector *points, int numpoints, float heig int n = p + 1; if (n == numpoints) n = 0; - fa->planenormal = normalize(cross(points[p] - bt_point[n], tmp_faces[0].planenormal)); + fa->planenormal = normalize(crossproduct(points[p] - bt_point[n], tmp_faces[0].planenormal)); fa->planedist = points[p] * fa->planenormal; fa->shadername = autocvar_ca_newbrushtexture; reset_texturecoords(fa); @@ -389,7 +420,7 @@ vector(vector in) channelizeangle = in_y -= 360; if (in_z > 180) in_z -= 360; - + float fx = fabs(in_x); float fy = fabs(in_y); float fz = fabs(in_z); @@ -406,7 +437,7 @@ vector(vector p1, vector p2, vector norm, float dist) planelinepoint = float d1 = p1*norm - dist; float d2 = p2*norm - dist; float frac = d1 / (d2-d1); - + // frac = bound(0, frac, 1); return p2 + (p1-p2)*frac; //convert that frac into an actual position @@ -514,7 +545,7 @@ int(int model, int brush1, int brush2, int face1, int face2) mergebrushes = //FIXME: determine volume and reject it if we shrink? tmp_faces[bestface].planenormal = -infa->planenormal; tmp_faces[bestface].planedist = -infa->planedist; - + // if (isconcave(tmp_faces, tmp_numfaces)) // { // print("Resulting brush would be concave\n"); @@ -536,7 +567,7 @@ int(int model, int brush1, int brush2, int face1, int face2) mergebrushes = numpoints += i; for(f = 0; (i = brush_getfacepoints(model, brush2, ++f, points+numpoints, 64*64-numpoints)); ) numpoints += i; - + if (isconcave(tmp_faces, tmp_numfaces, points, numpoints)) { print("Resulting brush would be concave\n"); @@ -544,7 +575,7 @@ int(int model, int brush1, int brush2, int face1, int face2) mergebrushes = return 0; } memfree(points); - + //FIXME: verify that no planes got dropped, as this indicates that the region wasn't convex, and we probably just destroyed the entire thing. brush_history_delete(model, brush1); brush_history_delete(model, brush2); @@ -553,6 +584,28 @@ int(int model, int brush1, int brush2, int face1, int face2) mergebrushes = } }; +void(int id) DrawEngineBrushWireframe = +{ + const vector col = '1 0 0'; + for(int facenum = 0;;) + { + int points = brush_getfacepoints(selectedbrushmodel, id, ++facenum, &facepoints[0], facepoints.length); + if (!points) + break; //end of face list, I guess + + R_BeginPolygon("chop"); + R_PolygonVertex(facepoints[0], '0 0', col, 1); + for (int point = 1; point < points; point++) + { + R_PolygonVertex(facepoints[point], '0 0', col, 1); + R_EndPolygon(); + R_PolygonVertex(facepoints[point], '0 0', col, 1); + } + R_PolygonVertex(facepoints[0], '0 0', col, 1); + R_EndPolygon(); + } +}; + void(brushface_t *faces, int numfaces, string shader, vector col, float alpha) DrawQCBrushWireframe = { int f; @@ -610,7 +663,7 @@ void(brushface_t *faces, int numfaces, vector col, float alpha) DrawQCBrushTextu "alphagen vertex\n" "}\n" "}", faces[f].shadername)); - + vector sz = drawgetimagesize(faces[f].shadername); R_BeginPolygon(faces[f].shadername); for (point = 0; point < points; point++) @@ -645,7 +698,7 @@ static float(vertsoup_t *soup, int *idx, __inout vector norm, __inout float dist vector d1 = v3 - v1; vector d2 = v2 - v1; vector d3 = v3 - v2; - norm = normalize(cross(d1, d2)); + norm = normalize(crossproduct(d1, d2)); dist = norm * v1; if (!d1 || !d2 || !d3 || !norm) @@ -813,7 +866,7 @@ p3p1edge: } } } - cprint(sprintf("%i internal splits, %i faces\n", internals, numfaces)); +// cprint(sprintf("%i internal splits, %i faces\n", internals, numfaces)); if (numfaces <= 3) return; //can't possibly be valid. @@ -1007,11 +1060,35 @@ void() brushedit_resettextures = for (int i = 0; i < planecount; i++) reset_texturecoords(&tmp_faces[i]); - brush_history_delete(selectedbrushmodel, selectedbrush); - selectedbrush = brush_history_create(selectedbrushmodel, tmp_faces, planecount, tmp_contents); + selectedbrush = brush_history_edit(selectedbrushmodel, selectedbrush, tmp_faces, planecount, tmp_contents); }; -void(vector mousepos) editor_brushes_add = +//when we're drawing sideviews, we normally need them wireframe in order to actually see anything +static void(vector sizes) drawwireframeview = +{ + //figure out the planes that we care about. + vector org = getviewprop(VF_ORIGIN); + vector vaxis[6]; + vaxis[0] = v_right; + vaxis[1] = v_up; + vaxis[2] = v_forward; + for (int i = 0; i < 3; i++) + dists[i] = (org*vaxis[i]) + sizes[i]; + for (int i = 0; i < 3; i++) + { + vaxis[i+3] = -vaxis[i]; + dists[i+3] = (org*vaxis[i+3]) + sizes[i]; + } + + int numbrushes = brush_findinvolume(selectedbrushmodel, vaxis, dists, dists.length, &brushlist[0], __NULL__, brushlist.length); + + //this can be a bit slow with lots of brushes. would be nice to batch them a bit better. + while(numbrushes --> 0) + DrawEngineBrushWireframe(brushlist[numbrushes]); + setviewprop(VF_DRAWWORLD, FALSE); +}; + +void(vector mousepos) editor_brushes_addentities = { vector col = '0 0 0'; int points, point; @@ -1021,9 +1098,42 @@ void(vector mousepos) editor_brushes_add = float bestdist, dist; vector mid; + autocvar_ca_brush_view = fabs(autocvar_ca_brush_view) % 4f; + if (autocvar_ca_brush_view) + { + vector vmn = (vector)getproperty(VF_MIN); + vector vsz = (vector)getproperty(VF_SIZE); + vector ang = vectoangles([autocvar_ca_brush_view==1,autocvar_ca_brush_view==2,autocvar_ca_brush_view==3]); + vector fov; + if (vsz_x > vsz_y) + fov = [autocvar_ca_brush_viewsize, autocvar_ca_brush_viewsize * (vsz_y/vsz_x), 8192]; + else + fov = [autocvar_ca_brush_viewsize * (vsz_x/vsz_y), autocvar_ca_brush_viewsize, 8192]; + makevectors(ang); + drawfill(vmn, vsz, '0 0 0', 1, 0); + setviewprop(VF_VIEWENTITY, 0); + setviewprop(VF_PERSPECTIVE, FALSE); + setviewprop(VF_ANGLES, ang); + setviewprop(VF_MAXDIST, fov_z*0.5); + setviewprop(VF_MINDIST, -fov_z*0.5); //I'm not entirely sure where the near clip plane should be. oh well. + if (vsz_x > vsz_y) + setviewprop(VF_FOV, fov); + else + setviewprop(VF_FOV, fov); + + if (curmousepos_x >= vmn_x && curmousepos_x <= vmn_x+vsz_x && curmousepos_y >= vmn_y && curmousepos_y <= vmn_y+vsz_y) + { + mousefar = unproject(curmousepos + '0 0 1'); + mousenear = unproject(curmousepos); + } + + drawwireframeview(fov*0.5); + } + else + makevectors(input_angles); + if ((mousetool == BT_VERTEXEDIT || mousetool == BT_CREATEDRAG || brushtool == BT_PUSHFACE || brushtool == BT_CLONEDISPLACE || brushtool == BT_MOVE || brushtool == BT_MOVETEXTURE) && bt_points) { - makevectors(input_angles); vector dir = v_forward; if (!altdown) //if alt is pressed, we'll axialize later. dir = axialize(dir); @@ -1179,7 +1289,6 @@ void(vector mousepos) editor_brushes_add = if (!mousedown) { - brush_selected(selectedbrushmodel, selectedbrush, -1, FALSE); selectedbrush = brush_history_create(selectedbrushmodel, tmp_faces, tmp_numfaces, tmp_contents); brush_selected(selectedbrushmodel, selectedbrush, selectedbrushface, TRUE); bt_points = 0; @@ -1191,117 +1300,129 @@ void(vector mousepos) editor_brushes_add = } else if ((mousetool == BT_PUSHFACE || mousetool == BT_MOVETEXTURE || mousetool == BT_MOVE || mousetool == BT_CLONEDISPLACE || mousetool == BT_ROTATE) && bt_points) { - tmp_numfaces = brush_get(selectedbrushmodel, selectedbrush, tmp_faces, tmp_faces.length, &tmp_contents); - - if (mousetool == BT_PUSHFACE) - tmp_faces[selectedbrushface-1].planedist += tmp_faces[selectedbrushface-1].planenormal * displace; - else if (mousetool == BT_MOVETEXTURE) + int oldselectedbrushcount = selectedbrushcount; + int *oldselectedbrushes = memalloc(sizeof(int)*selectedbrushcount); + memcpy(oldselectedbrushes, selectedbrushes, selectedbrushcount*sizeof(int)); + for (int sb = 0; sb < oldselectedbrushcount; sb++) { - tmp_faces[selectedbrushface-1].sbias -= tmp_faces[selectedbrushface-1].sdir * displace; - tmp_faces[selectedbrushface-1].tbias -= tmp_faces[selectedbrushface-1].tdir * displace; - } - else if (mousetool == BT_MOVE || mousetool == BT_CLONEDISPLACE) - brushface_translate(tmp_faces, tmp_numfaces, displace); - else if (mousetool == BT_ROTATE) - { - //find the brush's middle (based on its bbox) - brush_getfacepoints(selectedbrushmodel, selectedbrush, 0, &tmp, 1); - - makevectors(col); + int brush = oldselectedbrushes[sb]; + if (!brush) + continue; - //move it so its pivot is at the origin - brushface_translate(tmp_faces, tmp_numfaces, -tmp); - //rotate it - brushface_rotate(tmp_faces, tmp_numfaces); - //reposition it around its pivot again - brushface_translate(tmp_faces, tmp_numfaces, tmp); - } + tmp_numfaces = brush_get(selectedbrushmodel, brush, tmp_faces, tmp_faces.length, &tmp_contents); - if (mousetool == BT_MOVETEXTURE) - { - points = brush_calcfacepoints(selectedbrushface, tmp_faces, tmp_numfaces, facepoints, facepoints.length); - if (points) + if (mousetool == BT_PUSHFACE && brush == selectedbrush) + tmp_faces[selectedbrushface-1].planedist += tmp_faces[selectedbrushface-1].planenormal * displace; + else if (mousetool == BT_MOVETEXTURE && brush == selectedbrush) { - //this is unfortunate. the built in shaders expect to use lightmaps. we don't have those. - //because lightmaps are special things, we end up in a real mess. so lets just make sure there's a shader now, because we can. - shaderforname(tmp_faces[selectedbrushface-1].shadername, - sprintf("{" - "{\n" - "map \"%s.lmp\"\n" - "rgbgen vertex\n" - "alphagen vertex\n" - "}\n" - "}", tmp_faces[selectedbrushface-1].shadername)); - - col = '0.7 0.7 0.7'; //fullbright is typically TOO bright. overbrights? meh! - vector sz = drawgetimagesize(tmp_faces[selectedbrushface-1].shadername); - R_BeginPolygon(tmp_faces[selectedbrushface-1].shadername); - for (point = 0; point < points; point++) - R_PolygonVertex(facepoints[point] + tmp_faces[selectedbrushface-1].planenormal*0.1, [(facepoints[point] * tmp_faces[selectedbrushface-1].sdir + tmp_faces[selectedbrushface-1].sbias)/sz_x, (facepoints[point] * tmp_faces[selectedbrushface-1].tdir + tmp_faces[selectedbrushface-1].tbias)/sz_y], col, 1); - R_EndPolygon(); + tmp_faces[selectedbrushface-1].sbias -= tmp_faces[selectedbrushface-1].sdir * displace; + tmp_faces[selectedbrushface-1].tbias -= tmp_faces[selectedbrushface-1].tdir * displace; } - } - else - { - //draw it wireframe - for(facenum = 0; facenum < tmp_numfaces;) + else if (mousetool == BT_MOVE || mousetool == BT_CLONEDISPLACE) + brushface_translate(tmp_faces, tmp_numfaces, displace); + else if (mousetool == BT_ROTATE) { - points = brush_calcfacepoints(++facenum, tmp_faces, tmp_numfaces, facepoints, facepoints.length); - if (!points) - continue; //should probably warn somehow about this - //should we use two colour channels? one depth one not? - if (facenum == selectedbrushface) - col = '1 0 0'; - else - col = '0 0.5 0'; - R_BeginPolygon("chop"); - R_PolygonVertex(facepoints[0], '0 0', col, 1); - for (point = 0; point < points-1; ) - { - point++; - R_PolygonVertex(facepoints[point], '0 0', col, 1); - R_EndPolygon(); - R_PolygonVertex(facepoints[point], '0 0', col, 1); - } - R_PolygonVertex(facepoints[0], '0 0', col, 1); - R_EndPolygon(); - } - } - - //draw it wireframe WITH depth testing - DrawQCBrushWireframe(tmp_faces, tmp_numfaces, "terrainedit", '0 0 1', 1); - - DebrushifyLite(tmp_faces, tmp_numfaces); - DrawAxisExtensions(vertedit.p, vertedit.numverts, "terrainedit", '0 0 0.3', 1); + //find the brush's middle (based on its bbox) + brush_getfacepoints(selectedbrushmodel, brush, 0, &tmp, 1); - if (!mousedown) - { - brush_selected(selectedbrushmodel, selectedbrush, -1, FALSE); - if (mousetool == BT_CLONEDISPLACE) //doesn't affect the original brush. - { - if (displace*displace > 1) - selectedbrush = brush_history_create(selectedbrushmodel, tmp_faces, tmp_numfaces, tmp_contents); + makevectors(col); + + //move it so its pivot is at the origin + brushface_translate(tmp_faces, tmp_numfaces, -tmp); + //rotate it + brushface_rotate(tmp_faces, tmp_numfaces); + //reposition it around its pivot again + brushface_translate(tmp_faces, tmp_numfaces, tmp); } else - selectedbrush = brush_history_edit(selectedbrushmodel, selectedbrush, tmp_faces, tmp_numfaces, tmp_contents); - brush_selected(selectedbrushmodel, selectedbrush, selectedbrushface, TRUE); - bt_points = 0; - mousetool = BT_NONE; + continue; + + if (mousetool == BT_MOVETEXTURE) + { + points = brush_calcfacepoints(selectedbrushface, tmp_faces, tmp_numfaces, facepoints, facepoints.length); + if (points) + { + //this is unfortunate. the built in shaders expect to use lightmaps. we don't have those. + //because lightmaps are special things, we end up in a real mess. so lets just make sure there's a shader now, because we can. + shaderforname(tmp_faces[selectedbrushface-1].shadername, + sprintf("{" + "{\n" + "map \"%s.lmp\"\n" + "rgbgen vertex\n" + "alphagen vertex\n" + "}\n" + "}", tmp_faces[selectedbrushface-1].shadername)); + + col = '0.7 0.7 0.7'; //fullbright is typically TOO bright. overbrights? meh! + vector sz = drawgetimagesize(tmp_faces[selectedbrushface-1].shadername); + R_BeginPolygon(tmp_faces[selectedbrushface-1].shadername); + for (point = 0; point < points; point++) + R_PolygonVertex(facepoints[point] + tmp_faces[selectedbrushface-1].planenormal*0.1, [(facepoints[point] * tmp_faces[selectedbrushface-1].sdir + tmp_faces[selectedbrushface-1].sbias)/sz_x, (facepoints[point] * tmp_faces[selectedbrushface-1].tdir + tmp_faces[selectedbrushface-1].tbias)/sz_y], col, 1); + R_EndPolygon(); + } + } + else + { + //draw it wireframe + for(facenum = 0; facenum < tmp_numfaces;) + { + points = brush_calcfacepoints(++facenum, tmp_faces, tmp_numfaces, facepoints, facepoints.length); + if (!points) + continue; //should probably warn somehow about this + //should we use two colour channels? one depth one not? + if (facenum == selectedbrushface) + col = '1 0 0'; + else + col = '0 0.5 0'; + R_BeginPolygon("chop"); + R_PolygonVertex(facepoints[0], '0 0', col, 1); + for (point = 0; point < points-1; ) + { + point++; + R_PolygonVertex(facepoints[point], '0 0', col, 1); + R_EndPolygon(); + R_PolygonVertex(facepoints[point], '0 0', col, 1); + } + R_PolygonVertex(facepoints[0], '0 0', col, 1); + R_EndPolygon(); + } + } + + //draw it wireframe WITH depth testing + DrawQCBrushWireframe(tmp_faces, tmp_numfaces, "terrainedit", '0 0 1', 1); + + DebrushifyLite(tmp_faces, tmp_numfaces); + DrawAxisExtensions(vertedit.p, vertedit.numverts, "terrainedit", '0 0 0.3', 1); + + if (!mousedown) + { + if (mousetool == BT_CLONEDISPLACE) //doesn't affect the original brush. + { + if (displace*displace > 1) + selectedbrush = brush_history_create(selectedbrushmodel, tmp_faces, tmp_numfaces, tmp_contents); + } + else + selectedbrush = brush_history_edit(selectedbrushmodel, brush, tmp_faces, tmp_numfaces, tmp_contents); + brush_selected(selectedbrushmodel, brush, selectedbrushface, TRUE); + bt_points = 0; + mousetool = BT_NONE; + } } + memfree(oldselectedbrushes); } else if (brushtool == BT_SLICE && bt_points == 3) { tmp_numfaces = brush_get(selectedbrushmodel, selectedbrush, tmp_faces, tmp_faces.length, &tmp_contents); - tmp_faces[tmp_numfaces].planenormal = normalize(cross(bt_point[2] - bt_point[0], bt_point[1] - bt_point[0])); + tmp_faces[tmp_numfaces].planenormal = normalize(crossproduct(bt_point[2] - bt_point[0], bt_point[1] - bt_point[0])); tmp_faces[tmp_numfaces].planedist = bt_point[0] * tmp_faces[tmp_numfaces].planenormal; - + //draw it wireframe DrawQCBrushWireframe(tmp_faces, tmp_numfaces+1, "chop", '1 0 0', 1); - + //flip the split tmp_faces[tmp_numfaces].planenormal *= -1; tmp_faces[tmp_numfaces].planedist *= -1; - + //draw the other side wireframe DrawQCBrushWireframe(tmp_faces, tmp_numfaces+1, "chop", '0 1 0', 1); } @@ -1328,42 +1449,29 @@ void(vector mousepos) editor_brushes_add = } } - //draw the selected brush faces. - for(facenum = 0;;) + //draw all selected brush faces. + for (int sb = 0; sb < selectedbrushcount; sb++) { - points = brush_getfacepoints(selectedbrushmodel, selectedbrush, ++facenum, facepoints, facepoints.length); - if (!points) - break; //end of face list, I guess + int brush = selectedbrushes[sb]; + for(facenum = 0;;) + { + points = brush_getfacepoints(selectedbrushmodel, brush, ++facenum, facepoints, facepoints.length); + if (!points) + break; //end of face list, I guess - if (facenum == selectedbrushface) - col = [0,intensity,0]; - else - col = [intensity,0,0]; + if (facenum == selectedbrushface && brush == selectedbrush) + col = [0,intensity,0]; + else + col = [intensity,0,0]; - R_BeginPolygon("terrainedit"); - for (point = 0; point < points; point++) - R_PolygonVertex(facepoints[point], '0 0', col, 1); - R_EndPolygon(); + R_BeginPolygon("terrainedit"); + for (point = 0; point < points; point++) + R_PolygonVertex(facepoints[point], '0 0', col, 1); + R_EndPolygon(); + } } //now draw wireframe - for(facenum = 0;;) - { - points = brush_getfacepoints(selectedbrushmodel, selectedbrush, ++facenum, &facepoints[0], facepoints.length); - if (!points) - break; //end of face list, I guess - - col = '1 0 0'; - R_BeginPolygon("chop"); - R_PolygonVertex(facepoints[0], '0 0', col, 1); - for (point = 1; point < points; point++) - { - R_PolygonVertex(facepoints[point], '0 0', col, 1); - R_EndPolygon(); - R_PolygonVertex(facepoints[point], '0 0', col, 1); - } - R_PolygonVertex(facepoints[0], '0 0', col, 1); - R_EndPolygon(); - } + DrawEngineBrushWireframe(selectedbrush); tmp_numfaces = brush_get(selectedbrushmodel, selectedbrush, tmp_faces, tmp_faces.length, &tmp_contents); DebrushifyLite(tmp_faces, tmp_numfaces); @@ -1373,7 +1481,7 @@ void(vector mousepos) editor_brushes_add = vector t = mousefar; vector o = mousenear; - if (vlen(o - t) > 8192) + if (vlen(o - t) > 8192 && !autocvar_ca_brush_view) t = o + normalize(t)*8192; traceline(o, t, TRUE, world); @@ -1554,6 +1662,7 @@ brusheditormodes registercommand("brushedit_settexture"); registercommand("brushedit_subtract"); registercommand("brushedit_binds"); + registercommand("brushedit_binds_default"); registercommand("+brushedit_nogrid"); registercommand("-brushedit_nogrid"); }; @@ -1585,9 +1694,28 @@ brusheditormodes case "-brushedit_nogrid": nogrid = FALSE; break; case "brushedit_matchface": - //IMPLEMENTME brushtool = BT_NONE; bt_points = 0; + + + { + vector t = mousefar; + vector o = mousenear; + int i; + if (vlen(o - t) > 8192 && !autocvar_ca_brush_view) + t = o + normalize(t)*8192; + + traceline(o, t, TRUE, world); + + i = mergebrushes(selectedbrushmodel, selectedbrush, trace_brush_id, selectedbrushface, trace_brush_faceid); + if (i) + { + brush_selected(selectedbrushmodel, selectedbrush, -1, FALSE); + selectedbrush = i; + selectedbrushface = 0; + brush_selected(selectedbrushmodel, selectedbrush, selectedbrushface, TRUE); + } + } break; case "brushedit_resettexcoords": brushedit_resettextures(); @@ -1605,9 +1733,27 @@ brusheditormodes brush_history_delete(selectedbrushmodel, selectedbrush); selectedbrush = 0; break; + case "brushedit_binds_default": + localcmd("echo Setting default brusheditor bindings\n"); + localcmd("bind ctrl+z brushedit_undo\n"); + localcmd("bind ctrl+y brushedit_redo\n"); + localcmd("bind ctrl+i brushedit_create\n"); + localcmd("bind ctrl+s brushedit_slice\n"); + localcmd("bind ctrl+m brushedit_matchface\n"); + localcmd("bind ctrl+delete brushedit_delete\n"); + localcmd("bind ctrl+backspace brushedit_subtract\n"); + localcmd("bind ctrl+c +brushedit_clone\n"); + localcmd("bind ctrl+d +brushedit_draw\n"); + localcmd("bind ctrl+r +brushedit_rotate\n"); + localcmd("bind ctrl+f +brushedit_pushface\n"); + localcmd("bind ctrl+t +brushedit_scrolltex\n"); + localcmd("bind ctrl+v +brushedit_vertex\n"); + break; case "brushedit_binds": localcmd("conmenu \"\"\n"); float foo = 0; + localcmd(sprintf("menutext 0 %g \"Set Default Bindings\" \"brushedit_binds_default\"\n", foo+=8)); + foo+=8; localcmd(sprintf("menubind 0 %g \"brushedit_create\" \"Creation Tool\"\n", foo+=8)); localcmd(sprintf("menubind 0 %g \"brushedit_slice\" \"Slice Tool\"\n", foo+=8)); localcmd(sprintf("menubind 0 %g \"brushedit_delete\" \"Delete\"\n", foo+=8)); @@ -1642,7 +1788,7 @@ float(float key, float unic, vector mousepos) editor_brushes_key = vector o = mousenear; int i, p; - if (vlen(o - t) > 8192) + if (vlen(o - t) > 8192 && !autocvar_ca_brush_view) t = o + normalize(t)*8192; if (key == K_ESCAPE) { @@ -1652,9 +1798,7 @@ float(float key, float unic, vector mousepos) editor_brushes_key = brushtool = BT_NONE; else { - brush_selected(selectedbrushmodel, selectedbrush, -1, FALSE); - selectedbrush = 0; - selectedbrushface = 0; + brush_deselectall(); } return TRUE; } @@ -1739,6 +1883,14 @@ float(float key, float unic, vector mousepos) editor_brushes_key = else if (trace_brush_id != selectedbrush || selectedbrushface != trace_brush_faceid || selectedbrushmodel != tracemodel) { brush_selected(selectedbrushmodel, selectedbrush, -1, FALSE); + if (!shiftdown) + { + if (!brush_isselected(trace_brush_id)) + brush_deselectall(); + } + else if (brush_deselect(trace_brush_id)) + return TRUE; + brush_select(trace_brush_id); selectedbrush = trace_brush_id; selectedbrushface = trace_brush_faceid; selectedbrushmodel = tracemodel; @@ -1794,13 +1946,12 @@ float(float key, float unic, vector mousepos) editor_brushes_key = { if (bt_points >= 3) { - brush_selected(selectedbrushmodel, selectedbrush, -1, FALSE); - tmp_numfaces = BrushFromPoints(tmp_faces, tmp_faces.length, bt_point, bt_points, autocvar_ca_newbrushheight); bt_points = 0; - selectedbrush = brush_history_create(selectedbrushmodel, tmp_faces, tmp_numfaces, 1i); + int newbr = brush_history_create(selectedbrushmodel, tmp_faces, tmp_numfaces, 1i); - brush_selected(selectedbrushmodel, selectedbrush, selectedbrushface, TRUE); + brush_deselectall(); + brush_select(newbr); } return TRUE; } @@ -1811,7 +1962,7 @@ float(float key, float unic, vector mousepos) editor_brushes_key = //generate a new face plane fa = &tmp_faces[tmp_numfaces]; - fa->planenormal = normalize(cross(bt_point[2] - bt_point[0], bt_point[1] - bt_point[0])); + fa->planenormal = normalize(crossproduct(bt_point[2] - bt_point[0], bt_point[1] - bt_point[0])); fa->planedist = bt_point[0] * fa->planenormal; fa->shadername = tmp_faces[0].shadername; //find a neighbour? @@ -1830,34 +1981,19 @@ float(float key, float unic, vector mousepos) editor_brushes_key = reset_texturecoords(fa); //delete the old one and insert the new - brush_history_delete(selectedbrushmodel, selectedbrush); - selectedbrush = brush_history_create(selectedbrushmodel, tmp_faces, tmp_numfaces, tmp_contents); + int newbr1 = brush_history_edit(selectedbrushmodel, selectedbrush, tmp_faces, tmp_numfaces, tmp_contents); //and insert another new one too, because inserting a plane like this generates two fragments and I'm too lazy to work out which is the front and which is the back. fa->planenormal *= -1; fa->planedist *= -1; - selectedbrush = brush_history_create(selectedbrushmodel, tmp_faces, tmp_numfaces, tmp_contents); - selectedbrushface = 0; + int newbr2 = brush_history_create(selectedbrushmodel, tmp_faces, tmp_numfaces, tmp_contents); + + brush_deselectall(); + brush_select(newbr2); return TRUE; } else return FALSE; } - if (key == 'm') //I'd use #, but that would cause a problem for americans - { - traceline(o, t, TRUE, world); - - i = mergebrushes(selectedbrushmodel, selectedbrush, trace_brush_id, selectedbrushface, trace_brush_faceid); - if (i) - { - brush_selected(selectedbrushmodel, selectedbrush, -1, FALSE); - selectedbrush = i; - selectedbrushface = 0; - brush_selected(selectedbrushmodel, selectedbrush, selectedbrushface, TRUE); - } - - return TRUE; - } - return FALSE; }; diff --git a/quakec/csaddon/src/editor_ents.qc b/quakec/csaddon/src/editor_ents.qc index 27e27a84f..8a0ee5dd7 100644 --- a/quakec/csaddon/src/editor_ents.qc +++ b/quakec/csaddon/src/editor_ents.qc @@ -369,7 +369,7 @@ void(string shadername, vector min, vector max, vector col) editor_ents_drawbbox #undef line }; -void() editor_ents_add = +void() editor_ents_addentities = { int e; string shadername; diff --git a/quakec/csaddon/src/editor_lights.qc b/quakec/csaddon/src/editor_lights.qc index a94ea18ba..87dd07b1b 100644 --- a/quakec/csaddon/src/editor_lights.qc +++ b/quakec/csaddon/src/editor_lights.qc @@ -12,7 +12,7 @@ static float editfield; static string editvalue; static entity tempent; static float helphidden; -void() editor_lights_add = +void() editor_lights_addentities = { float l; if (!tempent) diff --git a/quakec/csaddon/src/editor_terrain.qc b/quakec/csaddon/src/editor_terrain.qc index 7fb6be99f..9c37ae65b 100644 --- a/quakec/csaddon/src/editor_terrain.qc +++ b/quakec/csaddon/src/editor_terrain.qc @@ -441,7 +441,7 @@ float(float keyc, float unic, vector m) editor_terrain_key = return TRUE; }; -void(vector mousepos) editor_terrain_add = +void(vector mousepos) editor_terrain_addentities = { float s,c; float r; diff --git a/specs/replacementdeltas.txt b/specs/replacementdeltas.txt index c159d3906..03bbf7bc2 100644 --- a/specs/replacementdeltas.txt +++ b/specs/replacementdeltas.txt @@ -109,7 +109,7 @@ Updated network primitives: 16 = reserved: low precision. 32 = specific colourmap. UF_COLORMAP byte's low 4 bits are pants. high 4 bits are shirt. 64 = reserved: act as if part of world. - 128 = complex animation. If set, there are more bytes following. This is not implemented by FTE at this time. + 128 = onground, this hint is for nq engines. UF_EXTEND2(15): nothing here - already sent. listed for completeness. UF_ALPHA(16): byte - 0-255 expresses effective range of 0-1. 0 is invisible. 255 is fully visible and default. @@ -154,7 +154,7 @@ Updated network primitives: FIXME weirdness: predicting non-players? yes please. - player angles are a huge wtf. this protocol uses view angles then divides the angle by 1/3rd post-prediction for MOVETYPE_WALK/STEP entities. + player angles are a huge wtf. this protocol uses view angles then divides the angle by 1/3rd post-prediction for MOVETYPE_WALK entities, see PEXT2_PREDINFO for a fix. there is no information regarding the nextthink of entities. It *should* be possible to guess well enough without it. Either way it is rare enough that engines already need to interpolate well enough without this info, making it somewhat redundant, imho. @@ -247,3 +247,17 @@ Hack Zone: FTE servers will not wait for modellist/soundlist/prespawn commands to be acked before sending the next part of data, other than to correctly parse the map checksum. FTE servers will only burst all this information if ReplacementDeltas is supported. It was found that certain clients (namely FTE) was triggering some code to detect messed up ordering - messed up ordering that is fixed having the server track progress instead of relying on echos. This extension is simply a handy check to see if the client can cope and without spamming the console. Vanilla QuakeWorld clients appeared to be able to cope with this, but there are indeed certain ways it can fail, like if the client tries to pause for downloads. + + + +PredInfo Extension: + #define PEXT2_PREDINFO 0x00000020 + Purpose: + This extra extension was to fix up some issues with the NQ protocol, but some parts also apply to QW too. + Protocol changes: + stats are sent semi-unreliably using the same nack mechanism. some extra stats are added to ensure that clc_clientdata is fully redundant. + weaponframe is moved out of entity deltas and back into a stat, the bit is reused for the client's v_angle to allow spectating without assumptions about angles. + there is an added 16-bit movement sequence number added directly after both clc_move and svcfte_updateentities bytes, allowing the server to ack the client's input sequence, allowing for prediction in NQ. + nq's clc_move's timestamp is no longer used to calculate pings. the server calculates pings based upon acks instead. this is harder to spoof. + guarentees that clc_move has at least 16bit precision. + \ No newline at end of file