From 6d36834f8e692ae54ababa67ff603605c6564ce0 Mon Sep 17 00:00:00 2001 From: Spoike Date: Sun, 26 Jul 2015 10:56:18 +0000 Subject: [PATCH] Reworked client support for DPP5+. less code now, its much more graceful. added waterfog command. waterfog overrides regular fog only when the view is in water. fixed 64bit printf format specifiers. should work better on winxp64. fixed some spec angle weirdness. fixed viewsize 99.99 weirdness with ezhud. fixed extra offset on the console (exhibited in 64bit builds, but not limited to). fixed .avi playback, can now actually display frames again. reimplemented line sparks. fixed r_editlights_save flipping the light's pitch. fixed issue with oggs failing to load. fixed condump to cope with unicode properly. made sv_bigcoords default except in quake. hexen2 kinda needs it for bsp angle precision. fixed nq server to not stall weirdly on map changes. fixed qwprogs svc_cdtrack not bugging out with nq clients on the server. fixed restart command to load the last map run by the server, instead of start.bsp (when idle) optimised d3d9 renderer a little. now uses less draw calls, especially with complex scenes. seems to get higher framerates than opengl now. fixed d3d9 renderer to not bug out quite so much when run fullscreen (shader subsystem is now correctly initialised). fixed a couple of bugs from font change. also now supports utf-8 in a few more places. r_editlights_reload no longer generates rtlights inside the void. this resolves a few glitches (but should also help framerates a little). fixed so corona-only lights won't generate shadowmaps and waste lots of time. removed lots of #defines from qclib. I should never have made them in the first place, but I was lazy. obviously there's more left that I cba to remove yet. fixed nested calls with variant-vectors. this fixes csaddon's light editor. fixed qcc hc calling conventions using redundant stores. disabled keywords can still be used by using __keyword instead. fixed ftegccgui grep feature. fixed motionless-dog qcc bug. tweaked qcc warnings a little. -Wall is now a viable setting. you should be able to fix all those warnings. fixed qw svc_intermission + dpp5+ clients bug. fixed annoying spam about disconnecting in hexen2. rewrote status command a little to cope with ipv6 addresses more gracefully fixed significant stall when hibernating/debugging a server with a player sitting on it. fixed truelightning. fixed rocketlight overriding pflags. fixed torches vanishing on vid_restart. fixed issue with decal scaling. fixed findentityfield builtin. fixed fteqcc issue with ptr+1 fixed use of arrays inside class functions. fixed/implemented fteqcc emulation of pointer opcodes. added __inout keyword to fteqcc, so that it doesn't feel so horrendous. fixed sizeof(*foo) fixed *struct = struct; fixed recursive structs. fixed fteqcc warning report. fixed sdl2 controller support, hopefully. attempted to implement xinput, including per-player audio playback. slightly fixed relaxed attitude to mouse focus when running fullscreen. fixed weird warnings/errors with 'ent.arrayhead' terms. now generates sane errors. implemented bindmaps (for csqc). fixed crashing bug with eprint builtin. implemented subset of music_playlist_* functionality. significant changes to music playback. fixed some more dpcsqc compat. fixed binds menu. now displays and accepts modifiers. fixed issues with huge lightmaps. fixed protocol determinism with dp clients connecting to fte servers. the initial getchallenge request now inhibits vanilla nq connection requests. implemented support for 'dupe' userinfo key, allowing clients to request client->server packet duplication. should probably queue them tbh. implemented sv_saveentfile command. fixed resume after breaking inside a stepped-over function. fixed erroneous footer after debugging. (I wonder just how many things I broke with these fixes) git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4946 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- engine/client/cl_cg.c | 20 +- engine/client/cl_ents.c | 281 +++++++++----- engine/client/cl_ignore.c | 82 ++-- engine/client/cl_ignore.h | 6 +- engine/client/cl_input.c | 3 + engine/client/cl_main.c | 113 ++++-- engine/client/cl_parse.c | 62 +-- engine/client/cl_plugin.inc | 2 +- engine/client/cl_pred.c | 5 - engine/client/cl_screen.c | 28 +- engine/client/cl_tent.c | 23 +- engine/client/cl_ui.c | 2 +- engine/client/client.h | 26 +- engine/client/console.c | 19 +- engine/client/image.c | 136 +++++-- engine/client/in_generic.c | 197 ++++++++-- engine/client/in_win.c | 624 ++++++++++++++---------------- engine/client/input.h | 2 + engine/client/keys.c | 221 +++++++++-- engine/client/keys.h | 11 +- engine/client/m_items.c | 54 ++- engine/client/m_mp3.c | 242 ++++++++---- engine/client/m_options.c | 2 +- engine/client/m_script.c | 3 + engine/client/menu.c | 51 ++- engine/client/menu.h | 4 +- engine/client/merged.h | 1 + engine/client/p_script.c | 143 ++++--- engine/client/pr_clcmd.c | 33 +- engine/client/pr_csqc.c | 220 ++++++++--- engine/client/pr_menu.c | 35 +- engine/client/quakedef.h | 45 +++ engine/client/r_2d.c | 8 +- engine/client/r_part.c | 88 ++++- engine/client/r_surf.c | 61 ++- engine/client/render.h | 21 +- engine/client/renderer.c | 25 +- engine/client/sbar.c | 16 +- engine/client/screen.h | 2 +- engine/client/snd_al.c | 8 +- engine/client/snd_dma.c | 289 +++++++++----- engine/client/snd_mem.c | 2 +- engine/client/snd_mix.c | 4 +- engine/client/snd_ov.c | 9 +- engine/client/sound.h | 47 ++- engine/client/sys_win.c | 78 ++-- engine/client/view.c | 58 ++- engine/client/wad.c | 7 +- engine/common/bothdefs.h | 1 + engine/common/cmd.c | 11 +- engine/common/com_mesh.c | 28 +- engine/common/common.c | 2 +- engine/common/common.h | 42 ++ engine/common/fs.c | 4 +- engine/common/gl_q2bsp.c | 1 + engine/common/net.h | 3 +- engine/common/net_chan.c | 10 +- engine/common/net_ssl_winsspi.c | 48 +-- engine/common/net_wins.c | 4 +- engine/common/particles.h | 3 +- engine/common/plugin.c | 13 +- engine/common/pr_bgcmd.c | 57 ++- engine/common/pr_common.h | 4 + engine/common/protocol.h | 1 + engine/common/vm.h | 40 -- engine/common/world.h | 2 +- engine/common/zone.c | 3 + engine/d3d/d3d_backend.c | 190 ++++++--- engine/d3d/d3d_image.c | 4 +- engine/d3d/vid_d3d.c | 13 +- engine/d3d/vid_d3d11.c | 2 - engine/dotnet2005/ftequake.vcproj | 4 + engine/gl/gl_alias.c | 3 + engine/gl/gl_backend.c | 29 +- engine/gl/gl_font.c | 2 +- engine/gl/gl_heightmap.c | 25 +- engine/gl/gl_model.c | 85 +++- engine/gl/gl_model.h | 8 +- engine/gl/gl_rlight.c | 49 ++- engine/gl/gl_rmain.c | 7 + engine/gl/gl_shader.c | 41 +- engine/gl/gl_shadow.c | 35 +- engine/gl/gl_vidnt.c | 2 +- engine/gl/glquake.h | 5 +- engine/gl/shader.h | 1 + engine/qclib/execloop.h | 4 +- engine/qclib/initlib.c | 64 +-- engine/qclib/pr_comp.h | 6 +- engine/qclib/pr_edict.c | 88 ++--- engine/qclib/pr_exec.c | 99 ++--- engine/qclib/pr_multi.c | 70 ++-- engine/qclib/progsint.h | 22 +- engine/qclib/progslib.h | 9 +- engine/qclib/qcc.h | 2 + engine/qclib/qcc_pr_comp.c | 398 +++++++++++++++---- engine/qclib/qcc_pr_lex.c | 94 +++-- engine/qclib/qccgui.c | 87 ++++- engine/qclib/qccmain.c | 95 +++-- engine/server/net_preparse.c | 72 +++- engine/server/pr_cmds.c | 65 ++-- engine/server/pr_q1qvm.c | 14 +- engine/server/savegame.c | 33 +- engine/server/server.h | 9 +- engine/server/sv_ccmds.c | 142 ++++--- engine/server/sv_ents.c | 149 ++++--- engine/server/sv_init.c | 24 +- engine/server/sv_main.c | 257 ++++++------ engine/server/sv_mvd.c | 10 +- engine/server/sv_phys.c | 6 +- engine/server/sv_send.c | 43 +- engine/server/sv_user.c | 27 +- engine/server/svq2_game.c | 2 +- engine/server/svq3_game.c | 5 +- plugins/jabber/jabberclient.c | 6 +- 114 files changed, 4049 insertions(+), 2054 deletions(-) diff --git a/engine/client/cl_cg.c b/engine/client/cl_cg.c index dd1e0d33..4e359943 100644 --- a/engine/client/cl_cg.c +++ b/engine/client/cl_cg.c @@ -934,10 +934,10 @@ static qintptr_t CG_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con break; case CG_S_STARTBACKGROUNDTRACK: - Media_BackgroundTrack(VM_POINTER(arg[0]), VM_POINTER(arg[1])); + Media_NamedTrack(VM_POINTER(arg[0]), VM_POINTER(arg[1])); return 0; case CG_S_STOPBACKGROUNDTRACK: - Media_BackgroundTrack(NULL, NULL); + Media_NamedTrack(NULL, NULL); return 0; case CG_S_CLEARLOOPINGSOUNDS: //clearall @@ -954,13 +954,13 @@ static qintptr_t CG_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con vec3_t *axis = VM_POINTER(arg[2]); int inwater = VM_LONG(arg[3]); - r_refdef.audio.defaulted = false; + cl.playerview[0].audio.defaulted = false; //r_refdef.audio.entity = VM_LONG(arg[0]); - VectorCopy(org, r_refdef.audio.origin); - VectorCopy(axis[0], r_refdef.audio.forward); - VectorCopy(axis[1], r_refdef.audio.right); - VectorCopy(axis[2], r_refdef.audio.up); - r_refdef.audio.inwater = inwater; + VectorCopy(org, cl.playerview[0].audio.origin); + VectorCopy(axis[0], cl.playerview[0].audio.forward); + VectorCopy(axis[1], cl.playerview[0].audio.right); + VectorCopy(axis[2], cl.playerview[0].audio.up); + cl.playerview[0].audio.inwater = inwater; } break; @@ -976,8 +976,8 @@ static qintptr_t CG_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con case CG_KEY_GETKEY: { - int ret[2]; - M_FindKeysForCommand (0, VM_POINTER(arg[0]), ret); + int ret[1]; + M_FindKeysForCommand (0, 0, VM_POINTER(arg[0]), ret, NULL, countof(ret)); return ret[0]; } break; diff --git a/engine/client/cl_ents.c b/engine/client/cl_ents.c index c0d62ec4..010692c4 100644 --- a/engine/client/cl_ents.c +++ b/engine/client/cl_ents.c @@ -562,7 +562,7 @@ void CLFTE_ReadDelta(unsigned int entnum, entity_state_t *news, entity_state_t * if ((bits & (UF_EFFECTS | UF_EFFECTS2)) == (UF_EFFECTS | UF_EFFECTS2)) news->effects = MSG_ReadLong(); else if (bits & UF_EFFECTS2) - news->effects = MSG_ReadShort(); + news->effects = (unsigned short)MSG_ReadShort(); else if (bits & UF_EFFECTS) news->effects = MSG_ReadByte(); @@ -639,9 +639,12 @@ void CLFTE_ReadDelta(unsigned int entnum, entity_state_t *news, entity_state_t * if (!(predbits & UFP_VIEWANGLE) || !(cls.fteprotocolextensions2 & PEXT2_PREDINFO)) { - news->u.q1.vangle[0] = ANGLE2SHORT(news->angles[0]); - news->u.q1.vangle[1] = ANGLE2SHORT(news->angles[1]); - news->u.q1.vangle[2] = ANGLE2SHORT(news->angles[2]); + if (bits & UF_ANGLESXZ) + news->u.q1.vangle[0] = ANGLE2SHORT(news->angles[0] * ((bits & UF_PREDINFO)?-3:-1)); + if (bits & UF_ANGLESY) + news->u.q1.vangle[1] = ANGLE2SHORT(news->angles[1]); + if (bits & UF_ANGLESXZ) + news->u.q1.vangle[2] = ANGLE2SHORT(news->angles[2]); } if (bits & UF_MODEL) @@ -1202,6 +1205,10 @@ entity_state_t *CL_FindOldPacketEntity(int num) void DP5_ParseDelta(entity_state_t *s) { int bits; + + if (cl_shownet.ival >= 3) + Con_Printf("%3i: Update %i", msg_readcount, s->number); + bits = MSG_ReadByte(); if (bits & E5_EXTEND1) { @@ -1214,6 +1221,43 @@ void DP5_ParseDelta(entity_state_t *s) } } + if (cl_shownet.ival >= 3) + { + if (bits & E5_FULLUPDATE) Con_Printf(" full"); + if (bits & E5_ORIGIN) Con_Printf(" origin"); + if (bits & E5_ANGLES) Con_Printf(" angles"); + if (bits & E5_MODEL) Con_Printf(" model"); + if (bits & E5_FRAME) Con_Printf(" frame"); + if (bits & E5_SKIN) Con_Printf(" kin"); + if (bits & E5_EFFECTS) Con_Printf(" effects"); + if (bits & E5_EXTEND1) Con_Printf(" extend1"); + if (bits & E5_FLAGS) Con_Printf(" flags"); + if (bits & E5_ALPHA) Con_Printf(" alpha"); + if (bits & E5_SCALE) Con_Printf(" scale"); + if (bits & E5_ORIGIN32) Con_Printf(" origin32"); + if (bits & E5_ANGLES16) Con_Printf(" angles16"); + if (bits & E5_MODEL16) Con_Printf(" model16"); + if (bits & E5_COLORMAP) Con_Printf(" colormap"); + if (bits & E5_EXTEND2) Con_Printf(" extend2"); + if (bits & E5_ATTACHMENT) Con_Printf(" attachment"); + if (bits & E5_LIGHT) Con_Printf(" light"); + if (bits & E5_GLOW) Con_Printf(" glow"); + if (bits & E5_EFFECTS16) Con_Printf(" effects16"); + if (bits & E5_EFFECTS32) Con_Printf(" effects32"); + if (bits & E5_FRAME16) Con_Printf(" frame16"); + if (bits & E5_COLORMOD) Con_Printf(" colormod"); + if (bits & E5_EXTEND3) Con_Printf(" extend3"); + if (bits & E5_GLOWMOD) Con_Printf(" glowmod"); + if (bits & E5_COMPLEXANIMATION) Con_Printf(" complexanimation"); + if (bits & E5_TRAILEFFECTNUM) Con_Printf(" traileffectnum"); + if (bits & E5_UNUSED27) Con_Printf(" unused27"); + if (bits & E5_UNUSED28) Con_Printf(" unused28"); + if (bits & E5_UNUSED29) Con_Printf(" unused29"); + if (bits & E5_UNUSED30) Con_Printf(" unused30"); + if (bits & E5_EXTEND4) Con_Printf(" extend4"); + Con_Printf("\n"); + } + if (bits & E5_ALLUNUSED) { Host_EndGame("Detected 'unused' bits in DP5+ entity delta - %x (%x)\n", bits, (bits & E5_ALLUNUSED)); @@ -1329,6 +1373,17 @@ void DP5_ParseDelta(entity_state_t *s) s->u.q1.traileffectnum = MSG_ReadShort(); } +static int QDECL CLDP_SortEntities(const void *va, const void *vb) +{ + const entity_state_t *a = va, *b = vb; + if (a->inactiveflag && b->inactiveflag) + return 0; + if ((a->number < b->number || b->inactiveflag) && !a->inactiveflag) + return -1; + else + return 1; +} + void CLDP_ParseDarkPlaces5Entities(void) //the things I do.. :o( { //the incoming entities do not come in in any order. :( @@ -1339,12 +1394,11 @@ void CLDP_ParseDarkPlaces5Entities(void) //the things I do.. :o( //this gets in the way of tracking multiple frames, and thus doesn't match fte too well - packet_entities_t *pack, oldpack; - static packet_entities_t newpack; + packet_entities_t *oldpack, *newpack; entity_state_t *to, *from; unsigned int read; - int oldi, newi, lowesti, lowestv, newremaining; + int oldi; qboolean remove; //server->client sequence @@ -1356,14 +1410,31 @@ void CLDP_ParseDarkPlaces5Entities(void) //the things I do.. :o( if (cls.protocol_nq >= CPNQ_DP7) CL_AckedInputFrame(cls.netchan.incoming_sequence, MSG_ReadLong(), true); /*client input sequence which has been acked*/ + if (cl.validsequence) + oldpack = &cl.inframes[(cl.validsequence)&UPDATE_MASK].packet_entities; + else + oldpack = NULL; + cl.validsequence = cls.netchan.incoming_sequence; cl.inframes[(cls.netchan.incoming_sequence)&UPDATE_MASK].receivedtime = realtime; cl.inframes[(cls.netchan.incoming_sequence)&UPDATE_MASK].frameid = cls.netchan.incoming_sequence; - pack = &cl.inframes[(cls.netchan.incoming_sequence)&UPDATE_MASK].packet_entities; - pack->servertime = cl.gametime; - oldpack = *pack; - oldi = 0; + newpack = &cl.inframes[(cls.netchan.incoming_sequence)&UPDATE_MASK].packet_entities; + newpack->servertime = cl.gametime; + + //copy old state to new state + if (newpack != oldpack) + { + if (oldpack) + { + newpack->num_entities = oldpack->num_entities; + newpack->max_entities = newpack->num_entities+16; //for slop for new ents, to reduce reallocs + newpack->entities = BZ_Realloc(newpack->entities, sizeof(entity_state_t)*newpack->max_entities); + memcpy(newpack->entities, oldpack->entities, sizeof(entity_state_t)*newpack->num_entities); + } + else + newpack->num_entities = 0; + } + oldpack = NULL; - newpack.num_entities = 0; for (;;) { read = MSG_ReadShort(); @@ -1378,98 +1449,57 @@ void CLDP_ParseDarkPlaces5Entities(void) //the things I do.. :o( Host_EndGame("Too many entities.\n"); from = &nullentitystate; + to = NULL; - for (oldi=0 ; oldinum_entities ; oldi++) { - if (read == oldpack.entities[oldi].number) + if (read == newpack->entities[oldi].number) { - from = &oldpack.entities[oldi]; - from->inactiveflag |= 1; //so we don't copy it. + from = &newpack->entities[oldi]; + to = &newpack->entities[oldi]; break; } } - if (remove) - { - continue; - } + if (!to) + { //okay, so this is new + if (newpack->num_entities==newpack->max_entities) + { + newpack->max_entities = newpack->num_entities+16; + newpack->entities = BZ_Realloc(newpack->entities, sizeof(entity_state_t)*newpack->max_entities); + } - if (newpack.num_entities==newpack.max_entities) - { - newpack.max_entities = newpack.num_entities+16; - newpack.entities = BZ_Realloc(newpack.entities, sizeof(entity_state_t)*newpack.max_entities); + to = &newpack->entities[newpack->num_entities]; + newpack->num_entities++; } - to = &newpack.entities[newpack.num_entities]; - newpack.num_entities++; - memcpy(to, from, sizeof(*to)); to->number = read; - DP5_ParseDelta(to); - to->inactiveflag &= ~1; - } - /*we're writing into the old one, clear it out prematurely (to make the malloc below trigger, and free it at the end)*/ - pack->max_entities = 0; - pack->entities = NULL; - - //make sure there's enough space for both lists - if (oldpack.num_entities + newpack.num_entities>=pack->max_entities) - { - pack->max_entities = oldpack.num_entities + newpack.num_entities; - pack->entities = BZ_Realloc(pack->entities, sizeof(entity_state_t)*pack->max_entities); - } - pack->num_entities = 0; - - //we're read all the new states, so have current info - //merge the packets, sorting the new ones (so the output is always sorted) - for (oldi = 0, lowesti=0, lowestv = 0, newremaining = newpack.num_entities; newremaining || oldi < oldpack.num_entities; ) - { - if (oldi == oldpack.num_entities) - from = NULL; - else - { - from = &oldpack.entities[oldi]; - if (from->inactiveflag & 1) - { - oldi++; - continue; - } - } - - if (newremaining && !lowestv) - { - lowestv = 0x7ffffffe; - for(newi = 0; newi < newpack.num_entities; newi++) - { - if (newpack.entities[newi].inactiveflag & 1) - continue; - if (newpack.entities[newi].number < lowestv) - { - lowestv = newpack.entities[newi].number; - lowesti = newi; - } - } - } - - /*use the new packet instead if we need to*/ - if (!from || (from->number > lowestv && lowestv)) - { - from = &newpack.entities[lowesti]; - from->inactiveflag |= 1; - lowestv = 0; /*find the next oldest*/ - newremaining--; + if (remove) + { //ent is meant to be removed. flag it as such. we'll strip it out later. + if (cl_shownet.ival >= 3) + Con_Printf("Remove %i\n", read); + to->inactiveflag = 1; } else - oldi++; - - to = &pack->entities[pack->num_entities]; - pack->num_entities++; - memcpy(to, from, sizeof(*to)); - to->inactiveflag &= ~1; + { +// Con_Printf("Update %i\n", read); + DP5_ParseDelta(to); + to->inactiveflag = 0; + } } - BZ_Free(oldpack.entities); + qsort(newpack->entities, newpack->num_entities, sizeof(entity_state_t), CLDP_SortEntities); + + //get rid of any removed ents (we sorted these to the end) + while (newpack->num_entities) + { + if (newpack->entities[newpack->num_entities-1].inactiveflag) + newpack->num_entities--; + else + break; + } } void CLNQ_ParseEntity(unsigned int bits) @@ -2659,6 +2689,70 @@ void CL_AddDecal(shader_t *shader, vec3_t origin, vec3_t up, vec3_t side, vec3_t cl_numstris--; } +void R_AddItemTimer(vec3_t shadoworg, float yaw, float radius, float percent) +{ + vec3_t eang; + shader_t *s; + scenetris_t *t; + cl_adddecal_ctx_t ctx; + +// if (!r_shadows.value) +// return; + + s = R_RegisterShader("timershader", SUF_NONE, + "{\n" + "polygonoffset\n" + "program itemtimer\n" + "{\n" + "map $diffuse\n" + "blendfunc src_alpha one\n" + "rgbgen vertex\n" + "alphagen vertex\n" + "}\n" + "}\n"); + if (!s->prog) + return; + TEXASSIGN(s->defaulttextures->base, balltexture); + + + eang[0] = 0; + eang[1] = yaw; + eang[2] = 0; + AngleVectors(eang, ctx.axis[1], ctx.axis[2], ctx.axis[0]); + VectorNegate(ctx.axis[0], ctx.axis[0]); + + ctx.offset[2] = DotProduct(shadoworg, ctx.axis[2]) + 0.5*radius; + ctx.offset[1] = DotProduct(shadoworg, ctx.axis[1]) + 0.5*radius; + ctx.offset[0] = DotProduct(shadoworg, ctx.axis[0]); + ctx.scale[1] = 1/radius; + ctx.scale[2] = 1/radius; + ctx.scale[0] = 0;//.5/radius; + + /*reuse the previous trigroup if its the same shader*/ + if (cl_numstris && cl_stris[cl_numstris-1].shader == s && cl_stris[cl_numstris-1].flags == (BEF_NODLIGHT|BEF_NOSHADOWS)) + t = &cl_stris[cl_numstris-1]; + else + { + if (cl_numstris == cl_maxstris) + { + cl_maxstris += 8; + cl_stris = BZ_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris); + } + t = &cl_stris[cl_numstris++]; + t->shader = s; + t->flags = BEF_NODLIGHT|BEF_NOSHADOWS; + t->numidx = 0; + t->numvert = 0; + t->firstidx = cl_numstrisidx; + t->firstvert = cl_numstrisvert; + } + + ctx.t = t; + Vector4Set(ctx.rgbavalue, 0.1, 0.1, 0.1, percent); + Mod_ClipDecal(cl.worldmodel, shadoworg, ctx.axis[0], ctx.axis[1], ctx.axis[2], radius, CL_AddDecal_Callback, &ctx); + if (!t->numidx) + cl_numstris--; +} void CLQ1_AddShadow(entity_t *ent) { float radius; @@ -2738,7 +2832,7 @@ void CLQ1_AddShadow(entity_t *ent) void CLQ1_AddPowerupShell(entity_t *ent, qboolean viewweap, unsigned int effects) { entity_t *shell; - if (!(effects & (EF_BLUE | EF_RED)) || !v_powerupshell.value || !ent) + if (!(effects & (EF_BLUE | EF_RED | EF_GREEN)) || !v_powerupshell.value || !ent) return; if (cl_numvisedicts == cl_maxvisedicts) @@ -3359,7 +3453,7 @@ void CL_LinkPacketEntities (void) //bots or powerup glows. items always glow, bots can be disabled if (state->modelindex != cl_playerindex || r_powerupglow.ival) - if (state->effects & (EF_BLUE | EF_RED | EF_BRIGHTLIGHT | EF_DIMLIGHT)) + if (state->effects & (EF_GREEN | EF_BLUE | EF_RED | EF_BRIGHTLIGHT | EF_DIMLIGHT)) { vec3_t colour; float radius; @@ -3396,6 +3490,13 @@ void CL_LinkPacketEntities (void) colour[1] += 0.5; colour[2] += 0.5; } + if (state->effects & EF_GREEN) + { + radius = max(radius,200); + colour[0] += 0.5; + colour[1] += 3.0; + colour[2] += 0.5; + } if (radius) { @@ -3657,7 +3758,7 @@ void CL_LinkPacketEntities (void) P_EmitEffect (ent->origin, model->particleeffect, &(le->emitstate)); //dlights are not so customisable. - if (r_rocketlight.value && (modelflags & MF_ROCKET)) + if (r_rocketlight.value && (modelflags & MF_ROCKET) && !(state->lightpflags & (PFLAGS_FULLDYNAMIC|PFLAGS_CORONA))) { float rad = 0; vec3_t dclr; diff --git a/engine/client/cl_ignore.c b/engine/client/cl_ignore.c index 0facb60e..5ef0050e 100644 --- a/engine/client/cl_ignore.c +++ b/engine/client/cl_ignore.c @@ -66,7 +66,7 @@ int Player_StringtoSlot(char *arg) return ((slot = Player_IdtoSlot(Q_atoi(arg))) >= 0) ? slot : PLAYER_ID_NOMATCH; } -int Player_NametoSlot(char *name) +int Player_NametoSlot(const char *name) { int i; @@ -95,17 +95,18 @@ char *Player_MyName (void) -cvar_t ignore_spec = SCVAR("ignore_spec", "0"); -cvar_t ignore_qizmo_spec = SCVAR("ignore_qizmo_spec", "0"); -cvar_t ignore_mode = SCVAR("ignore_mode", "0"); -cvar_t ignore_flood_duration = SCVAR("ignore_flood_duration", "4"); -cvar_t ignore_flood = SCVAR("ignore_flood", "0"); -cvar_t ignore_opponents = SCVAR("ignore_opponents", "0"); +cvar_t ignore_spec = CVAR("ignore_spec", "0"); +cvar_t ignore_qizmo_spec = CVAR("ignore_qizmo_spec", "0"); +cvar_t ignore_mode = CVAR("ignore_mode", "0"); +cvar_t ignore_flood_duration = CVARD("ignore_flood_duration", "4", "Time limit for inbound messages to be considered duplicates."); +cvar_t ignore_flood = CVARD("ignore_flood", "0", "Provides a way to reduce inbound spam from flooding out your chat (dupe messages are ignored).\n0: No inbound flood protection.\n1: Duplicate non-team messages will be filtered.\n2: ALL duplicate messages will be filtered\n"); +cvar_t ignore_opponents = CVAR("ignore_opponents", "0"); char ignoreteamlist[MAX_TEAMIGNORELIST][16 + 1]; typedef struct flood_s { + int playernum; char data[2048]; float time; } flood_t; @@ -492,47 +493,31 @@ static void UnignoreteamAll_f (void) Con_Printf("Team ignore list cleared\n"); } -char Ignore_Check_Flood(char *s, int flags, int offset) +char Ignore_Check_Flood(player_info_t *sender, const char *s, int flags) { - int i, p, q, len; - char name[MAX_INFO_KEY]; + int i; + int slot; if ( !( - ( (ignore_flood.value == 1 && (flags & TPM_NORMAL || flags & TPM_SPECTATOR)) || + ( (ignore_flood.value == 1 && ((flags & TPM_NORMAL) || (flags & TPM_SPECTATOR))) || (ignore_flood.value == 2 && flags != 0) ) ) ) { return NO_IGNORE_NO_ADD; } - if (flags == 1 || flags == TPM_SPECTATOR) - { - p = 0; - q = offset - 3; - } - else if (flags == TPM_TEAM) - { - p = 1; - q = offset - 4; - } - else if (flags == 8) - { - p = 7; - q = offset -3; - } - else + if (!sender) //don't ignore system messages. return NO_IGNORE_NO_ADD; - len = bound (0, q - p + 1, sizeof(name) - 1); + slot = sender - cl.players; - Q_strncpyz(name, s + p, len + 1); - if (!cls.demoplayback && !strcmp(name, Player_MyName())) + if (!cls.demoplayback && !strcmp(sender->name, Player_MyName())) { return NO_IGNORE_NO_ADD; } for (i = 0; i < FLOODLIST_SIZE; i++) { - if (floodlist[i].data[0] && !strncmp(floodlist[i].data, s, sizeof(floodlist[i].data) - 1) && + if (floodlist[i].playernum == slot && floodlist[i].data[0] && !strncmp(floodlist[i].data, s, sizeof(floodlist[i].data) - 1) && realtime - floodlist[i].time < ignore_flood_duration.value) { return IGNORE_NO_ADD; } @@ -540,8 +525,9 @@ char Ignore_Check_Flood(char *s, int flags, int offset) return NO_IGNORE_ADD; } -void Ignore_Flood_Add(char *s) +void Ignore_Flood_Add(player_info_t *sender, const char *s) { + floodlist[floodindex].playernum = sender - cl.players; floodlist[floodindex].data[0] = 0; Q_strncpyz(floodlist[floodindex].data, s, sizeof(floodlist[floodindex].data)); floodlist[floodindex].time = realtime; @@ -551,10 +537,9 @@ void Ignore_Flood_Add(char *s) } -qboolean Ignore_Message(char *s, int flags, int offset) +qboolean Ignore_Message(const char *sendername, const char *s, int flags) { - int slot, i, p, q, len; - char name[MAX_SCOREBOARDNAME]; + int slot, i; if (!ignore_mode.ival && (flags & 2)) return false; @@ -565,35 +550,14 @@ qboolean Ignore_Message(char *s, int flags, int offset) else if (ignore_spec.ival == 1 && (flags == 4) && !cl.spectator) return true; - if (flags == 1 || flags == 4) - { - p = 0; - q = offset - 3; - } - else if (flags == 2) - { - p = 1; - q = offset - 4; - } - else if (flags == 8) - { - p = 7; - q = offset - 3; - } - else - { + if (!sendername) return false; - } - len = bound (0, q - p + 1, sizeof(name) - 1); - Q_strncpyz(name, s + p, len + 1); - - if ((slot = Player_NametoSlot(name)) == PLAYER_NAME_NOMATCH) + if ((slot = Player_NametoSlot(sendername)) == PLAYER_NAME_NOMATCH) return false; if (IsIgnored(slot)) return true; - if (ignore_opponents.ival && ( (int) ignore_opponents.ival == 1 || @@ -610,7 +574,7 @@ qboolean Ignore_Message(char *s, int flags, int offset) if (!cl.teamplay) return false; - if (cl.players[slot].spectator || !strcmp(Player_MyName(), name)) + if (cl.players[slot].spectator || !strcmp(Player_MyName(), sendername)) return false; for (i = 0; i < MAX_TEAMIGNORELIST && ignoreteamlist[i][0]; i++) diff --git a/engine/client/cl_ignore.h b/engine/client/cl_ignore.h index f89dc6c7..886f7ebb 100644 --- a/engine/client/cl_ignore.h +++ b/engine/client/cl_ignore.h @@ -28,9 +28,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define IGNORE_NO_ADD 2 void Ignore_Init(void); -qboolean Ignore_Message(char *s, int flags, int offset); -char Ignore_Check_Flood(char *s, int flags, int offset); -void Ignore_Flood_Add(char *s); +qboolean Ignore_Message(const char *sendername, const char *s, int flags); +char Ignore_Check_Flood(player_info_t *sender, const char *s, int flags); +void Ignore_Flood_Add(player_info_t *sender, const char *s); void Ignore_ResetFloodList(void); #endif diff --git a/engine/client/cl_input.c b/engine/client/cl_input.c index d7291716..73c549a1 100644 --- a/engine/client/cl_input.c +++ b/engine/client/cl_input.c @@ -847,6 +847,9 @@ void CL_ClampPitch (int pnum) pv->viewangles[PITCH] += pv->viewanglechange[PITCH]; pv->viewangles[YAW] += pv->viewanglechange[YAW]; pv->viewangles[ROLL] += pv->viewanglechange[ROLL]; + pv->viewangles[YAW] /= 360; + pv->viewangles[YAW] = pv->viewangles[YAW] - (int)pv->viewangles[YAW]; + pv->viewangles[YAW] *= 360; VectorClear(pv->viewanglechange); #ifdef Q2CLIENT diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 40f49c83..bacc554d 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -134,7 +134,7 @@ cvar_t cl_teamchatsound = CVAR("cl_teamchatsound", "misc/talk.wav"); cvar_t r_torch = CVARF("r_torch", "0", CVAR_CHEAT); cvar_t r_rocketlight = CVARFC("r_rocketlight", "1", CVAR_ARCHIVE, Cvar_Limiter_ZeroToOne_Callback); cvar_t r_lightflicker = CVAR("r_lightflicker", "1"); -cvar_t cl_r2g = CVARF("cl_r2g", "0", CVAR_ARCHIVE); +cvar_t cl_r2g = CVARFD("cl_r2g", "0", CVAR_ARCHIVE, "Uses progs/grenade.mdl instead of progs/missile.mdl when 1."); cvar_t r_powerupglow = CVAR("r_powerupglow", "1"); cvar_t v_powerupshell = CVARF("v_powerupshell", "0", CVAR_ARCHIVE); cvar_t cl_gibfilter = CVARF("cl_gibfilter", "0", CVAR_ARCHIVE); @@ -298,7 +298,7 @@ void CL_UpdateWindowTitle(void) default: #ifndef CLIENTONLY if (sv.state) - Q_snprintfz(title, sizeof(title), "%s: %s", fs_gamename.string, sv.name); + Q_snprintfz(title, sizeof(title), "%s: %s", fs_gamename.string, svs.name); else #endif if (cls.demoplayback) @@ -337,6 +337,7 @@ void CL_MakeActive(char *gamename) //kill models left over from the last map. Mod_Purge(MP_MAPCHANGED); + Image_Purge(); //and reload shaders now if needed (this was blocked earlier) Shader_DoReload(); @@ -708,6 +709,7 @@ void CL_CheckForResend (void) #ifndef CLIENTONLY if (!cls.state && (!connectinfo.trying || sv.state != ss_clustermode) && sv.state) { + extern cvar_t dpcompat_nopreparse; unsigned int pext1, pext2; pext1 = 0; pext2 = 0; @@ -784,6 +786,22 @@ void CL_CheckForResend (void) { cls.protocol = CP_NETQUAKE; cls.protocol_nq = CPNQ_FITZ666; + //FIXME: pext + } + + if (dpcompat_nopreparse.ival) + { + if (progstype == PROG_QW && cls.protocol != CP_QUAKEWORLD) + { + cls.protocol = CP_QUAKEWORLD; + pext1 = Net_PextMask(1, false); + pext2 = Net_PextMask(2, false); + } + else if (progstype != PROG_QW && cls.protocol == CP_QUAKEWORLD) + { + cls.protocol = CP_NETQUAKE; + cls.protocol_nq = CPNQ_DP7; //dpcompat_nopreparse is only really needed for DP mods that send unknowable svc_tempentity messages to the client. + } } //make sure the protocol within demos is actually correct/sane @@ -1306,24 +1324,24 @@ void CL_BlendFog(fogstate_t *result, fogstate_t *oldf, float time, fogstate_t *n result->time = time; } -void CL_ResetFog(void) +void CL_ResetFog(int ftype) { //blend from the current state, not the old state. this means things work properly if we've not reached the new state yet. - CL_BlendFog(&cl.oldfog, &cl.oldfog, realtime, &cl.fog); + CL_BlendFog(&cl.oldfog[ftype], &cl.oldfog[ftype], realtime, &cl.fog[ftype]); //reset the new state to defaults, to be filled in by the caller. - memset(&cl.fog, 0, sizeof(cl.fog)); - cl.fog.time = realtime; - cl.fog.density = 0; - cl.fog.colour[0] = 0.3; - cl.fog.colour[1] = 0.3; - cl.fog.colour[2] = 0.3; - cl.fog.alpha = 1; - cl.fog.depthbias = 0; + memset(&cl.fog[ftype], 0, sizeof(cl.fog[ftype])); + cl.fog[ftype].time = realtime; + cl.fog[ftype].density = 0; + cl.fog[ftype].colour[0] = 0.3; + cl.fog[ftype].colour[1] = 0.3; + cl.fog[ftype].colour[2] = 0.3; + cl.fog[ftype].alpha = 1; + cl.fog[ftype].depthbias = 0; /* - cl.fog.end = 16384; - cl.fog.height = 1<<30; - cl.fog.fadedepth = 128; + cl.fog[ftype].end = 16384; + cl.fog[ftype].height = 1<<30; + cl.fog[ftype].fadedepth = 128; */ } @@ -1352,7 +1370,6 @@ void CL_ClearState (void) S_StopAllSounds (true); S_UntouchAll(); S_ResetFailedLoad(); - r_regsequence++; Cvar_ApplyLatches(CVAR_SERVEROVERRIDE); @@ -1364,6 +1381,7 @@ void CL_ClearState (void) SV_UnspawnServer(); #endif Mod_ClearAll (); + r_regsequence++; Cvar_ApplyLatches(CVAR_LATCH); } @@ -1440,7 +1458,8 @@ void CL_ClearState (void) // wipe the entire cl structure memset (&cl, 0, sizeof(cl)); - CL_ResetFog(); + CL_ResetFog(0); + CL_ResetFog(1); cl.allocated_client_slots = QWMAX_CLIENTS; #ifndef CLIENTONLY @@ -3548,52 +3567,53 @@ void CL_FTP_f(void) //fixme: make a cvar void CL_Fog_f(void) { + int ftype = strcmp(Cmd_Argv(0), "fog"); if ((cl.fog_locked && !Cmd_FromGamecode()) || Cmd_Argc() <= 1) - Con_Printf("Current fog %f (r:%f g:%f b:%f, a:%f bias:%f)\n", cl.fog.density, cl.fog.colour[0], cl.fog.colour[1], cl.fog.colour[2], cl.fog.alpha, cl.fog.depthbias); + Con_Printf("Current fog %f (r:%f g:%f b:%f, a:%f bias:%f)\n", cl.fog[ftype].density, cl.fog[ftype].colour[0], cl.fog[ftype].colour[1], cl.fog[ftype].colour[2], cl.fog[ftype].alpha, cl.fog[ftype].depthbias); else { - CL_ResetFog(); + CL_ResetFog(ftype); switch(Cmd_Argc()) { case 1: break; case 2: - cl.fog.density = atof(Cmd_Argv(1)); + cl.fog[ftype].density = atof(Cmd_Argv(1)); break; case 3: - cl.fog.density = atof(Cmd_Argv(1)); - cl.fog.colour[0] = cl.fog.colour[1] = cl.fog.colour[2] = atof(Cmd_Argv(2)); + cl.fog[ftype].density = atof(Cmd_Argv(1)); + cl.fog[ftype].colour[0] = cl.fog[ftype].colour[1] = cl.fog[ftype].colour[2] = atof(Cmd_Argv(2)); break; case 4: - cl.fog.density = 0.05; //make something up for vauge compat with fitzquake, so it doesn't get the default of 0 - cl.fog.colour[0] = atof(Cmd_Argv(1)); - cl.fog.colour[1] = atof(Cmd_Argv(2)); - cl.fog.colour[2] = atof(Cmd_Argv(3)); + cl.fog[ftype].density = 0.05; //make something up for vauge compat with fitzquake, so it doesn't get the default of 0 + cl.fog[ftype].colour[0] = atof(Cmd_Argv(1)); + cl.fog[ftype].colour[1] = atof(Cmd_Argv(2)); + cl.fog[ftype].colour[2] = atof(Cmd_Argv(3)); break; case 5: default: - cl.fog.density = atof(Cmd_Argv(1)); - cl.fog.colour[0] = atof(Cmd_Argv(2)); - cl.fog.colour[1] = atof(Cmd_Argv(3)); - cl.fog.colour[2] = atof(Cmd_Argv(4)); + cl.fog[ftype].density = atof(Cmd_Argv(1)); + cl.fog[ftype].colour[0] = atof(Cmd_Argv(2)); + cl.fog[ftype].colour[1] = atof(Cmd_Argv(3)); + cl.fog[ftype].colour[2] = atof(Cmd_Argv(4)); break; } if (cls.state == ca_active) - cl.fog.time += 1; + cl.fog[ftype].time += 1; //fitz: //if (Cmd_Argc() >= 6) cl.fog_time += atof(Cmd_Argv(5)); //dp: - if (Cmd_Argc() >= 6) cl.fog.alpha = atof(Cmd_Argv(5)); - if (Cmd_Argc() >= 7) cl.fog.depthbias = atof(Cmd_Argv(6)); + if (Cmd_Argc() >= 6) cl.fog[ftype].alpha = atof(Cmd_Argv(5)); + if (Cmd_Argc() >= 7) cl.fog[ftype].depthbias = atof(Cmd_Argv(6)); //if (Cmd_Argc() >= 8) cl.fog.end = atof(Cmd_Argv(7)); //if (Cmd_Argc() >= 9) cl.fog.height = atof(Cmd_Argv(8)); //if (Cmd_Argc() >= 10) cl.fog.fadedepth = atof(Cmd_Argv(9)); if (Cmd_FromGamecode()) - cl.fog_locked = !!cl.fog.density; + cl.fog_locked = !!cl.fog[ftype].density; } } @@ -3924,6 +3944,7 @@ void CL_Init (void) Cmd_AddCommand ("topten", NULL); Cmd_AddCommandD ("fog", CL_Fog_f, "fog "); + Cmd_AddCommandD ("waterfog", CL_Fog_f, "waterfog "); Cmd_AddCommand ("kill", NULL); Cmd_AddCommand ("pause", NULL); Cmd_AddCommand ("say", CL_Say_f); @@ -4673,7 +4694,7 @@ double Host_Frame (double time) static double time1 = 0; static double time2 = 0; static double time3 = 0; - int pass0, pass1, pass2, pass3; + int pass0, pass1, pass2, pass3, i; // float fps; double newrealtime; static double spare; @@ -4924,12 +4945,15 @@ double Host_Frame (double time) if (host_speeds.ival) time1 = Sys_DoubleTime (); - r_refdef.audio.defaulted = true; - VectorClear(r_refdef.audio.origin); - VectorSet(r_refdef.audio.forward, 1, 0, 0); - VectorSet(r_refdef.audio.right, 0, 1, 0); - VectorSet(r_refdef.audio.up, 0, 0, 1); - r_refdef.audio.inwater = false; + for (i = 0; i < MAX_SPLITS; i++) + { + cl.playerview[i].audio.defaulted = true; + VectorClear(cl.playerview[i].audio.origin); + VectorSet(cl.playerview[i].audio.forward, 1, 0, 0); + VectorSet(cl.playerview[i].audio.right, 0, 1, 0); + VectorSet(cl.playerview[i].audio.up, 0, 0, 1); + cl.playerview[i].audio.inwater = false; + } if (SCR_UpdateScreen && !vid.isminimized) { @@ -4947,8 +4971,11 @@ double Host_Frame (double time) time2 = Sys_DoubleTime (); // update audio - S_UpdateListener (r_refdef.audio.origin, r_refdef.audio.forward, r_refdef.audio.right, r_refdef.audio.up); - S_SetUnderWater(r_refdef.audio.inwater); + for (i = 0 ; i < MAX_SPLITS; i++) + { + playerview_t *pv = &cl.playerview[cl.splitclients?i % cl.splitclients:0]; + S_UpdateListener (i, pv->audio.origin, pv->audio.forward, pv->audio.right, pv->audio.up, pv->audio.inwater); + } S_Update (); diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index e007a7f2..b678b339 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -2283,7 +2283,7 @@ void DL_Abort(qdownload_t *dl, enum qdlabort aborttype) for (b = dl->dlblocks; b; b = n) { if (b->state == DLB_RECEIVED) - VFS_PRINTF(parts, "c %#llx %#llx\n", (long long)b->start, (long long)b->end); + VFS_PRINTF(parts, "c "fPRIllx" "fPRIllx"\n", (long long)b->start, (long long)b->end); else { for(;;) @@ -2298,7 +2298,7 @@ void DL_Abort(qdownload_t *dl, enum qdlabort aborttype) } break; } - VFS_PRINTF(parts, "m %#llx %#llx\n", (long long)b->start, (long long)b->end); + VFS_PRINTF(parts, "m "fPRIllx" "fPRIllx"\n", (long long)b->start, (long long)b->end); } n = b->next; @@ -3888,7 +3888,7 @@ void CLQ2_ParseConfigString (void) } else if (i == Q2CS_CDTRACK) { - Media_BackgroundTrack (s, NULL); + Media_NamedTrack (s, NULL); } else if (i >= Q2CS_MODELS && i < Q2CS_MODELS+Q2MAX_MODELS) { @@ -4050,6 +4050,7 @@ Static entities are non-interactive world objects like torches ===================== */ +void R_StaticEntityToRTLight(int i); void CL_ParseStatic (int version) { entity_t *ent; @@ -4168,6 +4169,11 @@ void CL_ParseStatic (int version) VectorCopy(es.origin, maxs); } cl.worldmodel->funcs.FindTouchedLeafs(cl.worldmodel, &cl_static_entities[i].pvscache, mins, maxs); + +#ifdef RTLIGHTS + //and now handle any rtlight fields on it + R_StaticEntityToRTLight(i); +#endif } /* @@ -4246,7 +4252,7 @@ void CLQW_ParseStartSoundPacket(void) Host_EndGame ("CL_ParseStartSoundPacket: ent = %i", ent); #ifdef PEXT_CSQC - if (!CSQC_StartSound(ent, channel, cl.sound_name[sound_num], pos, volume/255.0, attenuation, 100)) + if (!CSQC_StartSound(ent, channel, cl.sound_name[sound_num], pos, volume/255.0, attenuation, 100, 0)) #endif { if (!sound_num) @@ -4347,23 +4353,24 @@ void CLQ2_ParseStartSoundPacket(void) #if defined(NQPROT) || defined(PEXT_SOUNDDBL) void CLNQ_ParseStartSoundPacket(void) { - vec3_t pos; - int channel, ent; - int sound_num; - int volume; - int field_mask; - float attenuation; + vec3_t pos; + int channel, ent; + int sound_num; + int volume; + int field_mask; + float attenuation; int i; int pitchadj; + float timeofs; - field_mask = MSG_ReadByte(); + field_mask = MSG_ReadByte(); - if (field_mask & NQSND_VOLUME) + if (field_mask & NQSND_VOLUME) volume = MSG_ReadByte (); else volume = DEFAULT_SOUND_PACKET_VOLUME; - if (field_mask & NQSND_ATTENUATION) + if (field_mask & NQSND_ATTENUATION) attenuation = MSG_ReadByte () / 64.0; else attenuation = DEFAULT_SOUND_PACKET_ATTENUATION; @@ -4373,6 +4380,11 @@ void CLNQ_ParseStartSoundPacket(void) else pitchadj = 100; + if (field_mask & FTESND_TIMEOFS) + timeofs = MSG_ReadShort() / 1000.0; + else + timeofs = 0; + if (field_mask & DPSND_LARGEENTITY) { ent = MSGCL_ReadEntity(); @@ -4400,13 +4412,13 @@ void CLNQ_ParseStartSoundPacket(void) pos[i] = MSG_ReadCoord (); #ifdef PEXT_CSQC - if (!CSQC_StartSound(ent, channel, cl.sound_name[sound_num], pos, volume/255.0, attenuation, pitchadj)) + if (!CSQC_StartSound(ent, channel, cl.sound_name[sound_num], pos, volume/255.0, attenuation, pitchadj, timeofs)) #endif { if (!sound_num) S_StopSound(ent, channel); else - S_StartSound (ent, channel, cl.sound_precache[sound_num], pos, volume/255.0, attenuation, 0, pitchadj); + S_StartSound (ent, channel, cl.sound_precache[sound_num], pos, volume/255.0, attenuation, timeofs, pitchadj); } if (ent == cl.playerview[0].playernum+1) @@ -4493,7 +4505,7 @@ void CL_NewTranslation (int slot) if (player->model->loadstate == MLS_FAILED && strcmp(mod, "male")) { //fall back on male if the model doesn't exist. yes, sexist. mod = "male"; - player->model = Mod_ForName(va("players/male/tris.md2", mod), 0); + player->model = Mod_ForName(va("players/%s/tris.md2", mod), 0); } player->skinid = Mod_RegisterSkinFile(va("players/%s/%s.skin", mod,skin)); if (!player->skinid) @@ -5217,11 +5229,11 @@ char *CL_ParseChat(char *text, player_info_t **player, int *msgflags) if ((int)msg_filter.value & flags) return NULL; //filter chat - check_flood = Ignore_Check_Flood(s, flags, offset); + check_flood = Ignore_Check_Flood(*player, s, flags); if (check_flood == IGNORE_NO_ADD) return NULL; else if (check_flood == NO_IGNORE_ADD) - Ignore_Flood_Add(s); + Ignore_Flood_Add(*player, s); } #ifdef PLUGINS else @@ -7264,13 +7276,13 @@ void CLNQ_ParseServerMessage (void) Cmd_ExecuteString("bf", RESTRICT_SERVER); break; case svcfitz_fog: - CL_ResetFog(); - cl.fog.density = MSG_ReadByte()/255.0f; - cl.fog.colour[0] = MSG_ReadByte()/255.0f; - cl.fog.colour[1] = MSG_ReadByte()/255.0f; - cl.fog.colour[2] = MSG_ReadByte()/255.0f; - cl.fog.time += ((unsigned short)MSG_ReadShort()) / 100.0; - cl.fog_locked = !!cl.fog.density; + CL_ResetFog(0); + cl.fog[0].density = MSG_ReadByte()/255.0f; + cl.fog[0].colour[0] = MSG_ReadByte()/255.0f; + cl.fog[0].colour[1] = MSG_ReadByte()/255.0f; + cl.fog[0].colour[2] = MSG_ReadByte()/255.0f; + cl.fog[0].time += ((unsigned short)MSG_ReadShort()) / 100.0; + cl.fog_locked = !!cl.fog[0].density; break; case svcfitz_spawnbaseline2: i = MSGCL_ReadEntity (); diff --git a/engine/client/cl_plugin.inc b/engine/client/cl_plugin.inc index ecc00a3f..6fefdfee 100644 --- a/engine/client/cl_plugin.inc +++ b/engine/client/cl_plugin.inc @@ -661,7 +661,7 @@ static qintptr_t VARGS Plug_GetNetworkInfo(void *offset, quintptr_t mask, const if (has(mlatency)) outptr->mlatency = 0; if (has(mrate)) - outptr->mrate = 0; + outptr->mrate = IN_DetermineMouseRate(); if (has(vlatency)) outptr->vlatency = 0; diff --git a/engine/client/cl_pred.c b/engine/client/cl_pred.c index 7e28ef63..816f2372 100644 --- a/engine/client/cl_pred.c +++ b/engine/client/cl_pred.c @@ -754,16 +754,11 @@ static void CL_EntStateToPlayerState(player_state_t *plstate, entity_state_t *st } else VectorScale(state->u.q1.velocity, 1/8.0, plstate->velocity); - VectorCopy(state->angles, plstate->viewangles); -// plstate->viewangles[2] = V_CalcRoll(plstate->viewangles, plstate->velocity); plstate->viewangles[0] = SHORT2ANGLE(state->u.q1.vangle[0]); plstate->viewangles[1] = SHORT2ANGLE(state->u.q1.vangle[1]); plstate->viewangles[2] = SHORT2ANGLE(state->u.q1.vangle[2]); - if (state->u.q1.pmovetype) - plstate->viewangles[0] *= -3; - a[0] = ((-192-state->u.q1.gravitydir[0])/256.0f) * 360; a[1] = (state->u.q1.gravitydir[1]/256.0f) * 360; a[2] = 0; diff --git a/engine/client/cl_screen.c b/engine/client/cl_screen.c index a7b02b74..89b124c4 100644 --- a/engine/client/cl_screen.c +++ b/engine/client/cl_screen.c @@ -619,7 +619,7 @@ void SCR_DrawCenterString (vrect_t *rect, cprint_t *p, struct font_s *font) x = left; else { - x = (right + left - Font_LineWidth(line_start[l], line_end[l]))/2; + x = left + (right - left - Font_LineWidth(line_start[l], line_end[l]))/2; } remaining -= line_end[l]-line_start[l]; @@ -763,6 +763,9 @@ void SCR_DrawCursor(void) return; //system doesn't support a hardware cursor, so try to draw a software one. + if (!*key_customcursor[cmod].name) + return; + p = R2D_SafeCachePic(key_customcursor[cmod].name); if (!p || !R_GetShaderSizes(p, NULL, NULL, false)) p = R2D_SafeCachePic("gfx/cursor.lmp"); @@ -1379,25 +1382,32 @@ void SCR_DrawNet (void) R2D_ScalePic (scr_vrect.x+64, scr_vrect.y, 64, 64, scr_net); } -//FIXME: no support for UTF-8 input void SCR_StringXY(char *str, float x, float y) { char *s2; int px, py; + unsigned int codepoint; + int error; Font_BeginString(font_default, ((x<0)?vid.width:x), ((y<0)?vid.height - sb_lines:y), &px, &py); if (x < 0) { - for (s2 = str; *s2; s2++) - px -= Font_CharWidth(CON_WHITEMASK, *s2); + for (s2 = str; *s2; ) + { + codepoint = unicode_decode(&error, s2, &s2, true); + px -= Font_CharWidth(CON_WHITEMASK, codepoint); + } } if (y < 0) py += y*Font_CharHeight(); while (*str) - px = Font_DrawChar(px, py, CON_WHITEMASK, *str++); + { + codepoint = unicode_decode(&error, str, &str, true); + px = Font_DrawChar(px, py, CON_WHITEMASK, codepoint); + } Font_EndString(font_default); } @@ -2482,16 +2492,16 @@ void SCR_BringDownConsole (void) cl.cshifts[CSHIFT_CONTENTS].percent = 0; // no area contents palette on next frame } -void SCR_TileClear (void) +void SCR_TileClear (int skipbottom) { if (r_refdef.vrect.width < r_refdef.grect.width) { float w; // left - R2D_TileClear (r_refdef.grect.x, r_refdef.grect.y, r_refdef.vrect.x-r_refdef.grect.x, r_refdef.grect.height - sb_lines); + R2D_TileClear (r_refdef.grect.x, r_refdef.grect.y, r_refdef.vrect.x-r_refdef.grect.x, r_refdef.grect.height - skipbottom); // right w = (r_refdef.grect.x+r_refdef.grect.width) - (r_refdef.vrect.x+r_refdef.vrect.width); - R2D_TileClear ((r_refdef.grect.x+r_refdef.grect.width) - (w), r_refdef.grect.y, w, r_refdef.grect.height - sb_lines); + R2D_TileClear ((r_refdef.grect.x+r_refdef.grect.width) - (w), r_refdef.grect.y, w, r_refdef.grect.height - skipbottom); } if (r_refdef.vrect.height < r_refdef.grect.height) { @@ -2503,7 +2513,7 @@ void SCR_TileClear (void) R2D_TileClear (r_refdef.vrect.x, r_refdef.vrect.y + r_refdef.vrect.height, r_refdef.vrect.width, - (r_refdef.grect.y+r_refdef.grect.height) - sb_lines - (r_refdef.vrect.y + r_refdef.vrect.height)); + (r_refdef.grect.y+r_refdef.grect.height) - skipbottom - (r_refdef.vrect.y + r_refdef.vrect.height)); } } diff --git a/engine/client/cl_tent.c b/engine/client/cl_tent.c index 06847f0c..e8527b24 100644 --- a/engine/client/cl_tent.c +++ b/engine/client/cl_tent.c @@ -2434,9 +2434,9 @@ static struct{ #define ATTN_NONE 0 #define ATTN_NORM 1 #define ATTN_STATIC 1 -void Q2S_StartSound(vec3_t origin, int entnum, int entchannel, sfx_t *sfx, float fvol, float attenuation, float timeofs) +void Q2S_StartSound(vec3_t origin, int entnum, int entchannel, sfx_t *sfx, float fvol, float attenuation, float delay) { - S_StartSound(entnum, entchannel, sfx, origin, fvol, attenuation, timeofs, 0); + S_StartSound(entnum, entchannel, sfx, origin, fvol, attenuation, -delay, 0); } void CLQ2_ParseTEnt (void) { @@ -3401,7 +3401,7 @@ void CL_UpdateBeams (void) int i, j; beam_t *b; vec3_t dist, org; - float *vieworg, *viewang; + float *vieworg; float d; entity_t *ent; entity_state_t *st; @@ -3447,7 +3447,7 @@ void CL_UpdateBeams (void) // pl = &cl.inframes[cl.parsecount&UPDATE_MASK].playerstate[b->entity-1]; // if (pl->messagenum == cl.parsecount || cls.protocol == CP_NETQUAKE) { - vec3_t fwd, org, ang; + vec3_t fwd, org, ang, viewang; float delta, f, len; // if (cl.spectator && pv->cam_auto) @@ -3456,15 +3456,16 @@ void CL_UpdateBeams (void) // } // else vieworg = pv->simorg; - viewang = pv->simangles; - if (cl_truelightning.ival >= 2 && cls.netchan.outgoing_sequence > cl_truelightning.ival) + if (cl_truelightning.ival > 1 && cl.movesequence > cl_truelightning.ival) { - inframe_t *frame = &cl.inframes[(cls.netchan.outgoing_sequence-cl_truelightning.ival)&UPDATE_MASK]; - viewang = frame->playerstate[pv->playernum].viewangles; - viewang[0] = (frame->playerstate[pv->playernum].command.angles[0] * 360) / 65336.0; - viewang[1] = (frame->playerstate[pv->playernum].command.angles[1] * 360) / 65336.0; + outframe_t *frame = &cl.outframes[(cl.movesequence-cl_truelightning.ival)&UPDATE_MASK]; + viewang[0] = SHORT2ANGLE(frame->cmd[j].angles[0]); + viewang[1] = SHORT2ANGLE(frame->cmd[j].angles[1]); + viewang[2] = SHORT2ANGLE(frame->cmd[j].angles[2]); } + else + VectorCopy(pv->simangles, viewang); VectorCopy (vieworg, b->start); b->start[2] += pv->crouch + bound(-7, v_viewheight.value, 4); @@ -3486,7 +3487,7 @@ void CL_UpdateBeams (void) ang[0] += (viewang[0] - ang[0]) * f; // lerp yaw - delta = viewang[1] - ang[1]; + delta = anglemod(viewang[1] - ang[1]); if (delta > 180) delta -= 360; if (delta < -180) diff --git a/engine/client/cl_ui.c b/engine/client/cl_ui.c index 599db3e9..e8053c03 100644 --- a/engine/client/cl_ui.c +++ b/engine/client/cl_ui.c @@ -953,7 +953,7 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con if (VM_LONG(arg[0]) < 0 || VM_LONG(arg[0]) > 255 || (int)arg[1] + VM_LONG(arg[2]) >= mask || VM_POINTER(arg[1]) < offset || VM_LONG(arg[2]) < 1) break; //out of bounds. - Q_strncpyz(VM_POINTER(arg[1]), Key_KeynumToString(VM_LONG(arg[0])), VM_LONG(arg[2])); + Q_strncpyz(VM_POINTER(arg[1]), Key_KeynumToString(VM_LONG(arg[0]), 0), VM_LONG(arg[2])); break; case UI_KEY_GETBINDINGBUF: diff --git a/engine/client/client.h b/engine/client/client.h index ef7405a6..ac07135b 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -665,6 +665,16 @@ struct playerview_s int prevframe; int oldframe; } vm; + + struct + { + qboolean defaulted; + vec3_t origin; + vec3_t forward; + vec3_t right; + vec3_t up; + int inwater; + } audio; }; // @@ -789,8 +799,8 @@ typedef struct vec3_t skyaxis; qboolean fog_locked; - fogstate_t fog; - fogstate_t oldfog; + fogstate_t fog[2]; //0 = air, 1 = water. if water has no density fall back on air. + fogstate_t oldfog[2]; char levelname[40]; // for display on solo scoreboard @@ -1071,9 +1081,11 @@ void CL_BaseMove (usercmd_t *cmd, int pnum, float extra, float wantfps); int Master_FindBestRoute(char *server, char *out, size_t outsize, int *directcost, int *chainedcost); float CL_KeyState (kbutton_t *key, int pnum, qboolean noslowstart); -char *Key_KeynumToString (int keynum); +char *Key_KeynumToString (int keynum, int modifier); int Key_StringToKeynum (const char *str, int *modifier); -char *Key_GetBinding(int keynum); +char *Key_GetBinding(int keynum, int bindmap, int modifier); +void Key_GetBindMap(int *bindmaps); +void Key_SetBindMap(int *bindmaps); void CL_UseIndepPhysics(qboolean allow); @@ -1083,8 +1095,6 @@ float CL_FilterTime (double time, float wantfps, qboolean ignoreserver); int CL_RemoveClientCommands(char *command); void CL_AllowIndependantSendCmd(qboolean allow); -void CL_DrawPrydonCursor(void); - // // cl_demo.c // @@ -1284,7 +1294,7 @@ qboolean CSQC_MouseMove(float xdelta, float ydelta, int devid); qboolean CSQC_MousePosition(float xabs, float yabs, int devid); qboolean CSQC_JoystickAxis(int axis, float value, int devid); qboolean CSQC_Accelerometer(float x, float y, float z); -int CSQC_StartSound(int entnum, int channel, char *soundname, vec3_t pos, float vol, float attenuation, float pitchmod); +int CSQC_StartSound(int entnum, int channel, char *soundname, vec3_t pos, float vol, float attenuation, float pitchmod, float timeofs); void CSQC_ParseEntities(void); void CSQC_ResetTrails(void); @@ -1501,7 +1511,7 @@ struct cin_s *Media_StartCin(char *name); texid_tf Media_UpdateForShader(cin_t *cin); void Media_ShutdownCin(cin_t *cin); #endif -qboolean Media_BackgroundTrack(const char *initialtrack, const char *looptrack); //new background music interface +qboolean Media_NamedTrack(const char *initialtrack, const char *looptrack); //new background music interface void Media_NumberedTrack(unsigned int initialtrack, unsigned int looptrack); //legacy cd interface for protocols that only support numbered tracks. void Media_EndedTrack(void); //cd is no longer running, media code needs to pick a new track (cd track or faketrack) diff --git a/engine/client/console.c b/engine/client/console.c index 95d686e3..76b8793d 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -878,7 +878,7 @@ void SV_FlushRedirect (void); #define MAXPRINTMSG 4096 static void Con_PrintFromThread (void *ctx, void *data, size_t a, size_t b) { - Con_Printf("%s", data); + Con_Printf("%s", (char*)data); BZ_Free(data); } @@ -1154,7 +1154,7 @@ int Con_DrawInput (console_t *con, qboolean focused, int left, int right, int y, cursorframe = ((int)(realtime*con_cursorspeed)&1); //FIXME: support tab somehow - for (lhs = 0, cchar = maskedtext-1; cchar < cursor; ) + for (lhs = 0, cchar = maskedtext; cchar < cursor; ) { cchar = Font_Decode(cchar, &codeflags, &codepoint); lhs += Font_CharWidth(codeflags, codepoint); @@ -1181,7 +1181,7 @@ int Con_DrawInput (console_t *con, qboolean focused, int left, int right, int y, lhs = Font_DrawChar(lhs, y, codeflags, codepoint); } rhs = x; - Font_Decode(cursor, &codeflags, &codepoint); + cchar = Font_Decode(cursor, &codeflags, &codepoint); if (cursorframe) { // extern cvar_t com_parseutf8; @@ -1190,15 +1190,18 @@ int Con_DrawInput (console_t *con, qboolean focused, int left, int right, int y, // else Font_DrawChar(rhs, y, CON_WHITEMASK, 0xe000|11); } - else if (*cursor) + else if (codepoint) { Font_DrawChar(rhs, y, codeflags, codepoint); } - rhs += Font_CharWidth(codeflags, codepoint); - for (cchar = cursor+1; *cchar; ) + if (codepoint) { - cchar = Font_Decode(cchar, &codeflags, &codepoint); - rhs = Font_DrawChar(rhs, y, codeflags, codepoint); + rhs += Font_CharWidth(codeflags, codepoint); + while (*cchar) + { + cchar = Font_Decode(cchar, &codeflags, &codepoint); + rhs = Font_DrawChar(rhs, y, codeflags, codepoint); + } } /*if its getting completed to something, show some help about the command that is going to be used*/ diff --git a/engine/client/image.c b/engine/client/image.c index b7cdd706..737a580c 100644 --- a/engine/client/image.c +++ b/engine/client/image.c @@ -1,6 +1,8 @@ #include "quakedef.h" #include "shader.h" +//#define PURGEIMAGES //somewhat experimental still. we're still flushing more than we should. + //FIXME texid_t GL_FindTextureFallback (const char *identifier, unsigned int flags, void *fallback, int fallbackwidth, int fallbackheight, uploadfmt_t fallbackfmt); //FIXME @@ -3444,6 +3446,7 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag { unsigned int *rgbadata = rawdata; int i; + qboolean valid; mips->mip[0].width = imgwidth; mips->mip[0].height = imgheight; @@ -3604,16 +3607,24 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag case TF_TRANS8_FULLBRIGHT: mips->encoding = PTI_RGBA8; rgbadata = BZ_Malloc(imgwidth * imgheight*4); - for (i = 0; i < imgwidth * imgheight; i++) + for (i = 0, valid = false; i < imgwidth * imgheight; i++) { - if (((qbyte*)rawdata)[i] < 255-vid.fullbright) + if (((qbyte*)rawdata)[i] < 256-vid.fullbright) rgbadata[i] = 0; else + { rgbadata[i] = d_8to24rgbtable[((qbyte*)rawdata)[i]]; + valid = true; + } } if (freedata) BZ_Free(rawdata); freedata = true; + if (!valid) + { + BZ_Free(rgbadata); + return false; + } break; case TF_HEIGHT8PAL: @@ -3641,6 +3652,27 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag freedata = true; break; + case TF_BGR24_FLIP: + mips->encoding = PTI_RGBX8; + rgbadata = BZ_Malloc(imgwidth * imgheight*4); + for (i = 0; i < imgheight; i++) + { + int x; + qbyte *in = (qbyte*)rawdata + (imgheight-i-1) * imgwidth * 3; + qbyte *out = (qbyte*)rgbadata + i * imgwidth * 4; + for (x = 0; x < imgwidth; x++, in+=3, out+=4) + { + out[0] = in[2]; + out[1] = in[1]; + out[2] = in[0]; + out[3] = 0xff; + } + } + if (freedata) + BZ_Free(rawdata); + freedata = true; + break; + case TF_8PAL24: if (!palettedata) { @@ -3723,7 +3755,7 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag mips->encoding = PTI_RGBX8; break; case PTI_BGRA8: - mips->encoding = PTI_RGBX8; + mips->encoding = PTI_BGRX8; break; case PTI_RGBA16F: case PTI_RGBA32F: @@ -3823,6 +3855,10 @@ static qboolean Image_LoadRawTexture(texid_t tex, unsigned int flags, void *rawd if (!Image_GenMip0(mips, flags, rawdata, palettedata, imgwidth, imgheight, fmt, true)) { Z_Free(mips); + if (flags & IF_NOWORKER) + Image_LoadTexture_Failed(tex, NULL, 0, 0); + else + COM_AddWork(0, Image_LoadTexture_Failed, tex, NULL, 0, 0); return false; } Image_GenerateMips(mips, flags); @@ -4264,15 +4300,22 @@ void Image_LoadHiResTextureWorker(void *ctx, void *data, size_t a, size_t b) image_t *Image_FindTexture(const char *identifier, const char *subdir, unsigned int flags) { image_t *tex; + if (!subdir) + subdir = ""; tex = Hash_Get(&imagetable, identifier); while(tex) { - if ((tex->flags ^ flags) & IF_CLAMP) + if (!((tex->flags ^ flags) & IF_CLAMP)) { - tex = Hash_GetNext(&imagetable, identifier, tex); - continue; +#ifdef PURGEIMAGES + if (!strcmp(subdir, tex->subpath?tex->subpath:"")) +#endif + { + tex->regsequence = r_regsequence; + return tex; + } } - return tex; + tex = Hash_GetNext(&imagetable, identifier, tex); } return NULL; } @@ -4471,6 +4514,7 @@ image_t *Image_GetTexture(const char *identifier, const char *subpath, unsigned void Image_Upload (texid_t tex, uploadfmt_t fmt, void *data, void *palette, int width, int height, unsigned int flags) { struct pendingtextureinfo mips; + size_t i; mips.extrafree = NULL; mips.type = (flags & IF_3DMAP)?PTI_3D:PTI_2D; @@ -4482,6 +4526,12 @@ void Image_Upload (texid_t tex, uploadfmt_t fmt, void *data, void *palette, in tex->width = mips.mip[0].width; tex->height = mips.mip[0].height; tex->status = TEX_LOADED; + + for (i = 0; i < mips.mipcount; i++) + if (mips.mip[i].needfree) + BZ_Free(mips.mip[i].data); + if (mips.extrafree) + BZ_Free(mips.extrafree); } typedef struct @@ -4593,26 +4643,68 @@ void Image_DestroyTexture(image_t *tex) Hash_RemoveData(&imagetable, tex->ident, tex); Z_Free(tex); } -void Image_List_f(void) + + +void Shader_TouchTextures(void); +void Image_Purge(void) { - image_t *tex; - char *status; +#ifdef PURGEIMAGES + image_t *tex, *a; + int loaded = 0, total = 0; + size_t mem = 0; + Shader_TouchTextures(); for (tex = imagelist; tex; tex = tex->next) { - if (tex->status == TEX_LOADED) - status = "^2loaded"; - else if (tex->status == TEX_FAILED) - status = "^1failed"; - else if (tex->status == TEX_NOTLOADED) - status = "^5not loaded"; - else - status = "^bloading"; - if (tex->subpath) - Con_Printf("%s^h(%s)^h: %s\n", tex->ident, tex->subpath, status); - else - Con_Printf("%s: %s\n", tex->ident, status); + if (tex->flags & IF_NOPURGE) + continue; + if (tex->regsequence != r_regsequence) + Image_UnloadTexture(tex); } +#endif } + + +void Image_List_f(void) +{ + image_t *tex, *a; + int loaded = 0, total = 0; + size_t mem = 0; + for (tex = imagelist; tex; tex = tex->next) + { + total++; + if (tex->subpath) + Con_Printf("^h(%s)^h%s: ", tex->subpath, tex->ident); + else + Con_Printf("%s: ", tex->ident); + + for (a = tex->aliasof; a; a = a->aliasof) + { + if (a->subpath) + Con_Printf("^3^h(%s)^h%s: ", a->subpath, a->ident); + else + Con_Printf("^3%s: ", a->ident); + } + + if (tex->status == TEX_LOADED) + { + Con_Printf("^2loaded\n"); + if (!tex->aliasof) + { + mem += tex->width * tex->height * 4; + loaded++; + } + } + else if (tex->status == TEX_FAILED) + Con_Printf("^1failed\n"); + else if (tex->status == TEX_NOTLOADED) + Con_Printf("^5not loaded\n"); + else + Con_Printf("^bloading\n"); + } + + Con_Printf("%i images loaded (%i known)\n", loaded, total); +} + //may not create any images yet. void Image_Init(void) { diff --git a/engine/client/in_generic.c b/engine/client/in_generic.c index 50121ed6..d177dae8 100644 --- a/engine/client/in_generic.c +++ b/engine/client/in_generic.c @@ -12,8 +12,53 @@ static cvar_t m_forcewheel_threshold = CVARD("m_forcewheel_threshold", "32", "Mo static cvar_t m_strafeonright = CVARFD("m_strafeonright", "1", CVAR_ARCHIVE, "If 1, touching the right half of the touchscreen will strafe/move, while the left side will turn."); static cvar_t m_fatpressthreshold = CVARFD("m_fatpressthreshold", "0.2", CVAR_ARCHIVE, "How fat your thumb has to be to register a fat press (touchscreens)."); static cvar_t m_touchmajoraxis = CVARFD("m_touchmajoraxis", "1", CVAR_ARCHIVE, "When using a touchscreen, use only the major axis for strafing."); -static cvar_t m_slidethreshold = CVARFD("m_slidethreshold", "10", CVAR_ARCHIVE, "How far your finger needs to move to be considered a slide event (touchscreens)."); - +static cvar_t m_slidethreshold = CVARFD("m_slidethreshold", "10", CVAR_ARCHIVE, "How far your finger needs to move to be considered a slide event (touchscreens)."); +static cvar_t joy_advaxis[6] = +{ + CVARD("joyadvaxisx", "4", "Provides a way to remap each joystick/controller axis.\n0:dead, 1:fwd, 2:pitch, 3:side, 4:yaw, 5:up, 6:roll"), + CVAR("joyadvaxisy", "2"), + CVAR("joyadvaxisz", "5"), + CVAR("joyadvaxisr", "3"), + CVAR("joyadvaxisu", "1"), + CVAR("joyadvaxisv", "6") +}; +static cvar_t joy_advaxisscale[6] = +{ + CVARD("joyadvaxisx_scale", "1.0", "Because joyadvaxisx etc can be added together, this provides a way to rescale or invert an individual axis without affecting another with the same action."), + CVAR("joyadvaxisy_scale", "1.0"), + CVAR("joyadvaxisz_scale", "1.0"), + CVAR("joyadvaxisr_scale", "1.0"), + CVAR("joyadvaxisu_scale", "1.0"), + CVAR("joyadvaxisv_scale", "1.0") +}; +static cvar_t joy_anglesens[3] = +{ + CVARD("joypitchsensitivity", "1.0", "Scaler value for the controller when it is at its most extreme value"), + CVAR("joyyawsensitivity", "-1.0"), + CVAR("joyrollsensitivity", "1.0") +}; +static cvar_t joy_movesens[3] = +{ + CVAR("joyforwardsensitivity", "1.0"), + CVAR("joysidesensitivity", "-1.0"), + CVAR("joyupsensitivity", "1.0") +}; +//comments on threshholds comes from microsoft's xinput docs. +static cvar_t joy_anglethreshold[3] = +{ + CVARD("joypitchthreshold", "0.19", "Values reported near the center of the analog joystick/controller are often erroneous and undesired.\nThe joystick threshholds are how much of the total values to ignore."), //8689/32767 (right thumb) + CVAR("joyyawthreshold", "0.19"), //8689/32767 (right thumb) + CVAR("joyrollthreshold", "0.118"), //30/255 (trigger) +}; +static cvar_t joy_movethreshold[3] = +{ + CVAR("joyforwardthreshold", "0.17"),//7849/32767 (left thumb) + CVAR("joysidethreshold", "0.17"), //7849/32767 (left thumb) + CVAR("joyupthreshold", "0.118"), //30/255 (trigger) +}; + +static cvar_t joy_exponent = CVARD("joyexponent", "2", "Scales joystick/controller sensitivity non-linearly to increase precision in the center.\nA value of 1 is linear."); +static cvar_t joy_radialdeadzone = CVARD("joyradialdeadzone", "1", "Treat controller dead zones as a pair, rather than per-axis."); extern cvar_t _windowed_mouse; @@ -80,15 +125,16 @@ struct mouse_s vec2_t old_delta; //how far its moved previously, for mouse smoothing float wheeldelta; int down; + unsigned int updates; //tracks updates per second } ptr[MAXPOINTERS]; int touchcursor; //the cursor follows whichever finger was most recently pressed in preference to any mouse also on the same system #define MAXJOYAXIS 6 -#define MAXJOYSTICKS 4 +#define MAXJOYSTICKS 8 struct joy_s { int qdeviceid; - int axis[MAXJOYAXIS]; + float axis[MAXJOYAXIS]; } joy[MAXJOYSTICKS]; void IN_Shutdown(void) @@ -102,12 +148,14 @@ void IN_ReInit(void) for (i = 0; i < MAXPOINTERS; i++) { + memset(&ptr[i], 0, sizeof(ptr[i])); ptr[i].type = M_INVALID; ptr[i].qdeviceid = i; } for (i = 0; i < MAXJOYSTICKS; i++) { + memset(&joy[i], 0, sizeof(joy[i])); joy[i].qdeviceid = i; } @@ -150,8 +198,23 @@ void IN_DeviceIDs_f(void) INS_EnumerateDevices(NULL, IN_DeviceIDs_Enumerate); } +float IN_DetermineMouseRate(void) +{ + float time = Sys_DoubleTime(); + static float timer; + static float last; + if (fabs(time - timer) > 1) + { + timer = time; + last = ptr[0].updates; + ptr[0].updates = 0; + } + return last; +} + void IN_Init(void) { + int i; events_avail = 0; events_used = 0; @@ -164,6 +227,21 @@ void IN_Init(void) Cvar_Register (&m_slidethreshold, "input controls"); Cvar_Register (&m_touchmajoraxis, "input controls"); + for (i = 0; i < 6; i++) + { + Cvar_Register (&joy_advaxis[i], "input controls"); + Cvar_Register (&joy_advaxisscale[i], "input controls"); + } + for (i = 0; i < 3; i++) + { + Cvar_Register (&joy_anglesens[i], "input controls"); + Cvar_Register (&joy_movesens[i], "input controls"); + Cvar_Register (&joy_anglethreshold[i], "input controls"); + Cvar_Register (&joy_movethreshold[i], "input controls"); + } + Cvar_Register (&joy_exponent, "input controls"); + Cvar_Register (&joy_radialdeadzone, "input controls"); + Cmd_AddCommand ("in_deviceids", IN_DeviceIDs_f); INS_Init(); @@ -287,6 +365,9 @@ void IN_Commands(void) else if (ev->mouse.z < -mfwt) ptr[ev->devid].wheeldelta += mfwt; } + + if (ev->mouse.x || ev->mouse.y) + ptr[ev->devid].updates++; } break; case IEV_MOUSEABS: @@ -320,6 +401,10 @@ void IN_Commands(void) ptr[ev->devid].moveddist += fabs(ev->mouse.x - ptr[ev->devid].oldpos[0]) + fabs(ev->mouse.y - ptr[ev->devid].oldpos[1]); } + if (ptr[ev->devid].delta[0] != ev->mouse.x - ptr[ev->devid].oldpos[0] || + ptr[ev->devid].delta[1] != ev->mouse.y - ptr[ev->devid].oldpos[1]) + ptr[ev->devid].updates++; + ptr[ev->devid].oldpos[0] = ev->mouse.x; ptr[ev->devid].oldpos[1] = ev->mouse.y; @@ -573,6 +658,7 @@ void IN_MoveMouse(struct mouse_s *mouse, float *movements, int pnum) } } +//rescales threshold-1 down 0-1 static float joydeadzone(float mag, float deadzone) { if (mag > 1) //erg? @@ -592,7 +678,12 @@ void IN_MoveJoystick(struct joy_s *joy, float *movements, int pnum, float framet float mag; vec3_t jlook, jstrafe; - int wpnum; + int wpnum, i; + for (i = 0; i < MAXJOYAXIS; i++) + if (joy->axis[i]) + break; + if (i == MAXJOYAXIS) + return; /*each device will be processed when its player comes to be processed*/ wpnum = cl.splitclients; @@ -605,29 +696,69 @@ void IN_MoveJoystick(struct joy_s *joy, float *movements, int pnum, float framet if (wpnum != pnum) return; - jlook[0] = joy->axis[0]; - jlook[1] = joy->axis[1]; - jlook[2] = joy->axis[2]; + memset(jstrafe, 0, sizeof(jstrafe)); + memset(jlook, 0, sizeof(jlook)); - jstrafe[0] = joy->axis[3]; - jstrafe[1] = joy->axis[4]; - jstrafe[2] = joy->axis[5]; + for (i = 0; i < 6; i++) + { + switch(joy_advaxis[i].ival) + { + default: + case 0: //dead axis + break; + case 1: + jstrafe[0] += joy->axis[i] * joy_advaxisscale[i].value; + break; + case 2: + jlook[0] += joy->axis[i] * joy_advaxisscale[i].value; + break; + case 3: + jstrafe[1] += joy->axis[i] * joy_advaxisscale[i].value; + break; + case 4: + jlook[1] += joy->axis[i] * joy_advaxisscale[i].value; + break; + case 5: + jstrafe[2] += joy->axis[i] * joy_advaxisscale[i].value; + break; + case 6: + jlook[2] += joy->axis[i] * joy_advaxisscale[i].value; + break; + } + } //uses a radial deadzone for x+y axis, and separate out the z axis, just because most controllers are 2d affairs with any 3rd axis being a separate knob. //deadzone values are stolen from microsoft's xinput documentation. they seem quite large to me - I guess that means that xbox controllers are just dodgy imprecise crap with excessive amounts of friction and finger grease. - mag = joydeadzone(sqrt(jlook[0]*jlook[0] + jlook[1]*jlook[1]), 0.239); - mag = pow(mag, 2); - jlook[0] *= mag; - jlook[1] *= mag; - mag = joydeadzone(fabs(jlook[2]), 0.00092); - jlook[2] *= mag; - mag = joydeadzone(sqrt(jstrafe[0]*jstrafe[0] + jstrafe[1]*jstrafe[1]), 0.265); - mag = pow(mag, 2); - jstrafe[0] *= mag; - jstrafe[1] *= mag; - mag = joydeadzone(fabs(jstrafe[2]), 0.00092); - jstrafe[2] *= mag; + if (joy_radialdeadzone.ival) + { + mag = joydeadzone(sqrt(jlook[0]*jlook[0] + jlook[1]*jlook[1]), sqrt(joy_anglethreshold[0].value*joy_anglethreshold[0].value + joy_anglethreshold[1].value*joy_anglethreshold[1].value)); + mag = pow(mag, joy_exponent.value); + jlook[0] *= mag; + jlook[1] *= mag; + mag = joydeadzone(fabs(jlook[2]), joy_anglethreshold[2].value); + jlook[2] *= mag; + + mag = joydeadzone(sqrt(jstrafe[0]*jstrafe[0] + jstrafe[1]*jstrafe[1]), sqrt(joy_movethreshold[0].value*joy_movethreshold[0].value + joy_movethreshold[1].value*joy_movethreshold[1].value)); + mag = pow(mag, joy_exponent.value); + jstrafe[0] *= mag; + jstrafe[1] *= mag; + mag = joydeadzone(fabs(jstrafe[2]), joy_movethreshold[2].value); + jstrafe[2] *= mag; + } + else + { + for (i = 0; i < 3; i++) + { + mag = joydeadzone(fabs(jlook[i]), joy_anglethreshold[i].value); + mag = pow(mag, joy_exponent.value); + jlook[i] *= mag; + + mag = joydeadzone(fabs(jstrafe[i]), joy_movethreshold[i].value); + mag = pow(mag, joy_exponent.value); + jstrafe[i] *= mag; + } + } if (Key_Dest_Has(~kdm_game)) { @@ -635,24 +766,28 @@ void IN_MoveJoystick(struct joy_s *joy, float *movements, int pnum, float framet VectorClear(jstrafe); } - VectorScale(jlook, frametime, jlook); - VectorScale(jstrafe, frametime, jstrafe); + if (in_speed.state[pnum] & 1) + { + VectorScale(jlook, 360*cl_movespeedkey.value, jlook); + VectorScale(jstrafe, 360*cl_movespeedkey.value, jstrafe); + } + VectorScale(jlook, 360*frametime, jlook); if (!movements) //if this is null, gamecode should still get inputs, just no camera looking or anything. return; //angle changes - cl.playerview[pnum].viewanglechange[YAW] -= m_yaw.value * jlook[0]; - cl.playerview[pnum].viewanglechange[PITCH] += m_pitch.value * jlook[1]; -// cl.playerview[pnum].viewanglechange[ROLL] += m_roll.value * jlook[2]; //this would be too weird. + cl.playerview[pnum].viewanglechange[PITCH] += joy_anglesens[0].value * jlook[0]; + cl.playerview[pnum].viewanglechange[YAW] += joy_anglesens[1].value * jlook[1]; + cl.playerview[pnum].viewanglechange[ROLL] += joy_anglesens[2].value * jlook[2]; if (in_mlook.state[pnum] & 1) V_StopPitchDrift (&cl.playerview[pnum]); //movement - movements[1] += m_side.value * jstrafe[0]; //x=right=1 - movements[0] -= m_forward.value * jstrafe[1]; //y=forward=0 - movements[2] += m_side.value * jstrafe[2]; //z=up=2 + movements[0] -= joy_movesens[0].value * cl_forwardspeed.value * jstrafe[0]; + movements[1] -= joy_movesens[1].value * cl_sidespeed.value * jstrafe[1]; + movements[2] -= joy_movesens[2].value * cl_upspeed.value * jstrafe[2]; } void IN_Move (float *movements, int pnum, float frametime) diff --git a/engine/client/in_win.c b/engine/client/in_win.c index 146410bc..f7047dae 100644 --- a/engine/client/in_win.c +++ b/engine/client/in_win.c @@ -32,6 +32,49 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. void INS_Accumulate (void); +#define AVAIL_XINPUT +#ifdef AVAIL_XINPUT +//#define AVAIL_XINPUT_DLL "xinput9_1_0.dll" +#define AVAIL_XINPUT_DLL "xinput1_3.dll" +typedef struct _XINPUT_GAMEPAD { + WORD wButtons; + BYTE bLeftTrigger; + BYTE bRightTrigger; + SHORT sThumbLX; + SHORT sThumbLY; + SHORT sThumbRX; + SHORT sThumbRY; +} XINPUT_GAMEPAD, *PXINPUT_GAMEPAD; +typedef struct _XINPUT_STATE { + DWORD dwPacketNumber; + XINPUT_GAMEPAD Gamepad; +} XINPUT_STATE, *PXINPUT_STATE; +typedef struct _XINPUT_VIBRATION { + WORD wLeftMotorSpeed; + WORD wRightMotorSpeed; +} XINPUT_VIBRATION, *PXINPUT_VIBRATION; +DWORD (WINAPI *pXInputGetState)(DWORD dwUserIndex, XINPUT_STATE *pState); +DWORD (WINAPI *pXInputSetState)(DWORD dwUserIndex, XINPUT_VIBRATION *pState); +enum +{ + XINPUT_GAMEPAD_DPAD_UP = 0x0001, + XINPUT_GAMEPAD_DPAD_DOWN = 0x0002, + XINPUT_GAMEPAD_DPAD_LEFT = 0x0004, + XINPUT_GAMEPAD_DPAD_RIGHT = 0x0008, + XINPUT_GAMEPAD_START = 0x0010, + XINPUT_GAMEPAD_BACK = 0x0020, + XINPUT_GAMEPAD_LEFT_THUMB = 0x0040, + XINPUT_GAMEPAD_RIGHT_THUMB = 0x0080, + XINPUT_GAMEPAD_LEFT_SHOULDER = 0x0100, + XINPUT_GAMEPAD_RIGHT_SHOULDER = 0x0200, + XINPUT_GAMEPAD_A = 0x1000, + XINPUT_GAMEPAD_B = 0x2000, + XINPUT_GAMEPAD_X = 0x4000, + XINPUT_GAMEPAD_Y = 0x8000 +}; +#endif + + #ifdef AVAIL_DINPUT #ifndef _MSC_VER @@ -53,10 +96,14 @@ HRESULT (WINAPI *pDirectInputCreate)(HINSTANCE hinst, DWORD dwVersion, // mouse variables static cvar_t in_dinput = CVARF("in_dinput","0", CVAR_ARCHIVE); +static cvar_t in_xinput = CVARFD("in_xinput","0", CVAR_ARCHIVE, "Enables the use of xinput for controllers.\nNote that if you have a headset plugged in, that headset will be used for audio playback if no specific audio device is configured (may require snd_restart too)."); static cvar_t in_builtinkeymap = CVARF("in_builtinkeymap", "0", CVAR_ARCHIVE); static cvar_t in_simulatemultitouch = CVAR("in_simulatemultitouch", "0"); static cvar_t in_nonstandarddeadkeys = CVARD("in_nonstandarddeadkeys", "1", "Discard input events that result in multiple keys. Only the last key will be used. This results in behaviour that differs from eg notepad. To use a dead key, press it twice instead of the dead key followed by space."); +static cvar_t xinput_leftvibrator = CVARFD("xinput_leftvibrator","0", CVAR_ARCHIVE, ""); +static cvar_t xinput_rightvibrator = CVARFD("xinput_rightvibrator","0", CVAR_ARCHIVE, "Enables the use of xinput for controllers.\nNote that if you have a headset plugged in, that headset will be used for audio playback if no specific audio device is configured (may require snd_restart too)."); + static cvar_t m_accel_noforce = CVAR("m_accel_noforce", "0"); static cvar_t m_threshold_noforce = CVAR("m_threshold_noforce", "0"); @@ -102,25 +149,7 @@ qboolean mouseactive; #define JOY_ABSOLUTE_AXIS 0x00000000 // control like a joystick #define JOY_RELATIVE_AXIS 0x00000010 // control like a mouse, spinner, trackball #define JOY_MAX_AXES 6 // X, Y, Z, R, U, V -#define JOY_AXIS_X 0 -#define JOY_AXIS_Y 1 -#define JOY_AXIS_Z 2 -#define JOY_AXIS_R 3 -#define JOY_AXIS_U 4 -#define JOY_AXIS_V 5 -enum _ControlList -{ - AxisNada = 0, AxisForward, AxisLook, AxisSide, AxisTurn -}; - -static DWORD dwAxisFlags[JOY_MAX_AXES] = -{ - JOY_RETURNX, JOY_RETURNY, JOY_RETURNZ, JOY_RETURNR, JOY_RETURNU, JOY_RETURNV -}; - -static DWORD dwAxisMap[JOY_MAX_AXES]; -static DWORD dwControlMap[JOY_MAX_AXES]; // none of these cvars are saved over a session // this means that advanced controller configuration needs to be executed @@ -128,30 +157,12 @@ static DWORD dwControlMap[JOY_MAX_AXES]; // or when changing from one controller to another. this way at least something // works. static cvar_t in_joystick = CVARF("joystick","0", CVAR_ARCHIVE); -static cvar_t joy_name = CVAR("joyname", "joystick"); -static cvar_t joy_advanced = CVAR("joyadvanced", "0"); -static cvar_t joy_advaxisx = CVAR("joyadvaxisx", "0"); -static cvar_t joy_advaxisy = CVAR("joyadvaxisy", "0"); -static cvar_t joy_advaxisz = CVAR("joyadvaxisz", "0"); -static cvar_t joy_advaxisr = CVAR("joyadvaxisr", "0"); -static cvar_t joy_advaxisu = CVAR("joyadvaxisu", "0"); -static cvar_t joy_advaxisv = CVAR("joyadvaxisv", "0"); -static cvar_t joy_forwardthreshold = CVAR("joyforwardthreshold", "0.15"); -static cvar_t joy_sidethreshold = CVAR("joysidethreshold", "0.15"); -static cvar_t joy_pitchthreshold = CVAR("joypitchthreshold", "0.15"); -static cvar_t joy_yawthreshold = CVAR("joyyawthreshold", "0.15"); -static cvar_t joy_forwardsensitivity = CVAR("joyforwardsensitivity", "-1.0"); -static cvar_t joy_sidesensitivity = CVAR("joysidesensitivity", "-1.0"); -static cvar_t joy_pitchsensitivity = CVAR("joypitchsensitivity", "1.0"); -static cvar_t joy_yawsensitivity = CVAR("joyyawsensitivity", "-1.0"); -static cvar_t joy_wwhack1 = CVAR("joywwhack1", "0.0"); -static cvar_t joy_wwhack2 = CVAR("joywwhack2", "0.0"); - static qboolean joy_advancedinit; static DWORD joy_flags; -#define MAX_JOYSTICKS 4 +#define MAX_JOYSTICKS 8 static struct wjoy_s { + qboolean isxinput; //xinput device unsigned int id; //windows id. device id is the index. unsigned int devid; //quake id (generally player index) DWORD numbuttons; @@ -160,8 +171,6 @@ static struct wjoy_s { DWORD oldpovstate; DWORD buttonstate; DWORD oldbuttonstate; - - DWORD axis[JOY_MAX_AXES]; } wjoy[MAX_JOYSTICKS]; static int joy_count; @@ -285,7 +294,6 @@ void INS_RawInput_DeInit(void); // forward-referenced functions void INS_StartupJoystick (void); -void Joy_AdvancedUpdate_f (void); void INS_JoyMove (float *movements, int pnum); /* @@ -538,7 +546,7 @@ void INS_UpdateGrabs(int fullscreen, int activeapp) if (activeapp) { - if (!SCR_HardwareCursorIsActive() && (fullscreen || in_simulatemultitouch.ival || _windowed_mouse.value) && (current_mouse_pos.x >= window_rect.left && current_mouse_pos.y >= window_rect.top && current_mouse_pos.x <= window_rect.right && current_mouse_pos.y <= window_rect.bottom)) + if (!SCR_HardwareCursorIsActive() && (fullscreen || in_simulatemultitouch.ival || _windowed_mouse.value) && (fullscreen || (current_mouse_pos.x >= window_rect.left && current_mouse_pos.y >= window_rect.top && current_mouse_pos.x <= window_rect.right && current_mouse_pos.y <= window_rect.bottom))) { INS_HideMouse(); } @@ -1115,7 +1123,14 @@ void INS_Init (void) //keyboard variables Cvar_Register (&cl_keypad, "Input Controls"); +#ifdef AVAIL_DINPUT Cvar_Register (&in_dinput, "Input Controls"); +#endif +#ifdef AVAIL_XINPUT + Cvar_Register (&in_xinput, "Input Controls"); + Cvar_Register (&xinput_leftvibrator, "Input Controls"); + Cvar_Register (&xinput_rightvibrator, "Input Controls"); +#endif Cvar_Register (&in_builtinkeymap, "Input Controls"); Cvar_Register (&in_nonstandarddeadkeys, "Input Controls"); Cvar_Register (&in_simulatemultitouch, "Input Controls"); @@ -1143,27 +1158,7 @@ void INS_Init (void) // joystick variables Cvar_Register (&in_joystick, "Joystick variables"); - Cvar_Register (&joy_name, "Joystick variables"); - Cvar_Register (&joy_advanced, "Joystick variables"); - Cvar_Register (&joy_advaxisx, "Joystick variables"); - Cvar_Register (&joy_advaxisy, "Joystick variables"); - Cvar_Register (&joy_advaxisz, "Joystick variables"); - Cvar_Register (&joy_advaxisr, "Joystick variables"); - Cvar_Register (&joy_advaxisu, "Joystick variables"); - Cvar_Register (&joy_advaxisv, "Joystick variables"); - Cvar_Register (&joy_forwardthreshold, "Joystick variables"); - Cvar_Register (&joy_sidethreshold, "Joystick variables"); - Cvar_Register (&joy_pitchthreshold, "Joystick variables"); - Cvar_Register (&joy_yawthreshold, "Joystick variables"); - Cvar_Register (&joy_forwardsensitivity, "Joystick variables"); - Cvar_Register (&joy_sidesensitivity, "Joystick variables"); - Cvar_Register (&joy_pitchsensitivity, "Joystick variables"); - Cvar_Register (&joy_yawsensitivity, "Joystick variables"); - Cvar_Register (&joy_wwhack1, "Joystick variables"); - Cvar_Register (&joy_wwhack2, "Joystick variables"); - Cmd_AddCommand ("force_centerview", Force_CenterView_f); - Cmd_AddCommand ("joyadvancedupdate", Joy_AdvancedUpdate_f); uiWheelMessage = RegisterWindowMessageA ( "MSWHEEL_ROLLMSG" ); @@ -1590,6 +1585,46 @@ static void INS_StartupJoystickId(unsigned int id) joy_count++; } +void INS_SetupControllerAudioDevices(void) +{ +#ifdef AVAIL_XINPUT + int i; + static DWORD (WINAPI *pXInputGetDSoundAudioDeviceGuids)(DWORD dwUserIndex, GUID* pDSoundRenderGuid, GUID* pDSoundCaptureGuid); + static dllhandle_t *xinput; + if (!xinput) + { + dllfunction_t funcs[] = + { + {(void**)&pXInputGetDSoundAudioDeviceGuids, "XInputGetDSoundAudioDeviceGuids"}, + {NULL} + }; + xinput = Sys_LoadLibrary(AVAIL_XINPUT_DLL, funcs); + } + + if (!pXInputGetDSoundAudioDeviceGuids) + return; + + for (i = 0; i < joy_count; i++) + { + char audiodevicename[MAX_QPATH]; + wchar_t mssuck[MAX_QPATH]; + GUID gplayback = GUID_NULL; + GUID gcapture = GUID_NULL; + if (!wjoy[i].isxinput) + continue; + if (pXInputGetDSoundAudioDeviceGuids(wjoy[i].id, &gplayback, &gcapture) != ERROR_SUCCESS) + continue; //probably not plugged in + + if (!memcmp(&gplayback, &GUID_NULL, sizeof(gplayback))) + continue; //we have a controller, but no headset. + + StringFromGUID2(&gplayback, mssuck, sizeof(mssuck)/sizeof(mssuck[0])); + narrowen(audiodevicename, sizeof(audiodevicename), mssuck); + Con_Printf("Controller %i uses audio device %s\n", wjoy[i].id, audiodevicename); + S_SetupDeviceSeat("DirectSound", audiodevicename, wjoy[i].devid); + } +#endif +} void INS_StartupJoystick (void) { unsigned int id, numdevs; @@ -1598,8 +1633,45 @@ void INS_StartupJoystick (void) // assume no joysticks joy_count = 0; +#ifdef AVAIL_XINPUT + if (in_xinput.ival) + { + static dllhandle_t *xinput; + if (!xinput) + { + dllfunction_t funcs[] = + { + {(void**)&pXInputGetState, "XInputGetState"}, + {(void**)&pXInputSetState, "XInputSetState"}, + {NULL} + }; + xinput = Sys_LoadLibrary(AVAIL_XINPUT_DLL, funcs); + } + if (pXInputGetState) + { + DWORD (WINAPI *pXInputGetDSoundAudioDeviceGuids)(DWORD dwUserIndex, GUID* pDSoundRenderGuid, GUID* pDSoundCaptureGuid); + pXInputGetDSoundAudioDeviceGuids = Sys_GetAddressForName(xinput, "XInputGetDSoundAudioDeviceGuids"); + + for (id = 0; id < 4; id++) + { + if (joy_count == countof(wjoy)) + break; + memset(&wjoy[id], 0, sizeof(wjoy[id])); + wjoy[joy_count].isxinput = true; + wjoy[joy_count].id = id; + wjoy[joy_count].devid = id; + wjoy[joy_count].numbuttons = 16; + joy_count++; + } + Con_Printf("XInput is enabled (max %i controllers)\n", id); + } + else + Con_Printf("XInput not installed\n"); + } +#endif + // abort startup if user requests no joystick - if ( COM_CheckParm ("-nojoy") ) + if (!in_joystick.ival ) return; // verify joystick driver is present @@ -1630,77 +1702,6 @@ void INS_StartupJoystick (void) Con_DPrintf ("found no joysticks\n"); } -/* -=========== -Joy_AdvancedUpdate_f -=========== -*/ -void Joy_AdvancedUpdate_f (void) -{ - - // called once by INS_ReadJoystick and by user whenever an update is needed - // cvars are now available - int i; - DWORD dwTemp; - - // initialize all the maps - for (i = 0; i < JOY_MAX_AXES; i++) - { - dwAxisMap[i] = AxisNada; - dwControlMap[i] = JOY_ABSOLUTE_AXIS; - } - - if( joy_advanced.value == 0.0) - { - // default joystick initialization - // 2 axes only with joystick control - dwAxisMap[JOY_AXIS_X] = AxisTurn; - // dwControlMap[JOY_AXIS_X] = JOY_ABSOLUTE_AXIS; - dwAxisMap[JOY_AXIS_Y] = AxisForward; - // dwControlMap[JOY_AXIS_Y] = JOY_ABSOLUTE_AXIS; - } - else - { - if (Q_strcmp (joy_name.string, "joystick") != 0) - { - // notify user of advanced controller - Con_Printf ("\n%s configured\n\n", joy_name.string); - } - - // advanced initialization here - // data supplied by user via joy_axisn cvars - dwTemp = (DWORD) joy_advaxisx.value; - dwAxisMap[JOY_AXIS_X] = dwTemp & 0x0000000f; - dwControlMap[JOY_AXIS_X] = dwTemp & JOY_RELATIVE_AXIS; - dwTemp = (DWORD) joy_advaxisy.value; - dwAxisMap[JOY_AXIS_Y] = dwTemp & 0x0000000f; - dwControlMap[JOY_AXIS_Y] = dwTemp & JOY_RELATIVE_AXIS; - dwTemp = (DWORD) joy_advaxisz.value; - dwAxisMap[JOY_AXIS_Z] = dwTemp & 0x0000000f; - dwControlMap[JOY_AXIS_Z] = dwTemp & JOY_RELATIVE_AXIS; - dwTemp = (DWORD) joy_advaxisr.value; - dwAxisMap[JOY_AXIS_R] = dwTemp & 0x0000000f; - dwControlMap[JOY_AXIS_R] = dwTemp & JOY_RELATIVE_AXIS; - dwTemp = (DWORD) joy_advaxisu.value; - dwAxisMap[JOY_AXIS_U] = dwTemp & 0x0000000f; - dwControlMap[JOY_AXIS_U] = dwTemp & JOY_RELATIVE_AXIS; - dwTemp = (DWORD) joy_advaxisv.value; - dwAxisMap[JOY_AXIS_V] = dwTemp & 0x0000000f; - dwControlMap[JOY_AXIS_V] = dwTemp & JOY_RELATIVE_AXIS; - } - - // compute the axes to collect from DirectInput - joy_flags = JOY_RETURNCENTERED | JOY_RETURNBUTTONS | JOY_RETURNPOV; - for (i = 0; i < JOY_MAX_AXES; i++) - { - if (dwAxisMap[i] != AxisNada) - { - joy_flags |= dwAxisFlags[i]; - } - } -} - - /* =========== INS_Commands @@ -1708,11 +1709,67 @@ INS_Commands */ void INS_Commands (void) { - int i, key_index; + int i; DWORD buttonstate, povstate; unsigned int idx; struct wjoy_s *joy; + static const int xinputjbuttons[] = + { + K_UPARROW, //XINPUT_GAMEPAD_DPAD_UP + K_DOWNARROW, //XINPUT_GAMEPAD_DPAD_DOWN + K_LEFTARROW, //XINPUT_GAMEPAD_DPAD_LEFT + K_RIGHTARROW, //XINPUT_GAMEPAD_DPAD_RIGHT + K_AUX5, //XINPUT_GAMEPAD_START + K_AUX6, //XINPUT_GAMEPAD_BACK + K_AUX3, //XINPUT_GAMEPAD_LEFT_THUMB + K_AUX4, //XINPUT_GAMEPAD_RIGHT_THUMB + K_AUX1, //XINPUT_GAMEPAD_LEFT_SHOULDER + K_AUX2, //XINPUT_GAMEPAD_RIGHT_SHOULDER + K_JOY2, //XINPUT_GAMEPAD_A + K_JOY4, //XINPUT_GAMEPAD_B + K_JOY1, //XINPUT_GAMEPAD_X + K_JOY3 //XINPUT_GAMEPAD_Y + }; + static const int dinputjbuttons[32] = + { + K_JOY1, + K_JOY2, + K_JOY3, + K_JOY4, + //yes, aux1-4 skipped for compat with other quake engines. + K_AUX5, + K_AUX6, + K_AUX7, + K_AUX8, + K_AUX9, + K_AUX10, + K_AUX11, + K_AUX12, + K_AUX13, + K_AUX14, + K_AUX15, + K_AUX16, + K_AUX17, + K_AUX18, + K_AUX19, + K_AUX20, + K_AUX21, + K_AUX22, + K_AUX23, + K_AUX24, + K_AUX25, + K_AUX26, + K_AUX27, + K_AUX28, + //29-32 used for the pov stuff, so lets switch back to aux1-4 to avoid wastage + K_AUX1, + K_AUX2, + K_AUX3, + K_AUX4 + }; + + for (idx = 0; idx < joy_count; idx++) { joy = &wjoy[idx]; @@ -1720,18 +1777,26 @@ void INS_Commands (void) // loop through the joystick buttons // key a joystick event or auxillary event for higher number buttons for each state change buttonstate = joy->buttonstate; - for (i=0 ; i < joy->numbuttons ; i++) + if (joy->isxinput) { - if ( (buttonstate & (1<oldbuttonstate & (1<numbuttons ; i++) { - key_index = (i < 4) ? K_JOY1 : K_AUX1; - Key_Event (joy->devid, key_index + i, 0, true); - } + if ( (buttonstate & (1<oldbuttonstate & (1<devid, xinputjbuttons[i], 0, true); - if ( !(buttonstate & (1<oldbuttonstate & (1<oldbuttonstate & (1<devid, xinputjbuttons[i], 0, false); + } + } + else + { + for (i=0 ; i < joy->numbuttons ; i++) { - key_index = (i < 4) ? K_JOY1 : K_AUX1; - Key_Event (joy->devid, key_index + i, 0, false); + if ( (buttonstate & (1<oldbuttonstate & (1<devid, dinputjbuttons[i], 0, true); + + if ( !(buttonstate & (1<oldbuttonstate & (1<devid, dinputjbuttons[i], 0, false); } } joy->oldbuttonstate = buttonstate; @@ -1779,54 +1844,88 @@ INS_ReadJoystick */ qboolean INS_ReadJoystick (struct wjoy_s *joy) { - memset (&ji, 0, sizeof(ji)); - ji.dwSize = sizeof(ji); - ji.dwFlags = joy_flags; - - if (joyGetPosEx (joy->id, &ji) == JOYERR_NOERROR) +#ifdef AVAIL_XINPUT + if (joy->isxinput) { - // this is a hack -- there is a bug in the Logitech WingMan Warrior DirectInput Driver - // rather than having 32768 be the zero point, they have the zero point at 32668 - // go figure -- anyway, now we get the full resolution out of the device - if (joy_wwhack1.value != 0.0) + XINPUT_STATE xistate; + XINPUT_VIBRATION vibrator; + HRESULT hr = pXInputGetState(joy->id, &xistate); + +#if 1 + //I don't have a controller to test this with, so we fake stuff. + if (joy->id == 3) { - ji.dwUpos += 100; + POINT p; + GetCursorPos(&p); + hr = ERROR_SUCCESS; + xistate.Gamepad.wButtons = 0; + xistate.Gamepad.sThumbRX = 0;//(p.x/1920.0)*0xffff - 0x8000; + xistate.Gamepad.sThumbRY = 0;//(p.y/1080.0)*0xffff - 0x8000; + xistate.Gamepad.sThumbLX = (p.x/1920.0)*0xffff - 0x8000; + xistate.Gamepad.sThumbLY = (p.y/1080.0)*0xffff - 0x8000; + xistate.Gamepad.bLeftTrigger = 0; + xistate.Gamepad.bRightTrigger = 0; + } +#endif + + if (hr == ERROR_SUCCESS) + { //ERROR_SUCCESS + //do we care about the dwPacketNumber? + joy->buttonstate = xistate.Gamepad.wButtons & 0xffff; + + IN_JoystickAxisEvent(joy->devid, 0, xistate.Gamepad.sThumbRX / 32768.0); + IN_JoystickAxisEvent(joy->devid, 1, xistate.Gamepad.sThumbRY / 32768.0); + IN_JoystickAxisEvent(joy->devid, 2, xistate.Gamepad.bRightTrigger/255.0); + IN_JoystickAxisEvent(joy->devid, 3, xistate.Gamepad.sThumbLX / 32768.0); + IN_JoystickAxisEvent(joy->devid, 4, xistate.Gamepad.sThumbLY / 32768.0); + IN_JoystickAxisEvent(joy->devid, 5, xistate.Gamepad.bLeftTrigger/255.0); + + vibrator.wLeftMotorSpeed = xinput_leftvibrator.value * 0xffff; + vibrator.wRightMotorSpeed = xinput_rightvibrator.value * 0xffff; + pXInputSetState(joy->id, &vibrator); + return true; } - joy->povstate = ji.dwPOV; - joy->buttonstate = ji.dwButtons; - joy->axis[JOY_AXIS_X] = ji.dwXpos; - joy->axis[JOY_AXIS_Y] = ji.dwYpos; - joy->axis[JOY_AXIS_Z] = ji.dwZpos; - joy->axis[JOY_AXIS_R] = ji.dwRpos; - joy->axis[JOY_AXIS_U] = ji.dwUpos; - joy->axis[JOY_AXIS_V] = ji.dwVpos; - return true; } else +#endif { - joy->povstate = 0; - joy->buttonstate = 0; - joy->axis[JOY_AXIS_X] = 32768; - joy->axis[JOY_AXIS_Y] = 32768; - joy->axis[JOY_AXIS_Z] = 32768; - joy->axis[JOY_AXIS_R] = 32768; - joy->axis[JOY_AXIS_U] = 32768; - joy->axis[JOY_AXIS_V] = 32768; + memset (&ji, 0, sizeof(ji)); + ji.dwSize = sizeof(ji); + ji.dwFlags = joy_flags; - // read error occurred - // turning off the joystick seems too harsh for 1 read error, - // but what should be done? - // Con_Printf ("INS_ReadJoystick: no response\n"); - // joy_avail = false; - return false; + if (joyGetPosEx (joy->id, &ji) == JOYERR_NOERROR) + { + joy->povstate = ji.dwPOV; + joy->buttonstate = ji.dwButtons; + IN_JoystickAxisEvent(joy->devid, 0, (ji.dwXpos - 32768.0) / 32768); + IN_JoystickAxisEvent(joy->devid, 1, (ji.dwYpos - 32768.0) / 32768); + IN_JoystickAxisEvent(joy->devid, 2, (ji.dwZpos - 32768.0) / 32768); + IN_JoystickAxisEvent(joy->devid, 3, (ji.dwRpos - 32768.0) / 32768); + IN_JoystickAxisEvent(joy->devid, 4, (ji.dwUpos - 32768.0) / 32768); + IN_JoystickAxisEvent(joy->devid, 5, (ji.dwVpos - 32768.0) / 32768); + return true; + } } + + joy->povstate = 0; + joy->buttonstate = 0; + IN_JoystickAxisEvent(joy->devid, 0, 0); + IN_JoystickAxisEvent(joy->devid, 1, 0); + IN_JoystickAxisEvent(joy->devid, 2, 0); + IN_JoystickAxisEvent(joy->devid, 3, 0); + IN_JoystickAxisEvent(joy->devid, 4, 0); + IN_JoystickAxisEvent(joy->devid, 5, 0); + + // read error occurred + // turning off the joystick seems too harsh for 1 read error, + // but what should be done? + // Con_Printf ("INS_ReadJoystick: no response\n"); + // joy_avail = false; + return false; } static void INS_JoyMovePtr (struct wjoy_s *joy, float *movements, int pnum) { - float speed, aspeed; - float fAxisValue, fTemp; - int i; int wpnum; /*each device will be processed when its player comes to be processed*/ @@ -1846,153 +1945,6 @@ static void INS_JoyMovePtr (struct wjoy_s *joy, float *movements, int pnum) { return; } - - if (in_speed.state[pnum] & 1) - speed = cl_movespeedkey.value; - else - speed = 1; - aspeed = speed * host_frametime; - - // loop through the axes - for (i = 0; i < JOY_MAX_AXES; i++) - { - // get the floating point zero-centered, potentially-inverted data for the current axis - fAxisValue = (float) joy->axis[i]; - // move centerpoint to zero - fAxisValue -= 32768.0; - - if (joy_wwhack2.value != 0.0) - { - if (dwAxisMap[i] == AxisTurn) - { - // this is a special formula for the Logitech WingMan Warrior - // y=ax^b; where a = 300 and b = 1.3 - // also x values are in increments of 800 (so this is factored out) - // then bounds check result to level out excessively high spin rates - fTemp = 300.0 * pow(abs(fAxisValue) / 800.0, 1.3); - if (fTemp > 14000.0) - fTemp = 14000.0; - // restore direction information - fAxisValue = (fAxisValue > 0.0) ? fTemp : -fTemp; - } - } - - // convert range from -32768..32767 to -1..1 - fAxisValue /= 32768.0; - -#ifdef CSQC_DAT - if (CSQC_JoystickAxis(i, fAxisValue, joy->devid)) - continue; -#endif - - switch (dwAxisMap[i]) - { - case AxisForward: - if ((joy_advanced.value == 0.0) && (in_mlook.state[pnum] & 1)) - { - // user wants forward control to become look control - if (fabs(fAxisValue) > joy_pitchthreshold.value) - { - // if mouse invert is on, invert the joystick pitch value - // only absolute control support here (joy_advanced is false) - if (m_pitch.value < 0.0) - { - cl.playerview[pnum].viewanglechange[PITCH] -= (fAxisValue * joy_pitchsensitivity.value) * aspeed * cl_pitchspeed.value; - } - else - { - cl.playerview[pnum].viewanglechange[PITCH] += (fAxisValue * joy_pitchsensitivity.value) * aspeed * cl_pitchspeed.value; - } - V_StopPitchDrift(&cl.playerview[pnum]); - } - else - { - // no pitch movement - // disable pitch return-to-center unless requested by user - // *** this code can be removed when the lookspring bug is fixed - // *** the bug always has the lookspring feature on - if(lookspring.value == 0.0) - V_StopPitchDrift(&cl.playerview[pnum]); - } - } - else - { - // user wants forward control to be forward control - if (fabs(fAxisValue) > joy_forwardthreshold.value) - { - movements[0] += (fAxisValue * joy_forwardsensitivity.value) * speed * cl_forwardspeed.value; - } - } - break; - - case AxisSide: - if (fabs(fAxisValue) > joy_sidethreshold.value) - { - movements[1] += (fAxisValue * joy_sidesensitivity.value) * speed * cl_sidespeed.value; - } - break; - - case AxisTurn: - if ((in_strafe.state[pnum] & 1) || (lookstrafe.value && (in_mlook.state[pnum] & 1))) - { - // user wants turn control to become side control - if (fabs(fAxisValue) > joy_sidethreshold.value) - { - movements[2] -= (fAxisValue * joy_sidesensitivity.value) * speed * cl_sidespeed.value; - } - } - else - { - // user wants turn control to be turn control - if (fabs(fAxisValue) > joy_yawthreshold.value) - { - if(dwControlMap[i] == JOY_ABSOLUTE_AXIS) - { - cl.playerview[pnum].viewanglechange[YAW] += (fAxisValue * joy_yawsensitivity.value) * aspeed * cl_yawspeed.value; - } - else - { - cl.playerview[pnum].viewanglechange[YAW] += (fAxisValue * joy_yawsensitivity.value) * speed * 180.0; - } - - } - } - break; - - case AxisLook: - if (in_mlook.state[pnum] & 1) - { - if (fabs(fAxisValue) > joy_pitchthreshold.value) - { - // pitch movement detected and pitch movement desired by user - if(dwControlMap[i] == JOY_ABSOLUTE_AXIS) - { - cl.playerview[pnum].viewanglechange[PITCH] += (fAxisValue * joy_pitchsensitivity.value) * aspeed * cl_pitchspeed.value; - } - else - { - cl.playerview[pnum].viewanglechange[PITCH] += (fAxisValue * joy_pitchsensitivity.value) * speed * 180.0; - } - V_StopPitchDrift(&cl.playerview[pnum]); - } - else - { - // no pitch movement - // disable pitch return-to-center unless requested by user - // *** this code can be removed when the lookspring bug is fixed - // *** the bug always has the lookspring feature on - if(lookspring.value == 0.0) - V_StopPitchDrift(&cl.playerview[pnum]); - } - } - break; - - default: - break; - } - } - - CL_ClampPitch(pnum); } /* =========== @@ -2002,19 +1954,6 @@ INS_JoyMove void INS_JoyMove (float *movements, int pnum) { unsigned int idx; - - // complete initialization if first time in - // this is needed as cvars are not available at initialization time - if( joy_advancedinit != true ) - { - Joy_AdvancedUpdate_f(); - joy_advancedinit = true; - } - - // verify joystick is available and that the user wants to use it - if (!in_joystick.value) - return; - for (idx = 0; idx < joy_count; idx++) { INS_JoyMovePtr(&wjoy[idx], movements, pnum); @@ -2040,7 +1979,12 @@ void INS_EnumerateDevices(void *ctx, void(*callback)(void *ctx, char *type, char callback(ctx, "mouse", "system", NULL); for (idx = 0; idx < joy_count; idx++) - callback(ctx, "joy", va("mmj%i", idx), &wjoy[idx].devid); + { + if (wjoy[idx].isxinput) + callback(ctx, "joy", va("xi%i", wjoy[idx].id), &wjoy[idx].devid); + else + callback(ctx, "joy", va("mmj%i", wjoy[idx].id), &wjoy[idx].devid); + } } static qbyte scantokey[] = diff --git a/engine/client/input.h b/engine/client/input.h index 422f6cfb..403d499c 100644 --- a/engine/client/input.h +++ b/engine/client/input.h @@ -22,6 +22,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. void IN_ReInit (void); void IN_Init (void); +float IN_DetermineMouseRate(void); void IN_Shutdown (void); @@ -58,6 +59,7 @@ void INS_Init (void); void INS_Shutdown (void); void INS_Commands (void); //final chance to call IN_MouseMove/IN_KeyEvent each frame void INS_EnumerateDevices(void *ctx, void(*callback)(void *ctx, char *type, char *devicename, int *qdevid)); +void INS_SetupControllerAudioDevices(void); //creates audio devices for each controller (where controllers have their own audio devices) extern cvar_t cl_nodelta; extern cvar_t cl_c2spps; diff --git a/engine/client/keys.c b/engine/client/keys.c index 99ef04b2..b30c228a 100644 --- a/engine/client/keys.c +++ b/engine/client/keys.c @@ -46,6 +46,7 @@ struct key_cursor_s key_customcursor[kc_max]; int key_count; // incremented every key event +int key_bindmaps[2]; char *keybindings[K_MAX][KEY_MODIFIERSTATES]; qbyte bindcmdlevel[K_MAX][KEY_MODIFIERSTATES]; qboolean consolekeys[K_MAX]; // if true, can't be rebound while in console @@ -79,6 +80,30 @@ static int KeyModifier (qboolean shift, qboolean alt, qboolean ctrl) return stateset; } +void Key_GetBindMap(int *bindmaps) +{ + int i; + for (i = 0; i < countof(key_bindmaps); i++) + { + if (key_bindmaps[i]) + bindmaps[i] = (key_bindmaps[i]&~KEY_MODIFIER_ALTBINDMAP) + 1; + else + bindmaps[i] = 0; + } +} + +void Key_SetBindMap(int *bindmaps) +{ + int i; + for (i = 0; i < countof(key_bindmaps); i++) + { + if (bindmaps[i] > 0 && bindmaps[i] <= KEY_MODIFIER_ALTBINDMAP) + key_bindmaps[i] = (bindmaps[i]-1)|KEY_MODIFIER_ALTBINDMAP; + else + key_bindmaps[i] = 0; + } +} + typedef struct { char *name; @@ -227,6 +252,7 @@ keyname_t keynames[] = {"SCROLLLOCK", K_SCRLCK}, {"SEMICOLON", ';'}, // because a raw semicolon seperates commands + {"PLUS", '+'}, // because "shift++" is inferior to shift+plus {"TILDE", '~'}, {"BACKQUOTE", '`'}, @@ -1543,11 +1569,28 @@ void Key_Message (int key, int unicode) //============================================================================ -char *Key_GetBinding(int keynum) +//for qc +char *Key_GetBinding(int keynum, int bindmap, int modifier) { - if (keynum >= 0 && keynum < K_MAX) - return keybindings[keynum][0]; - return NULL; + char *key = NULL; + if (keynum < 0 || keynum >= K_MAX) + ; + else if (bindmap < 0) + { + key = NULL; + if (!key) + key = keybindings[keynum][key_bindmaps[0]]; + if (!key) + key = keybindings[keynum][key_bindmaps[1]]; + } + else + { + if (bindmap) + modifier = (bindmap-1) + KEY_MODIFIER_ALTBINDMAP; + if (modifier >= 0 && modifier < KEY_MODIFIERSTATES) + key = keybindings[keynum][modifier]; + } + return key; } /* @@ -1562,27 +1605,36 @@ the K_* names are matched up. int Key_StringToKeynum (const char *str, int *modifier) { keyname_t *kn; - char *underscore; - if (!strnicmp(str, "std_", 4)) + if (!strnicmp(str, "std_", 4) || !strnicmp(str, "std+", 4)) *modifier = 0; else { - *modifier = 0; - while(1) + struct { - underscore = strchr(str, '_'); - if (!underscore || !underscore[1]) - break; //nothing afterwards or no underscore. - if (!strnicmp(str, "shift_", 6)) - *modifier |= KEY_MODIFIER_SHIFT; - else if (!strnicmp(str, "alt_", 4)) - *modifier |= KEY_MODIFIER_ALT; - else if (!strnicmp(str, "ctrl_", 5)) - *modifier |= KEY_MODIFIER_CTRL; - else - break; - str = underscore+1; //next char. + char *prefix; + int len; + int mod; + } mods[] = + { + {"shift", 5, KEY_MODIFIER_SHIFT}, + {"ctrl", 4, KEY_MODIFIER_CTRL}, + {"alt", 3, KEY_MODIFIER_ALT}, + }; + int i; + *modifier = 0; + for (i = 0; i < countof(mods); ) + { + if (!Q_strncasecmp(mods[i].prefix, str, mods[i].len)) + if (str[mods[i].len] == '_' || str[mods[i].len] == '+' || str[mods[i].len] == ' ') + if (str[mods[i].len+1]) + { + *modifier |= mods[i].mod; + str += mods[i].len+1; + i = 0; + continue; + } + i++; } if (!*modifier) *modifier = ~0; @@ -1623,7 +1675,7 @@ given keynum. FIXME: handle quote special (general escape sequence?) =================== */ -char *Key_KeynumToString (int keynum) +static char *Key_KeynumToStringRaw (int keynum) { keyname_t *kn; static char tinystr[2]; @@ -1650,6 +1702,31 @@ char *Key_KeynumToString (int keynum) return ""; } +char *Key_KeynumToString (int keynum, int modifier) +{ + char *r = Key_KeynumToStringRaw(keynum); + if (r[0] == '<' && r[1]) + modifier = 0; //would be too weird. + switch(modifier) + { + case KEY_MODIFIER_CTRL|KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT: + return va("Ctrl+Alt+Shift+%s", r); + case KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT: + return va("Alt+Shift+%s", r); + case KEY_MODIFIER_CTRL|KEY_MODIFIER_SHIFT: + return va("Ctrl+Shift+%s", r); + case KEY_MODIFIER_CTRL|KEY_MODIFIER_ALT: + return va("Ctrl+Alt+%s", r); + case KEY_MODIFIER_CTRL: + return va("Ctrl+%s", r); + case KEY_MODIFIER_ALT: + return va("Alt+%s", r); + case KEY_MODIFIER_SHIFT: + return va("Shift+%s", r); + default: + return r; + } +} /* =================== @@ -1663,8 +1740,16 @@ void Key_SetBinding (int keynum, int modifier, char *binding, int level) if (modifier == ~0) //all of the possibilities. { - for (l = 0; l < KEY_MODIFIERSTATES; l++) - Key_SetBinding(keynum, l, binding, level); + if (binding) + { //bindmaps are meant to be independant of each other. + for (l = 0; l < KEY_MODIFIER_ALTBINDMAP; l++) + Key_SetBinding(keynum, l, binding, level); + } + else + { //when unbinding, unbind all bindmaps. + for (l = 0; l < KEY_MODIFIERSTATES; l++) + Key_SetBinding(keynum, l, binding, level); + } return; } @@ -1753,6 +1838,13 @@ void Key_Bind_f (void) { int i, c, b, modifier; char cmd[1024]; + int bindmap = 0; + + if (!strcmp("in_bind", Cmd_Argv(0))) + { + bindmap = atoi(Cmd_Argv(1)); + Cmd_ShiftArgs(1, Cmd_ExecLevel==RESTRICT_LOCAL); + } c = Cmd_Argc(); @@ -1768,6 +1860,20 @@ void Key_Bind_f (void) Con_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1)); return; } + if (bindmap) + { + if (bindmap < 0 || bindmap > KEY_MODIFIER_ALTBINDMAP) + { + Con_Printf ("unsupported bindmap %i\n", bindmap); + return; + } + if (modifier != ~0) + { + Con_Printf ("modifiers cannot be combined with bindmaps\n"); + return; + } + modifier = (bindmap-1) | KEY_MODIFIER_ALTBINDMAP; + } if (c == 2) { @@ -1874,7 +1980,6 @@ void Key_WriteBindings (vfsfile_t *f) int i, m; char *binding, *base; - char prefix[128]; char keybuf[256]; char commandbuf[2048]; @@ -1883,22 +1988,14 @@ void Key_WriteBindings (vfsfile_t *f) base = keybindings[i][0]; //plus we can use the config with other clients. if (!base) base = ""; - for (m = 0; m < KEY_MODIFIERSTATES; m++) + for (m = 0; m < KEY_MODIFIER_ALTBINDMAP; m++) { binding = keybindings[i][m]; if (!binding) binding = ""; if (strcmp(binding, base) || (m==0 && keybindings[i][0]) || bindcmdlevel[i][m] != bindcmdlevel[i][0]) { - *prefix = '\0'; - if (m & KEY_MODIFIER_CTRL) - strcat(prefix, "CTRL_"); - if (m & KEY_MODIFIER_ALT) - strcat(prefix, "ALT_"); - if (m & KEY_MODIFIER_SHIFT) - strcat(prefix, "SHIFT_"); - - s = va("%s%s", prefix, Key_KeynumToString(i)); + s = Key_KeynumToString(i, m); //quote it as required if (i == ';' || i <= ' ' || i == '\"') s = COM_QuotedString(s, keybuf, sizeof(keybuf), false); @@ -1910,6 +2007,21 @@ void Key_WriteBindings (vfsfile_t *f) VFS_WRITE(f, s, strlen(s)); } } + //now generate some special in_binds for bindmaps. + for (m = 0; m < KEY_MODIFIER_ALTBINDMAP; m++) + { + binding = keybindings[i][m|KEY_MODIFIER_ALTBINDMAP]; + if (binding && *binding) + { + s = va("%s", Key_KeynumToString(i, 0)); + //quote it as required + if (i == ';' || i <= ' ' || i == '\"') + s = COM_QuotedString(s, keybuf, sizeof(keybuf), false); + + s = va("in_bind %i %s %s\n", m+1, s, COM_QuotedString(binding, commandbuf, sizeof(commandbuf), false)); + VFS_WRITE(f, s, strlen(s)); + } + } } } @@ -2012,6 +2124,7 @@ void Key_Init (void) // register our functions // Cmd_AddCommand ("bind",Key_Bind_f); + Cmd_AddCommand ("in_bind",Key_Bind_f); Cmd_AddCommand ("bindlevel",Key_BindLevel_f); Cmd_AddCommand ("unbind",Key_Unbind_f); Cmd_AddCommand ("unbindall",Key_Unbindall_f); @@ -2125,8 +2238,8 @@ void Key_Event (int devid, int key, unsigned int unicode, qboolean down) } //yes, csqc is allowed to steal the escape key. - if (key != '`' && (!down || key != K_ESCAPE || (!Key_Dest_Has(~kdm_game) && !shift_down))) - if (!Key_Dest_Has(~kdm_game) && !Media_PlayingFullScreen()) + if (key != '`' && (!down || key != K_ESCAPE || (!Key_Dest_Has(~kdm_game) && !shift_down)) && + !Key_Dest_Has(~kdm_game) && !Media_PlayingFullScreen()) { #ifdef CSQC_DAT if (CSQC_KeyPress(key, unicode, down, devid)) //give csqc a chance to handle it. @@ -2135,6 +2248,13 @@ void Key_Event (int devid, int key, unsigned int unicode, qboolean down) #ifdef VM_CG if (CG_KeyPress(key, unicode, down)) return; +#endif + } + else if (!down && key) + { +#ifdef CSQC_DAT + //csqc should still be told of up events. note that there's some filering to prevent notifying about events that it shouldn't receive (like all the up events when typing at the console). + CSQC_KeyPress(key, unicode, down, devid); #endif } @@ -2159,7 +2279,13 @@ void Key_Event (int devid, int key, unsigned int unicode, qboolean down) #endif if (!down) + { +#ifdef MENU_DAT + if (Key_Dest_Has(kdm_gmenu) && !Key_Dest_Has(kdm_editor|kdm_console|kdm_cwindows)) + MP_Keyup (key, unicode); +#endif return; + } if (Key_Dest_Has(kdm_console)) { @@ -2342,8 +2468,27 @@ void Key_Event (int devid, int key, unsigned int unicode, qboolean down) else *p = 0; - dc = keybindings[key][modifierstate]; - bl = bindcmdlevel[key][modifierstate]; + //assume the worst + dc = NULL; + bl = 0; + //try bindmaps if they're set + if (key_bindmaps[0] && (!dc || !*dc)) + { + dc = keybindings[key][key_bindmaps[0]]; + bl = bindcmdlevel[key][key_bindmaps[0]]; + } + if (key_bindmaps[1] && (!dc || !*dc)) + { + dc = keybindings[key][key_bindmaps[1]]; + bl = bindcmdlevel[key][key_bindmaps[1]]; + } + + //regular ctrl_alt_shift_foo binds + if (!dc || !*dc) + { + dc = keybindings[key][modifierstate]; + bl = bindcmdlevel[key][modifierstate]; + } //simulate singular shift+alt+ctrl for binds (no left/right). really though, this code should translate to csqc/menu keycodes and back to resolve the weirdness instead. if (key == K_RALT && (!dc || !*dc)) diff --git a/engine/client/keys.h b/engine/client/keys.h index 85782b51..82480a77 100644 --- a/engine/client/keys.h +++ b/engine/client/keys.h @@ -166,10 +166,11 @@ K_PRINTSCREEN = 248, K_MAX = 256 }; -#define KEY_MODIFIER_SHIFT (1<<0) -#define KEY_MODIFIER_ALT (1<<1) -#define KEY_MODIFIER_CTRL (1<<2) -#define KEY_MODIFIERSTATES (1<<3) +#define KEY_MODIFIER_SHIFT (1<<0) +#define KEY_MODIFIER_ALT (1<<1) +#define KEY_MODIFIER_CTRL (1<<2) +#define KEY_MODIFIER_ALTBINDMAP (1<<3) +#define KEY_MODIFIERSTATES (1<<4) #define K_SHIFT K_LSHIFT #define K_CTRL K_LCTRL @@ -194,7 +195,7 @@ typedef enum //highest has priority extern unsigned int key_dest_absolutemouse; //if the active key dest bit is set, the mouse is absolute. extern unsigned int key_dest_mask; -extern char *keybindings[K_MAX][8]; +extern char *keybindings[K_MAX][16]; extern int key_repeats[K_MAX]; extern int key_count; // incremented every key event extern int key_lastpress; diff --git a/engine/client/m_items.c b/engine/client/m_items.c index 8dc16848..044c2447 100644 --- a/engine/client/m_items.c +++ b/engine/client/m_items.c @@ -641,33 +641,32 @@ static void MenuDrawItems(int xpos, int ypos, menuoption_t *option, menu_t *menu { int x = xpos+option->common.posx; int y = ypos+option->common.posy; - int keys[2]; + int keys[8], keymods[countof(keys)]; + int keycount; char *keyname; + int j; Draw_FunStringWidth(x, y, option->bind.caption, option->bind.captionwidth, true, !menu->cursoritem && menu->selecteditem == option); x += option->bind.captionwidth + 3*8; - { - M_FindKeysForCommand (cl_forceseat.ival, option->bind.command, keys); + keycount = M_FindKeysForCommand (0, cl_forceseat.ival, option->bind.command, keys, keymods, countof(keys)); - if (bindingactive && menu->selecteditem == option) - { - Draw_FunString (x, y, "Press key"); - } - else if (keys[0] == -1) - { - Draw_FunString (x, y, "???"); - } - else - { - keyname = Key_KeynumToString (keys[0]); + if (bindingactive && menu->selecteditem == option) + Draw_FunString (x, y, "Press key"); + else if (!keycount) + Draw_FunString (x, y, "???"); + else + { + for (j = 0; j < keycount; j++) + { /*these offsets are wrong*/ + if (j) + { + Draw_FunString (x + 8, y, "or"); + x += 32; + } + keyname = Key_KeynumToString (keys[j], keymods[j]); Draw_FunString (x, y, keyname); x += strlen(keyname) * 8; - if (keys[1] != -1) - { /*these offsets are wrong*/ - Draw_FunString (x + 8, y, "or"); - Draw_FunString (x + 32, y, Key_KeynumToString (keys[1])); - } } } } @@ -1840,7 +1839,22 @@ void M_Complex_Key(int key, int unicode) if (key != K_ESCAPE && key != '`') { - Cbuf_InsertText (va("bind \"%s\" \"%s\"\n", Key_KeynumToString (key), currentmenu->selecteditem->bind.command), RESTRICT_LOCAL, false); + int modifiers = 0; + extern qboolean keydown[]; + if (keydown[K_LSHIFT] && key != K_LSHIFT) + modifiers |= 1; + if (keydown[K_RSHIFT] && key != K_RSHIFT) + modifiers |= 1; + if (keydown[K_LALT] && key != K_LALT) + modifiers |= 2; + if (keydown[K_RALT] && key != K_RALT) + modifiers |= 2; + if (keydown[K_LCTRL] && key != K_LCTRL) + modifiers |= 4; + if (keydown[K_RCTRL] && key != K_RCTRL) + modifiers |= 4; + + Cbuf_InsertText (va("bind \"%s\" \"%s\"\n", Key_KeynumToString (key, modifiers), currentmenu->selecteditem->bind.command), RESTRICT_LOCAL, false); } bindingactive = false; return; diff --git a/engine/client/m_mp3.c b/engine/client/m_mp3.c index a1d3f8ef..6b143487 100644 --- a/engine/client/m_mp3.c +++ b/engine/client/m_mp3.c @@ -22,30 +22,68 @@ typedef struct mediatrack_s{ int length; struct mediatrack_s *next; } mediatrack_t; + qboolean media_fadeout; float media_fadeouttime; - -static mediatrack_t currenttrack; -int media_playing=true;//try to continue from the standard playlist #endif +//higher bits have priority (if they have something to play). +#define MEDIA_GAMEMUSIC (1u<<0) //cd music. also music command etc. +#define MEDIA_CVARLIST (1u<<1) //cvar abuse. handy for preserving times when switching tracks. +#define MEDIA_PLAYLIST (1u<<2) // +static unsigned int media_playlisttypes; +//info about the current stuff that is playing. +static unsigned int media_playlistcurrent; +static char media_currenttrack[MAX_QPATH]; +static char media_friendlyname[MAX_QPATH]; +static int cdplayingtrack; //currently playing cd track (becomes 0 when paused) +static int cdpausedtrack; //currently paused cd track + +//info about (fake)cd tracks that we want to play +int cdplaytracknum; +static char cdplaytrack[MAX_QPATH]; static char cdloopingtrack[MAX_QPATH]; -static qboolean fakecdactive; - -#define REMAPPED_TRACKS 100 +//info about (fake)cd tracks that we could play if asked. +#define REMAPPED_TRACKS 256 static struct { char fname[MAX_QPATH]; } cdremap[REMAPPED_TRACKS]; static qboolean cdenabled; -static int cdplayingtrack; //currently playing cd track (becomes 0 when paused) -static int cdpausedtrack; //currently paused cd track static int cdnumtracks; //maximum cd track we can play. -//flushes music channel on all soundcards, and the tracks that arn't decoded yet. -void Media_Clear (void) + +//cvar abuse +static int music_playlist_last; +static cvar_t music_playlist_index = CVAR("music_playlist_index", "-1"); +static cvar_t music_playlist_list[] = { + CVAR("music_playlist_list0", ""), + CVAR("music_playlist_list1", ""), + CVAR("music_playlist_list2", ""), + CVAR("music_playlist_list3", ""), + CVAR("music_playlist_list4", ""), + CVAR("music_playlist_list5", "") +}; +static cvar_t music_playlist_sampleposition[] = +{ + CVAR("music_playlist_sampleposition0", "-1"), + CVAR("music_playlist_sampleposition1", "-1"), + CVAR("music_playlist_sampleposition2", "-1"), + CVAR("music_playlist_sampleposition3", "-1"), + CVAR("music_playlist_sampleposition4", "-1"), + CVAR("music_playlist_sampleposition5", "-1") +}; +#define CVAR_ABUSE_LIMIT countof(music_playlist_list) + + +qboolean Media_Changed (unsigned int mediatype) +{ + //something changed, but it has a lower priority so we don't care + if (mediatype < media_playlistcurrent) + return false; + //make sure we're not playing any cd music. if (cdplayingtrack || cdpausedtrack) { @@ -55,17 +93,14 @@ void Media_Clear (void) } #if !defined(NOMEDIA) - Q_strncpyz(currenttrack.filename, "", sizeof(currenttrack.filename)); - fakecdactive = false; - media_playing = false; - media_fadeout = true; media_fadeouttime = realtime; #endif + return true; } //fake cd tracks. -qboolean Media_BackgroundTrack(const char *track, const char *looptrack) +qboolean Media_NamedTrack(const char *track, const char *looptrack) { unsigned int tracknum; #if !defined(NOMEDIA) @@ -148,13 +183,10 @@ qboolean Media_BackgroundTrack(const char *track, const char *looptrack) if (found) { + cdplaytracknum = 0; + Q_strncpyz(cdplaytrack, trackname, sizeof(cdplaytrack)); Q_strncpyz(cdloopingtrack, looptrack, sizeof(cdloopingtrack)); - - Media_Clear(); - Q_strncpyz(currenttrack.filename, trackname, sizeof(currenttrack.filename)); - fakecdactive = true; - media_playing = true; - cdplayingtrack = tracknum; + Media_Changed(MEDIA_GAMEMUSIC); return true; } #endif @@ -177,10 +209,10 @@ qboolean Media_BackgroundTrack(const char *track, const char *looptrack) if (cdplayingtrack == tracknum) return true; //already playing, don't need to do anything - Media_Clear(); - cdpausedtrack = 0; - cdplayingtrack = tracknum; - CDAudio_Play(tracknum); + cdplaytracknum = tracknum; + Q_strncpyz(cdplaytrack, "", sizeof(cdplaytrack)); + Q_strncpyz(cdloopingtrack, "", sizeof(cdloopingtrack)); + Media_Changed(MEDIA_GAMEMUSIC); return true; } return false; @@ -190,9 +222,9 @@ qboolean Media_BackgroundTrack(const char *track, const char *looptrack) void Media_NamedTrack_f(void) { if (Cmd_Argc() == 3) - Media_BackgroundTrack(Cmd_Argv(1), Cmd_Argv(2)); + Media_NamedTrack(Cmd_Argv(1), Cmd_Argv(2)); else - Media_BackgroundTrack(Cmd_Argv(1), Cmd_Argv(1)); + Media_NamedTrack(Cmd_Argv(1), Cmd_Argv(1)); } void Media_NumberedTrack(unsigned int initialtrack, unsigned int looptrack) @@ -200,7 +232,7 @@ void Media_NumberedTrack(unsigned int initialtrack, unsigned int looptrack) char *init = initialtrack?va("%u", initialtrack):NULL; char *loop = looptrack?va("%u", looptrack):NULL; - Media_BackgroundTrack(init, loop); + Media_NamedTrack(init, loop); } void Media_EndedTrack(void) @@ -209,7 +241,7 @@ void Media_EndedTrack(void) cdpausedtrack = 0; if (*cdloopingtrack) - Media_BackgroundTrack(cdloopingtrack, cdloopingtrack); + Media_NamedTrack(cdloopingtrack, cdloopingtrack); } @@ -219,7 +251,7 @@ void Media_EndedTrack(void) #include "winquake.h" #if defined(_WIN32) && !defined(WINRT) -#define WINAMP +//#define WINAMP #endif #if defined(_WIN32) && !defined(WINRT) #define WINAVI @@ -364,7 +396,8 @@ qboolean Media_EvaluateNextTrack(void) { if (!trnum) { - memcpy(¤ttrack, track->filename, sizeof(mediatrack_t)); + Q_strncpyz(media_currenttrack, track->filename, sizeof(media_currenttrack)); + Q_strncpyz(media_friendlyname, track->nicename, sizeof(media_friendlyname)); lasttrackplayed = nexttrack; break; } @@ -385,10 +418,8 @@ qboolean Media_EvaluateNextTrack(void) nexttrack = 0; else { - *currenttrack.filename='\0'; - *currenttrack.nicename='\0'; - nexttrack = -1; - media_playing = false; + *media_currenttrack='\0'; + *media_friendlyname='\0'; return false; } } @@ -398,7 +429,8 @@ qboolean Media_EvaluateNextTrack(void) { if (!trnum) { - memcpy(¤ttrack, track->filename, sizeof(mediatrack_t)); + Q_strncpyz(media_currenttrack, track->filename, sizeof(media_currenttrack)); + Q_strncpyz(media_friendlyname, track->nicename, sizeof(media_friendlyname)); lasttrackplayed = nexttrack; break; } @@ -443,16 +475,16 @@ void CD_f (void) if (Q_strcasecmp(command, "play") == 0) { - Media_BackgroundTrack(Cmd_Argv(2), "-"); + Media_NamedTrack(Cmd_Argv(2), "-"); return; } if (Q_strcasecmp(command, "loop") == 0) { if (Cmd_Argc() < 4) - Media_BackgroundTrack(Cmd_Argv(2), NULL); + Media_NamedTrack(Cmd_Argv(2), NULL); else - Media_BackgroundTrack(Cmd_Argv(2), Cmd_Argv(3)); + Media_NamedTrack(Cmd_Argv(2), Cmd_Argv(3)); return; } @@ -473,7 +505,9 @@ void CD_f (void) if (Q_strcasecmp(command, "stop") == 0) { - Media_Clear(); + *cdplaytrack = *cdloopingtrack = 0; + cdplaytracknum = 0; + Media_Changed(MEDIA_GAMEMUSIC); return; } @@ -500,6 +534,10 @@ void CD_f (void) if (cdplayingtrack || cdpausedtrack) CDAudio_Stop(); cdenabled = false; + + *cdplaytrack = *cdloopingtrack = 0; + cdplaytracknum = 0; + Media_Changed(MEDIA_GAMEMUSIC); return; } @@ -565,8 +603,7 @@ void CD_f (void) //actually, this func just flushes and states that it should be playing. the ambientsound func actually changes the track. void Media_Next_f (void) { - Media_Clear(); - media_playing=true; + Media_Changed(MEDIA_PLAYLIST); #ifdef WINAMP if (media_hijackwinamp.value) @@ -599,6 +636,9 @@ void Media_AddTrack(const char *fname) newtrack->next = tracks; tracks = newtrack; numtracks++; + + if (numtracks == 1) + Media_Changed(MEDIA_PLAYLIST); } void Media_RemoveTrack(const char *fname) { @@ -611,8 +651,11 @@ void Media_RemoveTrack(const char *fname) if (!strcmp(newtrack->filename, fname)) { *link = newtrack->next; - Z_Free(newtrack); numtracks--; + + if (!strcmp(media_currenttrack, newtrack->filename)) + Media_Changed(MEDIA_PLAYLIST); + Z_Free(newtrack); return; } } @@ -661,7 +704,7 @@ void M_Media_Draw (menu_t *menu) if (!bgmvolume.value) M_Print (12, 32, "Not playing - no volume"); - else if (!*currenttrack.nicename) + else if (!*media_currenttrack) { if (!tracks) M_Print (12, 32, "Not playing - no track to play"); @@ -678,7 +721,7 @@ void M_Media_Draw (menu_t *menu) else { M_Print (12, 32, "Currently playing:"); - M_Print (12, 40, currenttrack.nicename); + M_Print (12, 40, *media_friendlyname?media_friendlyname:media_currenttrack); } y=52; @@ -858,8 +901,10 @@ qboolean M_Media_Key (int key, menu_t *menu) prevtrack->next = tr->next; else tracks = tr->next; - Z_Free(tr); numtracks--; + if (!strcmp(media_currenttrack, tr->filename)) + Media_Changed(MEDIA_PLAYLIST); + Z_Free(tr); break; } @@ -891,6 +936,7 @@ qboolean M_Media_Key (int key, menu_t *menu) numtracks=0; Con_SafePrintf("numtracks should be 0\n"); } + Media_Changed(MEDIA_PLAYLIST); } break; case MEDIA_ADDTRACK: @@ -912,7 +958,6 @@ qboolean M_Media_Key (int key, menu_t *menu) default: if (selectedoption>=0) { - media_playing = true; nexttrack = selectedoption; Media_Next_f(); return true; @@ -1093,7 +1138,7 @@ void Media_LoadTrackNames (char *listname) #endif //mixer is locked, its safe to do stuff, but try not to block -float Media_CrossFade(int musicchanel, float vol) +float Media_CrossFade(int musicchanel, float vol, float time) { if (media_fadeout) { @@ -1101,44 +1146,85 @@ float Media_CrossFade(int musicchanel, float vol) float frac = (fadetime + media_fadeouttime - realtime)/fadetime; vol *= frac; } + else if (music_playlist_index.modified) + { + if (Media_Changed(MEDIA_CVARLIST)) + { + if (music_playlist_last >= 0 && music_playlist_sampleposition[music_playlist_last].value != -1) + { + Cvar_SetValue(&music_playlist_sampleposition[music_playlist_last], time); + } + vol = -1; //kill it NOW + } + } return vol; } //mixer is locked, its safe to do stuff, but try not to block -char *Media_NextTrack(int musicchannelnum) +char *Media_NextTrack(int musicchannelnum, float *starttime) { - if (bgmvolume.value <= 0 || !media_playing) + if (bgmvolume.value <= 0) return NULL; if (media_fadeout) { if (S_Music_Playing(musicchannelnum)) return NULL; //can't pick a new track until they've all stopped. + + //okay, it has actually stopped everywhere. } + media_fadeout = false; //it has actually ended now - if (!fakecdactive) - Media_EndedTrack(); - media_fadeout = false; - -#ifndef NOMEDIAMENU - if (!loadedtracknames) - Media_LoadTrackNames("sound/media.m3u"); -#endif - if (!tracks && !fakecdactive) + music_playlist_index.modified = false; + music_playlist_last = -1; + media_playlistcurrent = 0; + Q_strncpyz(media_currenttrack, "", sizeof(media_currenttrack)); + Q_strncpyz(media_friendlyname, "", sizeof(media_friendlyname)); + if (!media_playlistcurrent && (media_playlisttypes & MEDIA_PLAYLIST)) { - *currenttrack.filename='\0'; - *currenttrack.nicename='\0'; - lasttrackplayed=-1; - media_playing = false; - return NULL; +#ifndef NOMEDIAMENU + if (!loadedtracknames) + Media_LoadTrackNames("sound/media.m3u"); +#endif + if (Media_EvaluateNextTrack()) + { + media_playlistcurrent = MEDIA_PLAYLIST; + return media_currenttrack; + } + } + if (!media_playlistcurrent && (media_playlisttypes & MEDIA_CVARLIST)) + { + if (music_playlist_index.ival >= 0 && music_playlist_index.ival < countof(music_playlist_list)) + { + Q_snprintfz(media_currenttrack, sizeof(media_currenttrack), "sound/cdtracks/%s", music_playlist_list[music_playlist_index.ival].string); + Q_strncpyz(media_friendlyname, "", sizeof(media_friendlyname)); + media_playlistcurrent = MEDIA_CVARLIST; + music_playlist_last = music_playlist_index.ival; + *starttime = music_playlist_sampleposition[music_playlist_last].value; + if (*starttime == -1) + *starttime = 0; + } + } + if (!media_playlistcurrent && (media_playlisttypes & MEDIA_GAMEMUSIC)) + { + if (cdplaytracknum) + { + if (cdplayingtrack != cdplaytracknum && cdpausedtrack != cdplaytracknum) + { + CDAudio_Play(cdplaytracknum); + cdplayingtrack = cdplaytracknum; + } + media_playlistcurrent = MEDIA_GAMEMUSIC; + } + else if (*cdplaytrack) + { + Q_strncpyz(media_currenttrack, cdplaytrack, sizeof(media_currenttrack)); + Q_strncpyz(media_friendlyname, "", sizeof(media_friendlyname)); + media_playlistcurrent = MEDIA_GAMEMUSIC; + } } - fakecdactive = false; -// if (cursndcard == sndcardinfo) //figure out the next track (primary sound card, we could actually get different tracks on different cards (and unfortunatly do)) -// { - Media_EvaluateNextTrack(); -// } - return currenttrack.filename; + return media_currenttrack; } @@ -4003,6 +4089,7 @@ qboolean S_LoadMP3Sound (sfx_t *s, qbyte *data, int datalen, int sndspeed); void Media_Init(void) { + int i; #if defined(_WIN32) && !defined(WINRT) Cmd_AddCommand("tts", TTS_Say_f); Cmd_AddCommand("stt", STT_Init_f); @@ -4020,8 +4107,17 @@ void Media_Init(void) Cmd_AddCommand("music_fforward", Media_FForward_f); Cmd_AddCommand("music_rewind", Media_Rewind_f); Cmd_AddCommand("music_next", Media_Next_f); + Cmd_AddCommand("media_next", Media_Next_f); Cmd_AddCommand("music", Media_NamedTrack_f); + Cvar_Register(&music_playlist_index, "compat"); + for (i = 0; i < countof(music_playlist_list); i++) + { + Cvar_Register(&music_playlist_list[i], "compat"); + Cvar_Register(&music_playlist_sampleposition[i], "compat"); + } + music_playlist_last = -1; + Cmd_AddCommand("cd", CD_f); cdenabled = false; if (COM_CheckParm("-nocdaudio")) @@ -4029,6 +4125,8 @@ void Media_Init(void) if (COM_CheckParm("-cdaudio")) cdenabled = true; + media_playlisttypes = MEDIA_PLAYLIST | MEDIA_GAMEMUSIC | MEDIA_CVARLIST; + #if defined(GLQUAKE) Cmd_AddCommand("capture", Media_RecordFilm_f); Cmd_AddCommand("capturedemo", Media_RecordDemo_f); @@ -4056,7 +4154,7 @@ void Media_Init(void) Cvar_Register(&media_shuffle, "Media player things"); Cvar_Register(&media_repeat, "Media player things"); Cmd_AddCommand ("media_add", M_Media_Add_f); - Cmd_AddCommand ("media_rmeove", M_Media_Remove_f); + Cmd_AddCommand ("media_remove", M_Media_Remove_f); Cmd_AddCommand ("menu_media", M_Menu_Media_f); } @@ -4099,7 +4197,7 @@ static void S_MP3_Purge(sfx_t *sfx) } /*must be thread safe*/ -sfxcache_t *S_MP3_Locate(sfx_t *sfx, sfxcache_t *buf, int start, int length) +sfxcache_t *S_MP3_Locate(sfx_t *sfx, sfxcache_t *buf, ssamplepos_t start, int length) { int newlen; if (buf) diff --git a/engine/client/m_options.c b/engine/client/m_options.c index 4f122635..a58da30c 100644 --- a/engine/client/m_options.c +++ b/engine/client/m_options.c @@ -349,7 +349,7 @@ void M_Audio_StartSound (struct menu_s *menu) if (lasttime+0.5 < Sys_DoubleTime()) { - S_GetListenerInfo(mat[0], mat[1], mat[2], mat[3]); + S_GetListenerInfo(0, mat[0], mat[1], mat[2], mat[3]); lasttime = Sys_DoubleTime(); org[0] = mat[0][0] + 2*(mat[1][0]*(info->testsoundsource->common.posx-320/2) + mat[1][0]*(info->testsoundsource->common.posy-200/2)); diff --git a/engine/client/m_script.c b/engine/client/m_script.c index 5e3c41c9..b2a5c12f 100644 --- a/engine/client/m_script.c +++ b/engine/client/m_script.c @@ -13,6 +13,9 @@ void M_Script_Option (menu_t *menu, char *optionvalue) menuoption_t *mo; char buf[8192]; + + Cbuf_AddText("wait\n", RESTRICT_LOCAL); + //update the option Cbuf_AddText(va("set option %s\n", COM_QuotedString(optionvalue, buf, sizeof(buf), false)), RESTRICT_LOCAL); diff --git a/engine/client/menu.c b/engine/client/menu.c index d9494d6d..4182d884 100644 --- a/engine/client/menu.c +++ b/engine/client/menu.c @@ -105,19 +105,32 @@ void M_DrawTextBox (int x, int y, int width, int lines) M_DrawScalePic (cx, cy+8, 8, 8, p); } -int M_FindKeysForBind (const char *command, int *keylist, int *keymods, int total) +int M_FindKeysForBind (int bindmap, const char *command, int *keylist, int *keymods, int keycount) { int count; int j, m; int l, p; char *b; + int firstmod, lastmod; l = strlen(command); count = 0; + if (bindmap > 0 && bindmap <= KEY_MODIFIER_ALTBINDMAP) + { + //bindmaps don't support modifiers + firstmod = (bindmap-1)|KEY_MODIFIER_ALTBINDMAP; + lastmod = firstmod+1; + } + else + { + firstmod = 0; + lastmod = KEY_MODIFIER_ALTBINDMAP; + } + for (j=0 ; j<256 ; j++) { - for (m = 0; m < KEY_MODIFIERSTATES; m++) + for (m = firstmod; m < lastmod; m++) { b = keybindings[j][m]; if (!b) @@ -125,11 +138,11 @@ int M_FindKeysForBind (const char *command, int *keylist, int *keymods, int tota if (!strncmp (b, command, l) && (!b[l] || b[l] == ' ' || b[l] == ';')) { //if ctrl_a and ctrl_shift_a do the same thing, don't report ctrl_shift_a because its redundant. - for (p = 0; p < m; p++) + for (p = firstmod; p < m; p++) { if (p&~m) //ignore shift_a if we're checking ctrl_a continue; - if (!strcmp(keybindings[j][p], b)) + if (keybindings[j][p] && !strcmp(keybindings[j][p], b)) break; //break+continue } if (p != m) @@ -137,14 +150,14 @@ int M_FindKeysForBind (const char *command, int *keylist, int *keymods, int tota keylist[count] = j; if (keymods) - keymods[count] = j; + keymods[count] = m; count++; - if (count == total) + if (count == keycount) return count; } } } - for (j = count; j < total; j++) + for (j = count; j < keycount; j++) { keylist[j] = -1; if (keymods) @@ -152,7 +165,7 @@ int M_FindKeysForBind (const char *command, int *keylist, int *keymods, int tota } return count; } -void M_FindKeysForCommand (int pnum, const char *command, int *twokeys) +int M_FindKeysForCommand (int bindmap, int pnum, const char *command, int *keylist, int *keymods, int keycount) { char prefix[5]; @@ -180,7 +193,7 @@ void M_FindKeysForCommand (int pnum, const char *command, int *twokeys) prefix[3] = 0; } } - M_FindKeysForBind(va("%s%s", prefix, command), twokeys, NULL, 2); + return M_FindKeysForBind(bindmap, va("%s%s", prefix, command), keylist, keymods, keycount); } #ifndef NOBUILTINMENUS @@ -582,16 +595,20 @@ void M_UnbindCommand (const char *command) int j; int l; char *b; + int m; l = strlen(command); for (j=0 ; j<256 ; j++) - { - b = keybindings[j][0]; - if (!b) - continue; - if (!strncmp (b, command, l) ) - Key_SetBinding (j, ~0, "", RESTRICT_LOCAL); + { //FIXME: not sure what to do about bindmaps here. oh well. + for (m = 0; m < KEY_MODIFIERSTATES; m++) + { + b = keybindings[j][m]; + if (!b) + continue; + if (!strncmp (b, command, l) ) + Key_SetBinding (j, m, "", RESTRICT_LOCAL); + } } } @@ -1406,6 +1423,8 @@ void M_Keydown (int key, int unicode) case m_complex: if (key == K_MOUSE1) //mouse clicks are deferred until the release event. this is for touch screens and aiming. menu_mousedown = true; + else if (key == K_LSHIFT || key == K_RSHIFT || key == K_LALT || key == K_RALT || key == K_LCTRL || key == K_RCTRL) + ; else M_Complex_Key (key, unicode); return; @@ -1428,6 +1447,8 @@ void M_Keyup (int key, int unicode) case m_complex: if (key == K_MOUSE1 && menu_mousedown) M_Complex_Key (key, unicode); + else if (key == K_LSHIFT || key == K_RSHIFT || key == K_LALT || key == K_RALT || key == K_LCTRL || key == K_RCTRL) + M_Complex_Key (key, unicode); menu_mousedown = false; return; #endif diff --git a/engine/client/menu.h b/engine/client/menu.h index 02b1410b..6f23340a 100644 --- a/engine/client/menu.h +++ b/engine/client/menu.h @@ -456,8 +456,8 @@ void M_Keydown (int key, int unicode); void M_Keyup (int key, int unicode); void M_Draw (int uimenu); #endif -void M_FindKeysForCommand (int pnum, const char *command, int *twokeys); -int M_FindKeysForBind (const char *command, int *keylist, int *keymods, int total); +int M_FindKeysForCommand (int bindmap, int pnum, const char *command, int *keylist, int *keymods, int keycount); +int M_FindKeysForBind (int bindmap, const char *command, int *keylist, int *keymods, int keycount); #ifdef MENU_DAT void MP_CvarChanged(cvar_t *var); diff --git a/engine/client/merged.h b/engine/client/merged.h index 3e7fc706..0a0e9100 100644 --- a/engine/client/merged.h +++ b/engine/client/merged.h @@ -229,6 +229,7 @@ typedef struct image_s unsigned int flags; struct image_s *next; struct image_s *prev; + struct image_s *aliasof; #if defined(D3DQUAKE) || defined(SWQUAKE) void *ptr; //texture void *ptr2; //view diff --git a/engine/client/p_script.c b/engine/client/p_script.c index aba66bd5..96151d8c 100644 --- a/engine/client/p_script.c +++ b/engine/client/p_script.c @@ -34,13 +34,6 @@ The engine has a few builtins. #endif #include "shader.h" -#ifdef D3DQUAKE -//d3d is awkward -//we can't include two versions of header files -extern void *d3dexplosiontexture; -extern void *d3dballtexture; -#endif - #include "renderque.h" #include "r_partset.h" @@ -152,7 +145,7 @@ typedef struct skytriblock_s //this is the required render state for each particle //dynamic per-particle stuff isn't important. only static state. typedef struct { - enum {PT_NORMAL, PT_SPARK, PT_SPARKFAN, PT_TEXTUREDSPARK, PT_BEAM, PT_CDECAL, PT_UDECAL} type; + enum {PT_NORMAL, PT_SPARK, PT_SPARKFAN, PT_TEXTUREDSPARK, PT_BEAM, PT_CDECAL, PT_UDECAL, PT_INVISIBLE} type; blendmode_t blendmode; shader_t *shader; @@ -779,7 +772,13 @@ static void P_LoadTexture(part_type_t *ptype, qboolean warn) ptype->s2 = 1; ptype->t2 = 1; ptype->randsmax = 1; - if (ptype->looks.type == PT_BEAM) + if (ptype->looks.type == PT_SPARK) + { + extern texid_t r_whiteimage; + ptype->looks.shader = R_RegisterShader(va("line%s", namepostfix), SUF_NONE, defaultshader); + TEXASSIGNF(tn.base, r_whiteimage); + } + else if (ptype->looks.type == PT_BEAM) { /*untextured beams get a single continuous blob*/ ptype->looks.shader = R_RegisterShader(va("beam%s", namepostfix), SUF_NONE, defaultshader); @@ -904,7 +903,7 @@ void Cmd_if_f(void); //Uses FTE's multiline console stuff. //This is the function that loads the effect descriptions (via console). -static void P_ParticleEffect_f(void) +void P_ParticleEffect_f(void) { char *var, *value; char *buf; @@ -940,7 +939,9 @@ static void P_ParticleEffect_f(void) } var = Cmd_Argv(1); - if (*var == '+') + if (!pe_script_enabled) + ptype = NULL; + else if (*var == '+') ptype = P_GetParticleType(config, var+1); else ptype = P_GetParticleType(config, var); @@ -1827,6 +1828,17 @@ static void P_ParticleEffect_f(void) } } + if (ptype->looks.type == PT_SPARK && r_part_sparks.ival<0) + ptype->looks.type = PT_INVISIBLE; + if (ptype->looks.type == PT_TEXTUREDSPARK && !r_part_sparks_textured.ival) + ptype->looks.type = PT_SPARK; + if (ptype->looks.type == PT_SPARKFAN && !r_part_sparks_trifan.ival) + ptype->looks.type = PT_SPARK; + if (ptype->looks.type == PT_SPARK && !r_part_sparks.ival) + ptype->looks.type = PT_INVISIBLE; + if (ptype->looks.type == PT_BEAM && r_part_beams.ival <= 0) + ptype->looks.type = PT_INVISIBLE; + if (ptype->looks.type == PT_BEAM && !setbeamlen) ptype->rotationstartmin = 1/128.0; @@ -2219,6 +2231,7 @@ static void P_BeamInfo_f (void) static void P_PartInfo_f (void) { particle_t *p; + clippeddecal_t *d; part_type_t *ptype; int t = 0, r = 0, e = 0; @@ -2237,6 +2250,8 @@ static void P_PartInfo_f (void) j = 0; for (p = part_type[i].particles; p; p = p->next) j++; + for (d = part_type[i].clippeddecals; d; d = d->next) + j++; if (j) { @@ -2731,7 +2746,6 @@ static qboolean PScript_InitParticles (void) Cmd_AddCommand("pointfile", P_ReadPointFile_f); //load the leak info produced from qbsp into the particle system to show a line. :) - Cmd_AddCommand("r_part", P_ParticleEffect_f); pe_script_enabled = true; Cmd_AddCommand("r_exportbuiltinparticles", P_ExportBuiltinSet_f); @@ -3670,7 +3684,7 @@ static void PScript_EffectSpawned(part_type_t *ptype, vec3_t org, vec3_t axis[3] if (w <= tw) { if (*ptype->sounds[i].name && ptype->sounds[i].vol > 0) - S_StartSound(0, 0, S_PrecacheSound(ptype->sounds[i].name), org, ptype->sounds[i].vol, ptype->sounds[i].atten, ptype->sounds[i].delay, ptype->sounds[i].pitch); + S_StartSound(0, 0, S_PrecacheSound(ptype->sounds[i].name), org, ptype->sounds[i].vol, ptype->sounds[i].atten, -ptype->sounds[i].delay, ptype->sounds[i].pitch); break; } } @@ -3903,6 +3917,9 @@ static int PScript_RunParticleEffectState (vec3_t org, vec3_t dir, float count, Matrix4x4_CM_Transform3(Matrix4x4_CM_NewRotation(frandom()*360, dir[0], dir[1], dir[2]), ctx.tangent1, ctx.tangent2); CrossProduct(dir, ctx.tangent2, ctx.tangent1); + VectorNormalize(ctx.tangent1); + VectorNormalize(ctx.tangent2); + ctx.ptype = ptype; ctx.scale1 = ptype->s2 - ptype->s1; ctx.bias1 = ptype->s1 + ctx.scale1/2; @@ -5227,45 +5244,38 @@ static void GL_DrawTrifanParticle(int count, particle_t **plist, plooks_t *type) } } -static void R_AddLineSparkParticle(int count, particle_t **plist, plooks_t *type) +//static void R_AddLineSparkParticle(int count, particle_t **plist, plooks_t *type) +static void R_AddLineSparkParticle(scenetris_t *t, particle_t *p, plooks_t *type) { -/* - particle_t *p; - while (count--) + if (cl_numstrisvert+2 > cl_maxstrisvert) { - p = *plist++; - - if (cl_numstrisvert+2 > cl_maxstrisvert) - { - cl_maxstrisvert+=64*2; - cl_strisvertv = BZ_Realloc(cl_strisvertv, sizeof(*cl_strisvertv)*cl_maxstrisvert); - cl_strisvertt = BZ_Realloc(cl_strisvertt, sizeof(*cl_strisvertt)*cl_maxstrisvert); - cl_strisvertc = BZ_Realloc(cl_strisvertc, sizeof(*cl_strisvertc)*cl_maxstrisvert); - } - - Vector4Copy(p->rgba, cl_strisvertc[cl_numstrisvert+0]); - VectorCopy(p->rgba, cl_strisvertc[cl_numstrisvert+1]); - cl_strisvertc[cl_numstrisvert+1][3] = 0; - Vector2Set(cl_strisvertt[cl_numstrisvert+0], p->s1, p->t1); - Vector2Set(cl_strisvertt[cl_numstrisvert+1], p->s2, p->t2); - - VectorCopy(p->org, cl_strisvertv[cl_numstrisvert+0]); - VectorMA(p->org, -1/10, p->vel, cl_strisvertv[cl_numstrisvert+1]); - - if (cl_numstrisidx+2 > cl_maxstrisidx) - { - cl_maxstrisidx += 64*2; - cl_strisidx = BZ_Realloc(cl_strisidx, sizeof(*cl_strisidx)*cl_maxstrisidx); - } - cl_strisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 0; - cl_strisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 1; - - cl_numstrisvert += 2; - - t->numvert += 2; - t->numidx += 2; + cl_maxstrisvert+=64*2; + cl_strisvertv = BZ_Realloc(cl_strisvertv, sizeof(*cl_strisvertv)*cl_maxstrisvert); + cl_strisvertt = BZ_Realloc(cl_strisvertt, sizeof(*cl_strisvertt)*cl_maxstrisvert); + cl_strisvertc = BZ_Realloc(cl_strisvertc, sizeof(*cl_strisvertc)*cl_maxstrisvert); } -*/ + + Vector4Copy(p->rgba, cl_strisvertc[cl_numstrisvert+0]); + VectorCopy(p->rgba, cl_strisvertc[cl_numstrisvert+1]); + cl_strisvertc[cl_numstrisvert+1][3] = 0; + Vector2Set(cl_strisvertt[cl_numstrisvert+0], p->s1, p->t1); + Vector2Set(cl_strisvertt[cl_numstrisvert+1], p->s2, p->t2); + + VectorCopy(p->org, cl_strisvertv[cl_numstrisvert+0]); + VectorMA(p->org, -1.0/10, p->vel, cl_strisvertv[cl_numstrisvert+1]); + + if (cl_numstrisidx+2 > cl_maxstrisidx) + { + cl_maxstrisidx += 64*2; + cl_strisidx = BZ_Realloc(cl_strisidx, sizeof(*cl_strisidx)*cl_maxstrisidx); + } + cl_strisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 0; + cl_strisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 1; + + cl_numstrisvert += 2; + + t->numvert += 2; + t->numidx += 2; } static void R_AddTSparkParticle(scenetris_t *t, particle_t *p, plooks_t *type) @@ -5763,7 +5773,7 @@ static void R_AddTexturedParticle(scenetris_t *t, particle_t *p, plooks_t *type) static void PScript_DrawParticleTypes (void) { - void (*sparklineparticles)(int count, particle_t **plist, plooks_t *type)=R_AddLineSparkParticle; +// void (*sparklineparticles)(int count, particle_t **plist, plooks_t *type)=R_AddLineSparkParticle; void (*sparkfanparticles)(int count, particle_t **plist, plooks_t *type)=GL_DrawTrifanParticle; void (*sparktexturedparticles)(int count, particle_t **plist, plooks_t *type)=GL_DrawTexturedSparkParticle; @@ -5789,6 +5799,7 @@ static void PScript_DrawParticleTypes (void) int traces=r_particle_tracelimit.ival; int rampind; static float oldtime; + int batchflags; RSpeedMark(); if (r_plooksdirty) @@ -5827,25 +5838,6 @@ static void PScript_DrawParticleTypes (void) kill_list = kill_first = NULL; - if (r_part_sparks_textured.ival < 0) - sparktexturedparticles = NULL; - else if (!r_part_sparks_textured.ival) - sparktexturedparticles = sparklineparticles; - - if (r_part_sparks_trifan.ival < 0) - sparkfanparticles = NULL; - else if (!r_part_sparks_trifan.ival) - sparkfanparticles = sparklineparticles; - - if (r_part_sparks.ival < 0) - sparklineparticles = NULL; - else if (!r_part_sparks.ival) - { - sparktexturedparticles = NULL; - sparkfanparticles = NULL; - sparklineparticles = NULL; - } - for (type = part_run_list, lastvalidtype = NULL; type != NULL; type = type->nexttorun) { if (type->clippeddecals) @@ -5940,16 +5932,14 @@ static void PScript_DrawParticleTypes (void) bdraw = NULL; pdraw = NULL; tdraw = NULL; + batchflags = 0; // set drawing methods by type and cvars and hope branch // prediction takes care of the rest switch(type->looks.type) { case PT_BEAM: - if (r_part_beams.ival <= 0) - bdraw = NULL; - else - bdraw = GL_DrawParticleBeam; + bdraw = GL_DrawParticleBeam; break; case PT_CDECAL: break; @@ -5961,7 +5951,8 @@ static void PScript_DrawParticleTypes (void) tdraw = R_AddTexturedParticle; break; case PT_SPARK: - pdraw = sparklineparticles; + tdraw = R_AddLineSparkParticle; + batchflags = BEF_LINES; break; case PT_SPARKFAN: pdraw = sparkfanparticles; @@ -5974,7 +5965,7 @@ static void PScript_DrawParticleTypes (void) if (!tdraw || type->looks.shader->sort == SHADER_SORT_BLEND) scenetri = NULL; - else if (cl_numstris && cl_stris[cl_numstris-1].shader == type->looks.shader && cl_stris[cl_numstris-1].flags == 0) + else if (cl_numstris && cl_stris[cl_numstris-1].shader == type->looks.shader && cl_stris[cl_numstris-1].flags == batchflags) scenetri = &cl_stris[cl_numstris-1]; else { @@ -5987,7 +5978,7 @@ static void PScript_DrawParticleTypes (void) scenetri->shader = type->looks.shader; scenetri->firstidx = cl_numstrisidx; scenetri->firstvert = cl_numstrisvert; - scenetri->flags = 0; + scenetri->flags = batchflags; scenetri->numvert = 0; scenetri->numidx = 0; } diff --git a/engine/client/pr_clcmd.c b/engine/client/pr_clcmd.c index 505ae8a4..5f827039 100644 --- a/engine/client/pr_clcmd.c +++ b/engine/client/pr_clcmd.c @@ -285,11 +285,11 @@ int MP_TranslateQCtoFTECodes(int code) void QCBUILTIN PF_cl_findkeysforcommand (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { const char *cmdname = PR_GetStringOfs(prinst, OFS_PARM0); - //float bindmap = G_FLOAT(OFS_PARM1); + int bindmap = G_FLOAT(OFS_PARM1); int keynums[2]; char keyname[512]; - M_FindKeysForCommand(0, cmdname, keynums); + M_FindKeysForCommand(bindmap, 0, cmdname, keynums, NULL, countof(keynums)); keyname[0] = '\0'; @@ -302,18 +302,19 @@ void QCBUILTIN PF_cl_findkeysforcommand (pubprogfuncs_t *prinst, struct globalva void QCBUILTIN PF_cl_findkeysforcommandex (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { const char *cmdname = PR_GetStringOfs(prinst, OFS_PARM0); + int bindmap = G_FLOAT(OFS_PARM1); int keynums[256]; int keymods[countof(keynums)]; char keyname[512]; int i, count; - count = M_FindKeysForBind(cmdname, keynums, keymods, countof(keynums)); + count = M_FindKeysForBind(bindmap, cmdname, keynums, keymods, countof(keynums)); keyname[0] = '\0'; for (i = 0; i < count; i++) { - Q_strncatz (keyname, va("%s%s%s%s ", (keymods[i]&KEY_MODIFIER_CTRL)?"CTRL_":"", (keymods[i]&KEY_MODIFIER_ALT)?"ALT_":"", (keymods[i]&KEY_MODIFIER_SHIFT)?"SHIFT_":"", Key_KeynumToString(keynums[i])), sizeof(keyname)); + Q_strncatz (keyname, Key_KeynumToString(keynums[i], keymods[i]), sizeof(keyname)); } RETURN_TSTRING(keyname); @@ -321,7 +322,9 @@ void QCBUILTIN PF_cl_findkeysforcommandex (pubprogfuncs_t *prinst, struct global void QCBUILTIN PF_cl_getkeybind (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *binding = Key_GetBinding(MP_TranslateQCtoFTECodes(G_FLOAT(OFS_PARM0))); + int keymap = (prinst->callargc > 1)?G_FLOAT(OFS_PARM1):0; + int modifier = (prinst->callargc > 2)?G_FLOAT(OFS_PARM2):0; + char *binding = Key_GetBinding(MP_TranslateQCtoFTECodes(G_FLOAT(OFS_PARM0)), keymap, modifier); RETURN_TSTRING(binding); } @@ -349,7 +352,7 @@ void QCBUILTIN PF_cl_keynumtostring (pubprogfuncs_t *prinst, struct globalvars_s code = MP_TranslateQCtoFTECodes (code); - RETURN_TSTRING(Key_KeynumToString(code)); + RETURN_TSTRING(Key_KeynumToString(code, 0)); } @@ -700,17 +703,21 @@ void QCBUILTIN PF_shaderforname (pubprogfuncs_t *prinst, struct globalvars_s *pr void QCBUILTIN PF_cl_GetBindMap (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - G_VECTOR(OFS_RETURN)[0] = 1; - G_VECTOR(OFS_RETURN)[1] = 0; + int bm[2]; + Key_GetBindMap(bm); + G_VECTOR(OFS_RETURN)[0] = bm[0]; + G_VECTOR(OFS_RETURN)[1] = bm[1]; G_VECTOR(OFS_RETURN)[2] = 0; } void QCBUILTIN PF_cl_SetBindMap (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { -// int primary = G_FLOAT(OFS_PARM0+0); -// int secondary = G_FLOAT(OFS_PARM0+1); -// if (IN_SetBindMap(primary, secondary)) -// G_FLOAT(OFS_RETURN) = 1; - G_FLOAT(OFS_RETURN) = 0; + int bm[2] = + { + G_FLOAT(OFS_PARM0+0), + G_FLOAT(OFS_PARM0+1) + }; + Key_SetBindMap(bm); + G_FLOAT(OFS_RETURN) = 1; } //evil builtins to pretend to be a server. diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index 63a698dd..327659f0 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -67,6 +67,7 @@ world_t csqc_world; int csqc_playerseat; //can be negative. static playerview_t *csqc_playerview; +qboolean csqc_dp_lastwas3d; //to emulate DP correctly, we need to track whether drawpic/drawfill or clearscene was called last. blame 515. static qboolean csqc_isdarkplaces; static qboolean csqc_singlecheats; /*single player or cheats active, allowing custom addons*/ static qboolean csqc_mayread; //csqc is allowed to ReadByte(); @@ -276,7 +277,7 @@ static void CSQC_ChangeLocalPlayer(int seat) } } -static void CSQC_FindGlobals(void) +static void CSQC_FindGlobals(qboolean nofuncs) { static float csphysicsmode = 0; static float dimension_default = 255; @@ -285,7 +286,7 @@ static void CSQC_FindGlobals(void) #define globalvector(name,qcname) csqcg.name = (float*)PR_FindGlobal(csqcprogs, qcname, 0, NULL); #define globalentity(name,qcname) csqcg.name = (int*)PR_FindGlobal(csqcprogs, qcname, 0, NULL); #define globalstring(name,qcname) csqcg.name = (string_t*)PR_FindGlobal(csqcprogs, qcname, 0, NULL); -#define globalfunction(name,qcname) csqcg.name = PR_FindFunction(csqcprogs,qcname,PR_ANY); +#define globalfunction(name,qcname) csqcg.name = nofuncs?0:PR_FindFunction(csqcprogs,qcname,PR_ANY); csqcglobals @@ -876,7 +877,7 @@ static void QCBUILTIN PF_R_DynamicLight_Set(pubprogfuncs_t *prinst, struct globa l->rebuildcache = true; break; case lfield_style: - l->style = G_FLOAT(OFS_PARM2); + l->style = G_FLOAT(OFS_PARM2)+1; break; case lfield_angles: AngleVectors(G_VECTOR(OFS_PARM2), l->axis[0], l->axis[1], l->axis[2]); @@ -960,11 +961,11 @@ static void QCBUILTIN PF_R_DynamicLight_Get(pubprogfuncs_t *prinst, struct globa G_FLOAT(OFS_RETURN) = l->flags; break; case lfield_style: - G_FLOAT(OFS_RETURN) = l->style; + G_FLOAT(OFS_RETURN) = l->style-1; break; case lfield_angles: VectorAngles(l->axis[0], l->axis[2], v); - G_FLOAT(OFS_RETURN+0) = v[0]?v[0]:0; + G_FLOAT(OFS_RETURN+0) = anglemod(v[0]?-v[0]:0); G_FLOAT(OFS_RETURN+1) = v[1]?v[1]:0; G_FLOAT(OFS_RETURN+2) = v[2]?v[2]:0; break; @@ -1172,12 +1173,31 @@ static shader_t *csqc_poly_shader; static int csqc_poly_startvert; static int csqc_poly_startidx; static int csqc_poly_flags; +static int csqc_poly_2d; // #306 void(string texturename) R_BeginPolygon (EXT_CSQC_???) void QCBUILTIN PF_R_PolygonBegin(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { csqc_poly_flags = (prinst->callargc > 1)?G_FLOAT(OFS_PARM1):0; - if (csqc_poly_flags & 4) + + if (prinst->callargc > 2) + csqc_poly_2d = G_FLOAT(OFS_PARM2); + else if (csqc_isdarkplaces) + { + csqc_poly_2d = !csqc_dp_lastwas3d; + csqc_deprecated("guessing 2d mode based upon random builtin calls"); + } + else + csqc_poly_2d = csqc_poly_flags & 4; + + if ((csqc_poly_flags & 3) == 1) + csqc_poly_flags = BEF_FORCEADDITIVE; + else + csqc_poly_flags = BEF_NOSHADOWS; + if (csqc_isdarkplaces) + csqc_poly_flags |= BEF_FORCETWOSIDED; + + if (csqc_poly_2d) csqc_poly_shader = R_RegisterPic(PR_GetStringOfs(prinst, OFS_PARM0)); else csqc_poly_shader = R_RegisterSkin(PR_GetStringOfs(prinst, OFS_PARM0), NULL); @@ -1209,7 +1229,7 @@ void QCBUILTIN PF_R_PolygonEnd(pubprogfuncs_t *prinst, struct globalvars_s *pr_g scenetris_t *t; int i; int nv; - int flags = BEF_NOSHADOWS; + int flags = csqc_poly_flags; if (!csqc_poly_shader) return; @@ -1271,7 +1291,7 @@ void QCBUILTIN PF_R_PolygonEnd(pubprogfuncs_t *prinst, struct globalvars_s *pr_g } } - if (csqc_poly_flags & 4) + if (csqc_poly_2d) { mesh_t mesh; memset(&mesh, 0, sizeof(mesh)); @@ -1287,7 +1307,7 @@ void QCBUILTIN PF_R_PolygonEnd(pubprogfuncs_t *prinst, struct globalvars_s *pr_g cl_numstrisvert = csqc_poly_startvert; cl_numstrisidx = csqc_poly_startidx; - BE_DrawMesh_Single(csqc_poly_shader, &mesh, NULL, 0); + BE_DrawMesh_Single(csqc_poly_shader, &mesh, NULL, csqc_poly_flags); } else { @@ -1404,6 +1424,8 @@ static void QCBUILTIN PF_R_ClearScene (pubprogfuncs_t *prinst, struct globalvars CSQC_ChangeLocalPlayer(G_FLOAT(OFS_PARM0)); csqc_rebuildmatricies = true; + csqc_dp_lastwas3d = true; //cleared by the next drawpic. + csqc_poly_shader = NULL; CL_DecayLights (); @@ -1770,6 +1792,8 @@ void R2D_PolyBlend (void); void R_DrawNameTags(void); static void QCBUILTIN PF_R_RenderScene(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { + csqc_poly_shader = NULL; + if (csqc_worldchanged) { csqc_worldchanged = false; @@ -1802,15 +1826,17 @@ static void QCBUILTIN PF_R_RenderScene(pubprogfuncs_t *prinst, struct globalvars if (r_refdef.drawsbar) { - SCR_TileClear (); #ifdef PLUGINS Plug_SBar (r_refdef.playerview); #else if (Sbar_ShouldDraw()) { + SCR_TileClear (sb_lines); Sbar_Draw (r_refdef.playerview); Sbar_DrawScoreboard (); } + else + SCR_TileClear (0); #endif SCR_ShowPics_Draw(); } @@ -1824,8 +1850,16 @@ static void QCBUILTIN PF_R_RenderScene(pubprogfuncs_t *prinst, struct globalvars static void QCBUILTIN PF_cs_getstati(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { int stnum = G_FLOAT(OFS_PARM0); - if (stnum >= 128 && csqc_isdarkplaces) + if (stnum < 0 || stnum >= MAX_CL_STATS) + { + G_FLOAT(OFS_RETURN) = 0; + PR_RunWarning(prinst, "invalid stat index"); + } + else if (stnum >= 128 && csqc_isdarkplaces) + { //dpp7 stats are fucked. G_FLOAT(OFS_RETURN) = csqc_playerview->statsf[stnum]; + csqc_deprecated("hacked stat type"); + } else G_INT(OFS_RETURN) = csqc_playerview->stats[stnum]; } @@ -1834,7 +1868,12 @@ static void QCBUILTIN PF_cs_getstatbits(pubprogfuncs_t *prinst, struct globalvar //if bits offsets are specified, reads explicitly the integer version of the stat, allowing high bits to be read for items2/serverflags. the float stat should have the same value, just with lower precision as a float can't hold a 32bit value. maybe we should just use doubles. int stnum = G_FLOAT(OFS_PARM0); - if (prinst->callargc > 1) + if (stnum < 0 || stnum >= MAX_CL_STATS) + { + G_FLOAT(OFS_RETURN) = 0; + PR_RunWarning(prinst, "invalid stat index"); + } + else if (prinst->callargc > 1) { int val = csqc_playerview->stats[stnum]; int first, count; @@ -1846,7 +1885,11 @@ static void QCBUILTIN PF_cs_getstatbits(pubprogfuncs_t *prinst, struct globalvar G_FLOAT(OFS_RETURN) = (((unsigned int)val)&(((1<>first; } else if (csqc_isdarkplaces) - G_FLOAT(OFS_RETURN) = (int)csqc_playerview->statsf[stnum]; //stupid. mods like xonotic have a stupid hud if they're actually given any precision + { + G_FLOAT(OFS_RETURN) = (int)csqc_playerview->statsf[stnum]; //stupid. mods like xonotic end up with an ugly hud if they're actually given any precision + if (G_FLOAT(OFS_RETURN) != csqc_playerview->statsf[stnum]) + csqc_deprecated("getstatf stat truncation"); //this is a common call. only get pissy if there's a reason to get pissy. + } else G_FLOAT(OFS_RETURN) = csqc_playerview->statsf[stnum]; } @@ -1854,20 +1897,32 @@ static void QCBUILTIN PF_cs_getstats(pubprogfuncs_t *prinst, struct globalvars_s { int stnum = G_FLOAT(OFS_PARM0); - RETURN_TSTRING(csqc_playerview->statsstr[stnum]); + if (stnum < 0 || stnum >= MAX_CL_STATS) + { + G_INT(OFS_RETURN) = 0; + PR_RunWarning(prinst, "invalid stat index"); + } + else if (cls.fteprotocolextensions & PEXT_CSQC) + RETURN_TSTRING(csqc_playerview->statsstr[stnum]); + else if (stnum >= MAX_CL_STATS-3) + { + G_INT(OFS_RETURN) = 0; + PR_RunWarning(prinst, "invalid stat index"); + } + else + { + char out[17]; - /* - char out[17]; + //the network protocol byteswaps - //the network protocol byteswaps + ((unsigned int*)out)[0] = LittleLong(csqc_playerview->stats[stnum+0]); + ((unsigned int*)out)[1] = LittleLong(csqc_playerview->stats[stnum+1]); + ((unsigned int*)out)[2] = LittleLong(csqc_playerview->stats[stnum+2]); + ((unsigned int*)out)[3] = LittleLong(csqc_playerview->stats[stnum+3]); + ((unsigned int*)out)[4] = 0; //make sure it's null terminated - ((unsigned int*)out)[0] = LittleLong(csqc_playerview->stats[stnum+0]); - ((unsigned int*)out)[1] = LittleLong(csqc_playerview->stats[stnum+1]); - ((unsigned int*)out)[2] = LittleLong(csqc_playerview->stats[stnum+2]); - ((unsigned int*)out)[3] = LittleLong(csqc_playerview->stats[stnum+3]); - ((unsigned int*)out)[4] = 0; //make sure it's null terminated - - RETURN_TSTRING(out);*/ + RETURN_TSTRING(out); + } } static void QCBUILTIN PF_cs_SetOrigin(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) @@ -1877,7 +1932,7 @@ static void QCBUILTIN PF_cs_SetOrigin(pubprogfuncs_t *prinst, struct globalvars_ float *org = G_VECTOR(OFS_PARM1); if (ent->readonly) { - Con_Printf("setorigin on entity %i\n", ent->entnum); + PR_RunWarning(prinst, "setorigin on entity %i\n", ent->entnum); return; } VectorCopy(org, ent->v->origin); @@ -2956,6 +3011,10 @@ static void QCBUILTIN PF_cs_serverkey (pubprogfuncs_t *prinst, struct globalvars if (!ret) ret = ""; } + else if (!strcmp(keyname, "maxplayers")) + { + ret = va("%i", cl.allocated_client_slots); + } else if (!strcmp(keyname, "dlstate")) { if (!cl.downloadlist && !cls.download) @@ -3192,12 +3251,34 @@ static void QCBUILTIN PF_checkextension (pubprogfuncs_t *prinst, struct globalva G_FLOAT(OFS_RETURN) = false; } +void QCBUILTIN PF_soundupdate (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + wedict_t *entity = G_WEDICT(prinst, OFS_PARM0); + int channel = G_FLOAT(OFS_PARM1); + const char *sample = PR_GetStringOfs(prinst, OFS_PARM2); + float volume = G_FLOAT(OFS_PARM3); + float attenuation = G_FLOAT(OFS_PARM4); + float pitchpct = (prinst->callargc >= 6)?G_FLOAT(OFS_PARM5):0; +// unsigned int flags = (prinst->callargc>=7)?G_FLOAT(OFS_PARM6):0; + float startoffset = (prinst->callargc>=8)?G_FLOAT(OFS_PARM7):0; + + sfx_t *sfx = S_PrecacheSound(sample); + + G_FLOAT(OFS_RETURN) = S_UpdateSound(-entity->entnum, channel, sfx, entity->v->origin, volume, attenuation, startoffset, pitchpct); +} +void QCBUILTIN PF_stopsound (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + wedict_t *entity = G_WEDICT(prinst, OFS_PARM0); + int channel = G_FLOAT(OFS_PARM1); + + S_StopSound(-entity->entnum, channel); +} void QCBUILTIN PF_getsoundtime (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { wedict_t *entity = G_WEDICT(prinst, OFS_PARM0); int channel = G_FLOAT(OFS_PARM1); - G_FLOAT(OFS_RETURN) = S_GetSoundTime(entity->entnum, channel); + G_FLOAT(OFS_RETURN) = S_GetSoundTime(-entity->entnum, channel); } static void QCBUILTIN PF_cs_sound(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { @@ -3207,6 +3288,8 @@ static void QCBUILTIN PF_cs_sound(pubprogfuncs_t *prinst, struct globalvars_s *p float volume; float attenuation; float pitchpct; +// unsigned int flags; + float startoffset; sfx_t *sfx; @@ -3215,14 +3298,13 @@ static void QCBUILTIN PF_cs_sound(pubprogfuncs_t *prinst, struct globalvars_s *p sample = PR_GetStringOfs(prinst, OFS_PARM2); volume = G_FLOAT(OFS_PARM3); attenuation = G_FLOAT(OFS_PARM4); - if (prinst->callargc >= 6) - pitchpct = G_FLOAT(OFS_PARM5); - else - pitchpct = 0; + pitchpct = (prinst->callargc>=6)?G_FLOAT(OFS_PARM5):0; +// flags = (prinst->callargc>=7)?G_FLOAT(OFS_PARM6):0; + startoffset = (prinst->callargc>=8)?G_FLOAT(OFS_PARM7):0; sfx = S_PrecacheSound(sample); if (sfx) - S_StartSound(-entity->entnum, channel, sfx, entity->v->origin, volume, attenuation, 0, pitchpct); + S_StartSound(-entity->entnum, channel, sfx, entity->v->origin, volume, attenuation, startoffset, pitchpct); }; static void QCBUILTIN PF_cs_pointsound(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) @@ -4015,13 +4097,15 @@ static void QCBUILTIN PF_cs_setlistener (pubprogfuncs_t *prinst, struct globalva float *up = G_VECTOR(OFS_PARM3); int inwater = (prinst->callargc>4)?G_FLOAT(OFS_PARM4):false; - r_refdef.audio.defaulted = false; + int i = (csqc_playerseat>=0)?csqc_playerseat:0; + + cl.playerview[i].audio.defaulted = false; // r_refdef.audio.entity = 0; - VectorCopy(origin, r_refdef.audio.origin); - VectorCopy(forward, r_refdef.audio.forward); - VectorCopy(right, r_refdef.audio.right); - VectorCopy(up, r_refdef.audio.up); - r_refdef.audio.inwater = inwater; + VectorCopy(origin, cl.playerview[i].audio.origin); + VectorCopy(forward, cl.playerview[i].audio.forward); + VectorCopy(right, cl.playerview[i].audio.right); + VectorCopy(up, cl.playerview[i].audio.up); + cl.playerview[i].audio.inwater = inwater; } #define RSES_NOLERP 1 @@ -4984,6 +5068,7 @@ static struct { //230 {"strncasecmp", PF_strncasecmp, 230}, // #230 float(string s1, string s2, float len) strncasecmp (FTE_STRINGS) + {"strtrim", PF_strtrim, 0}, {"calltimeofday", PF_calltimeofday, 231}, {"clientstat", PF_NoCSQC, 232}, // #231 clientstat {"runclientphys", PF_NoCSQC, 233}, // #232 runclientphys @@ -5018,6 +5103,8 @@ static struct { {"itos", PF_itos, 260}, {"stoh", PF_stoh, 261}, {"htos", PF_htos, 262}, + {"ftoi", PF_ftoi, 0}, + {"itof", PF_itof, 0}, {"skel_create", PF_skel_create, 263},//float(float modlindex) skel_create = #263; // (FTE_CSQC_SKELETONOBJECTS) {"skel_build", PF_skel_build, 264},//float(float skel, entity ent, float modelindex, float retainfrac, float firstbone, float lastbone, optional float addition) skel_build = #264; // (FTE_CSQC_SKELETONOBJECTS) @@ -5181,6 +5268,7 @@ static struct { {"memgetval", PF_memgetval, 388}, {"memsetval", PF_memsetval, 389}, {"memptradd", PF_memptradd, 390}, + {"memstrsize", PF_memstrsize, 0}, {"con_getset", PF_SubConGetSet, 391}, {"con_printf", PF_SubConPrintf, 392}, @@ -5385,6 +5473,8 @@ static struct { {"loadfromdata", PF_loadfromdata, 529}, {"loadfromfile", PF_loadfromfile, 530}, + {"stopsound", PF_stopsound, 0}, + {"soundupdate", PF_soundupdate, 0}, {"getsoundtime", PF_getsoundtime, 533}, {"soundlength", PF_soundlength, 534}, {"buf_loadfile", PF_buf_loadfile, 535}, @@ -5603,7 +5693,7 @@ void CSQC_Event_Think(world_t *w, wedict_t *s) PR_ExecuteProgram (w->progs, s->v->think); } -void CSQC_Event_Sound (float *origin, wedict_t *wentity, int channel, const char *sample, int volume, float attenuation, int pitchadj) +void CSQC_Event_Sound (float *origin, wedict_t *wentity, int channel, const char *sample, int volume, float attenuation, int pitchadj, float timeoffset) { int i; vec3_t originbuf; @@ -5619,7 +5709,7 @@ void CSQC_Event_Sound (float *origin, wedict_t *wentity, int channel, const char origin = wentity->v->origin; } - S_StartSound(NUM_FOR_EDICT(csqcprogs, (edict_t*)wentity), channel, S_PrecacheSound(sample), origin, volume, attenuation, 0, pitchadj); + S_StartSound(NUM_FOR_EDICT(csqcprogs, (edict_t*)wentity), channel, S_PrecacheSound(sample), origin, volume, attenuation, timeoffset, pitchadj); } qboolean CSQC_Event_ContentsTransition(world_t *w, wedict_t *ent, int oldwatertype, int newwatertype) @@ -5872,8 +5962,12 @@ pbool PDECL CSQC_CheckHeaderCrc(pubprogfuncs_t *progs, progsnum_t num, int crc) else { if (crc == 52195) + { csqc_isdarkplaces = true; - Con_Printf(CON_WARNING "Running outdated or unknown csprogs.dat version\n"); + Con_DPrintf(CON_WARNING "Running darkplaces csprogs.dat version\n"); + } + else + Con_Printf(CON_WARNING "Running outdated or unknown csprogs.dat version\n"); } } return true; @@ -6057,9 +6151,9 @@ qboolean CSQC_Init (qboolean anycsqc, qboolean csdatenabled, unsigned int checks } if (csqc_isdarkplaces) - memset(&csqcg, 0, sizeof(csqcg)); + CSQC_FindGlobals(true); else - CSQC_FindGlobals(); + CSQC_FindGlobals(false); csqcentsize = PR_InitEnts(csqcprogs, pr_csqc_maxedicts.value); @@ -6136,7 +6230,7 @@ void CSQC_WorldLoaded(void) return; if (csqc_isdarkplaces) - CSQC_FindGlobals(); + CSQC_FindGlobals(false); csqcmapentitydataloaded = true; csqcmapentitydata = cl.worldmodel->entities; @@ -6452,6 +6546,8 @@ qboolean CSQC_DrawView(void) if (csqcg.frametime) *csqcg.frametime = host_frametime; + csqc_dp_lastwas3d = false; + if (csqc_isdarkplaces && *csqc_world.g.physics_mode == 1) { csqc_world.physicstime = cl.servertime; @@ -6492,7 +6588,19 @@ qboolean CSQC_DrawView(void) CSQC_ChangeLocalPlayer(cl_forceseat.ival?(cl_forceseat.ival - 1) % cl.splitclients:0); if (csqcg.frametime) - *csqcg.frametime = host_frametime; + { + if (csqc_isdarkplaces) + { + static float oldtime; + if (cl.paused) + *csqcg.frametime = 0; //apparently people can't cope with microstutter when they're using this as a test to see if the game is paused. + else + *csqcg.frametime = bound(0, cl.time - oldtime, 0.1); + oldtime = cl.time; + } + else + *csqcg.frametime = host_frametime; + } if (csqcg.numclientseats) *csqcg.numclientseats = cl.splitclients; @@ -6575,6 +6683,7 @@ qboolean CSQC_DrawView(void) qboolean CSQC_KeyPress(int key, int unicode, qboolean down, int devid) { + static qbyte csqckeysdown[K_MAX/8]; void *pr_globals; #ifdef TEXTEDITOR extern qboolean editormodal; @@ -6597,6 +6706,14 @@ qboolean CSQC_KeyPress(int key, int unicode, qboolean down, int devid) { qcinput_scan = G_FLOAT(OFS_PARM1); qcinput_unicode = G_FLOAT(OFS_PARM2); + + csqckeysdown[key>>3] |= (1<<(key&7)); + } + else + { + if (key && !(csqckeysdown[key>>3] & (1<<(key&7)))) + return false; + csqckeysdown[key>>3] &= ~(1<<(key&7)); } PR_ExecuteProgram (csqcprogs, csqcg.input_event); qcinput_scan = 0; //and stop replay attacks @@ -6931,7 +7048,7 @@ static void CSQC_EntityCheck(unsigned int entnum) } } -int CSQC_StartSound(int entnum, int channel, char *soundname, vec3_t pos, float vol, float attenuation, float pitchmod) +int CSQC_StartSound(int entnum, int channel, char *soundname, vec3_t pos, float vol, float attenuation, float pitchmod, float timeofs) { void *pr_globals; csqcedict_t *ent; @@ -6942,13 +7059,22 @@ int CSQC_StartSound(int entnum, int channel, char *soundname, vec3_t pos, float { pr_globals = PR_globals(csqcprogs, PR_CURRENT); + CSQC_EntityCheck(entnum); + ent = csqcent[entnum]; + if (ent) + *csqcg.self = EDICT_TO_PROG(csqcprogs, (void*)ent); + else + *csqcg.self = 0; + G_FLOAT(OFS_PARM0) = entnum; G_FLOAT(OFS_PARM1) = channel; G_INT(OFS_PARM2) = PR_TempString(csqcprogs, soundname); G_FLOAT(OFS_PARM3) = vol; G_FLOAT(OFS_PARM4) = attenuation; VectorCopy(pos, G_VECTOR(OFS_PARM5)); - G_FLOAT(OFS_PARM6) = attenuation; + G_FLOAT(OFS_PARM6) = pitchmod; + G_FLOAT(OFS_PARM7) = 0/*flags*/; +// G_FLOAT(OFS_PARM8) = timeofs; PR_ExecuteProgram(csqcprogs, csqcg.event_sound); @@ -6969,6 +7095,8 @@ int CSQC_StartSound(int entnum, int channel, char *soundname, vec3_t pos, float VectorCopy(pos, G_VECTOR(OFS_PARM2)); G_FLOAT(OFS_PARM3) = vol; G_FLOAT(OFS_PARM4) = attenuation; + G_FLOAT(OFS_PARM5) = 0/*flags*/; + G_FLOAT(OFS_PARM6) = timeofs; PR_ExecuteProgram(csqcprogs, csqcg.serversound); diff --git a/engine/client/pr_menu.c b/engine/client/pr_menu.c index 40e9d4c0..2493c697 100644 --- a/engine/client/pr_menu.c +++ b/engine/client/pr_menu.c @@ -12,6 +12,8 @@ qbyte mpkeysdown[K_MAX/8]; +extern qboolean csqc_dp_lastwas3d; + extern unsigned int r2d_be_flags; #define DRAWFLAG_NORMAL 0 #define DRAWFLAG_ADD 1 @@ -19,6 +21,8 @@ extern unsigned int r2d_be_flags; #define DRAWFLAG_MODULATE2 3 static unsigned int PF_SelectDPDrawFlag(int flag) { + csqc_dp_lastwas3d = false; //for compat with dp's stupid beginpolygon + //flags: //0 = blend //1 = add @@ -51,6 +55,8 @@ void QCBUILTIN PF_CL_drawfill (pubprogfuncs_t *prinst, struct globalvars_s *pr_g void QCBUILTIN PF_CL_drawsetcliparea (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { srect_t srect; + csqc_dp_lastwas3d = false; + srect.x = G_FLOAT(OFS_PARM0) / (float)vid.width; srect.y = (G_FLOAT(OFS_PARM1) / (float)vid.height); srect.width = G_FLOAT(OFS_PARM2) / (float)vid.width; @@ -65,6 +71,8 @@ void QCBUILTIN PF_CL_drawsetcliparea (pubprogfuncs_t *prinst, struct globalvars_ //void drawresetcliparea(void) = #459; void QCBUILTIN PF_CL_drawresetcliparea (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { + csqc_dp_lastwas3d = false; + BE_Scissor(NULL); G_FLOAT(OFS_RETURN) = 1; } @@ -486,7 +494,10 @@ void QCBUILTIN PF_CL_drawpic (pubprogfuncs_t *prinst, struct globalvars_s *pr_gl r2d_be_flags = PF_SelectDPDrawFlag(flag); R2D_ImageColours(rgb[0], rgb[1], rgb[2], alpha); - R2D_Image(pos[0], pos[1], size[0], size[1], 0, 0, 1, 1, p); + if ((size[0] < 0) ^ (size[1] < 0)) + R2D_Image(pos[0]+size[0], pos[1]+size[1], -size[0], -size[1], 1, 1, 0, 0, p); + else + R2D_Image(pos[0], pos[1], size[0], size[1], 0, 0, 1, 1, p); r2d_be_flags = 0; } @@ -554,11 +565,10 @@ void QCBUILTIN PF_CL_drawsubpic (pubprogfuncs_t *prinst, struct globalvars_s *pr r2d_be_flags = PF_SelectDPDrawFlag(flag); R2D_ImageColours(rgb[0], rgb[1], rgb[2], alpha); - R2D_Image( pos[0], pos[1], - size[0], size[1], - srcPos[0], srcPos[1], - srcPos[0]+srcSize[0], srcPos[1]+srcSize[1], - p); + if ((size[0] < 0) ^ (size[1] < 0)) + R2D_Image(pos[0]+size[0], pos[1]+size[1], -size[0], -size[1], srcPos[0]+srcSize[0], srcPos[1]+srcSize[1], srcPos[0], srcPos[1], p); + else + R2D_Image(pos[0], pos[1], size[0], size[1], srcPos[0], srcPos[1], srcPos[0]+srcSize[0], srcPos[1]+srcSize[1], p); r2d_be_flags = 0; G_FLOAT(OFS_RETURN) = 1; @@ -1872,6 +1882,14 @@ static struct { {"vtos", PF_vtos, 19}, {"etos", PF_etos, 20}, {"stof", PF_stof, 21}, + + {"stoi", PF_stoi, 0}, + {"itos", PF_itos, 0}, + {"stoh", PF_stoh, 0}, + {"htos", PF_htos, 0}, + {"ftoi", PF_ftoi, 0}, + {"itof", PF_itof, 0}, + {"spawn", PF_Spawn, 22}, {"remove", PF_Remove_, 23}, {"find", PF_FindString, 24}, @@ -1960,6 +1978,7 @@ static struct { {"strncmp", PF_strncmp, 228}, {"strcasecmp", PF_strncasecmp, 229}, {"strncasecmp", PF_strncasecmp, 230}, + {"strtrim", PF_strtrim, 0}, //gap {"shaderforname", PF_shaderforname, 238}, //gap @@ -2008,6 +2027,7 @@ static struct { {"memgetval", PF_memgetval, 388}, {"memsetval", PF_memsetval, 389}, {"memptradd", PF_memptradd, 390}, + {"memstrsize", PF_memstrsize, 0}, {"con_getset", PF_SubConGetSet, 391}, {"con_printf", PF_SubConPrintf, 392}, {"con_draw", PF_SubConDraw, 393}, @@ -2355,6 +2375,9 @@ qboolean MP_Init (void) menuprogparms.user = &menu_world; menu_world.keydestmask = kdm_gmenu; + //default to free mouse, to match dp's default setting, and because its generally the right thing for a menu. + key_dest_absolutemouse |= kdm_gmenu; + menutime = Sys_DoubleTime(); if (!menu_world.progs) { diff --git a/engine/client/quakedef.h b/engine/client/quakedef.h index e25642b6..277b7d86 100644 --- a/engine/client/quakedef.h +++ b/engine/client/quakedef.h @@ -200,6 +200,51 @@ extern "C" { #include #endif + +//msvcrt lacks any and all c99 support. +#ifdef _WIN32 + #define fPRIp "%p" + //totally different from any other system + #define fPRIllx "%I64x" + #define fPRIllu "%I64u" + #define fPRIlli "%I64i" +#else + //make SURE we get 0xdeadbeef for this + #if FTE_WORDSIZE != 32 + #define fPRIp "%#zx" + #else + #define fPRIp "%#x" + #endif + + //assume some c99 support where we print long long int types. + #define fPRIllx "%llx" + #define fPRIllu "%llu" + #define fPRIlli "%lli" +#endif +#ifdef _WIN32 + //windows does not follow c99 at all + #ifdef _WIN64 + #define fPRIzx "%Ix" + #define fPRIzu "%Iu" + #define fPRIzi "%Ii" + #else + #define fPRIzx "%x" + #define fPRIzu "%u" + #define fPRIzi "%i" + #endif +#elif FTE_WORDSIZE != 32 + //64bit systems are expected to have an awareness of c99 + #define fPRIzx "%zx" + #define fPRIzu "%zu" + #define fPRIzi "%zi" +#else + //regular old c89 for 32bit platforms. + #define fPRIzx "%x" + #define fPRIzu "%u" + #define fPRIzi "%i" +#endif + + #ifdef _WIN32 #if (_MSC_VER >= 1400) //with MSVC 8, use MS extensions diff --git a/engine/client/r_2d.c b/engine/client/r_2d.c index d81ad85b..80ae4832 100644 --- a/engine/client/r_2d.c +++ b/engine/client/r_2d.c @@ -179,11 +179,9 @@ void R2D_Init(void) nogloss[i] = glossval; nonorm[i] = normval; } - missing_texture = R_LoadHiResTexture("no_texture", NULL, IF_NEAREST|IF_NOWORKER); - if (!TEXLOADED(missing_texture)) - missing_texture = R_LoadTexture8("no_texture", 16, 16, (unsigned char*)(r_notexture_mip+1), IF_NOALPHA|IF_NOGAMMA, 0); - missing_texture_gloss = R_LoadTexture("no_texture_gloss", 4, 4, TF_RGBA32, (unsigned char*)nogloss, IF_NOGAMMA); - missing_texture_normal = R_LoadTexture("no_texture_normal", 4, 4, TF_RGBA32, (unsigned char*)nonorm, IF_NOGAMMA); + missing_texture = R_LoadTexture8("no_texture", 16, 16, (unsigned char*)(r_notexture_mip+1), IF_NOALPHA|IF_NOGAMMA|IF_NOPURGE, 0); + missing_texture_gloss = R_LoadTexture("no_texture_gloss", 4, 4, TF_RGBA32, (unsigned char*)nogloss, IF_NOGAMMA|IF_NOPURGE); + missing_texture_normal = R_LoadTexture("no_texture_normal", 4, 4, TF_RGBA32, (unsigned char*)nonorm, IF_NOGAMMA|IF_NOPURGE); translate_texture = r_nulltex; ch_int_texture = r_nulltex; diff --git a/engine/client/r_part.c b/engine/client/r_part.c index 5d22c507..ef55564a 100644 --- a/engine/client/r_part.c +++ b/engine/client/r_part.c @@ -47,7 +47,7 @@ typedef struct size_t numsoups; } cluttersector_t; static cluttersector_t cluttersector[3*3*3]; -cvar_t r_clutter_density = CVARD("r_clutter_density", "1", "Scaler for clutter counts. 0 disables clutter completely.\nClutter requires shaders with 'fte_clutter MODEL SPACING SCALEMIN SCALEMAX ZOFS ANGLEMIN ANGLEMAX' terms"); +cvar_t r_clutter_density = CVARD("r_clutter_density", "0", "Scaler for clutter counts. 0 disables clutter completely.\nClutter requires shaders with 'fte_clutter MODEL SPACING SCALEMIN SCALEMAX ZOFS ANGLEMIN ANGLEMAX' terms"); cvar_t r_clutter_distance = CVARD("r_clutter_distance", "1024", "Distance at which clutter will become invisible."); //should be used by various shaders to fade it out by here void R_Clutter_Init(void) { @@ -616,6 +616,9 @@ cvar_t r_part_classic_expgrav = CVARFD("r_part_classic_expgrav", "10", CVAR_ARCH particleengine_t *pe; +void P_ParticleEffect_f(void); +static void P_ParticleEffectAlias_f(void); + void P_InitParticleSystem(void) { char *particlecvargroupname = "Particle effects"; @@ -644,9 +647,92 @@ void P_InitParticleSystem(void) Cvar_Register (&r_rockettrail, particlecvargroupname); Cvar_Register (&r_grenadetrail, particlecvargroupname); + //always registered to suck up stray r_part commands even when the scripted system is not active. +#ifdef PSET_SCRIPT + Cmd_AddCommand("r_part", P_ParticleEffect_f); +#endif + Cmd_AddCommand("r_partredirect", P_ParticleEffectAlias_f); + R_Clutter_Init(); } +static struct partalias_s +{ + struct partalias_s *next; + const char *from; + const char *to; +} *partaliaslist; +static void P_ParticleEffectAlias_f(void) +{ + struct partalias_s **link, *l; + char *from = Cmd_Argv(1); + char *to = Cmd_Argv(2); + + //user wants to list all + if (!*from) + { + for (l = partaliaslist; l; l = l->next) + { + Con_Printf("%s -> %s\n", l->from, l->to); + } + return; + } + + //unlink the current value + for (link = &partaliaslist; (l=*link); link = &(*link)->next) + { + if (!Q_strcasecmp(l->from, from)) + { + //they didn't specify a to, so just print out this one effect without removing it. + if (Cmd_Argc() == 2) + { + Con_Printf("particle %s is currently remapped to %s\n", l->from, l->to); + return; + } + *link = l->next; + Z_Free(l); + break; + } + } + + //create a new entry. + if (*to && Q_strcasecmp(from, to)) + { + l = Z_Malloc(sizeof(*l) + strlen(from) + strlen(to) + 2); + l->from = (char*)(l + 1); + strcpy((char*)l->from, from); + l->to = l->from + strlen(l->from)+1; + strcpy((char*)l->to, to); + l->next = partaliaslist; + partaliaslist = l; + } + + CL_RegisterParticles(); +} +int P_FindParticleType(const char *efname) +{ + struct partalias_s *l; + int recurselimit = 5; + if (!pe) + return P_INVALID; + for (l = partaliaslist; l; ) + { + if (!Q_strcasecmp(l->from, efname)) + { + efname = l->to; + + if (recurselimit --> 0) + l = partaliaslist; + else + return P_INVALID; + } + else + l = l->next; + } + + return pe->FindParticleType(efname); +} + void P_Shutdown(void) { if (pe) diff --git a/engine/client/r_surf.c b/engine/client/r_surf.c index 59c0c8dd..43a86a7e 100644 --- a/engine/client/r_surf.c +++ b/engine/client/r_surf.c @@ -36,10 +36,9 @@ model_t *currentmodel; int lightmap_bytes; // 1, 3 or 4 qboolean lightmap_bgra; -#define MAX_LIGHTMAP_SIZE 1024//LMBLOCK_WIDTH - -vec3_t blocknormals[MAX_LIGHTMAP_SIZE*MAX_LIGHTMAP_SIZE]; -unsigned blocklights[3*MAX_LIGHTMAP_SIZE*MAX_LIGHTMAP_SIZE]; +size_t maxblocksize; +vec3_t *blocknormals; +unsigned *blocklights; lightmapinfo_t **lightmap; int numlightmaps; @@ -874,7 +873,8 @@ static void Surf_BuildLightMap (msurface_t *surf, qbyte *dest, qbyte *deluxdest, { int smax, tmax; int t; - int i, j, size; + int i, j; + size_t size; qbyte *lightmap; unsigned scale; int maps; @@ -887,13 +887,17 @@ static void Surf_BuildLightMap (msurface_t *surf, qbyte *dest, qbyte *deluxdest, smax = (surf->extents[0]>>surf->lmshift)+1; tmax = (surf->extents[1]>>surf->lmshift)+1; - size = smax*tmax; + size = (size_t)smax*tmax; lightmap = surf->samples; - if (size > MAX_LIGHTMAP_SIZE*MAX_LIGHTMAP_SIZE) + if (size > maxblocksize) { //fixme: fill in? - Con_Printf("Lightmap too large\n"); - return; + BZ_Free(blocklights); + BZ_Free(blocknormals); + + maxblocksize = size; + blocknormals = BZ_Malloc(maxblocksize * sizeof(*blocknormals)); //already a vector + blocklights = BZ_Malloc(maxblocksize * 3*sizeof(*blocklights)); } if (currentmodel->deluxdata) @@ -2136,18 +2140,18 @@ void Surf_SetupFrame(void) V_SetContentsColor (r_viewcontents); - if (r_refdef.audio.defaulted) + if (r_refdef.playerview->audio.defaulted) { //first scene is the 'main' scene and audio defaults to that (unless overridden later in the frame) - r_refdef.audio.defaulted = false; - VectorCopy(r_refdef.vieworg, r_refdef.audio.origin); - VectorCopy(vpn, r_refdef.audio.forward); - VectorCopy(vright, r_refdef.audio.right); - VectorCopy(vup, r_refdef.audio.up); + r_refdef.playerview->audio.defaulted = false; + VectorCopy(r_refdef.vieworg, r_refdef.playerview->audio.origin); + VectorCopy(vpn, r_refdef.playerview->audio.forward); + VectorCopy(vright, r_refdef.playerview->audio.right); + VectorCopy(vup, r_refdef.playerview->audio.up); if (r_viewcontents & FTECONTENTS_FLUID) - r_refdef.audio.inwater = true; + r_refdef.playerview->audio.inwater = true; else - r_refdef.audio.inwater = false; + r_refdef.playerview->audio.inwater = false; } } @@ -2451,6 +2455,7 @@ void Surf_DeInit(void) void Surf_Clear(model_t *mod) { + int i; vbo_t *vbo; if (mod->fromgame == fg_doom3) return;/*they're on the hunk*/ @@ -2461,12 +2466,29 @@ void Surf_Clear(model_t *mod) BE_ClearVBO(vbo); } + if (!mod->submodelof) + { + for (i = 0; i < mod->numtextures; i++) + { + R_UnloadShader(mod->textures[i]->shader); + mod->textures[i]->shader = NULL; + } + } + mod->numtextures = 0; + + BZ_Free(mod->shadowbatches); mod->numshadowbatches = 0; mod->shadowbatches = NULL; #ifdef RTLIGHTS Sh_PurgeShadowMeshes(); #endif + + BZ_Free(blocklights); + BZ_Free(blocknormals); + blocklights = NULL; + blocknormals = NULL; + maxblocksize = 0; } //pick fastest mode for lightmap data @@ -2953,6 +2975,11 @@ TRACE(("dbg: Surf_NewMap: tp\n")); //fixme: no rotation if (cl_static_entities[i].ent.model) { + //unfortunately, we need to know the actual size so that we can get this right. bum. + if (cl_static_entities[i].ent.model->loadstate == MLS_NOTLOADED) + Mod_LoadModel(cl_static_entities[i].ent.model, MLV_SILENT); + if (cl_static_entities[i].ent.model->loadstate == MLS_LOADING) + COM_WorkerPartialSync(cl_static_entities[i].ent.model, &cl_static_entities[i].ent.model->loadstate, MLS_LOADING); VectorAdd(cl_static_entities[i].ent.origin, cl_static_entities[i].ent.model->mins, mins); VectorAdd(cl_static_entities[i].ent.origin, cl_static_entities[i].ent.model->maxs, maxs); } diff --git a/engine/client/render.h b/engine/client/render.h index f7b97864..54482761 100644 --- a/engine/client/render.h +++ b/engine/client/render.h @@ -37,7 +37,11 @@ struct texture_s; static const texid_t r_nulltex = NULL; - +//GLES2 requires GL_UNSIGNED_SHORT +//geforce4 only does shorts. gffx can do ints, but with a performance hit (like most things on that gpu) +//ati is generally more capable, but generally also has a smaller market share +//desktop-gl will generally cope with ints, but expect a performance hit from that (so we don't bother) +//dx10 can cope with ints, #if 1 || defined(MINIMAL) || defined(D3DQUAKE) || defined(ANDROID) #define sizeof_index_t 2 #endif @@ -196,7 +200,7 @@ typedef struct float time; //timestamp for when its current. } fogstate_t; void CL_BlendFog(fogstate_t *result, fogstate_t *oldf, float time, fogstate_t *newf); -void CL_ResetFog(void); +void CL_ResetFog(int fogtype); typedef struct { char texname[MAX_QPATH]; @@ -254,16 +258,6 @@ typedef struct qbyte *forcedvis; qboolean areabitsknown; qbyte areabits[MAX_MAP_AREA_BYTES]; - - struct - { - qboolean defaulted; - vec3_t origin; - vec3_t forward; - vec3_t right; - vec3_t up; - int inwater; - } audio; } refdef_t; extern refdef_t r_refdef; @@ -373,6 +367,8 @@ enum imageflags IF_TEXTYPESHIFT = 8, /*0=2d, 1=3d, 2-7=cubeface*/ IF_MIPCAP = 1<<10, IF_PREMULTIPLYALPHA = 1<<12, //rgb *= alpha + + IF_NOPURGE = 1<<22, IF_HIGHPRIORITY = 1<<23, IF_LOWPRIORITY = 1<<24, IF_LOADNOW = 1<<25, /*hit the disk now, and delay the gl load until its actually needed. this is used only so that the width+height are known in advance*/ @@ -395,6 +391,7 @@ image_t *Image_GetTexture (const char *identifier, const char *subpath, unsigned qboolean Image_UnloadTexture(image_t *tex); //true if it did something. void Image_DestroyTexture (image_t *tex); void Image_Upload (texid_t tex, uploadfmt_t fmt, void *data, void *palette, int width, int height, unsigned int flags); +void Image_Purge(void); //purge any textures which are not needed any more (releases memory, but doesn't give null pointers). void Image_Init(void); void Image_Shutdown(void); diff --git a/engine/client/renderer.c b/engine/client/renderer.c index b835ef29..7c08d3ea 100644 --- a/engine/client/renderer.c +++ b/engine/client/renderer.c @@ -67,9 +67,9 @@ cvar_t mod_md3flags = CVARD ("mod_md3flags", "1", "The flags field of md3 cvar_t r_ambient = CVARF ("r_ambient", "0", CVAR_CHEAT); cvar_t r_bloodstains = CVARF ("r_bloodstains", "1", CVAR_ARCHIVE); -cvar_t r_bouncysparks = CVARFD ("r_bouncysparks", "0", +cvar_t r_bouncysparks = CVARFD ("r_bouncysparks", "1", CVAR_ARCHIVE, - "Enables particle interaction with world surfaces, allowing for bouncy particles."); + "Enables particle interaction with world surfaces, allowing for bouncy particles, stains, and decals."); cvar_t r_drawentities = CVAR ("r_drawentities", "1"); cvar_t r_drawflat = CVARF ("r_drawflat", "0", CVAR_ARCHIVE | CVAR_SEMICHEAT | CVAR_RENDERERCALLBACK | CVAR_SHADERSYSTEM); @@ -2640,7 +2640,7 @@ void R_InitParticleTexture (void) data[y*32+x][3] = 255; } } - particlecqtexture = R_LoadTexture32("classicparticle", 32, 32, data, IF_NOMIPMAP|IF_NOPICMIP); + particlecqtexture = Image_GetTexture("classicparticle", "particles", IF_NOMIPMAP|IF_NOPICMIP, data, NULL, 32, 32, TF_RGBA32); //draw a square in the top left. still a triangle. for (x=0 ; x<16 ; x++) @@ -2650,8 +2650,7 @@ void R_InitParticleTexture (void) data[y*32+x][3] = 255; } } - R_LoadTexture32("classicparticle_square", 32, 32, data, IF_NOMIPMAP|IF_NOPICMIP); - + Image_GetTexture("classicparticle_square", "particles", IF_NOMIPMAP|IF_NOPICMIP, data, NULL, 32, 32, TF_RGBA32); for (x=0 ; x<16 ; x++) @@ -2664,7 +2663,7 @@ void R_InitParticleTexture (void) data[y*16+x][3] = exptexture[x][y]*255/9.0; } } - explosiontexture = R_LoadTexture32("fte_fuzzyparticle", 16, 16, data, IF_NOMIPMAP|IF_NOPICMIP); + explosiontexture = Image_GetTexture("fte_fuzzyparticle", "particles", IF_NOMIPMAP|IF_NOPICMIP, data, NULL, 16, 16, TF_RGBA32); for (x=0 ; x<16 ; x++) { @@ -2676,7 +2675,19 @@ void R_InitParticleTexture (void) data[y*16+x][3] = exptexture[x][y]*255/9.0; } } - R_LoadTexture32("fte_bloodparticle", 16, 16, data, IF_NOMIPMAP|IF_NOPICMIP); + Image_GetTexture("fte_bloodparticle", "particles", IF_NOMIPMAP|IF_NOPICMIP, data, NULL, 16, 16, TF_RGBA32); + + for (x=0 ; x<16 ; x++) + { + for (y=0 ; y<16 ; y++) + { + data[y*16+x][0] = min(255, exptexture[x][y]*255/9.0); + data[y*16+x][1] = min(255, exptexture[x][y]*255/5.0); + data[y*16+x][2] = min(255, exptexture[x][y]*255/5.0); + data[y*16+x][3] = 255; + } + } + Image_GetTexture("fte_blooddecal", "particles", IF_NOMIPMAP|IF_NOPICMIP, data, NULL, 16, 16, TF_RGBA32); memset(data, 255, sizeof(data)); for (y = 0;y < PARTICLETEXTURESIZE;y++) diff --git a/engine/client/sbar.c b/engine/client/sbar.c index f02081e7..8db8b6fa 100644 --- a/engine/client/sbar.c +++ b/engine/client/sbar.c @@ -643,7 +643,7 @@ void Sbar_ExecuteLayoutString (char *s) static void Sbar_Q2DrawInventory(void) { - int keys[2]; + int keys[1], keymods[1]; char cmd[1024]; const char *boundkey; unsigned int validlist[Q2MAX_ITEMS], rows, i, item, selected = cl.q2frame.playerstate.stats[Q2STAT_SELECTED_ITEM]; @@ -679,11 +679,10 @@ static void Sbar_Q2DrawInventory(void) item = validlist[i]; Q_snprintfz(cmd, sizeof(cmd), "use %s", Get_Q2ConfigString(Q2CS_ITEMS+item)); - M_FindKeysForCommand(0, cmd, keys); - if (keys[0] == -1) + if (!M_FindKeysForCommand(0, 0, cmd, keys, keymods, countof(keys))) boundkey = ""; //we don't actually know which ones can be selected at all. else - boundkey = Key_KeynumToString(keys[0]); + boundkey = Key_KeynumToString(keys[0], keymods[0]); Q_snprintfz(cmd, sizeof(cmd), "%6s %3i %s", boundkey, cl.inventory[item], Get_Q2ConfigString(Q2CS_ITEMS+item)); Draw_FunStringWidth(x, y, cmd, 256-24*2+8, false, item != selected); y+=8; @@ -1144,6 +1143,8 @@ void Draw_TinyString (float x, float y, const qbyte *str) { float xstart; int px, py; + unsigned int codepoint; + int error; if (!font_tiny) { @@ -1157,15 +1158,16 @@ void Draw_TinyString (float x, float y, const qbyte *str) while (*str) { - if (*str == '\n') + codepoint = unicode_decode(&error, str, (char**)&str, true); + + if (codepoint == '\n') { px = xstart; py += Font_CharHeight(); str++; continue; } - //fixme: utf-8 encoding. - px = Font_DrawChar(px, py, CON_WHITEMASK, *str++); + px = Font_DrawChar(px, py, CON_WHITEMASK, codepoint); } Font_EndString(font_tiny); } diff --git a/engine/client/screen.h b/engine/client/screen.h index 505c1ada..56b2b791 100644 --- a/engine/client/screen.h +++ b/engine/client/screen.h @@ -56,7 +56,7 @@ void RSpeedShow(void); void SCR_CrosshairPosition(playerview_t *pview, float *x, float *y); void SCR_DrawLoading (qboolean opaque); -void SCR_TileClear (void); +void SCR_TileClear (int skipbottom); void SCR_DrawNotifyString (void); void SCR_CheckDrawCenterString (void); void SCR_DrawNet (void); diff --git a/engine/client/snd_al.c b/engine/client/snd_al.c index b6d74736..415e9207 100644 --- a/engine/client/snd_al.c +++ b/engine/client/snd_al.c @@ -554,7 +554,7 @@ static void OpenAL_ChannelUpdate(soundcardinfo_t *sc, channel_t *chan, unsigned } if (!schanged && sfx #ifndef FTE_TARGET_WEB - && (chan->looping || (!sfx->decoder.decodedata && sfx->decoder.buf && ((sfxcache_t*)sfx->decoder.buf)->loopstart)) + && ((chan->flags & CF_FORCELOOP) || (!sfx->decoder.decodedata && sfx->decoder.buf && ((sfxcache_t*)sfx->decoder.buf)->loopstart)) #endif ) { @@ -562,7 +562,7 @@ static void OpenAL_ChannelUpdate(soundcardinfo_t *sc, channel_t *chan, unsigned if (buf != AL_PLAYING) { schanged = true; - if (chan->looping) + if (chan->flags & CF_FORCELOOP) chan->pos = 0; else sfx = chan->sfx = NULL; @@ -625,7 +625,7 @@ static void OpenAL_ChannelUpdate(soundcardinfo_t *sc, channel_t *chan, unsigned palGetSourcei(src, AL_SOURCE_STATE, &buf); if (buf != AL_PLAYING) { - if (chan->looping) + if (chan->flags & CF_FORCELOOP) chan->pos = 0; else if(sbuf.loopstart != -1) chan->pos = sbuf.loopstart<effectslot, 0, AL_FILTER_NULL); #endif - palSourcei(src, AL_LOOPING, chan->looping?AL_TRUE:AL_FALSE); + palSourcei(src, AL_LOOPING, (chan->flags & CF_FORCELOOP)?AL_TRUE:AL_FALSE); if (chan->entnum == -1 || chan->entnum == cl.playerview[0].viewentity) { palSourcei(src, AL_SOURCE_RELATIVE, AL_TRUE); diff --git a/engine/client/snd_dma.c b/engine/client/snd_dma.c index cf3a06fa..c947415f 100644 --- a/engine/client/snd_dma.c +++ b/engine/client/snd_dma.c @@ -43,11 +43,15 @@ static qboolean snd_ambient = 1; qboolean snd_initialized = false; int snd_speed; -vec3_t listener_origin; -vec3_t listener_forward = {1, 0, 0}; -vec3_t listener_right = {0, 1, 0}; -vec3_t listener_up = {0, 0, 1}; -vec3_t listener_velocity; +static struct +{ + int entnum; + vec3_t origin; + vec3_t velocity; + vec3_t forward; + vec3_t right; + vec3_t up; +} listener[MAX_SPLITS]; vec_t sound_nominal_clip_dist=1000.0; #define MAX_SFX 8192 @@ -1483,7 +1487,12 @@ void S_DefaultSpeakerConfiguration(soundcardinfo_t *sc) } -sounddriver_t DSOUND_Output; +#ifdef AVAIL_XAUDIO2 +extern sounddriver_t XAUDIO2_Output; +#endif +#ifdef AVAIL_DSOUND +extern sounddriver_t DSOUND_Output; +#endif sounddriver_t SDL_Output; sounddriver_t ALSA_Output; sounddriver_t OSS_Output; @@ -1506,11 +1515,15 @@ extern sounddriver pPPAPI_InitCard; sounddriver_t *outputdrivers[] = { #ifdef AVAIL_OPENAL - &OPENAL_Output, + &OPENAL_Output, //refuses to run as the default device, at least until its perfected. #endif -#ifdef _WIN32 + +#ifdef AVAIL_DSOUND &DSOUND_Output, #endif +#ifdef AVAIL_XAUDIO2 + &XAUDIO2_Output, +#endif &SDL_Output, //prefered on linux #ifdef __linux__ @@ -1537,7 +1550,7 @@ sdriver_t olddrivers[] = { {NULL, NULL} }; -static soundcardinfo_t *SNDDMA_Init(char *driver, char *device) +static soundcardinfo_t *SNDDMA_Init(char *driver, char *device, int seat) { soundcardinfo_t *sc = Z_Malloc(sizeof(soundcardinfo_t)); sdriver_t *od; @@ -1546,6 +1559,7 @@ static soundcardinfo_t *SNDDMA_Init(char *driver, char *device) int st; memset(sc, 0, sizeof(*sc)); + sc->seat = seat; // set requested rate if (snd_khz.ival >= 1000) @@ -1661,6 +1675,17 @@ static soundcardinfo_t *SNDDMA_Init(char *driver, char *device) return NULL; } +void S_SetupDeviceSeat(char *driver, char *device, int seat) +{ + SNDDMA_Init(driver, device, seat); + /* + soundcardinfo_t *sc; + for (sc = sndcardinfo; sc; sc = sc->next) + { + sc->seat = seat; + }*/ +} + void QDECL S_EnumeratedOutDevice(const char *driver, const char *devicecode, const char *readabledevice) { const char *fullintname; @@ -1732,11 +1757,17 @@ void S_Startup (void) sep = strchr(com_token, ':'); if (sep) *sep++ = 0; - SNDDMA_Init(com_token, sep); + SNDDMA_Init(com_token, sep, 0); } } if (!sndcardinfo && !nodefault) - SNDDMA_Init(NULL, NULL); + { +#ifdef _WIN32 + INS_SetupControllerAudioDevices(); +#endif + if (!sndcardinfo) + SNDDMA_Init(NULL, NULL, 0); + } sound_started = true; @@ -1752,15 +1783,6 @@ void S_Startup (void) ambient_sfx[AMBIENT_SKY] = S_PrecacheSound ("ambience/wind2.wav"); } -void S_SetUnderWater(qboolean underwater) -{ - soundcardinfo_t *sc; - - for (sc = sndcardinfo; sc; sc=sc->next) - if (sc->SetWaterDistortion) - sc->SetWaterDistortion(sc, underwater); -} - //why isn't this part of S_Restart_f anymore? //so that the video code can call it directly without flushing the models it's just loaded. void S_DoRestart (void) @@ -2200,7 +2222,7 @@ channel_t *SND_PickChannel(soundcardinfo_t *sc, int entnum, int entchannel) } // don't let monster sounds override player sounds - if (sc->channel[ch_idx].entnum == cl.playerview[0].playernum+1 && entnum != cl.playerview[0].playernum+1 && sc->channel[ch_idx].sfx) + if (sc->channel[ch_idx].entnum == listener[sc->seat].entnum && entnum != listener[sc->seat].entnum && sc->channel[ch_idx].sfx) continue; if (!sc->channel[ch_idx].sfx) @@ -2261,14 +2283,14 @@ void SND_Spatialize(soundcardinfo_t *sc, channel_t *ch) } // calculate stereo seperation and distance attenuation - VectorSubtract(ch->origin, listener_origin, world_vec); + VectorSubtract(ch->origin, listener[sc->seat].origin, world_vec); dist = VectorNormalize(world_vec) * ch->dist_mult; //rotate the world_vec into listener space, so that the audio direction stored in the speakerdir array can be used directly. - listener_vec[0] = DotProduct(listener_forward, world_vec); - listener_vec[1] = DotProduct(listener_right, world_vec); - listener_vec[2] = DotProduct(listener_up, world_vec); + listener_vec[0] = DotProduct(listener[sc->seat].forward, world_vec); + listener_vec[1] = DotProduct(listener[sc->seat].right, world_vec); + listener_vec[2] = DotProduct(listener[sc->seat].up, world_vec); if (snd_leftisright.ival) listener_vec[1] = -listener_vec[1]; @@ -2285,22 +2307,24 @@ void SND_Spatialize(soundcardinfo_t *sc, channel_t *ch) // ======================================================================= // Start a sound effect // ======================================================================= - -static void S_StartSoundCard(soundcardinfo_t *sc, int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation, int startpos, float pitchadj) +static void S_UpdateSoundCard(soundcardinfo_t *sc, qboolean updateonly, channel_t *target_chan, sfx_t *sfx, vec3_t origin, float fvol, float attenuation, float timeoffset, float pitchadj) { - channel_t *target_chan, *check; + channel_t *check; int vol; int ch_idx; int skip; + int entnum = target_chan->entnum; + int entchannel = target_chan->entchannel; + int absstartpos = updateonly?target_chan->pos:0; - if (!sound_started) + if (fvol < 0) + { //stopsound, apparently. + target_chan->sfx = NULL; return; - + } + if (!sfx) - return; - - if (nosound.ival) - return; + sfx = target_chan->sfx; if (pitchadj <= 0) pitchadj = 100; @@ -2309,16 +2333,11 @@ static void S_StartSoundCard(soundcardinfo_t *sc, int entnum, int entchannel, sf vol = fvol*255; -// pick a channel to play on - target_chan = SND_PickChannel(sc, entnum, entchannel); - if (!target_chan) - return; - // spatialize memset (target_chan, 0, sizeof(*target_chan)); if (!origin) { - VectorCopy(listener_origin, target_chan->origin); + VectorCopy(listener[sc->seat].origin, target_chan->origin); } else { @@ -2331,35 +2350,43 @@ static void S_StartSoundCard(soundcardinfo_t *sc, int entnum, int entchannel, sf target_chan->entchannel = entchannel; SND_Spatialize(sc, target_chan); - if (!target_chan->vol[0] && !target_chan->vol[1] && !target_chan->vol[2] && !target_chan->vol[3] && !target_chan->vol[4] && !target_chan->vol[5] && sc->ChannelUpdate) - return; // not audible at all - -// new channel - if (!S_LoadSound (sfx)) + if (!updateonly && !target_chan->vol[0] && !target_chan->vol[1] && !target_chan->vol[2] && !target_chan->vol[3] && !target_chan->vol[4] && !target_chan->vol[5] && sc->ChannelUpdate) { target_chan->sfx = NULL; - return; // couldn't load the sound's data + return; // not audible at all + } + + if (sfx) + { + if (!S_LoadSound (sfx)) + { + target_chan->sfx = NULL; + return; // couldn't load the sound's data + } + + target_chan->sfx = sfx; } - target_chan->sfx = sfx; target_chan->rate = ((1<rate < 1) /*make sure the rate won't crash us*/ target_chan->rate = 1; - target_chan->pos = startpos*target_chan->rate; - target_chan->looping = false; + target_chan->pos = absstartpos + (int)(timeoffset*sc->sn.speed*target_chan->rate); + if (!updateonly) + { // if an identical sound has also been started this frame, offset the pos // a bit to keep it from just making the first one louder - check = &sc->channel[DYNAMIC_FIRST]; - for (ch_idx=DYNAMIC_FIRST; ch_idx < DYNAMIC_STOP; ch_idx++, check++) - { - if (check == target_chan) - continue; - if (check->sfx == sfx && !check->pos) + check = &sc->channel[DYNAMIC_FIRST]; + for (ch_idx=DYNAMIC_FIRST; ch_idx < DYNAMIC_STOP; ch_idx++, check++) { - skip = rand () % (int)(0.1*sc->sn.speed); - target_chan->pos -= skip*target_chan->rate; - break; + if (check == target_chan) + continue; + if (check->sfx == sfx && !check->pos) + { + skip = rand () % (int)(0.1*sc->sn.speed); + target_chan->pos -= skip*target_chan->rate; + break; + } } } @@ -2367,9 +2394,39 @@ static void S_StartSoundCard(soundcardinfo_t *sc, int entnum, int entchannel, sf sc->ChannelUpdate(sc, target_chan, true); } +float S_UpdateSound(int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation, float timeofs, float pitchadj) +{ + int i; + int result = 0; + int cards = 0; + soundcardinfo_t *sc; + + if (cls.demoseeking) + return result; + S_LockMixer(); + for (sc = sndcardinfo; sc; sc = sc->next) + { + cards++; + for (i = 0; i < sc->total_chans; i++) + { + if (sc->channel[i].entnum == entnum && sc->channel[i].entchannel == entchannel && sc->channel[i].sfx) + { + S_UpdateSoundCard(sc, true, &sc->channel[i], sfx, origin, fvol, attenuation, timeofs, pitchadj); + result++; + break; + } + } + } + S_UnlockMixer(); + if (!cards) + cards=1; + return result / (float)cards; +} + void S_StartSound(int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation, float timeofs, float pitchadj) { soundcardinfo_t *sc; + channel_t *target_chan; if (!sfx || !*sfx->name) //no named sounds would need specific starting. return; @@ -2377,9 +2434,22 @@ void S_StartSound(int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float f if (cls.demoseeking) return; + if (!sound_started) + return; + + if (nosound.ival) + return; + S_LockMixer(); for (sc = sndcardinfo; sc; sc = sc->next) - S_StartSoundCard(sc, entnum, entchannel, sfx, origin, fvol, attenuation, -(int)(timeofs * sc->sn.speed), pitchadj); + { + // pick a channel to play on + target_chan = SND_PickChannel(sc, entnum, entchannel); + if (!target_chan) + return; + + S_UpdateSoundCard(sc, false, target_chan, sfx, origin, fvol, attenuation, timeofs, pitchadj); + } S_UnlockMixer(); } @@ -2604,7 +2674,7 @@ void S_StaticSound (sfx_t *sfx, vec3_t origin, float vol, float attenuation) ss->master_vol = vol; ss->dist_mult = (attenuation/64) / sound_nominal_clip_dist; ss->pos = 0; - ss->looping = true; + ss->flags = CF_FORCELOOP; SND_Spatialize (scard, ss); @@ -2699,30 +2769,12 @@ void S_UpdateAmbientSounds (soundcardinfo_t *sc) { qboolean changed = false; chan = &sc->channel[i]; - if (!chan->sfx) - { - char *nexttrack = Media_NextTrack(i-MUSIC_FIRST); - sfx_t *newmusic; - - if (nexttrack && *nexttrack) - { - newmusic = S_PrecacheSound(nexttrack); - - if (newmusic && newmusic->loadstate != SLS_FAILED) - { - chan->sfx = newmusic; - chan->rate = 1<pos = 0; - changed = true; - } - } - } if (chan->sfx) { chan->flags = CF_ABSVOLUME; //bypasses volume cvar completely. vol = 255*bgmvolume.value*voicevolumemod; vol = bound(0, vol, 255); - vol = Media_CrossFade(i-MUSIC_FIRST, vol); + vol = Media_CrossFade(i-MUSIC_FIRST, vol, (chan->pos>>PITCHSHIFT) / (float)snd_speed); if (vol < 0) { //cross fading wants to KILL this track now, apparently. sfx_t *s = chan->sfx; @@ -2738,12 +2790,33 @@ void S_UpdateAmbientSounds (soundcardinfo_t *sc) if (s && s->decoder.ended && !S_IsPlayingSomewhere(s)) //if we aint playing it elsewhere, free it compleatly. s->decoder.ended(s); } - continue; } - chan->master_vol = bound(0, vol, 255); - chan->vol[0] = chan->vol[1] = chan->vol[2] = chan->vol[3] = chan->vol[4] = chan->vol[5] = chan->master_vol; - if (sc->ChannelUpdate) - sc->ChannelUpdate(sc, chan, changed); + else + { + chan->master_vol = bound(0, vol, 255); + chan->vol[0] = chan->vol[1] = chan->vol[2] = chan->vol[3] = chan->vol[4] = chan->vol[5] = chan->master_vol; + if (sc->ChannelUpdate) + sc->ChannelUpdate(sc, chan, changed); + } + } + if (!chan->sfx) + { + float time = 0; + char *nexttrack = Media_NextTrack(i-MUSIC_FIRST, &time); + sfx_t *newmusic; + + if (nexttrack && *nexttrack) + { + newmusic = S_PrecacheSound(nexttrack); + + if (newmusic && newmusic->loadstate != SLS_FAILED) + { + chan->sfx = newmusic; + chan->rate = 1<pos = (int)(time * sc->sn.speed) * chan->rate; + changed = true; + } + } } } @@ -2752,7 +2825,7 @@ void S_UpdateAmbientSounds (soundcardinfo_t *sc) if (!cl.worldmodel || cl.worldmodel->type != mod_brush || cl.worldmodel->fromgame != fg_quake || cl.worldmodel->loadstate != MLS_LOADED) return; - l = Q1BSP_LeafForPoint(cl.worldmodel, listener_origin); + l = Q1BSP_LeafForPoint(cl.worldmodel, listener[sc->seat].origin); if (!l || ambient_level.value <= 0) { for (ambient_channel = 0 ; ambient_channel< NUM_AMBIENTS ; ambient_channel++) @@ -2771,10 +2844,10 @@ void S_UpdateAmbientSounds (soundcardinfo_t *sc) chan = &sc->channel[AMBIENT_FIRST+ambient_channel]; chan->sfx = ambient_sfx[AMBIENT_FIRST+ambient_channel]; chan->entnum = -1; - chan->looping = true; + chan->flags = CF_FORCELOOP; chan->rate = 1<origin); + VectorCopy(listener[sc->seat].origin, chan->origin); vol = ambient_level.value * l->ambient_sound_level[ambient_channel]; if (vol < 8) @@ -2811,20 +2884,26 @@ S_Update Called once each time through the main loop ============ */ -void S_UpdateListener(vec3_t origin, vec3_t forward, vec3_t right, vec3_t up) +void S_UpdateListener(int seat, vec3_t origin, vec3_t forward, vec3_t right, vec3_t up, qboolean underwater) { - VectorCopy(origin, listener_origin); - VectorCopy(forward, listener_forward); - VectorCopy(right, listener_right); - VectorCopy(up, listener_up); + soundcardinfo_t *sc; + listener[seat].entnum = cl.playerview[seat].playernum+1; + VectorCopy(origin, listener[seat].origin); + VectorCopy(forward, listener[seat].forward); + VectorCopy(right, listener[seat].right); + VectorCopy(up, listener[seat].up); + + for (sc = sndcardinfo; sc; sc=sc->next) + if (sc->SetWaterDistortion && sc->seat == seat) + sc->SetWaterDistortion(sc, underwater); } -void S_GetListenerInfo(float *origin, float *forward, float *right, float *up) +void S_GetListenerInfo(int seat, float *origin, float *forward, float *right, float *up) { - VectorCopy(listener_origin, origin); - VectorCopy(listener_forward, forward); - VectorCopy(listener_right, right); - VectorCopy(listener_up, up); + VectorCopy(listener[seat].origin, origin); + VectorCopy(listener[seat].forward, forward); + VectorCopy(listener[seat].right, right); + VectorCopy(listener[seat].up, up); } static void S_UpdateCard(soundcardinfo_t *sc) @@ -2845,7 +2924,7 @@ static void S_UpdateCard(soundcardinfo_t *sc) #ifdef AVAIL_OPENAL if (sc->ListenerUpdate) { - sc->ListenerUpdate(sc, listener_origin, listener_forward, listener_right, listener_up, listener_velocity); + sc->ListenerUpdate(sc, listener[sc->seat].origin, listener[sc->seat].forward, listener[sc->seat].right, listener[sc->seat].up, listener[sc->seat].velocity); } #endif @@ -2951,7 +3030,8 @@ int S_GetMixerTime(soundcardinfo_t *sc) // calls to S_Update. Oh well. samplepos = sc->GetDMAPos(sc); - samplepos -= sc->samplequeue; + if (sc->samplequeue > 0) + samplepos -= sc->samplequeue; if (samplepos < 0) { @@ -3028,13 +3108,19 @@ static void S_Update_(soundcardinfo_t *sc) // Updates DMA time soundtime = S_GetMixerTime(sc); - if (sc->samplequeue) + if (sc->samplequeue > 0) { /*device uses a write-once queue*/ endtime = soundtime + sc->samplequeue/sc->sn.numchannels; soundtime = sc->paintedtime; samps = sc->samplequeue / sc->sn.numchannels; } + else if (sc->samplequeue < 0) + { + endtime = soundtime; + soundtime = sc->paintedtime; + samps = sc->sn.samples / sc->sn.numchannels; + } else { /*device uses memory-mapped output*/ @@ -3228,7 +3314,7 @@ void S_ClearRaw(void) } //returns an sfxcache_t stating where the data is -sfxcache_t *S_Raw_Locate(sfx_t *sfx, sfxcache_t *buf, int start, int length) +sfxcache_t *S_Raw_Locate(sfx_t *sfx, sfxcache_t *buf, ssamplepos_t start, int length) { streaming_t *s = sfx->decoder.buf; if (buf) @@ -3405,7 +3491,6 @@ void S_RawAudio(int sourceid, qbyte *data, int speed, int samples, int channels, c->entnum = -1; c->entchannel = 0; c->dist_mult = 0; - c->looping = false; c->master_vol = 255 * volume; c->pos = 0; c->rate = 1<pos = 0; if (scache->loopstart != -1) ch->pos = scache->loopstart<looping) + else if (!(ch->flags & CF_FORCELOOP)) { ch->sfx = NULL; if (s->decoder.ended) @@ -257,7 +257,7 @@ void S_PaintChannels(soundcardinfo_t *sc, int endtime) break; } } - else if (ch->looping && scache->length) /*(static)channels which are explicitly looping always loop from the start*/ + else if ((ch->flags & CF_FORCELOOP) && scache->length) /*(static)channels which are explicitly looping always loop from the start*/ { /*restart it*/ ch->pos = 0; diff --git a/engine/client/snd_ov.c b/engine/client/snd_ov.c index d17c3d4c..60d6ce89 100644 --- a/engine/client/snd_ov.c +++ b/engine/client/snd_ov.c @@ -76,7 +76,7 @@ typedef struct { } ovdecoderbuffer_t; static sfxcache_t *OV_Query(struct sfx_s *sfx, struct sfxcache_s *buf); -static sfxcache_t *OV_DecodeSome(struct sfx_s *sfx, struct sfxcache_s *buf, int start, int length); +static sfxcache_t *OV_DecodeSome(struct sfx_s *sfx, struct sfxcache_s *buf, ssamplepos_t start, int length); static void OV_CancelDecoder(sfx_t *s); static qboolean OV_StartDecode(unsigned char *start, unsigned long length, ovdecoderbuffer_t *buffer); @@ -137,7 +137,7 @@ static sfxcache_t *OV_Query(struct sfx_s *sfx, struct sfxcache_s *buf) return buf; } -static sfxcache_t *OV_DecodeSome(struct sfx_s *sfx, struct sfxcache_s *buf, int start, int length) +static sfxcache_t *OV_DecodeSome(struct sfx_s *sfx, struct sfxcache_s *buf, ssamplepos_t start, int length) { extern int snd_speed; extern cvar_t snd_linearresample_stream; @@ -183,6 +183,11 @@ static sfxcache_t *OV_DecodeSome(struct sfx_s *sfx, struct sfxcache_s *buf, int dec->decodedbytecount = 0; dec->decodedbytestart = start; } + else if (trim > dec->decodedbytecount) + { + dec->decodedbytecount = 0; + dec->decodedbytestart = start; + } else { //FIXME: retain an extra half-second for dual+ sound devices running slightly out of sync diff --git a/engine/client/sound.h b/engine/client/sound.h index 7c179f82..cefc6693 100644 --- a/engine/client/sound.h +++ b/engine/client/sound.h @@ -22,24 +22,21 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #ifndef __SOUND__ #define __SOUND__ -// !!! if this is changed, it much be changed in asm_i386.h too !!! #define MAXSOUNDCHANNELS 8 //on a per device basis -// !!! if this is changed, it much be changed in asm_i386.h too !!! +//pitch shifting can +#define ssamplepos_t qintptr_t +#define usamplepos_t quintptr_t +#define PITCHSHIFT 6 /*max audio file length = (1<<32>>PITCHSHIFT)/KHZ*/ + struct sfx_s; -/*typedef struct -{ - int left; - int right; -} portable_samplepair_t; -*/ typedef struct { int s[MAXSOUNDCHANNELS]; } portable_samplegroup_t; typedef struct { - struct sfxcache_s *(*decodedata) (struct sfx_s *sfx, struct sfxcache_s *buf, int start, int length); //retrurn true when done. + struct sfxcache_s *(*decodedata) (struct sfx_s *sfx, struct sfxcache_s *buf, ssamplepos_t start, int length); //return true when done. struct sfxcache_s *(*querydata) (struct sfx_s *sfx, struct sfxcache_s *buf); //reports length + format info without actually decoding anything. void (*ended) (struct sfx_s *sfx); //sound stopped playing and is now silent (allow rewinding or something). void (*purge) (struct sfx_s *sfx); //sound is being purged from memory. destroy everything. @@ -69,12 +66,12 @@ typedef struct sfx_s // !!! if this is changed, it much be changed in asm_i386.h too !!! typedef struct sfxcache_s { - unsigned int length; //sample count + usamplepos_t length; //sample count unsigned int loopstart; //-1 or sample index to begin looping at once the sample ends unsigned int speed; unsigned int width; unsigned int numchannels; - unsigned int soundoffset; //byte index into the sound + usamplepos_t soundoffset; //byte index into the sound qbyte *data; // variable sized } sfxcache_t; @@ -92,17 +89,15 @@ typedef struct unsigned char *buffer; // pointer to mixed pcm buffer (not directly used by mixer) } dma_t; -#define PITCHSHIFT 6 /*max audio file length = (1<<32>>PITCHSHIFT)/KHZ*/ - #define CF_ABSVOLUME 1 // ignores volume cvar. +#define CF_FORCELOOP 2 // forces looping. set on static sounds. typedef struct { sfx_t *sfx; // sfx number int vol[MAXSOUNDCHANNELS]; // volume, .8 fixed point. - int pos; // sample position in sfx, <0 means delay sound start (shifted up by 8) + ssamplepos_t pos; // sample position in sfx, <0 means delay sound start (shifted up by 8) int rate; // 24.8 fixed point rate scaling int flags; // cf_ flags - int looping; // where to loop, -1 = no looping int entnum; // to allow overriding a specific sound int entchannel; // to avoid overriding a specific sound too easily vec3_t origin; // origin of sound effect @@ -128,24 +123,28 @@ void S_Startup (void); void S_Shutdown (qboolean final); float S_GetSoundTime(int entnum, int entchannel); void S_StartSound (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation, float timeofs, float pitchadj); +float S_UpdateSound(int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation, float timeofs, float pitchadj); void S_StaticSound (sfx_t *sfx, vec3_t origin, float vol, float attenuation); void S_StopSound (int entnum, int entchannel); void S_StopAllSounds(qboolean clear); -void S_UpdateListener(vec3_t origin, vec3_t forward, vec3_t right, vec3_t up); -void S_GetListenerInfo(float *origin, float *forward, float *right, float *up); +void S_UpdateListener(int seat, vec3_t origin, vec3_t forward, vec3_t right, vec3_t up, qboolean underwater); +void S_GetListenerInfo(int seat, float *origin, float *forward, float *right, float *up); void S_Update (void); void S_ExtraUpdate (void); void S_MixerThread(soundcardinfo_t *sc); void S_Purge(qboolean retaintouched); +void S_LockMixer(void); +void S_UnlockMixer(void); + qboolean S_HaveOutput(void); void S_Music_Clear(sfx_t *onlyifsample); void S_Music_Seek(float time); qboolean S_GetMusicInfo(int musicchannel, float *time, float *duration); qboolean S_Music_Playing(int musicchannel); -float Media_CrossFade(int musicchanel, float vol); //queries the volume we're meant to be playing (checks for fade out). -1 for no more, otherwise returns vol. -char *Media_NextTrack(int musicchanel); //queries the track we're meant to be playing now. +float Media_CrossFade(int musicchanel, float vol, float time); //queries the volume we're meant to be playing (checks for fade out). -1 for no more, otherwise returns vol. +char *Media_NextTrack(int musicchanel, float *time); //queries the track we're meant to be playing now. sfx_t *S_FindName (const char *name, qboolean create); sfx_t *S_PrecacheSound (const char *sample); @@ -192,7 +191,6 @@ void SND_ResampleStream (void *in, int inrate, int inwidth, int inchannels, int // restart entire sound subsystem (doesn't flush old sounds, so make sure that happens) void S_DoRestart (void); -void S_SetUnderWater(qboolean underwater); void S_Restart_f (void); @@ -235,10 +233,6 @@ void OpenAL_CvarInit(void); extern int snd_speed; -extern vec3_t listener_origin; -extern vec3_t listener_forward; -extern vec3_t listener_right; -extern vec3_t listener_up; extern vec_t sound_nominal_clip_dist; extern cvar_t loadas8bit; @@ -284,7 +278,9 @@ extern sounddriver pAHI_InitCard; struct soundcardinfo_s { //windows has one defined AFTER directsound char name[256]; //a description of the card. + char guid[256]; //device name as detected (so input code can create sound devices without bugging out too much) struct soundcardinfo_s *next; + int seat; //speaker orientations for spacialisation. float dist[MAXSOUNDCHANNELS]; @@ -292,6 +288,7 @@ struct soundcardinfo_s { //windows has one defined AFTER directsound vec3_t speakerdir[MAXSOUNDCHANNELS]; //info on which sound effects are playing + //FIXME: use a linked list channel_t channel[MAX_CHANNELS]; int total_chans; @@ -338,4 +335,6 @@ typedef struct void (QDECL *Shutdown) (void *ctx); /*destroy everything*/ } snd_capture_driver_t; +void S_SetupDeviceSeat(char *driver, char *device, int seat); + #endif diff --git a/engine/client/sys_win.c b/engine/client/sys_win.c index 3648cc62..a00b4dd7 100644 --- a/engine/client/sys_win.c +++ b/engine/client/sys_win.c @@ -388,11 +388,15 @@ dllhandle_t *Sys_LoadLibrary(const char *name, dllfunction_t *funcs) { if (!strstr(COM_SkipPath(name), ".dll")) { //.dll implies that it is a system dll, or something that is otherwise windows-specific already. + char libname[MAX_OSPATH]; #ifdef _WIN64 - lib = LoadLibraryU(va("%s_64", name)); + Q_snprintfz(libname, sizeof(libname), "%s_64", name); #elif defined(_WIN32) - lib = LoadLibraryU(va("%s_32", name)); + Q_snprintfz(libname, sizeof(libname), "%s_32", name); +#else +#error wut? not win32? #endif + lib = LoadLibraryU(libname); } if (!lib) return NULL; @@ -2104,46 +2108,45 @@ void Sys_SendKeyEvents (void) } else if (avail) { - if (avail > sizeof(text)-1-avail) - avail = sizeof(text)-1-avail; + if (avail > sizeof(text)-1-textpos) + avail = sizeof(text)-1-textpos; if (ReadFile(input, text+textpos, avail, &avail, NULL)) { textpos += avail; if (textpos > sizeof(text)-1) Sys_Error("No."); - while(1) - { - text[textpos] = 0; - nl = strchr(text, '\n'); - if (nl) - { - *nl++ = 0; - if (qrenderer <= QR_NONE && !strncmp(text, "vid_recenter ", 13)) - { - Cmd_TokenizeString(text, false, false); - sys_parentleft = strtoul(Cmd_Argv(1), NULL, 0); - sys_parenttop = strtoul(Cmd_Argv(2), NULL, 0); - sys_parentwidth = strtoul(Cmd_Argv(3), NULL, 0); - sys_parentheight = strtoul(Cmd_Argv(4), NULL, 0); - sys_parentwindow = (HWND)(intptr_t)strtoull(Cmd_Argv(5), NULL, 16); - } -#if !defined(CLIENTONLY) || defined(CSQC_DAT) || defined(MENU_DAT) - else if (QCExternalDebuggerCommand(text)) - /*handled elsewhere*/; -#endif - else - { - Cbuf_AddText(text, RESTRICT_LOCAL); - Cbuf_AddText("\n", RESTRICT_LOCAL); - } - memmove(text, nl, textpos - (nl - text)); - textpos -= (nl - text); - } - else - break; - } } - + } + while (textpos) + { + text[textpos] = 0; + nl = strchr(text, '\n'); + if (nl) + { + *nl++ = 0; + if (qrenderer <= QR_NONE && !strncmp(text, "vid_recenter ", 13)) + { + Cmd_TokenizeString(text, false, false); + sys_parentleft = strtoul(Cmd_Argv(1), NULL, 0); + sys_parenttop = strtoul(Cmd_Argv(2), NULL, 0); + sys_parentwidth = strtoul(Cmd_Argv(3), NULL, 0); + sys_parentheight = strtoul(Cmd_Argv(4), NULL, 0); + sys_parentwindow = (HWND)(intptr_t)strtoull(Cmd_Argv(5), NULL, 16); + } +#if !defined(CLIENTONLY) || defined(CSQC_DAT) || defined(MENU_DAT) + else if (QCExternalDebuggerCommand(text)) + /*handled elsewhere*/; +#endif + else + { + Cbuf_AddText(text, RESTRICT_LOCAL); + Cbuf_AddText("\n", RESTRICT_LOCAL); + } + memmove(text, nl, textpos - (nl - text)); + textpos -= (nl - text); + } + else + break; } } if (isDedicated) @@ -2682,7 +2685,7 @@ void Update_PromptedDownloaded(void *ctx, int foo) narrowen(cmdline, sizeof(cmdline), GetCommandLineW()); widen(wideexe, sizeof(wideexe), ctx); - widen(widearg, sizeof(widearg), va("\"%s\" %s", ctx, COM_Parse(cmdline))); + widen(widearg, sizeof(widearg), va("\"%s\" %s", (char*)ctx, COM_Parse(cmdline))); CreateProcessW(wideexe, widearg, NULL, NULL, TRUE, 0, NULL, NULL, &startinfo, &childinfo); Z_Free(ctx); @@ -2809,6 +2812,7 @@ void Update_Check(void) dl = HTTP_CL_Get(va(UPDATE_URL_VERSION, updateroot), NULL, Update_Versioninfo_Available); dl->file = FS_OpenTemp(); dl->user_ctx = updateroot; + dl->isquery = true; #ifdef MULTITHREAD DL_CreateThread(dl, NULL, NULL); #endif diff --git a/engine/client/view.c b/engine/client/view.c index dbc21cfa..96bfad5c 100644 --- a/engine/client/view.c +++ b/engine/client/view.c @@ -1303,16 +1303,16 @@ void V_CalcRefdef (playerview_t *pv) { float bob; float viewheight; + r_refdef.playerview = pv; + memset(&r_refdef.globalfog, 0, sizeof(r_refdef.globalfog)); + #ifdef Q2CLIENT if (cls.protocol == CP_QUAKE2) return; #endif - CL_BlendFog(&r_refdef.globalfog, &cl.oldfog, realtime, &cl.fog); - r_refdef.globalfog.density /= 64; //FIXME - if (v_viewheight.value < -7) bob=-7; else if (v_viewheight.value > 4) @@ -1738,6 +1738,40 @@ void R_DrawNameTags(void) w = &csqc_world; #endif } + else if ((r_showfields.ival & 3) == 3) + { + inframe_t *frame; + packet_entities_t *pak; + entity_state_t *state; + model_t *mod; + + frame = &cl.inframes[cl.parsecount & UPDATE_MASK]; + pak = &frame->packet_entities; + + for (i=0 ; inum_entities ; i++) + { + state = &pak->entities[i]; + + mod = cl.model_precache[state->modelindex]; + VectorInterpolate(mod->mins, 0.5, mod->maxs, org); + VectorAdd(org, state->origin, org); + if (Matrix4x4_CM_Project(org, screenspace, r_refdef.viewangles, r_refdef.vieworg, r_refdef.fov_x, r_refdef.fov_y)) + { + char *entstr; + int x, y; + + entstr = va("%i", state->number); + if (entstr) + { + vec2_t scale = {8,8}; + x = screenspace[0]*r_refdef.vrect.width+r_refdef.vrect.x; + y = (1-screenspace[1])*r_refdef.vrect.height+r_refdef.vrect.y; + R_DrawTextField(x, y, vid.width - x, vid.height - y, entstr, CON_WHITEMASK, CPRINT_TALIGN|CPRINT_LALIGN, font_default, scale); + } + + } + } + } if (w && w->progs) { int best = 0; @@ -1841,7 +1875,7 @@ void R_DrawNameTags(void) if (!cl.teamplay || !scr_autoid_team.ival) isteam = false; - else if (cl.teamfortress && !cl.spectator) //teamfortress should go by their colours instead, because spies. primarily this is to allow enemy spies to appear through walls as well as your own team (note that the qc will also need tinfo stuff for tf, to avoid issues with just checking player names). + else if ((cl.teamfortress && !cl.spectator) || cls.protocol == CP_NETQUAKE) //teamfortress should go by their colours instead, because spies. primarily this is to allow enemy spies to appear through walls as well as your own team (note that the qc will also need tinfo stuff for tf, to avoid issues with just checking player names). isteam = cl.players[i].rbottomcolor == ourcolour; else isteam = !strcmp(cl.players[i].team, ourteam); @@ -2055,15 +2089,17 @@ void V_RenderView (void) V_RenderPlayerViews(r_refdef.playerview); #ifdef PLUGINS - Plug_SBar (r_refdef.playerview); + Plug_SBar (r_refdef.playerview); #else - if (Sbar_ShouldDraw()) - { - Sbar_Draw (r_refdef.playerview); - Sbar_DrawScoreboard (); - } + if (Sbar_ShouldDraw()) + { + SCR_TileClear (sb_lines); + Sbar_Draw (r_refdef.playerview); + Sbar_DrawScoreboard (); + } + else + SCR_TileClear (0); #endif - SCR_TileClear (); } r_refdef.playerview = NULL; } diff --git a/engine/client/wad.c b/engine/client/wad.c index 78b9fa0c..05637ef9 100644 --- a/engine/client/wad.c +++ b/engine/client/wad.c @@ -683,7 +683,6 @@ void Mod_ParseInfoFromEntityLump(model_t *wmodel) //actually, this should be in } else if (!strcmp("fog", key)) //q1 extension. FIXME: should be made temporary. { - void CL_Fog_f(void); key[0] = 'f'; key[1] = 'o'; key[2] = 'g'; @@ -691,6 +690,12 @@ void Mod_ParseInfoFromEntityLump(model_t *wmodel) //actually, this should be in Q_strncpyz(key+4, token, sizeof(key)-4); Cbuf_AddText(key, RESTRICT_INSECURE); } + else if (!strcmp("waterfog", key)) //q1 extension. FIXME: should be made temporary. + { + memcpy(key, "waterfog ", 9); + Q_strncpyz(key+9, token, sizeof(key)-9); + Cbuf_AddText(key, RESTRICT_INSECURE); + } else if (!strncmp("cvar_", key, 5)) //override cvars so mappers don't end up hacking cvars and fucking over configs (at least in other engines). { cvar_t *var = Cvar_FindVar(key+5); diff --git a/engine/common/bothdefs.h b/engine/common/bothdefs.h index 9b946e9d..1c19b3c0 100644 --- a/engine/common/bothdefs.h +++ b/engine/common/bothdefs.h @@ -94,6 +94,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define AVAIL_DINPUT #define AVAIL_DDRAW #define AVAIL_DSOUND +// #define AVAIL_XAUDIO2 #define AVAIL_D3D #endif #define AVAIL_XZDEC diff --git a/engine/common/cmd.c b/engine/common/cmd.c index 8dded2c5..26ca148b 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -3090,7 +3090,7 @@ void Cmd_Condump_f(void) { vfsfile_t *f; char *filename; - unsigned char c; + char line[8192]; if (!con_current) { @@ -3120,17 +3120,12 @@ void Cmd_Condump_f(void) { console_t *curcon = &con_main; conline_t *l; - int i; conchar_t *t; for (l = curcon->oldest; l; l = l->newer) { t = (conchar_t*)(l+1); - //FIXME: utf8? - for (i = 0; i < l->length; i++) - { - c = (qbyte)t[i]&0xff; - VFS_WRITE(f, &c, 1); - } + COM_DeFunString(t, t + l->length, line, sizeof(line), true); + VFS_WRITE(f, line, strlen(line)); VFS_WRITE(f, "\n", 1); } } diff --git a/engine/common/com_mesh.c b/engine/common/com_mesh.c index 954ce4a7..d71483c3 100644 --- a/engine/common/com_mesh.c +++ b/engine/common/com_mesh.c @@ -521,7 +521,7 @@ const float *Alias_ConvertBoneData(skeltype_t sourcetype, const float *sourcedat //a->ia->ir if (bonecount > destbonecount || bonecount > MAX_BONES) - Sys_Error("Alias_ConvertBoneData: too many bones %i>%i\n", bonecount, destbonecount); + Sys_Error("Alias_ConvertBoneData: too many bones "fPRIzu">"fPRIzu"\n", bonecount, destbonecount); //r(->a)->ia(->ir) if (desttype == SKEL_INVERSE_RELATIVE && sourcetype == SKEL_RELATIVE) @@ -3023,9 +3023,13 @@ static void *Q1_LoadSkins_GL (model_t *loadmodel, daliasskintype_t *pskintype, u slash--; memcpy(alttexpath, loadmodel->name, slash-loadmodel->name); Q_strncpyz(alttexpath+(slash-loadmodel->name), ":models", sizeof(alttexpath)-(slash-loadmodel->name)); //fuhquake compat + slash++; } else + { + slash = loadmodel->name; strcpy(alttexpath, "models"); //fuhquake compat + } s = pq1inmodel->skinwidth*pq1inmodel->skinheight; for (i = 0; i < pq1inmodel->numskins; i++) @@ -3128,21 +3132,21 @@ static void *Q1_LoadSkins_GL (model_t *loadmodel, daliasskintype_t *pskintype, u frames[0].defaultshader = NULL; Q_snprintfz(frames[0].shadername, sizeof(frames[0].shadername), "%s_%i.lmp", basename, i); - Q_snprintfz(skinname, sizeof(skinname), "%s_%i.lmp", loadmodel->name, i); + Q_snprintfz(skinname, sizeof(skinname), "%s_%i.lmp", slash, i); frames[0].texnums.base = R_LoadReplacementTexture(skinname, alttexpath, texflags, frames[0].texels, outskin->skinwidth, outskin->skinheight, skintranstype); if (r_fb_models.ival) { - Q_snprintfz(skinname, sizeof(skinname), "%s_%i_luma.lmp", loadmodel->name, i); + Q_snprintfz(skinname, sizeof(skinname), "%s_%i_luma.lmp", slash, i); frames[0].texnums.fullbright = R_LoadReplacementTexture(skinname, alttexpath, texflags, frames[0].texels, outskin->skinwidth, outskin->skinheight, TF_TRANS8_FULLBRIGHT); } if (r_loadbumpmapping) { - Q_snprintfz(skinname, sizeof(skinname), "%s_%i_norm.lmp", loadmodel->name, i); + Q_snprintfz(skinname, sizeof(skinname), "%s_%i_norm.lmp", slash, i); frames[0].texnums.bump = R_LoadReplacementTexture(skinname, alttexpath, texflags|IF_TRYBUMP, frames[0].texels, outskin->skinwidth, outskin->skinheight, TF_HEIGHT8PAL); } - Q_snprintfz(skinname, sizeof(skinname), "%s_%i_shirt.lmp", loadmodel->name, i); + Q_snprintfz(skinname, sizeof(skinname), "%s_%i_shirt.lmp", slash, i); frames[0].texnums.upperoverlay = R_LoadReplacementTexture(skinname, alttexpath, texflags, NULL, outskin->skinwidth, outskin->skinheight, TF_INVALID); - Q_snprintfz(skinname, sizeof(skinname), "%s_%i_pants.lmp", loadmodel->name, i); + Q_snprintfz(skinname, sizeof(skinname), "%s_%i_pants.lmp", slash, i); frames[0].texnums.loweroverlay = R_LoadReplacementTexture(skinname, alttexpath, texflags, NULL, outskin->skinwidth, outskin->skinheight, TF_INVALID); switch(skintranstype) @@ -3226,21 +3230,21 @@ static void *Q1_LoadSkins_GL (model_t *loadmodel, daliasskintype_t *pskintype, u //other engines apparently don't flood fill. because flood filling is horrible, we won't either. //Mod_FloodFillSkin(frames[t].texels, outskin->skinwidth, outskin->skinheight); - Q_snprintfz(skinname, sizeof(skinname), "%s_%i_%i.lmp", loadmodel->name, i, t); + Q_snprintfz(skinname, sizeof(skinname), "%s_%i_%i.lmp", slash, i, t); frames[t].texnums.base = R_LoadReplacementTexture(skinname, alttexpath, texflags, frames[t].texels, outskin->skinwidth, outskin->skinheight, skintranstype); if (r_fb_models.ival) { - Q_snprintfz(skinname, sizeof(skinname), "%s_%i_%i_luma.lmp", loadmodel->name, i, t); + Q_snprintfz(skinname, sizeof(skinname), "%s_%i_%i_luma.lmp", slash, i, t); frames[t].texnums.fullbright = R_LoadReplacementTexture(skinname, alttexpath, texflags, frames[t].texels, outskin->skinwidth, outskin->skinheight, TF_TRANS8_FULLBRIGHT); } if (r_loadbumpmapping) { - Q_snprintfz(skinname, sizeof(skinname), "%s_%i_%i_norm.lmp", loadmodel->name, i, t); + Q_snprintfz(skinname, sizeof(skinname), "%s_%i_%i_norm.lmp", slash, i, t); frames[t].texnums.bump = R_LoadReplacementTexture(skinname, alttexpath, texflags|IF_TRYBUMP, frames[t].texels, outskin->skinwidth, outskin->skinheight, TF_HEIGHT8PAL); } - Q_snprintfz(skinname, sizeof(skinname), "%s_%i_%i_shirt.lmp", loadmodel->name, i, t); + Q_snprintfz(skinname, sizeof(skinname), "%s_%i_%i_shirt.lmp", slash, i, t); frames[t].texnums.upperoverlay = R_LoadReplacementTexture(skinname, alttexpath, texflags, NULL, outskin->skinwidth, outskin->skinheight, TF_INVALID); - Q_snprintfz(skinname, sizeof(skinname), "%s_%i_%i_pants.lmp", loadmodel->name, i, t); + Q_snprintfz(skinname, sizeof(skinname), "%s_%i_%i_pants.lmp", slash, i, t); frames[t].texnums.loweroverlay = R_LoadReplacementTexture(skinname, alttexpath, texflags, NULL, outskin->skinwidth, outskin->skinheight, TF_INVALID); Q_snprintfz(frames[t].shadername, sizeof(frames[t].shadername), "%s_%i_%i.lmp", basename, i, t); @@ -6515,7 +6519,7 @@ galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer, size_t fsize) } if (h->filesize != fsize) { - Con_Printf("%s: size (%u != %u)\n", mod->name, h->filesize, fsize); + Con_Printf("%s: size (%u != "fPRIzu")\n", mod->name, h->filesize, fsize); return NULL; } diff --git a/engine/common/common.c b/engine/common/common.c index dc8cf013..21d51be6 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -5058,7 +5058,7 @@ void COM_WorkerPartialSync(void *priorityctx, int *address, int value) Sys_UnlockConditional(com_workercondition[thread]); } if (!found) - Con_DPrintf("Might be in for a long wait for %s\n", priorityctx); + Con_DPrintf("Might be in for a long wait for %s\n", (char*)priorityctx); } Sys_LockConditional(com_workercondition[0]); diff --git a/engine/common/common.h b/engine/common/common.h index f778a385..791e8323 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -30,6 +30,48 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #endif #endif + +#if __STDC_VERSION__ >= 199901L + //C99 has a stdint header which hopefully contains an intptr_t + //its optional... but if its not in there then its unlikely you'll actually be able to get the engine to a stage where it *can* load anything + #include + #define qintptr_t intptr_t + #define quintptr_t uintptr_t +#else + #if defined(_WIN64) + #define qintptr_t __int64 + #define FTE_WORDSIZE 64 + #define quintptr_t unsigned qintptr_t + #elif defined(_WIN32) + #ifndef _MSC_VER + #define __w64 + #endif + typedef __int32 __w64 qintptr_t; //add __w64 if you need msvc to shut up about unsafe type conversions + typedef unsigned __int32 __w64 quintptr_t; +// #define qintptr_t __int32 +// #define quintptr_t unsigned qintptr_t + #define FTE_WORDSIZE 32 + #else + #if __WORDSIZE == 64 + #define qintptr_t long long + #define FTE_WORDSIZE 64 + #else + #define qintptr_t long + #define FTE_WORDSIZE 32 + #endif + #define quintptr_t unsigned qintptr_t + #endif +#endif + +#ifndef FTE_WORDSIZE +#ifdef __WORDSIZE +#define FTE_WORDSIZE __WORDSIZE +#else +#define FTE_WORDSIZE 32 +#endif +#endif + + typedef unsigned char qbyte; // KJB Undefined true and false defined in SciTech's DEBUG.H header diff --git a/engine/common/fs.c b/engine/common/fs.c index ffef6b62..da30a525 100644 --- a/engine/common/fs.c +++ b/engine/common/fs.c @@ -2563,7 +2563,7 @@ void COM_Gamedir (const char *dir, const struct gamepacks *packagespaths) FS_ChangeGame(man, cfg_reload_on_gamedir.ival, false); } -#define QCFG "set allow_download_refpackages 0\nmap_autoopenportals 1\n" +#define QCFG "set allow_download_refpackages 0\nset sv_bigcoords \"\"\nmap_autoopenportals 1\n" /*stuff that makes dp-only mods work a bit better*/ #define DPCOMPAT QCFG "set _cl_playermodel \"\"\n set dpcompat_set 1\nset dpcompat_corruptglobals 1\nset vid_pixelheight 1\n" /*nexuiz/xonotic has a few quirks/annoyances...*/ @@ -3352,7 +3352,7 @@ qboolean Sys_DoDirectoryPrompt(char *basepath, size_t basepathsize, const char * narrowen(basepath, basepathsize, resultpath); if (savedname) { - if (MessageBoxU(mainwindow, va("Would you like to save the location of %s as:\n%s", poshname, resultpath), "Save Instaltion path", MB_YESNO|MB_DEFBUTTON2) == IDYES) + if (MessageBoxU(mainwindow, va("Would you like to save the location of %s as:\n%s", poshname, basepath), "Save Instaltion path", MB_YESNO|MB_DEFBUTTON2) == IDYES) MyRegSetValue(HKEY_CURRENT_USER, "SOFTWARE\\" FULLENGINENAME "\\GamePaths", savedname, REG_SZ, basepath, strlen(basepath)); } return true; diff --git a/engine/common/gl_q2bsp.c b/engine/common/gl_q2bsp.c index 209a1007..7ef9a263 100644 --- a/engine/common/gl_q2bsp.c +++ b/engine/common/gl_q2bsp.c @@ -4274,6 +4274,7 @@ static cmodel_t *CM_LoadMap (model_t *mod, qbyte *filein, size_t filelen, qboole Q_snprintfz (name, sizeof(name), "*%i:%s", i, wmod->name); mod = Mod_FindName (name); *mod = *wmod; + mod->submodelof = wmod; Q_strncpyz(mod->name, name, sizeof(mod->name)); memset(&mod->memgroup, 0, sizeof(mod->memgroup)); diff --git a/engine/common/net.h b/engine/common/net.h index 98695782..96ad9df4 100644 --- a/engine/common/net.h +++ b/engine/common/net.h @@ -145,10 +145,11 @@ typedef struct int isnqprotocol; qboolean nqreliable_allowed; //says the peer has acked the last reliable (or timed out and needs resending). float nqreliable_resendtime;//force nqreliable_allowed, thereby forcing a resend of anything n - qboolean nqunreliableonly; //prohibits new reliables, but allows resends. + qbyte nqunreliableonly; //nq can't cope with certain reliables some times. if 2, we have a reliable that result in a block (that should be sent). if 1, we are blocking. if 0, we can send reliables freely. #endif struct netprim_s netprim; int fragmentsize; + int dupe; float last_received; // for timeouts diff --git a/engine/common/net_chan.c b/engine/common/net_chan.c index 3cea9cbd..b0f1878c 100644 --- a/engine/common/net_chan.c +++ b/engine/common/net_chan.c @@ -582,8 +582,10 @@ int Netchan_Transmit (netchan_t *chan, int length, qbyte *data, int rate) if (chan->nqreliable_allowed) { //consume the new reliable when we can. - if (!chan->reliable_length && chan->message.cursize && !chan->nqunreliableonly) + if (!chan->reliable_length && chan->message.cursize && chan->nqunreliableonly != 1) { + if (chan->nqunreliableonly == 2) + chan->nqunreliableonly = 1; memcpy (chan->reliable_buf, chan->message_buf, chan->message.cursize); chan->reliable_length = chan->message.cursize; chan->reliable_start = 0; @@ -741,7 +743,11 @@ int Netchan_Transmit (netchan_t *chan, int length, qbyte *data, int rate) int hsz = 10 + ((chan->sock == NS_CLIENT)?2:0); /*header size, if fragmentation is in use*/ if ((!chan->fragmentsize) || send.cursize-hsz < ((chan->fragmentsize - hsz)&~7)) - NET_SendPacket (chan->sock, send.cursize, send.data, &chan->remote_address); + { + for (i = -1; i < chan->dupe; i++) + NET_SendPacket (chan->sock, send.cursize, send.data, &chan->remote_address); + send.cursize += send.cursize * chan->dupe; + } else { int offset = chan->fragmentsize - hsz, no; diff --git a/engine/common/net_ssl_winsspi.c b/engine/common/net_ssl_winsspi.c index 8c91397f..d8a0e7da 100644 --- a/engine/common/net_ssl_winsspi.c +++ b/engine/common/net_ssl_winsspi.c @@ -226,7 +226,7 @@ static void SSPI_Decode(sslfile_t *f) switch(ss) { case SEC_E_INVALID_HANDLE: SSPI_Error(f, "DecryptMessage failed: SEC_E_INVALID_HANDLE\n"); break; - default: SSPI_Error(f, va("DecryptMessage failed: %0#x\n", ss)); break; + default: SSPI_Error(f, va("DecryptMessage failed: %0#lx\n", ss)); break; } return; } @@ -392,37 +392,37 @@ static DWORD VerifyKnownCertificates(DWORD status, wchar_t *domain, qbyte *data, static DWORD VerifyServerCertificate(PCCERT_CONTEXT pServerCert, PWSTR pwszServerName, DWORD dwCertFlags) { - HTTPSPolicyCallbackData polHttps; - CERT_CHAIN_POLICY_PARA PolicyPara; - CERT_CHAIN_POLICY_STATUS PolicyStatus; - CERT_CHAIN_PARA ChainPara; - PCCERT_CHAIN_CONTEXT pChainContext; - DWORD Status; - LPSTR rgszUsages[] = + HTTPSPolicyCallbackData polHttps; + CERT_CHAIN_POLICY_PARA PolicyPara; + CERT_CHAIN_POLICY_STATUS PolicyStatus; + CERT_CHAIN_PARA ChainPara; + PCCERT_CHAIN_CONTEXT pChainContext; + DWORD Status; + LPSTR rgszUsages[] = { szOID_PKIX_KP_SERVER_AUTH, szOID_SERVER_GATED_CRYPTO, szOID_SGC_NETSCAPE }; - DWORD cUsages = sizeof(rgszUsages) / sizeof(LPSTR); + DWORD cUsages = sizeof(rgszUsages) / sizeof(LPSTR); - if(pServerCert == NULL) + if(pServerCert == NULL) return SEC_E_WRONG_PRINCIPAL; - if(!*pwszServerName) + if(!*pwszServerName) return SEC_E_WRONG_PRINCIPAL; - // Build certificate chain. - memset(&ChainPara, 0, sizeof(ChainPara)); - ChainPara.cbSize = sizeof(ChainPara); - ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR; - ChainPara.RequestedUsage.Usage.cUsageIdentifier = cUsages; - ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = rgszUsages; + // Build certificate chain. + memset(&ChainPara, 0, sizeof(ChainPara)); + ChainPara.cbSize = sizeof(ChainPara); + ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR; + ChainPara.RequestedUsage.Usage.cUsageIdentifier = cUsages; + ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = rgszUsages; - if (!crypt.pCertGetCertificateChain(NULL, pServerCert, NULL, pServerCert->hCertStore, &ChainPara, 0, NULL, &pChainContext)) - { - Status = GetLastError(); - Sys_Printf("Error 0x%x returned by CertGetCertificateChain!\n", (unsigned int)Status); - } + if (!crypt.pCertGetCertificateChain(NULL, pServerCert, NULL, pServerCert->hCertStore, &ChainPara, 0, NULL, &pChainContext)) + { + Status = GetLastError(); + Sys_Printf("Error %#lx returned by CertGetCertificateChain!\n", Status); + } else { // Validate certificate chain. @@ -442,7 +442,7 @@ static DWORD VerifyServerCertificate(PCCERT_CONTEXT pServerCert, PWSTR pwszServe if (!crypt.pCertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, pChainContext, &PolicyPara, &PolicyStatus)) { Status = GetLastError(); - Sys_Printf("Error 0x%x returned by CertVerifyCertificateChainPolicy!\n", (unsigned int)Status); + Sys_Printf("Error %#lx returned by CertVerifyCertificateChainPolicy!\n", Status); } else { @@ -700,7 +700,7 @@ static void SSPI_Handshake (sslfile_t *f) case SEC_E_INVALID_HANDLE: SSPI_Error(f, "InitializeSecurityContext failed: SEC_E_INVALID_HANDLE\n"); break; case SEC_E_ILLEGAL_MESSAGE: SSPI_Error(f, "InitializeSecurityContext failed: SEC_E_ILLEGAL_MESSAGE\n"); break; case SEC_E_INVALID_TOKEN: SSPI_Error(f, "InitializeSecurityContext failed: SEC_E_INVALID_TOKEN\n"); break; - default: SSPI_Error(f, va("InitializeSecurityContext failed: %#x\n", ss)); break; + default: SSPI_Error(f, va("InitializeSecurityContext failed: %#lx\n", ss)); break; } return; } diff --git a/engine/common/net_wins.c b/engine/common/net_wins.c index 14f0b493..93cf8fd6 100644 --- a/engine/common/net_wins.c +++ b/engine/common/net_wins.c @@ -3366,12 +3366,12 @@ handshakeerror: (unsigned long long)((unsigned char*)st->inbuffer)[payoffs+7]<< 0ull; if (ullpaylen < 0x10000) { - Con_Printf ("%s: payload size (%llu) encoded badly\n", NET_AdrToString (adr, sizeof(adr), &st->remoteaddr), ullpaylen); + Con_Printf ("%s: payload size ("fPRIllu") encoded badly\n", NET_AdrToString (adr, sizeof(adr), &st->remoteaddr), ullpaylen); goto closesvstream; } if (ullpaylen > 0x10000) { - Con_Printf ("%s: payload size (%llu) is abusive\n", NET_AdrToString (adr, sizeof(adr), &st->remoteaddr), st->inbuffer, ullpaylen); + Con_Printf ("%s: payload size ("fPRIllu") is abusive\n", NET_AdrToString (adr, sizeof(adr), &st->remoteaddr), ullpaylen); goto closesvstream; } paylen = ullpaylen; diff --git a/engine/common/particles.h b/engine/common/particles.h index 246af410..c4705724 100644 --- a/engine/common/particles.h +++ b/engine/common/particles.h @@ -95,8 +95,7 @@ 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 - -#define P_FindParticleType pe->FindParticleType +int P_FindParticleType(const char *efname); #define P_RunParticleEffectTypeString pe->RunParticleEffectTypeString #define P_ParticleTrail pe->ParticleTrail diff --git a/engine/common/plugin.c b/engine/common/plugin.c index 8909514a..c403bffd 100644 --- a/engine/common/plugin.c +++ b/engine/common/plugin.c @@ -688,7 +688,7 @@ static qintptr_t VARGS Plug_Cvar_GetString(void *offset, quintptr_t mask, const #ifdef CLIENTONLY Q_strncpyz(ret, "", retsize); #else - Q_strncpyz(ret, sv.name, retsize); + Q_strncpyz(ret, svs.name, retsize); #endif } else @@ -1635,9 +1635,13 @@ void Plug_SBar(playerview_t *pv) plugin_t *oc=currentplug; int ret; + int cleared = false; if (!Sbar_ShouldDraw()) + { + SCR_TileClear (0); return; + } ret = 0; if (!plug_sbar.ival) @@ -1650,6 +1654,11 @@ void Plug_SBar(playerview_t *pv) { //if you don't use splitscreen, use a full videosize rect. R2D_ImageColours(1, 1, 1, 1); // ensure menu colors are reset + if (!cleared) + { + cleared = true; + SCR_TileClear (0); + } ret |= VM_Call(currentplug->vm, currentplug->sbarlevel[0], pv-cl.playerview, (int)r_refdef.vrect.x, (int)r_refdef.vrect.y, (int)r_refdef.vrect.width, (int)r_refdef.vrect.height, sb_showscores+sb_showteamscores*2); break; } @@ -1657,6 +1666,8 @@ void Plug_SBar(playerview_t *pv) } if (!(ret & 1)) { + if (!cleared) + SCR_TileClear (sb_lines); Sbar_Draw(pv); } diff --git a/engine/common/pr_bgcmd.c b/engine/common/pr_bgcmd.c index 21fe6fec..308ea830 100644 --- a/engine/common/pr_bgcmd.c +++ b/engine/common/pr_bgcmd.c @@ -239,6 +239,7 @@ qboolean QCExternalDebuggerCommand(char *text) Cbuf_AddText("restart\n", RESTRICT_LOCAL); #endif // Host_EndGame("Reloading QC"); + debuggerresume = DEBUG_TRACE_ABORT; } else if (!strncmp(text, "qcbreakpoint ", 13)) { @@ -280,7 +281,7 @@ int QDECL QCEditor (pubprogfuncs_t *prinst, const char *filename, int *line, int return DEBUG_TRACE_ABORT; if (!*filename || !line || !*line) //don't try editing an empty line, it won't work { - Con_Printf("Unable to debug, pelase disable optimisations\n"); + Con_Printf("Unable to debug, please disable optimisations\n"); return DEBUG_TRACE_OFF; } Sys_SendKeyEvents(); @@ -321,6 +322,7 @@ int QDECL QCEditor (pubprogfuncs_t *prinst, const char *filename, int *line, int VID_SwapBuffers(); } } + Con_Footerf(NULL, false, ""); *line = debuggerresumeline; debuggerinstance = NULL; debuggerfile = NULL; @@ -1450,11 +1452,13 @@ void QCBUILTIN PF_memgetval (pubprogfuncs_t *prinst, struct globalvars_s *pr_glo if (ofs != (float)(int)ofs) PR_BIError(prinst, "PF_memgetval: non-integer offset\n"); dst += ofs; - if (dst & 3 || dst < 0 || dst+size >= prinst->stringtablesize) + if (dst < 0 || dst+size >= prinst->stringtablesize) { PR_BIError(prinst, "PF_memgetval: invalid dest\n"); return; } + if (dst & 3) + PF_Warningf(prinst, "PF_memgetval: misaligned pointer (%#x)\n", dst); G_INT(OFS_RETURN) = *(int*)(prinst->stringtable + dst); } @@ -1468,11 +1472,13 @@ void QCBUILTIN PF_memsetval (pubprogfuncs_t *prinst, struct globalvars_s *pr_glo if (ofs != (float)(int)ofs) PR_BIError(prinst, "PF_memsetval: non-integer offset\n"); dst += ofs; - if (dst & 3 || dst < 0 || dst+size >= prinst->stringtablesize) + if (dst < 0 || dst+size >= prinst->stringtablesize) { PR_BIError(prinst, "PF_memsetval: invalid dest\n"); return; } + if (dst & 3) + PF_Warningf(prinst, "PF_memgetval: misaligned pointer (%#x)\n", dst); *(int*)(prinst->stringtable + dst) = val; } @@ -1488,6 +1494,12 @@ void QCBUILTIN PF_memptradd (pubprogfuncs_t *prinst, struct globalvars_s *pr_glo G_INT(OFS_RETURN) = dst + ofs; } + +void QCBUILTIN PF_memstrsize(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + G_FLOAT(OFS_RETURN) = strlen(PR_GetStringOfs(prinst, OFS_PARM0)); +} + //memory stuff //////////////////////////////////////////////////// //hash table stuff @@ -1811,10 +1823,7 @@ void QCBUILTIN PF_fopen (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals { vfsfile_t *f = FS_OpenVFS(pf_fopen_files[i].name, "rb", FS_GAME); if (!f && fallbackread) - { - Q_strncpyz(pf_fopen_files[i].name, fallbackread, sizeof(pf_fopen_files[i].name)); - f = FS_OpenVFS(pf_fopen_files[i].name, "rb", FS_GAME); - } + f = FS_OpenVFS(fallbackread, "rb", FS_GAME); if (f) { pf_fopen_files[i].bufferlen = pf_fopen_files[i].len = VFS_GETLEN(f); @@ -2753,6 +2762,27 @@ void QCBUILTIN PF_strpad (pubprogfuncs_t *prinst, struct globalvars_s *pr_global RETURN_TSTRING(destbuf); } +void QCBUILTIN PF_strtrim (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + const char *str = PR_GetStringOfs(prinst, OFS_PARM0); + const char *end; + char *news; + + //figure out the new start + while (*str == ' ' || *str == '\t' || *str == '\n' || *str == '\r') + str++; + + //figure out the new end. + end = str + strlen(str); + while(end > str && (end[-1] == ' ' || end[-1] == '\t' || end[-1] == '\n' || end[-1] == '\r')) + end--; + + //copy that substring into a tempstring. + ((int *)pr_globals)[OFS_RETURN] = prinst->AllocTempString(prinst, &news, end - str + 1); + memcpy(news, str, end-str); + news[end-str] = 0; +} + //part of PF_strconv static int chrconv_number(int i, int base, int conv) { @@ -2987,6 +3017,15 @@ void QCBUILTIN PF_ftos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) RETURN_TSTRING(pr_string_temp); } +void QCBUILTIN PF_ftoi (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + G_INT(OFS_RETURN) = G_FLOAT(OFS_PARM0); +} +void QCBUILTIN PF_itof (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + G_FLOAT(OFS_RETURN) = G_INT(OFS_PARM0); +} + //tstring(integer input) itos void QCBUILTIN PF_itos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { @@ -4863,7 +4902,7 @@ void QCBUILTIN PF_eprint (pubprogfuncs_t *prinst, struct globalvars_s *pr_global { int max = 1024*1024; int size = 0; - char *buffer = BZ_Malloc(size); + char *buffer = BZ_Malloc(max); char *buf; buf = prinst->saveent(prinst, buffer, &size, max, (struct edict_s*)G_WEDICT(prinst, OFS_PARM0)); Con_Printf("Entity %i:\n%s\n", G_EDICTNUM(prinst, OFS_PARM0), buf); @@ -5322,7 +5361,7 @@ void QCBUILTIN PF_findentityfield (pubprogfuncs_t *prinst, struct globalvars_s * G_FLOAT(OFS_RETURN) = 0; for (fidx = 0; fidx < count; fidx++) { - if (!strcmp(fdef->name, fieldname)) + if (!strcmp(fdef[fidx].name, fieldname)) { G_FLOAT(OFS_RETURN) = fidx; break; diff --git a/engine/common/pr_common.h b/engine/common/pr_common.h index 9dd74374..15f08bbe 100644 --- a/engine/common/pr_common.h +++ b/engine/common/pr_common.h @@ -185,6 +185,8 @@ void QCBUILTIN PF_itos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) void QCBUILTIN PF_stoi (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_stoh (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_htos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); +void QCBUILTIN PF_ftoi (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); +void QCBUILTIN PF_itof (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void PR_fclose_progs (pubprogfuncs_t *prinst); char *PF_VarString (pubprogfuncs_t *prinst, int first, struct globalvars_s *pr_globals); void PR_ProgsAdded(pubprogfuncs_t *prinst, int newprogs, const char *modulename); @@ -322,6 +324,7 @@ void QCBUILTIN PF_infoget (pubprogfuncs_t *prinst, struct globalvars_s *pr_globa void QCBUILTIN PF_strncmp (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_strncasecmp (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_strpad (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); +void QCBUILTIN PF_strtrim (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_digest_hex (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); @@ -445,6 +448,7 @@ void QCBUILTIN PF_memfill8 (pubprogfuncs_t *prinst, struct globalvars_s *pr_glob void QCBUILTIN PF_memgetval (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_memsetval (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_memptradd (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); +void QCBUILTIN PF_memstrsize(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_soundlength (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_calltimeofday (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); diff --git a/engine/common/protocol.h b/engine/common/protocol.h index 464e6edc..38797b3c 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -811,6 +811,7 @@ enum clcq2_ops_e #define DPSND_LOOPING (1<<2) // a long, supposedly #define DPSND_LARGEENTITY (1<<3) #define DPSND_LARGESOUND (1<<4) +#define FTESND_TIMEOFS (1<<6) //signed short, in milliseconds. #define FTESND_PITCHADJ (1<<7) //a byte (speed percent (0=100%)) #define DEFAULT_SOUND_PACKET_VOLUME 255 diff --git a/engine/common/vm.h b/engine/common/vm.h index cac2787e..207976e1 100644 --- a/engine/common/vm.h +++ b/engine/common/vm.h @@ -7,46 +7,6 @@ #define EXPORT_FN #endif -#if __STDC_VERSION__ >= 199901L - //C99 has a stdint header which hopefully contains an intptr_t - //its optional... but if its not in there then its unlikely you'll actually be able to get the engine to a stage where it *can* load anything - #include - #define qintptr_t intptr_t - #define quintptr_t uintptr_t -#else - #if defined(_WIN64) - #define qintptr_t __int64 - #define FTE_WORDSIZE 64 - #define quintptr_t unsigned qintptr_t - #elif defined(_WIN32) - #ifndef _MSC_VER - #define __w64 - #endif - typedef __int32 __w64 qintptr_t; //add __w64 if you need msvc to shut up about unsafe type conversions - typedef unsigned __int32 __w64 quintptr_t; -// #define qintptr_t __int32 -// #define quintptr_t unsigned qintptr_t - #define FTE_WORDSIZE 32 - #else - #if __WORDSIZE == 64 - #define qintptr_t long long - #define FTE_WORDSIZE 64 - #else - #define qintptr_t long - #define FTE_WORDSIZE 32 - #endif - #define quintptr_t unsigned qintptr_t - #endif -#endif - -#ifndef FTE_WORDSIZE -#ifdef __WORDSIZE -#define FTE_WORDSIZE __WORDSIZE -#else -#define FTE_WORDSIZE 32 -#endif -#endif - typedef qintptr_t (EXPORT_FN *sys_calldll_t) (qintptr_t arg, ...); typedef int (*sys_callqvm_t) (void *offset, quintptr_t mask, int fn, const int *arg); diff --git a/engine/common/world.h b/engine/common/world.h index 69792e85..fc574f3a 100644 --- a/engine/common/world.h +++ b/engine/common/world.h @@ -171,7 +171,7 @@ struct world_s { void (*Event_Touch)(struct world_s *w, wedict_t *s, wedict_t *o); void (*Event_Think)(struct world_s *w, wedict_t *s); - void (*Event_Sound) (float *origin, wedict_t *entity, int channel, const char *sample, int volume, float attenuation, int pitchadj); + void (*Event_Sound) (float *origin, wedict_t *entity, int channel, const char *sample, int volume, float attenuation, int pitchadj, float timeoffset); qboolean (*Event_ContentsTransition) (struct world_s *w, wedict_t *ent, int oldwatertype, int newwatertype); model_t *(*Get_CModel)(struct world_s *w, int modelindex); void (*Get_FrameState)(struct world_s *w, wedict_t *s, framestate_t *fstate); diff --git a/engine/common/zone.c b/engine/common/zone.c index 0a6bd80c..cefac7f8 100644 --- a/engine/common/zone.c +++ b/engine/common/zone.c @@ -628,6 +628,9 @@ void Cache_Flush(void) S_Purge(false); #endif Mod_Purge(MP_FLUSH); +#ifndef SERVERONLY + Image_Purge(); +#endif } static void Hunk_Print_f (void) diff --git a/engine/d3d/d3d_backend.c b/engine/d3d/d3d_backend.c index 4e3ef2a0..d8d90613 100644 --- a/engine/d3d/d3d_backend.c +++ b/engine/d3d/d3d_backend.c @@ -43,7 +43,11 @@ Things to improve: extern LPDIRECT3DDEVICE9 pD3DDev9; //#define d3dcheck(foo) foo +#ifdef _DEBUG #define d3dcheck(foo) do{HRESULT err = foo; if (FAILED(err)) Sys_Error("D3D reported error on backend line %i - error 0x%x\n", __LINE__, (unsigned int)err);} while(0) +#else +#define d3dcheck(foo) foo +#endif #define MAX_TMUS 16 @@ -506,6 +510,9 @@ void D3D9BE_Reset(qboolean before) D3DVERTEXELEMENT9 decl[13], declend=D3DDECL_END(); int elements; + if (shaderstate.dynidx_buff) + return; + for (i = 0; i < D3D_VDEC_MAX; i++) { elements = 0; @@ -595,7 +602,7 @@ void D3D9BE_Reset(qboolean before) } IDirect3DDevice9_CreateVertexBuffer(pD3DDev9, shaderstate.dynxyz_size, D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &shaderstate.dynxyz_buff, NULL); - for (tmu = 0; tmu < D3D_VDEC_ST0; tmu++) + for (tmu = 0; tmu < MAX_TC_TMUS; tmu++) IDirect3DDevice9_CreateVertexBuffer(pD3DDev9, shaderstate.dynst_size, D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &shaderstate.dynst_buff[tmu], NULL); IDirect3DDevice9_CreateVertexBuffer(pD3DDev9, shaderstate.dynnorm_size, D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &shaderstate.dynnorm_buff, NULL); IDirect3DDevice9_CreateVertexBuffer(pD3DDev9, shaderstate.dyncol_size, D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &shaderstate.dyncol_buff, NULL); @@ -610,6 +617,9 @@ void D3D9BE_Reset(qboolean before) /*force all state to change, thus setting a known state*/ shaderstate.shaderbits = ~0; BE_ApplyShaderBits(0); + + + Surf_BuildLightmaps(); } } @@ -1676,41 +1686,15 @@ static qboolean BE_DrawMeshChain_SetupPass(shaderpass_t *pass, unsigned int vert return true; } -static void BE_SubmitMeshChain(int idxfirst) +static void BE_SubmitMeshChain(unsigned int vertbase, unsigned int firstvert, unsigned int vertcount, unsigned int idxfirst, int unsigned idxcount) { - int startv, starti, endv, endi; - int m; - mesh_t *mesh; + if (shaderstate.flags & BEF_LINES) + IDirect3DDevice9_DrawIndexedPrimitive(pD3DDev9, D3DPT_LINELIST, vertbase, firstvert, vertcount, idxfirst, idxcount/2); + else + IDirect3DDevice9_DrawIndexedPrimitive(pD3DDev9, D3DPT_TRIANGLELIST, vertbase, firstvert, vertcount, idxfirst, idxcount/3); + RQuantAdd(RQUANT_DRAWS, 1); -// if (shaderstate.batchvbo) -// IDirect3DDevice9_DrawIndexedPrimitive(pD3DDev9, D3DPT_TRIANGLELIST, 0, 0, shaderstate.batchvbo->vertcount, idxfirst, shaderstate.batchvbo->indexcount/3); - - for (m = 0, mesh = shaderstate.meshlist[0]; m < shaderstate.nummeshes; ) - { - startv = mesh->vbofirstvert; - starti = mesh->vbofirstelement; - - endv = startv+mesh->numvertexes; - endi = starti+mesh->numindexes; - - //find consecutive surfaces - for (++m; m < shaderstate.nummeshes; m++) - { - mesh = shaderstate.meshlist[m]; - if (endi == mesh->vbofirstelement) - { - endv = mesh->vbofirstvert+mesh->numvertexes; - endi = mesh->vbofirstelement+mesh->numindexes; - } - else - { - break; - } - } - - IDirect3DDevice9_DrawIndexedPrimitive(pD3DDev9, D3DPT_TRIANGLELIST, 0, startv, endv - startv, idxfirst + starti, (endi-starti)/3); - RQuantAdd(RQUANT_DRAWS, 1); - } + RQuantAdd(RQUANT_PRIMITIVEINDICIES, idxcount); } static void BE_ApplyUniforms(program_t *prog, int permu) @@ -1824,9 +1808,9 @@ static void BE_ApplyUniforms(program_t *prog, int permu) } } -static void BE_RenderMeshProgram(shader_t *s, unsigned int vertcount, unsigned int idxfirst, unsigned int idxcount) +static void BE_RenderMeshProgram(shader_t *s, unsigned int vertbase, unsigned int vertfirst, unsigned int vertcount, unsigned int idxfirst, unsigned int idxcount) { - int vdec = D3D_VDEC_ST0|D3D_VDEC_NORM; + int vdec = D3D_VDEC_ST0|D3D_VDEC_ST1|D3D_VDEC_NORM; int passno; int perm = 0; @@ -2014,7 +1998,7 @@ static void BE_RenderMeshProgram(shader_t *s, unsigned int vertcount, unsigned i } // IDirect3DDevice9_SetVertexShaderConstantF(pD3DDev9, - BE_SubmitMeshChain(idxfirst); + BE_SubmitMeshChain(vertbase, vertfirst, vertcount, idxfirst, idxcount); IDirect3DDevice9_SetVertexShader(pD3DDev9, NULL); IDirect3DDevice9_SetPixelShader(pD3DDev9, NULL); @@ -2049,7 +2033,7 @@ void D3D9BE_Cull(unsigned int cullflags) static void BE_DrawMeshChain_Internal(void) { - unsigned int vertcount, idxcount, idxfirst; + unsigned int vertbase, vertfirst, vertcount, idxcount, idxfirst; mesh_t *m; void *map; int i; @@ -2075,13 +2059,21 @@ static void BE_DrawMeshChain_Internal(void) // IDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_SLOPESCALEDEPTHBIAS, *(DWORD*)&shaderstate.depthfactor); // } + //if anything is dynamic ALL must be dynamic + //might want to flag this for multi-mesh batches on pre-t&l cards too, so that there's no gaps. + if (shaderstate.curshader->numdeforms) + shaderstate.batchvbo = NULL; + if (shaderstate.batchvbo) { + vertfirst = 0; vertcount = shaderstate.batchvbo->vertcount; + idxfirst = 0; idxcount = shaderstate.batchvbo->indexcount; } else { + vertfirst = 0; for (mno = 0, vertcount = 0, idxcount = 0; mno < shaderstate.nummeshes; mno++) { m = shaderstate.meshlist[mno]; @@ -2091,12 +2083,14 @@ static void BE_DrawMeshChain_Internal(void) } /*vertex buffers are common to all passes*/ - if (shaderstate.batchvbo && !shaderstate.curshader->numdeforms) + if (shaderstate.batchvbo) { d3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_VERT, shaderstate.batchvbo->coord.d3d.buff, shaderstate.batchvbo->coord.d3d.offs, sizeof(vbovdata_t))); + vertfirst = 0; } else { + vertfirst = 0; allocvertexbuffer(shaderstate.dynxyz_buff, shaderstate.dynxyz_size, &shaderstate.dynxyz_offs, &map, vertcount*sizeof(vecV_t)); for (mno = 0, vertcount = 0; mno < shaderstate.nummeshes; mno++) { @@ -2113,11 +2107,44 @@ static void BE_DrawMeshChain_Internal(void) d3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_VERT, shaderstate.dynxyz_buff, shaderstate.dynxyz_offs - vertcount*sizeof(vecV_t), sizeof(vecV_t))); } - /*so are index buffers*/ + /*index buffers are also common (note that we may still need to stream these when dealing with bsp geometry, to cope with gaps. this is faster than using multiple draw calls.)*/ if (shaderstate.batchvbo) { - d3dcheck(IDirect3DDevice9_SetIndices(pD3DDev9, shaderstate.batchvbo->indicies.d3d.buff)); - idxfirst = 0; + if (shaderstate.nummeshes != 1) + { //in this case, the vertex data is static, but the index data can have gaps. + //we're streaming index buffer data only so that we can avoid repeated draw calls. if this stuff was properly built in the first place we wouldn't need to do this. :s + idxcount = 0; + for (mno = 0; mno < shaderstate.nummeshes; mno++) + { + m = shaderstate.meshlist[mno]; + idxcount += m->numindexes; + } + idxfirst = allocindexbuffer(&map, idxcount); + for (mno = 0; mno < shaderstate.nummeshes; mno++) + { + m = shaderstate.meshlist[mno]; + for (i = 0; i < m->numindexes; i++) + ((index_t*)map)[i] = m->vbofirstvert + m->indexes[i]; + map = (char*)map + m->numindexes*sizeof(index_t); + } + d3dcheck(IDirect3DIndexBuffer9_Unlock(shaderstate.dynidx_buff)); + d3dcheck(IDirect3DDevice9_SetIndices(pD3DDev9, shaderstate.dynidx_buff)); + + //we could constrain vertfirst+vertcount, but I suspect those only matter on pre t&l cards, of which there are very few. + + vertbase = 0; + } + else + { + m = shaderstate.meshlist[0]; + vertbase = 0;//-m->vbofirstvert; + idxfirst = m->vbofirstelement; + idxcount = m->numindexes; + vertfirst = m->vbofirstvert; + vertcount = m->numvertexes; + + d3dcheck(IDirect3DDevice9_SetIndices(pD3DDev9, shaderstate.batchvbo->indicies.d3d.buff)); + } } else { @@ -2132,13 +2159,43 @@ static void BE_DrawMeshChain_Internal(void) } d3dcheck(IDirect3DIndexBuffer9_Unlock(shaderstate.dynidx_buff)); d3dcheck(IDirect3DDevice9_SetIndices(pD3DDev9, shaderstate.dynidx_buff)); + vertbase = 0; } switch (shaderstate.mode) { case BEM_LIGHT: if (shaderstate.shader_rtlight->prog) - BE_RenderMeshProgram(shaderstate.shader_rtlight, vertcount, idxfirst, idxcount); + BE_RenderMeshProgram(shaderstate.shader_rtlight, vertbase, vertfirst, vertcount, idxfirst, idxcount); + break; + case BEM_DEPTHDARK: + shaderstate.lastpasscount = 0; + i = 0; + if (i != shaderstate.curvertdecl) + { + shaderstate.curvertdecl = i; + d3dcheck(IDirect3DDevice9_SetVertexDeclaration(pD3DDev9, vertexdecls[shaderstate.curvertdecl])); + } + /*deactivate any extras*/ + for (passno = 1; passno < shaderstate.lastpasscount; ) + { + d3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_TC0+passno, NULL, 0, 0)); + BindTexture(passno, NULL); + d3dcheck(IDirect3DDevice9_SetTextureStageState(pD3DDev9, passno, D3DTSS_COLOROP, D3DTOP_DISABLE)); + d3dcheck(IDirect3DDevice9_SetTextureStageState(pD3DDev9, passno, D3DTSS_ALPHAOP, D3DTOP_DISABLE)); + passno++; + } + BindTexture(passno, NULL); + d3dcheck(IDirect3DDevice9_SetTextureStageState(pD3DDev9, 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1)); + d3dcheck(IDirect3DDevice9_SetTextureStageState(pD3DDev9, 0, D3DTSS_COLORARG1, D3DTA_CONSTANT)); + d3dcheck(IDirect3DDevice9_SetTextureStageState(pD3DDev9, 0, D3DTSS_CONSTANT, D3DCOLOR_RGBA(0,0,0,255))); + d3dcheck(IDirect3DDevice9_SetTextureStageState(pD3DDev9, 0, D3DTSS_ALPHAOP, D3DTOP_DISABLE)); + shaderstate.lastpasscount = 1; + BE_ApplyShaderBits(SBITS_MISC_DEPTHWRITE); + + BE_SubmitMeshChain(vertbase, vertfirst, vertcount, idxfirst, idxcount); + break; + case BEM_STENCIL: break; case BEM_DEPTHONLY: shaderstate.lastpasscount = 0; @@ -2159,14 +2216,15 @@ static void BE_DrawMeshChain_Internal(void) passno++; } shaderstate.lastpasscount = 0; - BE_SubmitMeshChain(idxfirst); + BE_ApplyShaderBits(SBITS_MISC_DEPTHWRITE); + BE_SubmitMeshChain(vertbase, vertfirst, vertcount, idxfirst, idxcount); IDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_RED|D3DCOLORWRITEENABLE_GREEN|D3DCOLORWRITEENABLE_BLUE|D3DCOLORWRITEENABLE_ALPHA); break; default: case BEM_STANDARD: if (shaderstate.curshader->prog) { - BE_RenderMeshProgram(shaderstate.curshader, vertcount, idxfirst, idxcount); + BE_RenderMeshProgram(shaderstate.curshader, vertbase, vertfirst, vertcount, idxfirst, idxcount); } else { @@ -2180,7 +2238,7 @@ static void BE_DrawMeshChain_Internal(void) if (shaderstate.bench.clamp && shaderstate.bench.clamp < shaderstate.bench.draws) continue; #endif - BE_SubmitMeshChain(idxfirst); + BE_SubmitMeshChain(vertbase, vertfirst, vertcount, idxfirst, idxcount); // d3dcheck(IDirect3DDevice9_DrawIndexedPrimitive(pD3DDev9, D3DPT_TRIANGLELIST, 0, 0, vertcount, idxfirst, idxcount/3)); } } @@ -2570,8 +2628,8 @@ static void BE_UploadLightmaps(qboolean force) glRect_t *theRect = &lm->rectchange; int r; - if (!lm->lightmap_texture) - lm->lightmap_texture = Image_CreateTexture("", NULL, (gl_lightmap_nearest.ival?IF_NEAREST:IF_LINEAR)|IF_NOMIPMAP); + if (!TEXLOADED(lm->lightmap_texture)) + lm->lightmap_texture = Image_CreateTexture("***lightmap***", NULL, (gl_lightmap_nearest.ival?IF_NEAREST:IF_LINEAR)|IF_NOMIPMAP); tex = lm->lightmap_texture->ptr; if (!tex) { @@ -2579,6 +2637,7 @@ static void BE_UploadLightmaps(qboolean force) if (!tex) continue; lm->lightmap_texture->ptr = tex; + lm->lightmap_texture->status = TEX_LOADED; } lm->modified = 0; @@ -2774,7 +2833,12 @@ void D3D9BE_DrawMesh_List(shader_t *shader, int nummeshes, mesh_t **meshlist, vb { shaderstate.batchvbo = vbo; shaderstate.curshader = shader; - shaderstate.curtexnums = texnums; + if (texnums) + shaderstate.curtexnums = texnums; + else if (shader->numdefaulttextures) + shaderstate.curtexnums = shader->defaulttextures + ((int)(shader->defaulttextures_fps * shaderstate.curtime) % shader->numdefaulttextures); + else + shaderstate.curtexnums = shader->defaulttextures; shaderstate.curlightmap = r_nulltex; shaderstate.curbatch = &shaderstate.dummybatch; shaderstate.meshlist = meshlist; @@ -3148,6 +3212,17 @@ void D3D9BE_BaseEntTextures(void) void D3D9BE_RenderShadowBuffer(unsigned int numverts, IDirect3DVertexBuffer9 *vbuf, unsigned int numindicies, IDirect3DIndexBuffer9 *ibuf) { + float pushdepth; + extern cvar_t r_polygonoffset_submodel_factor; +// D3D9BE_Cull(0);//shaderstate.curshader->flags & (SHADER_CULL_FRONT | SHADER_CULL_BACK)); + pushdepth = (shaderstate.curshader->polyoffset.factor + ((0/*shaderstate.flags & BEF_PUSHDEPTH*/)?r_polygonoffset_submodel_factor.value:0))/0xffff; + if (pushdepth != shaderstate.depthbias) + { + shaderstate.depthbias = pushdepth; + IDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_DEPTHBIAS, *(DWORD*)&shaderstate.depthbias); + } + + IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_VERT, vbuf, 0, sizeof(vecV_t)); IDirect3DDevice9_SetIndices(pD3DDev9, ibuf); @@ -3185,6 +3260,7 @@ void D3D9BE_DrawWorld (qboolean drawworld, qbyte *vis) if (drawworld) { + float shaderstate_identitylighting; BE_UploadLightmaps(false); //make sure the world draws correctly @@ -3196,7 +3272,19 @@ void D3D9BE_DrawWorld (qboolean drawworld, qbyte *vis) r_worldentity.axis[1][1] = 1; r_worldentity.axis[2][2] = 1; - BE_SelectMode(BEM_STANDARD); +#ifdef RTLIGHTS + if (drawworld && r_shadow_realtime_world.ival) + shaderstate_identitylighting = r_shadow_realtime_world_lightmaps.value; + else +#endif + shaderstate_identitylighting = 1; + shaderstate_identitylighting *= r_refdef.hdr_value; +// shaderstate_identitylightmap = shaderstate.identitylighting / (1<mip[i].data) + ; + else if (blocksize) { if (lock.Pitch == ((mips->mip[i].width+3)/4)*blocksize) //for (y = 0, out = lock.pBits, in = mips->mip[i].data; y < mips->mip[i].height; y++, out += lock.Pitch, in += mips->mip[i].width*pixelsize) diff --git a/engine/d3d/vid_d3d.c b/engine/d3d/vid_d3d.c index 8b911a5b..9cf49364 100644 --- a/engine/d3d/vid_d3d.c +++ b/engine/d3d/vid_d3d.c @@ -560,9 +560,8 @@ static qboolean initD3D9Device(HWND hWnd, rendererstate_t *info, unsigned int de rect.bottom = rect.top+d3dpp.BackBufferHeight; AdjustWindowRectEx(&rect, WS_OVERLAPPEDWINDOW, FALSE, 0); MoveWindow(d3dpp.hDeviceWindow, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, false); - - D3D9Shader_Init(); } + D3D9Shader_Init(); return true; //successful } else @@ -731,6 +730,8 @@ static qboolean D3D9_VID_Init(rendererstate_t *info, unsigned char *palette) mouseactive = false; } +// D3D9BE_Reset(false); + return true; } @@ -741,6 +742,8 @@ static void (D3D9_VID_DeInit) (void) /*final shutdown, kill the video stuff*/ if (pD3DDev9) { + D3D9BE_Reset(true); + /*try and knock it back into windowed mode to avoid d3d bugs*/ d3dpp.Windowed = true; IDirect3DDevice9_Reset(pD3DDev9, &d3dpp); @@ -906,6 +909,7 @@ static void (D3D9_SCR_UpdateScreen) (void) { case D3DERR_DEVICELOST: //the user has task switched away from us or something, don't draw anything until they switch back to us + D3D9BE_Reset(true); return; case D3DERR_DEVICENOTRESET: D3D9BE_Reset(true); @@ -916,7 +920,6 @@ static void (D3D9_SCR_UpdateScreen) (void) Cmd_ExecuteString("vid_restart", RESTRICT_LOCAL); return; } - D3D9BE_Reset(false); Cvar_ForceCallback(&v_gamma); break; @@ -924,6 +927,8 @@ static void (D3D9_SCR_UpdateScreen) (void) break; } + D3D9BE_Reset(false); + if (scr_disabled_for_loading) { extern float scr_disabled_time; @@ -1035,8 +1040,6 @@ static void (D3D9_SCR_UpdateScreen) (void) nohud = true; } - else if (!nohud) - SCR_TileClear (); SCR_DrawTwoDimensional(uimenu, nohud); diff --git a/engine/d3d/vid_d3d11.c b/engine/d3d/vid_d3d11.c index 75459655..068cfefe 100644 --- a/engine/d3d/vid_d3d11.c +++ b/engine/d3d/vid_d3d11.c @@ -1309,8 +1309,6 @@ static void (D3D11_SCR_UpdateScreen) (void) nohud = true; } - else if (!nohud) - SCR_TileClear (); SCR_DrawTwoDimensional(uimenu, nohud); diff --git a/engine/dotnet2005/ftequake.vcproj b/engine/dotnet2005/ftequake.vcproj index cbdba340..d3a774ad 100644 --- a/engine/dotnet2005/ftequake.vcproj +++ b/engine/dotnet2005/ftequake.vcproj @@ -15244,6 +15244,10 @@ /> + + engineflags & (MDLF_FLAME | MDLF_BOLT)) return; if (r_noaliasshadows.ival) diff --git a/engine/gl/gl_backend.c b/engine/gl/gl_backend.c index a66bc91f..da348276 100644 --- a/engine/gl/gl_backend.c +++ b/engine/gl/gl_backend.c @@ -926,7 +926,11 @@ void GLBE_RenderShadowBuffer(unsigned int numverts, int vbo, vecV_t *verts, unsi void GL_CullFace(unsigned int sflags) { - sflags ^= r_refdef.flipcull; + if (shaderstate.flags & BEF_FORCETWOSIDED) + sflags = 0; + else if (sflags) + sflags ^= r_refdef.flipcull; + #ifndef FORCESTATE if (shaderstate.curcull == sflags) return; @@ -2902,7 +2906,6 @@ static void DrawPass(const shaderpass_t *pass) tmu++; } -#endif //might need to break the pass here if (j > 1 && i != lastpass) @@ -2920,6 +2923,7 @@ static void DrawPass(const shaderpass_t *pass) BE_SendPassBlendDepthMask(pass[i+1].shaderbits); GenerateColourMods(&pass[i+1]); } +#endif } } @@ -3931,26 +3935,7 @@ static void DrawMeshes(void) } flags = shaderstate.curshader->flags; -#ifndef FORCESTATE - if (shaderstate.curcull != ((flags^r_refdef.flipcull) & (SHADER_CULL_FRONT|SHADER_CULL_BACK))) -#endif - { - shaderstate.curcull = ((flags^r_refdef.flipcull) & (SHADER_CULL_FRONT|SHADER_CULL_BACK)); - if (shaderstate.curcull & SHADER_CULL_FRONT) - { - qglEnable(GL_CULL_FACE); - qglCullFace(GL_FRONT); - } - else if (shaderstate.curcull & SHADER_CULL_BACK) - { - qglEnable(GL_CULL_FACE); - qglCullFace(GL_BACK); - } - else - { - qglDisable(GL_CULL_FACE); - } - } + GL_CullFace(flags & (SHADER_CULL_FRONT|SHADER_CULL_BACK)); #ifndef GLSLONLY if (shaderstate.sourcevbo->coord2.gl.addr && (shaderstate.curshader->numdeforms || !shaderstate.curshader->prog)) diff --git a/engine/gl/gl_font.c b/engine/gl/gl_font.c index 8fcf8654..b245d5bd 100644 --- a/engine/gl/gl_font.c +++ b/engine/gl/gl_font.c @@ -1739,7 +1739,7 @@ int Font_LineWidth(conchar_t *start, conchar_t *end) int x = 0; struct font_s *font = curfont; unsigned int codeflags, codepoint; - for (; start < end; start++) + for (; start < end; ) { start = Font_Decode(start, &codeflags, &codepoint); x = Font_CharEndCoord(font, x, codeflags, codepoint); diff --git a/engine/gl/gl_heightmap.c b/engine/gl/gl_heightmap.c index 96ca1f5e..75b75830 100644 --- a/engine/gl/gl_heightmap.c +++ b/engine/gl/gl_heightmap.c @@ -5180,7 +5180,7 @@ void Terr_Brush_Draw(heightmap_t *hm, batch_t **batches, entity_t *e) LightPlane (hm->relightcontext, hm->lightthreadmem, styles, br->faces[j].lightdata, NULL, br->planes[j], br->faces[j].stdir, exactmins, exactmaxs, br->faces[j].lmbias, texsize, br->faces[j].lmscale); //special version that doesn't know what a face is or anything. br->faces[j].relit = true; } - if (br->faces[j].relit) + if (br->faces[j].relit && br->faces[j].lightmap >= 0) { int s,t; qbyte *out, *in; @@ -5474,8 +5474,16 @@ static brushes_t *Terr_Brush_Insert(model_t *model, heightmap_t *hm, brushes_t * out->faces[oface].lmextents[k] = ceil((maxs[k])/out->faces[oface].lmscale)-out->faces[oface].lmbias[k]+1; if (out->faces[oface].lmextents[k] > 128) { //surface is too large for lightmap data. just drop its resolution, because splitting the face in plane-defined geometry is a bad idea. - out->faces[oface].lmscale *= 2; - k = 0; + if (out->faces[oface].lmscale > 256) + { + out->faces[oface].relight = false; + k++; + } + else + { + out->faces[oface].lmscale *= 2; + k = 0; + } } else k++; @@ -5483,8 +5491,13 @@ static brushes_t *Terr_Brush_Insert(model_t *model, heightmap_t *hm, brushes_t * out->faces[oface].lightmap = -1; out->faces[oface].lmbase[0] = 0; out->faces[oface].lmbase[1] = 0; - out->faces[oface].lightdata = BZ_Malloc(out->faces[oface].lmextents[0] * out->faces[oface].lmextents[1] * 3); - memset(out->faces[oface].lightdata, 0x3f, out->faces[oface].lmextents[0]*out->faces[oface].lmextents[1]*3); + if (out->faces[oface].relight) + { + out->faces[oface].lightdata = BZ_Malloc(out->faces[oface].lmextents[0] * out->faces[oface].lmextents[1] * 3); + memset(out->faces[oface].lightdata, 0x3f, out->faces[oface].lmextents[0]*out->faces[oface].lmextents[1]*3); + } + else + out->faces[oface].lightdata = NULL; // Con_Printf("lm extents: %u %u (%i points)\n", out->faces[oface].lmextents[0], out->faces[oface].lmextents[1], numpoints); oface++; @@ -5724,7 +5737,7 @@ static void *validateqcpointer(pubprogfuncs_t *prinst, size_t qcptr, size_t elem PR_BIError(prinst, "brush: elementcount %u is too large\n", (unsigned int)elementcount); return NULL; } - if (qcptr < 0 || qcptr+(elementsize*elementcount) >= prinst->stringtablesize) + if (qcptr < 0 || qcptr+(elementsize*elementcount) > prinst->stringtablesize) { PR_BIError(prinst, "brush: invalid qc pointer\n"); return NULL; diff --git a/engine/gl/gl_model.c b/engine/gl/gl_model.c index 2251e696..48f5ccbb 100644 --- a/engine/gl/gl_model.c +++ b/engine/gl/gl_model.c @@ -34,6 +34,7 @@ extern cvar_t r_replacemodels; extern cvar_t gl_lightmap_average; extern cvar_t r_softwarebanding; cvar_t mod_loadentfiles = CVAR("sv_loadentfiles", "1"); +cvar_t mod_loadentfiles_dir = CVAR("sv_loadentfiles_dir", ""); cvar_t mod_external_vis = CVARD("mod_external_vis", "1", "Attempt to load .vis patches for quake maps, allowing transparent water to work properly."); cvar_t mod_warnmodels = CVARD("mod_warnmodels", "1", "Warn if any models failed to load. Set to 0 if your mod is likely to lack optional models (like its in development)."); //set to 0 for hexen2 and its otherwise-spammy-as-heck demo. cvar_t mod_litsprites = CVARD("mod_litsprites", "0", "If set to 1, sprites will be lit according to world lighting (including rtlights), like Tenebrae. Use EF_ADDITIVE or EF_FULLBRIGHT to make emissive sprites instead."); @@ -60,6 +61,7 @@ qboolean QDECL Mod_LoadQ2BrushModel (model_t *mod, void *buffer, size_t fsize); #endif model_t *Mod_LoadModel (model_t *mod, enum mlverbosity_e verbose); static void Mod_PrintFormats_f(void); +static void Mod_SaveEntFile_f(void); #ifdef MAP_DOOM qboolean QDECL Mod_LoadDoomLevel(model_t *mod, void *buffer, size_t fsize); @@ -524,6 +526,7 @@ void Mod_Purge(enum mod_purge_e ptype) mod->meshinfo = NULL; mod->loadstate = MLS_NOTLOADED; + mod->submodelof = NULL; mod->pvs = NULL; mod->phs = NULL; } @@ -559,7 +562,9 @@ void Mod_Init (qboolean initial) Cvar_Register(&mod_warnmodels, "Graphical Nicaties"); Cvar_Register(&mod_litsprites, "Graphical Nicaties"); Cvar_Register(&mod_loadentfiles, NULL); + Cvar_Register(&mod_loadentfiles_dir, NULL); Cvar_Register(&temp_lit2support, NULL); + Cmd_AddCommand("sv_saveentfile", Mod_SaveEntFile_f); Cmd_AddCommand("version_modelformats", Mod_PrintFormats_f); } @@ -1717,7 +1722,7 @@ void Mod_LoadLighting (model_t *loadmodel, qbyte *mod_base, lump_t *l, qboolean if (!temp_lit2support.ival) { litdata = NULL; - Con_Printf("lit2 support is disabled, pending format finalisation.\n", litname); + Con_Printf("lit2 support is disabled, pending format finalisation (%s).\n", litname); } else if (loadmodel->numsurfaces != ql2->numsurfs) { @@ -2017,6 +2022,49 @@ char *Mod_ParseWorldspawnKey(const char *ents, const char *key, char *buffer, si return ""; //err... } +static void Mod_SaveEntFile_f(void) +{ + char fname[MAX_QPATH]; + model_t *mod = NULL; + char *n = Cmd_Argv(1); + if (*n) + mod = Mod_ForName(n, MLV_WARN); +#ifndef CLIENTONLY + if (sv.state && !mod) + mod = sv.world.worldmodel; +#endif +#ifndef SERVERONLY + if (cls.state && !mod) + mod = cl.worldmodel; +#endif + if (mod && mod->loadstate == MLS_LOADING) + COM_WorkerPartialSync(mod, &mod->loadstate, MLS_LOADING); + if (!mod || mod->loadstate != MLS_LOADED) + { + Con_Printf("Map not loaded\n"); + return; + } + if (!mod->entities) + { + Con_Printf("Map is not a map, and has no entities\n"); + return; + } + + if (*mod_loadentfiles_dir.string && !strncmp(mod->name, "maps/", 5)) + { + Q_snprintfz(fname, sizeof(fname), "maps/%s/%s", mod_loadentfiles_dir.string, mod->name+5); + COM_StripExtension(fname, fname, sizeof(fname)); + Q_strncatz(fname, ".ent", sizeof(fname)); + } + else + { + COM_StripExtension(mod->name, fname, sizeof(fname)); + Q_strncatz(fname, ".ent", sizeof(fname)); + } + + COM_WriteFile(fname, FS_GAMEONLY, mod->entities, strlen(mod->entities)); +} + /* ================= Mod_LoadEntities @@ -2031,6 +2079,16 @@ void Mod_LoadEntities (model_t *loadmodel, qbyte *mod_base, lump_t *l) if (!l->filelen) return; + if (mod_loadentfiles.value && !loadmodel->entities && *mod_loadentfiles_dir.string) + { + if (!strncmp(loadmodel->name, "maps/", 5)) + { + Q_snprintfz(fname, sizeof(fname), "maps/%s/%s", mod_loadentfiles_dir.string, loadmodel->name+5); + COM_StripExtension(fname, fname, sizeof(fname)); + Q_strncatz(fname, ".ent", sizeof(fname)); + loadmodel->entities = FS_LoadMallocGroupFile(&loadmodel->memgroup, fname, &sz); + } + } if (mod_loadentfiles.value && !loadmodel->entities) { COM_StripExtension(loadmodel->name, fname, sizeof(fname)); @@ -4726,6 +4784,7 @@ TRACE(("LoadBrushModel %i\n", __LINE__)); // Q_snprintfz (name, sizeof(name), "*%i", i+1); nextmod = Mod_FindName (name); *nextmod = *submod; + nextmod->submodelof = mod; Q_strncpyz(nextmod->name, name, sizeof(nextmod->name)); submod = nextmod; memset(&submod->memgroup, 0, sizeof(submod->memgroup)); @@ -4835,7 +4894,29 @@ void Mod_LoadSpriteFrameShader(model_t *spr, int frame, int subframe, mspritefra } if (i == -1) // a ! in the filename makes it non-fullbright (and can also be lit by rtlights too). - shadertext = SPRITE_SHADER_MAIN SPRITE_SHADER_LIT SPRITE_SHADER_FOOTER; + { + shadertext = + "{\n" + "program defaultsprite\n" + "{\n" + "map $diffuse\n" + "blendfunc GL_SRC_ALPHA GL_ONE\n" + "rgbgen vertex\n" + "alphagen vertex\n" + "}\n" + "surfaceparm noshadows\n" + "sort seethrough\n" + "bemode rtlight\n" + "{\n" + "program rtlight#NOBUMP\n" + "{\n" + "map $diffuse\n" + "blendfunc add\n" + "}\n" + "}\n" + "}\n" + ; + } else shadertext = SPRITE_SHADER_MAIN SPRITE_SHADER_UNLIT SPRITE_SHADER_FOOTER; frameinfo->shader = R_RegisterShader(name, SUF_NONE, shadertext); diff --git a/engine/gl/gl_model.h b/engine/gl/gl_model.h index f01167b7..93514076 100644 --- a/engine/gl/gl_model.h +++ b/engine/gl/gl_model.h @@ -177,7 +177,7 @@ m*_t structures are in-memory #define DPEF_DOUBLESIDED_ (1<<15) //disables culling #define DPEF_NOSELFSHADOW_ (1<<16) //doesn't cast shadows on any noselfshadow entities. #define DPEF_DYNAMICMODELLIGHT_ (1<<17) -#define EF_UNUSED18 (1<<18) +#define EF_GREEN (1<<18) #define EF_UNUSED19 (1<<19) #define DPEF_RESTARTANIM_BIT_ (1<<20) //exact semantics seems odd #define DPEF_TELEPORT_BIT_ (1<<21) //disable lerping while set @@ -370,7 +370,7 @@ typedef struct msurface_s unsigned short numedges; // are backwards edges unsigned short lmshift; //texels>>lmshift = lightmap samples. - short texturemins[2]; + int texturemins[2]; short extents[2]; unsigned short light_s[MAXRLIGHTMAPS], light_t[MAXRLIGHTMAPS]; // gl lightmap coordinates @@ -854,6 +854,8 @@ typedef struct model_s qboolean tainted; qboolean pushdepth; // bsp submodels have this flag set so you don't get z fighting on co-planar surfaces. + struct model_s *submodelof; + modtype_t type; fromgame_t fromgame; @@ -988,7 +990,7 @@ typedef struct model_s #endif // __MODEL__ - +float RadiusFromBounds (vec3_t mins, vec3_t maxs); // diff --git a/engine/gl/gl_rlight.c b/engine/gl/gl_rlight.c index da9e01ed..cee7b4db 100644 --- a/engine/gl/gl_rlight.c +++ b/engine/gl/gl_rlight.c @@ -546,7 +546,7 @@ void R_PushDlights (void) //rtlight loading #ifdef RTLIGHTS -void R_ImportRTLights(char *entlump) +qboolean R_ImportRTLights(char *entlump) { typedef enum lighttype_e {LIGHTTYPE_MINUSX, LIGHTTYPE_RECIPX, LIGHTTYPE_RECIPXX, LIGHTTYPE_NONE, LIGHTTYPE_SUN, LIGHTTYPE_MINUSXX} lighttype_t; @@ -556,6 +556,7 @@ void R_ImportRTLights(char *entlump) float origin[3], angles[3], radius, color[3], light[4], fadescale, lightscale, originhack[3], overridecolor[3], vec[4]; char key[256], value[8192]; int nest; + qboolean okay = false; COM_Parse(entlump); if (!strcmp(com_token, "Version")) @@ -743,7 +744,10 @@ void R_ImportRTLights(char *entlump) { //tenebrae compat. don't generate rtlights automagically if the world entity specifies this. if (atoi(value)) - return; + { + okay = true; + return okay; + } } } if (!islight) @@ -783,7 +787,7 @@ void R_ImportRTLights(char *entlump) break; } VectorAdd(origin, originhack, origin); - if (radius >= 1) + if (radius >= 1 && !(cl.worldmodel->funcs.PointContents(cl.worldmodel, NULL, origin) & FTECONTENTS_SOLID)) { dlight_t *dl = CL_AllocSlight(); if (!dl) @@ -803,11 +807,15 @@ void R_ImportRTLights(char *entlump) dl->lightcolourscales[2] = r_editlights_import_specular.value; if (skin >= 16) R_LoadNumberedLightTexture(dl, skin); + + okay = true; } } + + return okay; } -void R_LoadRTLights(void) +qboolean R_LoadRTLights(void) { dlight_t *dl; char fname[MAX_QPATH]; @@ -954,6 +962,7 @@ void R_LoadRTLights(void) } file = end+1; } + return !!file; } void R_SaveRTLights_f(void) @@ -995,7 +1004,7 @@ void R_SaveRTLights_f(void) light->radius, light->color[0], light->color[1], light->color[2], light->style-1, light->cubemapname, light->corona, - ang[0], ang[1], ang[2], + anglemod(-ang[0]), ang[1], ang[2], light->coronascale, light->lightcolourscales[0], light->lightcolourscales[1], light->lightcolourscales[2], light->flags&(LFLAG_NORMALMODE|LFLAG_REALTIMEMODE|LFLAG_CREPUSCULAR), light->rotation[0],light->rotation[1],light->rotation[2],light->fov )); @@ -1025,7 +1034,7 @@ void R_StaticEntityToRTLight(int i) if (!state->light[0] && !state->light[1] && !state->light[2]) VectorSet(dl->color, 1, 1, 1); dl->flags = 0; - dl->flags |= LFLAG_REALTIMEMODE; + dl->flags |= LFLAG_NORMALMODE|LFLAG_REALTIMEMODE; dl->flags |= (state->lightpflags & PFLAGS_NOSHADOW)?LFLAG_NOSHADOWS:0; if (state->lightpflags & PFLAGS_CORONA) dl->corona = 1; @@ -1209,7 +1218,7 @@ void GLQ3_LightGrid(model_t *mod, vec3_t point, vec3_t res_diffuse, vec3_t res_a VectorCopy(direction, res_dir); } -int GLRecursiveLightPoint (mnode_t *node, vec3_t start, vec3_t end) +static int GLRecursiveLightPoint (mnode_t *node, vec3_t start, vec3_t end) { int r; float front, back, frac; @@ -1361,7 +1370,7 @@ int R_LightPoint (vec3_t p) #ifdef PEXT_LIGHTSTYLECOL -float *GLRecursiveLightPoint3C (mnode_t *node, vec3_t start, vec3_t end) +static float *GLRecursiveLightPoint3C (model_t *mod, mnode_t *node, vec3_t start, vec3_t end) { static float l[6]; float *r; @@ -1377,7 +1386,7 @@ float *GLRecursiveLightPoint3C (mnode_t *node, vec3_t start, vec3_t end) float scale, overbright; int maps; - if (cl.worldmodel->fromgame == fg_quake2) + if (mod->fromgame == fg_quake2) { if (node->contents != -1) return NULL; // solid @@ -1394,7 +1403,7 @@ float *GLRecursiveLightPoint3C (mnode_t *node, vec3_t start, vec3_t end) side = front < 0; if ( (back < 0) == side) - return GLRecursiveLightPoint3C (node->children[side], start, end); + return GLRecursiveLightPoint3C (mod, node->children[side], start, end); frac = front / (front-back); mid[0] = start[0] + (end[0] - start[0])*frac; @@ -1402,7 +1411,7 @@ float *GLRecursiveLightPoint3C (mnode_t *node, vec3_t start, vec3_t end) mid[2] = start[2] + (end[2] - start[2])*frac; // go down front side - r = GLRecursiveLightPoint3C (node->children[side], start, mid); + r = GLRecursiveLightPoint3C (mod, node->children[side], start, mid); if (r && r[0]+r[1]+r[2] >= 0) return r; // hit something @@ -1413,7 +1422,7 @@ float *GLRecursiveLightPoint3C (mnode_t *node, vec3_t start, vec3_t end) VectorCopy (mid, lightspot); lightplane = plane; - surf = cl.worldmodel->surfaces + node->firstsurface; + surf = mod->surfaces + node->firstsurface; for (i=0 ; inumsurfaces ; i++, surf++) { if (surf->flags & SURF_DRAWTILED) @@ -1450,11 +1459,11 @@ float *GLRecursiveLightPoint3C (mnode_t *node, vec3_t start, vec3_t end) if (lightmap) { overbright = 1/255.0f; - if (cl.worldmodel->deluxdata) + if (mod->deluxdata) { - if (cl.worldmodel->engineflags & MDLF_RGBLIGHTING) + if (mod->engineflags & MDLF_RGBLIGHTING) { - deluxmap = surf->samples - cl.worldmodel->lightdata + cl.worldmodel->deluxdata; + deluxmap = surf->samples - mod->lightdata + mod->deluxdata; lightmap += (dt * ((surf->extents[0]>>surf->lmshift)+1) + ds)*3; deluxmap += (dt * ((surf->extents[0]>>surf->lmshift)+1) + ds)*3; @@ -1480,7 +1489,7 @@ float *GLRecursiveLightPoint3C (mnode_t *node, vec3_t start, vec3_t end) } else { - deluxmap = (surf->samples - cl.worldmodel->lightdata)*3 + cl.worldmodel->deluxdata; + deluxmap = (surf->samples - mod->lightdata)*3 + mod->deluxdata; lightmap += (dt * ((surf->extents[0]>>surf->lmshift)+1) + ds); deluxmap += (dt * ((surf->extents[0]>>surf->lmshift)+1) + ds)*3; @@ -1507,7 +1516,7 @@ float *GLRecursiveLightPoint3C (mnode_t *node, vec3_t start, vec3_t end) } else { - if (cl.worldmodel->engineflags & MDLF_RGBLIGHTING) + if (mod->engineflags & MDLF_RGBLIGHTING) { lightmap += (dt * ((surf->extents[0]>>surf->lmshift)+1) + ds)*3; for (maps = 0 ; maps < MAXQ1LIGHTMAPS && surf->styles[maps] != 255 ; @@ -1547,7 +1556,7 @@ float *GLRecursiveLightPoint3C (mnode_t *node, vec3_t start, vec3_t end) } // go down back side - return GLRecursiveLightPoint3C (node->children[!side], mid, end); + return GLRecursiveLightPoint3C (mod, node->children[!side], mid, end); } #endif @@ -1558,7 +1567,7 @@ void GLQ1BSP_LightPointValues(model_t *model, vec3_t point, vec3_t res_diffuse, float *r; extern cvar_t r_shadow_realtime_world, r_shadow_realtime_world_lightmaps; - if (!cl.worldmodel->lightdata || r_fullbright.ival) + if (!model->lightdata || r_fullbright.ival) { res_diffuse[0] = 0; res_diffuse[1] = 0; @@ -1579,7 +1588,7 @@ void GLQ1BSP_LightPointValues(model_t *model, vec3_t point, vec3_t res_diffuse, end[1] = point[1]; end[2] = point[2] - 2048; - r = GLRecursiveLightPoint3C(model->rootnode, point, end); + r = GLRecursiveLightPoint3C(model, model->rootnode, point, end); if (r == NULL) { res_diffuse[0] = 0; diff --git a/engine/gl/gl_rmain.c b/engine/gl/gl_rmain.c index 123275ed..1d6bd429 100644 --- a/engine/gl/gl_rmain.c +++ b/engine/gl/gl_rmain.c @@ -1735,6 +1735,13 @@ void GLR_RenderView (void) r_refdef.flags |= RDF_WATERWARP; //try fullscreen warp instead if we can } + if (!r_refdef.globalfog.density) + { + int fogtype = ((r_refdef.flags & RDF_UNDERWATER) && cl.fog[1].density)?1:0; + CL_BlendFog(&r_refdef.globalfog, &cl.oldfog[fogtype], realtime, &cl.fog[fogtype]); + r_refdef.globalfog.density /= 64; //FIXME + } + if (!(r_refdef.flags & RDF_NOWORLDMODEL) && (*r_postprocshader.string)) { custompostproc = R_RegisterCustom(r_postprocshader.string, SUF_NONE, NULL, NULL); diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c index 15e4eeb8..c87293cb 100644 --- a/engine/gl/gl_shader.c +++ b/engine/gl/gl_shader.c @@ -1488,7 +1488,7 @@ static void Shader_LoadGeneric(sgeneric_t *g, int qrtype) if (file) { - Con_DPrintf("Loaded %s from disk\n", basicname); + Con_DPrintf("Loaded %s from disk\n", va(sh_config.progpath, basicname)); g->failed = !Shader_LoadPermutations(g->name, &g->prog, file, qrtype, 0, blobname); FS_FreeFile(file); return; @@ -3029,6 +3029,7 @@ void Shader_Free (shader_t *shader) if (shader->skydome) Z_Free (shader->skydome); + shader->skydome = NULL; while (shader->clutter) { void *t = shader->clutter; @@ -5673,6 +5674,44 @@ void Shader_ShowShader_f(void) Con_Printf("Shader \"%s\" is not loaded\n", sourcename); } +void Shader_TouchTextures(void) +{ + int i, j, k; + shader_t *s; + shaderpass_t *p; + texnums_t *t; + for (i = 0; i < r_numshaders; i++) + { + s = r_shaders[i]; + if (!s || !s->uses) + continue; + + for (j = 0; j < s->numpasses; j++) + { + p = &s->passes[j]; + for (k = 0; k < countof(p->anim_frames); k++) + if (p->anim_frames[k]) + p->anim_frames[k]->regsequence = r_regsequence; + } + for (j = 0; j < max(1,s->numdefaulttextures); j++) + { + t = &s->defaulttextures[j]; + if (t->base) + t->base->regsequence = r_regsequence; + if (t->bump) + t->bump->regsequence = r_regsequence; + if (t->fullbright) + t->fullbright->regsequence = r_regsequence; + if (t->specular) + t->specular->regsequence = r_regsequence; + if (t->upperoverlay) + t->upperoverlay->regsequence = r_regsequence; + if (t->loweroverlay) + t->loweroverlay->regsequence = r_regsequence; + } + } +} + void Shader_DoReload(void) { shader_t *s; diff --git a/engine/gl/gl_shadow.c b/engine/gl/gl_shadow.c index 30d5ed47..9d7c39c5 100644 --- a/engine/gl/gl_shadow.c +++ b/engine/gl/gl_shadow.c @@ -2649,6 +2649,9 @@ static void Sh_DrawBrushModelShadow(dlight_t *dl, entity_t *e) model_t *model; msurface_t *surf; + if (qrenderer != QR_OPENGL) + return; + if (BE_LightCullModel(e->origin, e->model)) return; @@ -3054,10 +3057,9 @@ static qboolean Sh_DrawStencilLight(dlight_t *dl, vec3_t colour, vec3_t axis[3], /*draw the shadows*/ Sh_DrawStencilLightShadows(dl, lvis, vvis, false); - //disable stencil writing, switch culling back to normal + //disable stencil writing IDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP); IDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_TWOSIDEDSTENCILMODE, false); - IDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_CULLMODE, D3DCULL_CW); IDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_STENCILFUNC, D3DCMP_EQUAL); IDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_STENCILREF, sref); IDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_STENCILMASK, ~0); @@ -3250,6 +3252,7 @@ void Sh_PurgeShadowMeshes(void) maxedge = 0; } +void R_StaticEntityToRTLight(int i); void Sh_PreGenerateLights(void) { unsigned int ignoreflags; @@ -3260,16 +3263,22 @@ void Sh_PreGenerateLights(void) int i; r_shadow_realtime_world_lightmaps.value = atof(r_shadow_realtime_world_lightmaps.string); - if (r_shadow_realtime_dlight.ival || r_shadow_realtime_world.ival) + if ((r_shadow_realtime_dlight.ival || r_shadow_realtime_world.ival) && rtlights_max == RTL_FIRST) { - if (RTL_FIRST == rtlights_max) - R_LoadRTLights(); - if (RTL_FIRST == rtlights_max) - R_ImportRTLights(cl.worldmodel->entities); - if (RTL_FIRST == rtlights_max && r_shadow_realtime_world.ival) + qboolean okay = false; + if (!okay) + okay |= R_LoadRTLights(); + if (!okay) + okay |= R_ImportRTLights(cl.worldmodel->entities); + if (!okay && r_shadow_realtime_world.ival && r_shadow_realtime_world_lightmaps.value != 1) { r_shadow_realtime_world_lightmaps.value = 1; - Con_Printf(CON_ERROR "No lights detected in map. Disabling realtime lights.\n"); + Con_Printf(CON_WARNING "No lights detected in map.\n"); + } + + for (i = 0; i < cl.num_statics; i++) + { + R_StaticEntityToRTLight(i); } } @@ -3348,12 +3357,10 @@ void Sh_CheckSettings(void) #endif #ifdef D3D9QUAKE case QR_DIRECT3D9: - #ifndef GLQUAKE canshadowless = true; //the code still has a lot of ifdefs, so will crash if you try it in a merged build. //its not really usable in d3d-only builds either, so no great loss. canstencil = true; - #endif break; #endif #ifdef D3D11QUAKE @@ -3391,7 +3398,8 @@ void Sh_CheckSettings(void) //only one shadow method if (!!r_shadow_shadowmapping.ival != cansmap) { - Con_Printf("Missing driver extensions: forcing shadowmapping %s.\n", cansmap?"on":"off"); + if (r_shadow_shadowmapping.ival && ((r_shadow_realtime_world.ival&&r_shadow_realtime_world_shadows.ival)||(r_shadow_realtime_dlight.ival&&r_shadow_realtime_dlight_shadows.ival))) + Con_Printf("Missing driver extensions: forcing shadowmapping %s.\n", cansmap?"on":"off"); r_shadow_shadowmapping.ival = cansmap; } } @@ -3519,6 +3527,9 @@ void Sh_DrawLights(qbyte *vis) if (colour[0] < 0.001 && colour[1] < 0.001 && colour[2] < 0.001) continue; //just switch these off. + if (!dl->lightcolourscales[0] && !dl->lightcolourscales[1] && !dl->lightcolourscales[2]) + continue; //these lights are just coronas. + if (dl->rotation[0] || dl->rotation[1] || dl->rotation[2]) { //auto-rotating (static) rtlights vec3_t rot; diff --git a/engine/gl/gl_vidnt.c b/engine/gl/gl_vidnt.c index a2b7e858..2773c77e 100644 --- a/engine/gl/gl_vidnt.c +++ b/engine/gl/gl_vidnt.c @@ -2632,7 +2632,7 @@ qboolean GLVID_Init (rendererstate_t *info, unsigned char *palette) if (isPlugin >= 2) { - fprintf(stdout, "refocuswindow %#p\n", mainwindow); + fprintf(stdout, "refocuswindow "fPRIp"\n", mainwindow); fflush(stdout); } diff --git a/engine/gl/glquake.h b/engine/gl/glquake.h index 1f01752c..5efd4d65 100644 --- a/engine/gl/glquake.h +++ b/engine/gl/glquake.h @@ -47,7 +47,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. void D3D9_Set2D (void); -float RadiusFromBounds (vec3_t mins, vec3_t maxs); void AddPointToBounds (vec3_t v, vec3_t mins, vec3_t maxs); qboolean BoundsIntersect (vec3_t mins1, vec3_t maxs1, vec3_t mins2, vec3_t maxs2); void ClearBounds (vec3_t mins, vec3_t maxs); @@ -411,8 +410,8 @@ void GLR_MarkQ2Lights (dlight_t *light, int bit, mnode_t *node); #endif void GLQ3_LightGrid(model_t *mod, vec3_t point, vec3_t res_diffuse, vec3_t res_ambient, vec3_t res_dir); void R_ReloadRTLights_f(void); -void R_LoadRTLights(void); -void R_ImportRTLights(char *entlump); +qboolean R_LoadRTLights(void); +qboolean R_ImportRTLights(char *entlump); void R_SaveRTLights_f(void); //doom diff --git a/engine/gl/shader.h b/engine/gl/shader.h index 9bac911f..5152a280 100644 --- a/engine/gl/shader.h +++ b/engine/gl/shader.h @@ -641,6 +641,7 @@ mfog_t *Mod_FogForOrigin(model_t *wmodel, vec3_t org); #define BEF_NOSHADOWS 128 //don't appear in shadows #define BEF_FORCECOLOURMOD 256 //q3 shaders default to 'rgbgen identity', and ignore ent colours. this forces ent colours to be considered #define BEF_LINES 512 //draw line pairs instead of triangles. +#define BEF_FORCETWOSIDED 1024 //more evilness. typedef struct { diff --git a/engine/qclib/execloop.h b/engine/qclib/execloop.h index 473d5367..74f05f04 100644 --- a/engine/qclib/execloop.h +++ b/engine/qclib/execloop.h @@ -578,7 +578,7 @@ reeval: glob = NULL; //try to derestrict it. - callerprogs=pr_typecurrent; //so we can revert to the right caller. + callerprogs=prinst.pr_typecurrent; //so we can revert to the right caller. newpr = (fnum & 0xff000000)>>24; //this is the progs index of the callee fnum &= ~0xff000000; //the callee's function index. @@ -607,7 +607,7 @@ reeval: if (newf->first_statement <= 0) { // negative statements are built in functions /*calling a builtin in another progs may affect that other progs' globals instead, is the theory anyway, so args and stuff need to move over*/ - if (pr_typecurrent != 0) + if (prinst.pr_typecurrent != 0) { //builtins quite hackily refer to only a single global. //for builtins to affect the globals of other progs, we need to first switch to the progs that it will affect, so they'll be correct when we switch back diff --git a/engine/qclib/initlib.c b/engine/qclib/initlib.c index 8b39c403..d3c4dfc0 100644 --- a/engine/qclib/initlib.c +++ b/engine/qclib/initlib.c @@ -14,12 +14,12 @@ void *PRHunkAlloc(progfuncs_t *progfuncs, int ammount, char *name) ammount = sizeof(prmemb_t)+((ammount + 3)&~3); mem = progfuncs->funcs.parms->memalloc(ammount); memset(mem, 0, ammount); - mem->prev = memb; - if (!memb) + mem->prev = prinst.memblocks; + if (!prinst.memblocks) mem->level = 1; else - mem->level = ((prmemb_t *)memb)->level+1; - memb = mem; + mem->level = ((prmemb_t *)prinst.memblocks)->level+1; + prinst.memblocks = mem; return ((char *)mem)+sizeof(prmemb_t); } @@ -30,18 +30,18 @@ void *PDECL QC_HunkAlloc(pubprogfuncs_t *ppf, int ammount, char *name) int PRHunkMark(progfuncs_t *progfuncs) { - return ((prmemb_t *)memb)->level; + return ((prmemb_t *)prinst.memblocks)->level; } void PRHunkFree(progfuncs_t *progfuncs, int mark) { prmemb_t *omem; - while(memb) + while(prinst.memblocks) { - if (memb->level <= mark) + if (prinst.memblocks->level <= mark) return; - omem = memb; - memb = memb->prev; + omem = prinst.memblocks; + prinst.memblocks = prinst.memblocks->prev; externs->memfree(omem); } return; @@ -52,7 +52,7 @@ void PRAddressableRelocate(progfuncs_t *progfuncs, char *oldb, char *newb, int o { unsigned int i; edictrun_t *e; - for (i=0 ; ifields >= oldb && (char*)e->fields < oldb+oldlen) @@ -62,7 +62,7 @@ void PRAddressableRelocate(progfuncs_t *progfuncs, char *oldb, char *newb, int o if (progfuncs->funcs.stringtable >= oldb && progfuncs->funcs.stringtable < oldb+oldlen) progfuncs->funcs.stringtable = (progfuncs->funcs.stringtable - oldb) + newb; - for (i=0; i < maxprogs; i++) + for (i=0; i < prinst.maxprogs; i++) { if ((char*)prinst.progstate[i].globals >= oldb && (char*)prinst.progstate[i].globals < oldb+oldlen) prinst.progstate[i].globals = (float*)(((char*)prinst.progstate[i].globals - oldb) + newb); @@ -433,7 +433,7 @@ void PRAddressableFlush(progfuncs_t *progfuncs, size_t totalammount) int PDECL PR_InitEnts(pubprogfuncs_t *ppf, int max_ents) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; - maxedicts = max_ents; + prinst.maxedicts = max_ents; sv_num_edicts = 0; @@ -447,19 +447,19 @@ int PDECL PR_InitEnts(pubprogfuncs_t *ppf, int max_ents) } #endif - max_fields_size = fields_size; + prinst.max_fields_size = prinst.fields_size; - prinst.edicttable = PRHunkAlloc(progfuncs, maxedicts*sizeof(struct edicts_s *), "edicttable"); + prinst.edicttable = PRHunkAlloc(progfuncs, prinst.maxedicts*sizeof(struct edicts_s *), "edicttable"); sv_edicts = PRHunkAlloc(progfuncs, externs->edictsize, "edict0"); prinst.edicttable[0] = sv_edicts; - ((edictrun_t*)prinst.edicttable[0])->fields = PRAddressableExtend(progfuncs, NULL, fields_size, max_fields_size-fields_size); + ((edictrun_t*)prinst.edicttable[0])->fields = PRAddressableExtend(progfuncs, NULL, prinst.fields_size, prinst.max_fields_size-prinst.fields_size); QC_ClearEdict(&progfuncs->funcs, sv_edicts); sv_num_edicts = 1; if (externs->entspawn) externs->entspawn((struct edict_s *)sv_edicts, false); - return max_fields_size; + return prinst.max_fields_size; } edictrun_t tempedict; //used as a safty buffer static float tempedictfields[2048]; @@ -470,13 +470,13 @@ static void PDECL PR_Configure (pubprogfuncs_t *ppf, size_t addressable_size, in unsigned int i; edictrun_t *e; - max_fields_size=0; - fields_size = 0; + prinst.max_fields_size=0; + prinst.fields_size = 0; progfuncs->funcs.stringtable = 0; QC_StartShares(progfuncs); QC_InitShares(progfuncs); - for ( i=1 ; i>24; fnum = (func & 0x00ffffff); - if (pnum >= (unsigned)maxprogs || !pr_progstate[pnum].functions) + if (pnum >= prinst.maxprogs || !pr_progstate[pnum].functions) return -1; else if (fnum >= pr_progstate[pnum].progs->numfunctions) return -1; @@ -577,7 +577,7 @@ func_t PDECL PR_FindFunc(pubprogfuncs_t *ppf, const char *funcname, progsnum_t p mfunction_t *f=NULL; if (pnum == PR_ANY) { - for (pnum = 0; (unsigned)pnum < maxprogs; pnum++) + for (pnum = 0; (unsigned)pnum < prinst.maxprogs; pnum++) { if (!pr_progstate[pnum].progs) continue; @@ -588,7 +588,7 @@ func_t PDECL PR_FindFunc(pubprogfuncs_t *ppf, const char *funcname, progsnum_t p } else if (pnum == PR_ANYBACK) //run backwards { - for (pnum = maxprogs-1; pnum >= 0; pnum--) + for (pnum = prinst.maxprogs-1; pnum >= 0; pnum--) { if (!pr_progstate[pnum].progs) continue; @@ -634,10 +634,10 @@ void PDECL QC_FindPrefixedGlobals(pubprogfuncs_t *ppf, int pnum, char *prefix, v int len = strlen(prefix); if (pnum == PR_CURRENT) - pnum = pr_typecurrent; + pnum = prinst.pr_typecurrent; if (pnum == PR_ANY) { - for (pnum = 0; (unsigned)pnum < maxprogs; pnum++) + for (pnum = 0; (unsigned)pnum < prinst.maxprogs; pnum++) { if (!pr_progstate[pnum].progs) continue; @@ -679,11 +679,11 @@ eval_t *PDECL PR_FindGlobal(pubprogfuncs_t *ppf, const char *globname, progsnum_ ddef16_t *var16; ddef32_t *var32; if (pnum == PR_CURRENT) - pnum = pr_typecurrent; + pnum = prinst.pr_typecurrent; if (pnum == PR_ANY) { eval_t *ev; - for (i = 0; i < maxprogs; i++) + for (i = 0; i < prinst.maxprogs; i++) { if (!pr_progstate[i].progs) continue; @@ -693,7 +693,7 @@ eval_t *PDECL PR_FindGlobal(pubprogfuncs_t *ppf, const char *globname, progsnum_ } return NULL; } - if (pnum < 0 || (unsigned)pnum >= maxprogs || !pr_progstate[pnum].progs) + if (pnum < 0 || (unsigned)pnum >= prinst.maxprogs || !pr_progstate[pnum].progs) return NULL; switch(pr_progstate[pnum].structtype) { @@ -801,7 +801,7 @@ eval_t *PDECL QC_GetEdictFieldValue(pubprogfuncs_t *ppf, struct edict_s *ed, cha struct edict_s *PDECL ProgsToEdict (pubprogfuncs_t *ppf, int progs) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; - if ((unsigned)progs >= (unsigned)maxedicts) + if ((unsigned)progs >= (unsigned)prinst.maxedicts) { printf("Bad entity index %i\n", progs); if (pr_depth) @@ -1178,7 +1178,7 @@ pbool PDECL PR_DumpProfiles (pubprogfuncs_t *ppf, pbool resetprofiles) cpufrequency = Sys_GetClockRate(); - for (i = 0; i < maxprogs; i++) + for (i = 0; i < prinst.maxprogs; i++) { ps = &pr_progstate[i]; if (ps->progs == NULL) //we havn't loaded it yet, for some reason diff --git a/engine/qclib/pr_comp.h b/engine/qclib/pr_comp.h index 0004a566..7c2ca8d5 100644 --- a/engine/qclib/pr_comp.h +++ b/engine/qclib/pr_comp.h @@ -381,11 +381,13 @@ enum qcop_e { OP_BITCLR_I, OP_ADD_SI, + OP_ADD_IS, OP_ADD_PF, OP_ADD_FP, OP_ADD_PI, OP_ADD_IP, + OP_SUB_SI, OP_SUB_PF, OP_SUB_PI, @@ -395,12 +397,12 @@ enum qcop_e { OP_MOD_I, OP_MOD_V, - OP_BITXOR_F, //140 + OP_BITXOR_F, OP_RSHIFT_F, OP_LSHIFT_F, OP_AND_ANY, - OP_OR_ANY, //190 + OP_OR_ANY, OP_NUMOPS }; diff --git a/engine/qclib/pr_edict.c b/engine/qclib/pr_edict.c index 776e158b..fb795db3 100644 --- a/engine/qclib/pr_edict.c +++ b/engine/qclib/pr_edict.c @@ -24,7 +24,7 @@ void PDECL QC_ClearEdict (pubprogfuncs_t *ppf, struct edict_s *ed) progfuncs_t *progfuncs = (progfuncs_t*)ppf; edictrun_t *e = (edictrun_t *)ed; int num = e->entnum; - memset (e->fields, 0, fields_size); + memset (e->fields, 0, prinst.fields_size); e->isfree = false; e->entnum = num; } @@ -35,7 +35,7 @@ edictrun_t *ED_AllocIntoTable (progfuncs_t *progfuncs, int num) prinst.edicttable[num] = *(struct edict_s **)&e = (void*)externs->memalloc(externs->edictsize); memset(e, 0, externs->edictsize); - e->fields = PRAddressableExtend(progfuncs, NULL, fields_size, 0); + e->fields = PRAddressableExtend(progfuncs, NULL, prinst.fields_size, 0); e->entnum = num; QC_ClearEdict(&progfuncs->funcs, (struct edict_s*)e); @@ -80,7 +80,7 @@ struct edict_s *PDECL ED_Alloc (pubprogfuncs_t *ppf) } } - if (i >= maxedicts-1) //try again, but use timed out ents. + if (i >= prinst.maxedicts-1) //try again, but use timed out ents. { for ( i=0 ; i= maxedicts-2) + if (i >= prinst.maxedicts-2) { PR_RunWarning(&progfuncs->funcs, "Running out of edicts\n"); } - if (i >= maxedicts-1) + if (i >= prinst.maxedicts-1) { int size; char *buf; buf = PR_SaveEnts(&progfuncs->funcs, NULL, &size, 0, 0); progfuncs->funcs.parms->WriteFile("edalloc.dump", buf, size); - Sys_Error ("ED_Alloc: no free edicts (max is %i)", maxedicts); + Sys_Error ("ED_Alloc: no free edicts (max is %i)", prinst.maxedicts); } } @@ -456,14 +456,14 @@ mfunction_t *ED_FindFunction (progfuncs_t *progfuncs, const char *name, progsnum if (fromprogs>=0) pnum = fromprogs; else - pnum = pr_typecurrent; + pnum = prinst.pr_typecurrent; } *prnum = pnum; } else - pnum = pr_typecurrent; + pnum = prinst.pr_typecurrent; - if ((unsigned)pnum > (unsigned)maxprogs) + if ((unsigned)pnum > (unsigned)prinst.maxprogs) { printf("Progsnum %i out of bounds\n", pnum); return NULL; @@ -560,7 +560,7 @@ char *PR_ValueString (progfuncs_t *progfuncs, etype_t type, eval_t *val, pbool v QC_snprintfz (line, sizeof(line), "NULL function"); else { - if ((val->function & 0xff000000)>>24 >= (unsigned)maxprogs || !pr_progstate[(val->function & 0xff000000)>>24].functions) + if ((val->function & 0xff000000)>>24 >= prinst.maxprogs || !pr_progstate[(val->function & 0xff000000)>>24].functions) QC_snprintfz (line, sizeof(line), "Bad function %i:%i", (val->function & 0xff000000)>>24, val->function & ~0xff000000); else { @@ -696,7 +696,7 @@ char *PDECL PR_UglyValueString (pubprogfuncs_t *ppf, etype_t type, eval_t *val) break; case ev_function: i = (val->function & 0xff000000)>>24; //progs number - if ((unsigned)i >= maxprogs || !pr_progstate[(unsigned)i].progs) + if ((unsigned)i >= prinst.maxprogs || !pr_progstate[(unsigned)i].progs) sprintf (line, "BAD FUNCTION INDEX: %i", val->function); else { @@ -1445,7 +1445,7 @@ char *ED_WriteGlobals(progfuncs_t *progfuncs, char *buf, int *bufofs, int bufmax unsigned int j; const char *name; int type; - int curprogs = pr_typecurrent; + int curprogs = prinst.pr_typecurrent; int len; switch(current_progstate->structtype) { @@ -1693,7 +1693,7 @@ char *SaveCallStack (progfuncs_t *progfuncs, char *buf, int *bufofs, int bufmax) AddS ("\t}\n"); if (i == pr_depth) - globalbase = localstack + localstack_used - f->locals; + globalbase = prinst.localstack + prinst.localstack_used - f->locals; else globalbase -= f->locals; } @@ -1726,12 +1726,12 @@ char *PDECL PR_SaveEnts(pubprogfuncs_t *ppf, char *buf, int *bufofs, int bufmax, //engine will need to store references to progs type and will need to preload the progs and inti the ents itself before loading. //Make sure there is only 1 progs loaded. - for (a = 1; a < maxprogs; a++) + for (a = 1; a < prinst.maxprogs; a++) { if (pr_progstate[a].progs) break; } - if (!pr_progstate[0].progs || a != maxprogs) //the state of the progs wasn't Q1 compatible. + if (!pr_progstate[0].progs || a != prinst.maxprogs) //the state of the progs wasn't Q1 compatible. { externs->memfree(buffree); return NULL; @@ -1740,7 +1740,7 @@ char *PDECL PR_SaveEnts(pubprogfuncs_t *ppf, char *buf, int *bufofs, int bufmax, //write the globals AddS ("{\n"); - oldprogs = pr_typecurrent; + oldprogs = prinst.pr_typecurrent; PR_SwitchProgs(progfuncs, 0); ED_WriteGlobals(progfuncs, buf, bufofs, bufmax); @@ -1769,16 +1769,16 @@ char *PDECL PR_SaveEnts(pubprogfuncs_t *ppf, char *buf, int *bufofs, int bufmax, if (alldata) { AddS("general {\n"); - AddS(qcva("\"maxprogs\" \"%i\"\n", maxprogs)); + AddS(qcva("\"maxprogs\" \"%i\"\n", prinst.maxprogs)); // AddS(qcva("\"maxentities\" \"%i\"\n", maxedicts)); // AddS(qcva("\"mem\" \"%i\"\n", hunksize)); // AddS(qcva("\"crc\" \"%i\"\n", header_crc)); AddS(qcva("\"numentities\" \"%i\"\n", sv_num_edicts)); AddS("}\n"); - oldprogs = pr_typecurrent; + oldprogs = prinst.pr_typecurrent; - for (a = 0; a < maxprogs; a++) + for (a = 0; a < prinst.maxprogs; a++) { if (!pr_progstate[a].progs) continue; @@ -1798,7 +1798,7 @@ char *PDECL PR_SaveEnts(pubprogfuncs_t *ppf, char *buf, int *bufofs, int bufmax, AddS("}\n"); } - for (a = 0; a < maxprogs; a++) //I would mix, but external functions rely on other progs being loaded + for (a = 0; a < prinst.maxprogs; a++) //I would mix, but external functions rely on other progs being loaded { if (!pr_progstate[a].progs) continue; @@ -1892,7 +1892,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, float killonspawnfl { if (entsize == 0 && resethunk) //edicts have not yet been initialized, and this is a compleate load (memsize has been set) { - entsize = PR_InitEnts(&progfuncs->funcs, maxedicts); + entsize = PR_InitEnts(&progfuncs->funcs, prinst.maxedicts); // sv_num_edicts = numents; for (num = 0; num < numents; num++) @@ -1933,7 +1933,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, float killonspawnfl if (killonspawnflags) { - var = QC_GetEdictFieldValue (&progfuncs->funcs, (struct edict_s *)&ed, "spawnflags", &spawnflagscache); + var = QC_GetEdictFieldValue (&progfuncs->funcs, (struct edict_s *)&ed, "spawnflags", &prinst.spawnflagscache); if (var) { if ((int)var->_float & (int)killonspawnflags) @@ -2006,7 +2006,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, float killonspawnfl { if (entsize == 0 && resethunk) //by the time we parse some globals, we MUST have loaded all progs { - entsize = PR_InitEnts(&progfuncs->funcs, maxedicts); + entsize = PR_InitEnts(&progfuncs->funcs, prinst.maxedicts); // sv_num_edicts = numents; for (num = 0; num < numents; num++) @@ -2083,7 +2083,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, float killonspawnfl QC_StartShares(progfuncs); // QC_InitShares(); //forget stuff // pr_edict_size = 0; - max_fields_size=0; + prinst.max_fields_size=0; file = QCC_COM_Parse(file); if (qcc_token[0] != '{') @@ -2096,7 +2096,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, float killonspawnfl Sys_Error("EOF in general block"); if (!strcmp("maxprogs", qcc_token)) //check key get and save values - {file = QCC_COM_Parse(file); maxprogs = atoi(qcc_token);} + {file = QCC_COM_Parse(file); prinst.maxprogs = atoi(qcc_token);} // else if (!strcmp("maxentities", com_token)) // {file = QCC_COM_Parse(file); maxedicts = atoi(qcc_token);} // else if (!strcmp("mem", com_token)) @@ -2129,8 +2129,8 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, float killonspawnfl PRAddressableFlush(progfuncs, 0); resethunk=true; - pr_progstate = PRHunkAlloc(progfuncs, sizeof(progstate_t) * maxprogs, "progstatetable"); - pr_typecurrent=0; + pr_progstate = PRHunkAlloc(progfuncs, sizeof(progstate_t) * prinst.maxprogs, "progstatetable"); + prinst.pr_typecurrent=0; sv_num_edicts = 1; //set up a safty buffer so things won't go horribly wrong too often sv_edicts=(struct edict_s *)&tempedict; @@ -2206,7 +2206,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, float killonspawnfl if (entsize == 0 && resethunk) //edicts have not yet been initialized, and this is a compleate load (memsize has been set) { - entsize = PR_InitEnts(&progfuncs->funcs, maxedicts); + entsize = PR_InitEnts(&progfuncs->funcs, prinst.maxedicts); // sv_num_edicts = numents; for (num = 0; num < numents; num++) @@ -2232,7 +2232,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, float killonspawnfl if (killonspawnflags) { - var = QC_GetEdictFieldValue (&progfuncs->funcs, (struct edict_s *)ed, "spawnflags", &spawnflagscache); + var = QC_GetEdictFieldValue (&progfuncs->funcs, (struct edict_s *)ed, "spawnflags", &prinst.spawnflagscache); if (var) { if ((int)var->_float & (int)killonspawnflags) @@ -2311,7 +2311,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, float killonspawnfl //only warn on the first occurence of the classname, don't spam. int i; const char *fnc = PR_StringToNative(&progfuncs->funcs, var->string); - if (pr_typecurrent >= 0) + if (prinst.pr_typecurrent >= 0) for (i = 0; i < sizeof(spawnwarned)/sizeof(spawnwarned[0]); i++) { if (!spawnwarned[i]) @@ -2349,7 +2349,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, float killonspawnfl return entsize; } else - return max_fields_size; + return prinst.max_fields_size; } //FIXME: maxsize is ignored. @@ -2630,7 +2630,7 @@ int PR_ReallyLoadProgs (progfuncs_t *progfuncs, const char *filename, progstate_ // for (i=0 ; iautocompile == PR_COMPILEALWAYS) //always compile before loading { @@ -2751,7 +2751,7 @@ retry: if (!trysleft) //the progs exists, let's just be happy about it. printf("Progs is out of date and uncompilable\n"); - if (externs->CheckHeaderCrc && !externs->CheckHeaderCrc(&progfuncs->funcs, pr_typecurrent, pr_progs->crc)) + if (externs->CheckHeaderCrc && !externs->CheckHeaderCrc(&progfuncs->funcs, prinst.pr_typecurrent, pr_progs->crc)) { // printf ("%s system vars have been modified, progdefs.h is out of date\n", filename); PRHunkFree(progfuncs, hmark); @@ -3048,7 +3048,7 @@ retry: else type = fld16[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL); - if (progfuncs->funcs.fieldadjust && !pr_typecurrent) //we need to make sure all fields appear in their original place. + if (progfuncs->funcs.fieldadjust && !prinst.pr_typecurrent) //we need to make sure all fields appear in their original place. QC_RegisterFieldVar(&progfuncs->funcs, type, fld16[i].s_name+pr_strings, 4*(fld16[i].ofs+progfuncs->funcs.fieldadjust), -1); else if (type == ev_vector) //emit vector vars early, so their fields cannot be alocated before the vector itself. (useful against scramblers) { @@ -3073,14 +3073,14 @@ retry: nf->progsofs = fld16[i].ofs; nf->ofs = fld16[i].ofs; - if (fields_size < (nf->ofs+type_size[nf->type])*4) - fields_size = (nf->ofs+type_size[nf->type])*4; + if (prinst.fields_size < (nf->ofs+type_size[nf->type])*4) + prinst.fields_size = (nf->ofs+type_size[nf->type])*4; prinst.numfields++; } fld16[i].s_name += stringadjust; } - if (reorg && !(progfuncs->funcs.fieldadjust && !pr_typecurrent)) + if (reorg && !(progfuncs->funcs.fieldadjust && !prinst.pr_typecurrent)) for (i=0 ; inumfielddefs ; i++) { if (pr_types) @@ -3136,14 +3136,14 @@ retry: type = pr_types[pr_fielddefs32[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].type; else type = pr_fielddefs32[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL); - if (progfuncs->funcs.fieldadjust && !pr_typecurrent) //we need to make sure all fields appear in their original place. + if (progfuncs->funcs.fieldadjust && !prinst.pr_typecurrent) //we need to make sure all fields appear in their original place. QC_RegisterFieldVar(&progfuncs->funcs, type, pr_fielddefs32[i].s_name+pr_strings, 4*(pr_fielddefs32[i].ofs+progfuncs->funcs.fieldadjust), -1); else if (type == ev_vector) QC_RegisterFieldVar(&progfuncs->funcs, type, pr_fielddefs32[i].s_name+pr_strings, -1, pr_fielddefs32[i].ofs); } pr_fielddefs32[i].s_name += stringadjust; } - if (reorg && !(progfuncs->funcs.fieldadjust && !pr_typecurrent)) + if (reorg && !(progfuncs->funcs.fieldadjust && !prinst.pr_typecurrent)) for (i=0 ; inumfielddefs ; i++) { if (pr_types) @@ -3234,8 +3234,8 @@ retry: // QC_StartShares(progfuncs); isfriked = true; - if (!pr_typecurrent) //progs 0 always acts as string stripped. - isfriked = -1; //partly to avoid some bad progs. + if (!prinst.pr_typecurrent) //progs 0 always acts as string stripped. + isfriked = -1; //partly to avoid some bad/optimised progs. // len = 0; switch(current_progstate->structtype) @@ -3344,7 +3344,7 @@ retry: Sys_Error("Bad struct type"); } - if ((isfriked && pr_typecurrent)) //friked progs only allow one file. + if ((isfriked && prinst.pr_typecurrent)) //friked progs only allow one file. { printf("You are trying to load a string-stripped progs as an addon.\nThis behaviour is not supported. Try removing some optimizations."); PRHunkFree(progfuncs, hmark); @@ -3442,7 +3442,7 @@ retry: struct edict_s *PDECL QC_EDICT_NUM(pubprogfuncs_t *ppf, unsigned int n) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; - if (n >= maxedicts) + if (n >= prinst.maxedicts) Sys_Error ("QCLIB: EDICT_NUM: bad number %i", n); return prinst.edicttable[n]; @@ -3452,7 +3452,7 @@ unsigned int PDECL QC_NUM_FOR_EDICT(pubprogfuncs_t *ppf, struct edict_s *e) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; edictrun_t *er = (edictrun_t*)e; - if (er->entnum >= maxedicts) + if (!er || er->entnum >= prinst.maxedicts) Sys_Error ("QCLIB: NUM_FOR_EDICT: bad pointer (%p)", e); return er->entnum; } diff --git a/engine/qclib/pr_exec.c b/engine/qclib/pr_exec.c index 9090a09b..21f6a38f 100644 --- a/engine/qclib/pr_exec.c +++ b/engine/qclib/pr_exec.c @@ -356,19 +356,21 @@ void PDECL PR_StackTrace (pubprogfuncs_t *ppf, int showlocals) //locals:1 = top only //locals:2 = ALL locals. if ((i == pr_depth && showlocals == 1) || showlocals >= 2) - for (arg = 0; arg < f->locals; arg++) { - ddef16_t *local; - local = ED_GlobalAtOfs16(progfuncs, f->parm_start+arg); - if (!local) + for (arg = 0; arg < f->locals; arg++) { - //printf(" ofs %i: %f : %i\n", f->parm_start+arg, *(float *)(globalbase - f->locals+arg), *(int *)(globalbase - f->locals+arg) ); - } - else - { - printf(" %s: %s\n", local->s_name+progfuncs->funcs.stringtable, PR_ValueString(progfuncs, local->type, (eval_t*)(globalbase+arg), false)); - if (local->type == ev_vector) - arg+=2; + ddef16_t *local; + local = ED_GlobalAtOfs16(progfuncs, f->parm_start+arg); + if (!local) + { + //printf(" ofs %i: %f : %i\n", f->parm_start+arg, *(float *)(globalbase - f->locals+arg), *(int *)(globalbase - f->locals+arg) ); + } + else + { + printf(" %s: %s\n", local->s_name+progfuncs->funcs.stringtable, PR_ValueString(progfuncs, local->type, (eval_t*)(globalbase+arg), false)); + if (local->type == ev_vector) + arg+=2; + } } } if (i == pr_depth) @@ -377,7 +379,7 @@ void PDECL PR_StackTrace (pubprogfuncs_t *ppf, int showlocals) } if (i == pr_depth) - globalbase = localstack + localstack_used; + globalbase = prinst.localstack + prinst.localstack_used; } } progfuncs->funcs.debug_trace = tracing; @@ -405,7 +407,7 @@ int ASMCALL PR_EnterFunction (progfuncs_t *progfuncs, mfunction_t *f, int progsn pr_stack[pr_depth].s = pr_xstatement; pr_stack[pr_depth].f = pr_xfunction; pr_stack[pr_depth].progsnum = progsnum; - pr_stack[pr_depth].pushed = pr_spushed; + pr_stack[pr_depth].pushed = prinst.spushed; pr_stack[pr_depth].stepping = progfuncs->funcs.debug_trace; if (progfuncs->funcs.debug_trace == DEBUG_TRACE_OVER) progfuncs->funcs.debug_trace = DEBUG_TRACE_OFF; @@ -427,20 +429,20 @@ int ASMCALL PR_EnterFunction (progfuncs_t *progfuncs, mfunction_t *f, int progsn return pr_xstatement; } - localstack_used += pr_spushed; //make sure the call doesn't hurt pushed pointers + prinst.localstack_used += prinst.spushed; //make sure the call doesn't hurt pushed pointers // save off any locals that the new function steps on (to a side place, fromwhere they are restored on exit) c = f->locals; - if (localstack_used + c > LOCALSTACK_SIZE) + if (prinst.localstack_used + c > LOCALSTACK_SIZE) { - localstack_used -= pr_spushed; + prinst.localstack_used -= prinst.spushed; pr_depth--; PR_RunError (&progfuncs->funcs, "PR_ExecuteProgram: locals stack overflow\n"); } for (i=0 ; i < c ; i++) - localstack[localstack_used+i] = ((int *)pr_globals)[f->parm_start + i]; - localstack_used += c; + prinst.localstack[prinst.localstack_used+i] = ((int *)pr_globals)[f->parm_start + i]; + prinst.localstack_used += c; // copy parameters (set initial values) o = f->parm_start; @@ -471,18 +473,18 @@ int ASMCALL PR_LeaveFunction (progfuncs_t *progfuncs) // restore locals from the stack c = pr_xfunction->locals; - localstack_used -= c; - if (localstack_used < 0) + prinst.localstack_used -= c; + if (prinst.localstack_used < 0) PR_RunError (&progfuncs->funcs, "PR_ExecuteProgram: locals stack underflow\n"); for (i=0 ; i < c ; i++) - ((int *)pr_globals)[pr_xfunction->parm_start + i] = localstack[localstack_used+i]; + ((int *)pr_globals)[pr_xfunction->parm_start + i] = prinst.localstack[prinst.localstack_used+i]; // up stack pr_depth--; PR_SwitchProgsParms(progfuncs, pr_stack[pr_depth].progsnum); - pr_spushed = pr_stack[pr_depth].pushed; + prinst.spushed = pr_stack[pr_depth].pushed; if (!progfuncs->funcs.debug_trace) progfuncs->funcs.debug_trace = pr_stack[pr_depth].stepping; @@ -499,7 +501,7 @@ int ASMCALL PR_LeaveFunction (progfuncs_t *progfuncs) else pr_xfunction = pr_stack[pr_depth].f; - localstack_used -= pr_spushed; + prinst.localstack_used -= prinst.spushed; return pr_stack[pr_depth].s; } @@ -510,10 +512,10 @@ ddef32_t *ED_FindLocalOrGlobal(progfuncs_t *progfuncs, char *name, eval_t **val) ddef16_t *def16; int i; - if (pr_typecurrent < 0) + if (prinst.pr_typecurrent < 0) return NULL; - switch (pr_progstate[pr_typecurrent].structtype) + switch (pr_progstate[prinst.pr_typecurrent].structtype) { case PST_DEFAULT: case PST_KKQWSV: @@ -526,7 +528,7 @@ ddef32_t *ED_FindLocalOrGlobal(progfuncs_t *progfuncs, char *name, eval_t **val) continue; if (!strcmp(def16->s_name+progfuncs->funcs.stringtable, name)) { - *val = (eval_t *)&pr_progstate[pr_typecurrent].globals[pr_xfunction->parm_start+i]; + *val = (eval_t *)&pr_progstate[prinst.pr_typecurrent].globals[pr_xfunction->parm_start+i]; //we need something like this for functions that are not the top layer // *val = (eval_t *)&localstack[localstack_used-pr_xfunction->numparms*4]; @@ -555,7 +557,7 @@ ddef32_t *ED_FindLocalOrGlobal(progfuncs_t *progfuncs, char *name, eval_t **val) continue; if (!strcmp(def32->s_name+progfuncs->funcs.stringtable, name)) { - *val = (eval_t *)&pr_progstate[pr_typecurrent].globals[pr_xfunction->parm_start+i]; + *val = (eval_t *)&pr_progstate[prinst.pr_typecurrent].globals[pr_xfunction->parm_start+i]; //we need something like this for functions that are not the top layer // *val = (eval_t *)&localstack[localstack_used-pr_xfunction->numparms*4]; @@ -571,7 +573,7 @@ ddef32_t *ED_FindLocalOrGlobal(progfuncs_t *progfuncs, char *name, eval_t **val) def32 = NULL; } - *val = (eval_t *)&pr_progstate[pr_typecurrent].globals[def32->ofs]; + *val = (eval_t *)&pr_progstate[prinst.pr_typecurrent].globals[def32->ofs]; return &def; } @@ -672,7 +674,7 @@ pbool LocateDebugTerm(progfuncs_t *progfuncs, char *key, eval_t **result, etype_ ed = PROG_TO_EDICT(progfuncs, val->_int); if (!ed) return false; - if (fofs < 0 || fofs >= (int)max_fields_size) + if (fofs < 0 || fofs >= (int)prinst.max_fields_size) return false; val = (eval_t *) (((char *)ed->fields) + fofs*4); } @@ -908,7 +910,7 @@ char *PDECL PR_EvaluateDebugString(pubprogfuncs_t *ppf, char *key) //int EditorHighlightLine(window_t *wnd, int line); void SetExecutionToLine(progfuncs_t *progfuncs, int linenum) { - int pn = pr_typecurrent; + int pn = prinst.pr_typecurrent; int snum; const mfunction_t *f = pr_xfunction; @@ -945,11 +947,11 @@ int PDECL PR_ToggleBreakpoint(pubprogfuncs_t *ppf, char *filename, int linenum, int ret=0; unsigned int fl; unsigned int i; - int pn = pr_typecurrent; + int pn = prinst.pr_typecurrent; mfunction_t *f; int op = 0; //warning about not being initialized before use - for (pn = 0; (unsigned)pn < maxprogs; pn++) + for (pn = 0; (unsigned)pn < prinst.maxprogs; pn++) { if (!pr_progstate || !pr_progstate[pn].progs) continue; @@ -1119,7 +1121,7 @@ static int lastline = 0; static int ignorestatement = 0; // static const char *lastfile = 0; - int pn = pr_typecurrent; + int pn = prinst.pr_typecurrent; int i; const mfunction_t *f = pr_xfunction; int faultline; @@ -1202,6 +1204,13 @@ static const char *lastfile = 0; continue; else if(debugaction == DEBUG_TRACE_ABORT) progfuncs->funcs.parms->Abort ("Debugging terminated"); + else if (debugaction == DEBUG_TRACE_OFF) + { + //if we're resuming, don't hit any lingering step-over triggers + progfuncs->funcs.debug_trace = DEBUG_TRACE_OFF; + for (i = 0; i < pr_depth; i++) + pr_stack[pr_depth-1].stepping = DEBUG_TRACE_OFF; + } else if (debugaction == DEBUG_TRACE_OUT) { //clear tracing for now, but ensure that it'll be reactivated once we reach the caller (if from qc) @@ -1314,7 +1323,7 @@ pbool PR_RunWarning (pubprogfuncs_t *ppf, char *error, ...) const char *PR_GetEdictClassname(progfuncs_t *progfuncs, int edict) { fdef_t *cnfd = ED_FindField(progfuncs, "classname"); - if (cnfd && edict < maxedicts) + if (cnfd && edict < prinst.maxedicts) { string_t *v = (string_t *)((char *)edvars(PROG_TO_EDICT(progfuncs, edict)) + cnfd->ofs*4); return PR_StringToNative(&progfuncs->funcs, *v); @@ -1367,7 +1376,7 @@ static casecmprange_t casecmprange[] = printf ("runaway loop error\n"); \ while(pr_depth > prinst.exitdepth) \ PR_LeaveFunction(progfuncs); \ - pr_spushed = 0; \ + prinst.spushed = 0; \ return -1; \ } @@ -1562,10 +1571,10 @@ void PDECL PR_ExecuteProgram (pubprogfuncs_t *ppf, func_t fnum) unsigned int newprogs = (fnum & 0xff000000)>>24; - initial_progs = pr_typecurrent; + initial_progs = prinst.pr_typecurrent; if (newprogs != initial_progs) { - if (newprogs >= maxprogs || !&pr_progstate[newprogs].globals) //can happen with hexen2... + if (newprogs >= prinst.maxprogs || !&pr_progstate[newprogs].globals) //can happen with hexen2... { printf("PR_ExecuteProgram: tried branching into invalid progs\n"); return; @@ -1701,7 +1710,7 @@ struct qcthread_s *PDECL PR_ForkStack(pubprogfuncs_t *ppf) for (l = 0; l < f->locals; l++) { thread->lstack[localsoffset-baselocalsoffset + l ] = ((int *)pr_globals)[f->parm_start + l]; - ((int *)pr_globals)[f->parm_start + l] = localstack[localsoffset+l]; //copy the old value into the globals (so the older functions have the correct locals. + ((int *)pr_globals)[f->parm_start + l] = prinst.localstack[localsoffset+l]; //copy the old value into the globals (so the older functions have the correct locals. } } @@ -1721,8 +1730,8 @@ struct qcthread_s *PDECL PR_ForkStack(pubprogfuncs_t *ppf) thread->lstackused = localsoffset - baselocalsoffset; thread->xstatement = pr_xstatement; - thread->xfunction = pr_xfunction - pr_progstate[pr_typecurrent].functions; - thread->xprogs = pr_typecurrent; + thread->xfunction = pr_xfunction - pr_progstate[prinst.pr_typecurrent].functions; + thread->xprogs = prinst.pr_typecurrent; return thread; } @@ -1743,7 +1752,7 @@ void PDECL PR_ResumeThread (pubprogfuncs_t *ppf, struct qcthread_s *thread) progsnum_t prnum = thread->xprogs; int fnum = thread->xfunction; - if (localstack_used + thread->lstackused > LOCALSTACK_SIZE) + if (prinst.localstack_used + thread->lstackused > LOCALSTACK_SIZE) PR_RunError(&progfuncs->funcs, "Too many locals on resumtion of QC thread\n"); if (pr_depth + thread->fstackdepth > MAX_STACK_DEPTH) @@ -1751,7 +1760,7 @@ void PDECL PR_ResumeThread (pubprogfuncs_t *ppf, struct qcthread_s *thread) //do progs switching stuff as appropriate. (fteqw only) - initial_progs = pr_typecurrent; + initial_progs = prinst.pr_typecurrent; PR_SwitchProgsParms(progfuncs, prnum); @@ -1781,7 +1790,7 @@ void PDECL PR_ResumeThread (pubprogfuncs_t *ppf, struct qcthread_s *thread) f = pr_progstate[thread->fstack[i+1].progsnum].functions + thread->fstack[i+1].fnum; for (l = 0; l < f->locals; l++) { - localstack[localstack_used++] = ((int *)pr_globals)[f->parm_start + l]; + prinst.localstack[prinst.localstack_used++] = ((int *)pr_globals)[f->parm_start + l]; ((int *)pr_globals)[f->parm_start + l] = thread->lstack[ls++]; } @@ -1797,8 +1806,8 @@ void PDECL PR_ResumeThread (pubprogfuncs_t *ppf, struct qcthread_s *thread) // thread->lstackused -= f->locals; //the current function is the odd one out. //add on the locals stack - memcpy(localstack+localstack_used, thread->lstack, sizeof(int)*thread->lstackused); - localstack_used += thread->lstackused; + memcpy(prinst.localstack+prinst.localstack_used, thread->lstack, sizeof(int)*thread->lstackused); + prinst.localstack_used += thread->lstackused; //bung the locals of the current function on the stack. // for (i=0 ; i < f->locals ; i++) diff --git a/engine/qclib/pr_multi.c b/engine/qclib/pr_multi.c index e1c8ff92..ff23b62c 100644 --- a/engine/qclib/pr_multi.c +++ b/engine/qclib/pr_multi.c @@ -19,11 +19,11 @@ int maxshares; //switches progs without preserving parms/ret/shared pbool PR_SwitchProgs(progfuncs_t *progfuncs, progsnum_t type) { - if ((unsigned)type >= maxprogs) + if ((unsigned)type >= prinst.maxprogs) { if (type == -1) { - pr_typecurrent = -1; + prinst.pr_typecurrent = -1; current_progstate = NULL; return true; } @@ -36,7 +36,7 @@ pbool PR_SwitchProgs(progfuncs_t *progfuncs, progsnum_t type) current_progstate = &pr_progstate[(unsigned)type]; - pr_typecurrent = type; + prinst.pr_typecurrent = type; return true; } @@ -47,7 +47,7 @@ pbool PR_SwitchProgsParms(progfuncs_t *progfuncs, progsnum_t newpr) //from 2 to unsigned int a; progstate_t *np; progstate_t *op; - int oldpr = pr_typecurrent; + int oldpr = prinst.pr_typecurrent; if (newpr == oldpr) { @@ -58,12 +58,12 @@ pbool PR_SwitchProgsParms(progfuncs_t *progfuncs, progsnum_t newpr) //from 2 to np = &pr_progstate[(int)newpr]; op = &pr_progstate[(int)oldpr]; - if ((unsigned)newpr >= maxprogs || !np->globals) + if ((unsigned)newpr >= prinst.maxprogs || !np->globals) { printf("QCLIB: Bad prog type - %i", newpr); return false; } - if ((unsigned)oldpr >= maxprogs || !op->globals) //startup? + if ((unsigned)oldpr >= prinst.maxprogs || !op->globals) //startup? return PR_SwitchProgs(progfuncs, newpr); //copy parms. @@ -78,9 +78,9 @@ pbool PR_SwitchProgsParms(progfuncs_t *progfuncs, progsnum_t newpr) //from 2 to np->globals[OFS_RETURN+2] = op->globals[OFS_RETURN+2]; //move the vars defined as shared. - for (a = 0; a < numshares; a++)//fixme: make offset per progs + for (a = 0; a < prinst.numshares; a++)//fixme: make offset per progs { - memmove(&((int *)np->globals)[shares[a].varofs], &((int *)op->globals)[shares[a].varofs], shares[a].size*4); + memmove(&((int *)np->globals)[prinst.shares[a].varofs], &((int *)op->globals)[prinst.shares[a].varofs], prinst.shares[a].size*4); /* ((int *)p1->globals)[shares[a].varofs] = ((int *)p2->globals)[shares[a].varofs]; if (shares[a].size > 1) { @@ -98,12 +98,12 @@ progsnum_t PDECL PR_LoadProgs(pubprogfuncs_t *ppf, const char *s) progfuncs_t *progfuncs = (progfuncs_t*)ppf; unsigned int a; progsnum_t oldtype; - oldtype = pr_typecurrent; - for (a = 0; a < maxprogs; a++) + oldtype = prinst.pr_typecurrent; + for (a = 0; a < prinst.maxprogs; a++) { if (pr_progstate[a].progs == NULL) { - pr_typecurrent = a; + prinst.pr_typecurrent = a; current_progstate = &pr_progstate[a]; if (PR_ReallyLoadProgs(progfuncs, s, &pr_progstate[a], false)) //try and load it { @@ -136,7 +136,7 @@ void PR_ShiftParms(progfuncs_t *progfuncs, int amount) void PR_Clear(progfuncs_t *progfuncs) { unsigned int a; - for (a = 0; a < maxprogs; a++) + for (a = 0; a < prinst.maxprogs; a++) { #ifdef QCJIT if (pr_progstate[a].jit) @@ -150,11 +150,11 @@ void PR_Clear(progfuncs_t *progfuncs) void QC_StartShares(progfuncs_t *progfuncs) { - numshares = 0; - maxshares = 32; - if (shares) - externs->memfree(shares); - shares = externs->memalloc(sizeof(sharedvar_t)*maxshares); + prinst.numshares = 0; + prinst.maxshares = 32; + if (prinst.shares) + externs->memfree(prinst.shares); + prinst.shares = externs->memalloc(sizeof(sharedvar_t)*prinst.maxshares); } void PDECL QC_AddSharedVar(pubprogfuncs_t *ppf, int start, int size) //fixme: make offset per progs and optional { @@ -162,33 +162,33 @@ void PDECL QC_AddSharedVar(pubprogfuncs_t *ppf, int start, int size) //fixme: ma int ofs; unsigned int a; - if (numshares >= maxshares) + if (prinst.numshares >= prinst.maxshares) { void *buf; - buf = shares; - maxshares += 16; - shares = externs->memalloc(sizeof(sharedvar_t)*maxshares); + buf = prinst.shares; + prinst.maxshares += 16; + prinst.shares = externs->memalloc(sizeof(sharedvar_t)*prinst.maxshares); - memcpy(shares, buf, sizeof(sharedvar_t)*numshares); + memcpy(prinst.shares, buf, sizeof(sharedvar_t)*prinst.numshares); externs->memfree(buf); } ofs = start; - for (a = 0; a < numshares; a++) + for (a = 0; a < prinst.numshares; a++) { - if (shares[a].varofs+shares[a].size == ofs) + if (prinst.shares[a].varofs+prinst.shares[a].size == ofs) { - shares[a].size += size; //expand size. + prinst.shares[a].size += size; //expand size. return; } - if (shares[a].varofs == start) + if (prinst.shares[a].varofs == start) return; } - shares[numshares].varofs = start; - shares[numshares].size = size; - numshares++; + prinst.shares[prinst.numshares].varofs = start; + prinst.shares[prinst.numshares].size = size; + prinst.numshares++; } @@ -240,9 +240,9 @@ int PDECL QC_RegisterFieldVar(pubprogfuncs_t *ppf, unsigned int type, char *name if (!name) //engine can use this to offset all progs fields { //which fixes constant field offsets (some ktpro arrays) - progfuncs->funcs.fieldadjust = fields_size/4; + progfuncs->funcs.fieldadjust = prinst.fields_size/4; #ifdef MAPPING_DEBUG - printf("FIELD ADJUST: %i %i %i\n", progfuncs->funcs.fieldadjust, fields_size, (int)fields_size/4); + printf("FIELD ADJUST: %i %i %i\n", progfuncs->funcs.fieldadjust, prinst.fields_size, (int)prinst.fields_size/4); #endif return 0; } @@ -322,7 +322,7 @@ int PDECL QC_RegisterFieldVar(pubprogfuncs_t *ppf, unsigned int type, char *name } else { //we just found a new fieldname inside a progs - prinst.field[fnum].ofs = ofs = fields_size/4; //add on the end + prinst.field[fnum].ofs = ofs = prinst.fields_size/4; //add on the end //if the progs field offset matches annother offset in the same progs, make it match up with the earlier one. if (progsofs>=0) @@ -361,10 +361,10 @@ int PDECL QC_RegisterFieldVar(pubprogfuncs_t *ppf, unsigned int type, char *name } } // if (type != ev_vector) - if (fields_size < (ofs+type_size[type])*4) - fields_size = (ofs+type_size[type])*4; + if (prinst.fields_size < (ofs+type_size[type])*4) + prinst.fields_size = (ofs+type_size[type])*4; - if (max_fields_size && fields_size > max_fields_size) + if (prinst.max_fields_size && prinst.fields_size > prinst.max_fields_size) Sys_Error("Allocated too many additional fields after ents were inited."); #ifdef MAPPING_DEBUG diff --git a/engine/qclib/progsint.h b/engine/qclib/progsint.h index 5292fb9b..1114e432 100644 --- a/engine/qclib/progsint.h +++ b/engine/qclib/progsint.h @@ -68,6 +68,7 @@ typedef struct unsigned long long timestamp; } prstack_t; +//FIXME: the defines hidden inside this structure are evil. typedef struct prinst_s { char **tempstrings; @@ -86,10 +87,8 @@ typedef struct prinst_s struct progstate_s * progstate; #define pr_progstate prinst.progstate - progsnum_t pr_typecurrent; -#define pr_typecurrent prinst.pr_typecurrent + progsnum_t pr_typecurrent; //active index into progstate array. fixme: remove in favour of only using current_progstate unsigned int maxprogs; -#define maxprogs prinst.maxprogs struct progstate_s *current_progstate; #define current_progstate prinst.current_progstate @@ -100,20 +99,16 @@ typedef struct prinst_s etype_t watch_type; unsigned int numshares; -#define numshares prinst.numshares sharedvar_t *shares; //shared globals, not including parms -#define shares prinst.shares unsigned int maxshares; -#define maxshares prinst.maxshares struct prmemb_s *memblocks; -#define memb prinst.memblocks unsigned int maxfields; unsigned int numfields; fdef_t *field; //biggest size -int reorganisefields; + int reorganisefields; //pr_exec.c @@ -123,13 +118,10 @@ int reorganisefields; int pr_depth; #define pr_depth prinst.pr_depth int spushed; -#define pr_spushed prinst.spushed #define LOCALSTACK_SIZE 4096 int localstack[LOCALSTACK_SIZE]; -#define localstack prinst.localstack int localstack_used; -#define localstack_used prinst.localstack_used int debugstatement; int continuestatement; @@ -144,18 +136,10 @@ int reorganisefields; //pr_edict.c unsigned int maxedicts; -#define maxedicts prinst.maxedicts evalc_t spawnflagscache; -#define spawnflagscache prinst.spawnflagscache - - - - unsigned int fields_size; // in bytes -#define fields_size prinst.fields_size unsigned int max_fields_size; -#define max_fields_size prinst.max_fields_size //initlib.c diff --git a/engine/qclib/progslib.h b/engine/qclib/progslib.h index 416d8413..64a13eae 100644 --- a/engine/qclib/progslib.h +++ b/engine/qclib/progslib.h @@ -53,7 +53,14 @@ typedef struct { } evalc_t; #define sizeofevalc sizeof(evalc_t) typedef enum {ev_void, ev_string, ev_float, ev_vector, ev_entity, ev_field, ev_function, ev_pointer, ev_integer, ev_variant, ev_struct, ev_union, ev_accessor} etype_t; -enum {DEBUG_TRACE_OFF, DEBUG_TRACE_INTO, DEBUG_TRACE_OVER, DEBUG_TRACE_UNBREAK, DEBUG_TRACE_OUT, DEBUG_TRACE_ABORT, DEBUG_TRACE_NORESUME}; +enum { + DEBUG_TRACE_OFF, //debugging should be off. + DEBUG_TRACE_INTO, //debug into functions + DEBUG_TRACE_OVER, //switch debugging off while executing child functions (and back on afterwards) + DEBUG_TRACE_OUT, //keep running until the end of the current function (trigger single-stepping again at that point) + DEBUG_TRACE_ABORT, //give up with an endgame. + DEBUG_TRACE_NORESUME //line number or something changed, but we should still be sitting at the debugger. +}; typedef struct fdef_s { diff --git a/engine/qclib/qcc.h b/engine/qclib/qcc.h index d26beb22..2a504854 100644 --- a/engine/qclib/qcc.h +++ b/engine/qclib/qcc.h @@ -320,6 +320,7 @@ struct QCC_typeparam_s { struct QCC_type_s *type; pbool optional; + pbool out; unsigned int ofs; unsigned int arraysize; char *paramname; @@ -531,6 +532,7 @@ extern pbool keyword_break; extern pbool keyword_case; extern pbool keyword_class; extern pbool keyword_const; +extern pbool keyword_inout; extern pbool keyword_optional; extern pbool keyword_continue; extern pbool keyword_default; diff --git a/engine/qclib/qcc_pr_comp.c b/engine/qclib/qcc_pr_comp.c index a064aa29..008ce312 100644 --- a/engine/qclib/qcc_pr_comp.c +++ b/engine/qclib/qcc_pr_comp.c @@ -36,6 +36,7 @@ pbool keyword_until; //hexen2 pbool keyword_thinktime;//hexen2 pbool keyword_asm; pbool keyword_class; +pbool keyword_inout; pbool keyword_optional; pbool keyword_const; //fixme pbool keyword_entity; //for skipping the local @@ -632,10 +633,12 @@ QCC_opcode_t pr_opcodes[] = {7, "&~", "BITCLR_I", 6, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, {7, "+", "ADD_SI", 4, ASSOC_LEFT, &type_string, &type_integer, &type_string}, + {7, "+", "ADD_IS", 7, ASSOC_LEFT, &type_integer, &type_string, &type_string}, {7, "+", "ADD_PF", 6, ASSOC_LEFT, &type_pointer, &type_float, &type_pointer}, {7, "+", "ADD_FP", 6, ASSOC_LEFT, &type_float, &type_pointer, &type_pointer}, {7, "+", "ADD_PI", 6, ASSOC_LEFT, &type_pointer, &type_integer, &type_pointer}, {7, "+", "ADD_IP", 6, ASSOC_LEFT, &type_integer, &type_pointer, &type_pointer}, + {7, "-", "SUB_SI", 7, ASSOC_LEFT, &type_string, &type_integer, &type_string}, {7, "-", "SUB_PF", 6, ASSOC_LEFT, &type_pointer, &type_float, &type_pointer}, {7, "-", "SUB_PI", 6, ASSOC_LEFT, &type_pointer, &type_integer, &type_pointer}, {7, "-", "SUB_PP", 6, ASSOC_LEFT, &type_pointer, &type_pointer, &type_integer}, @@ -732,6 +735,12 @@ QCC_opcode_t *opcodes_addstore[] = &pr_opcodes[OP_ADD_FI], &pr_opcodes[OP_ADD_IF], &pr_opcodes[OP_ADD_SF], + &pr_opcodes[OP_ADD_PI], + &pr_opcodes[OP_ADD_IP], + &pr_opcodes[OP_ADD_PF], + &pr_opcodes[OP_ADD_FP], + &pr_opcodes[OP_ADD_SI], + &pr_opcodes[OP_ADD_IS], NULL }; QCC_opcode_t *opcodes_substore[] = @@ -752,6 +761,10 @@ QCC_opcode_t *opcodes_substore[] = &pr_opcodes[OP_SUB_FI], &pr_opcodes[OP_SUB_IF], &pr_opcodes[OP_SUB_S], + &pr_opcodes[OP_SUB_PP], + &pr_opcodes[OP_SUB_PI], + &pr_opcodes[OP_SUB_PF], + &pr_opcodes[OP_SUB_SI], NULL }; QCC_opcode_t *opcodes_mulstore[] = @@ -900,11 +913,12 @@ QCC_opcode_t *opcodeprioritized[TOP_PRIORITY+1][128] = &pr_opcodes[OP_BITOR_FI], &pr_opcodes[OP_BITXOR_I], - &pr_opcodes[OP_RSHIFT_I], - &pr_opcodes[OP_LSHIFT_I], - &pr_opcodes[OP_BITXOR_F], + + &pr_opcodes[OP_RSHIFT_I], &pr_opcodes[OP_RSHIFT_F], + + &pr_opcodes[OP_LSHIFT_I], &pr_opcodes[OP_LSHIFT_F], &pr_opcodes[OP_MOD_F], @@ -1339,7 +1353,11 @@ pbool QCC_StatementIsAJump(int stnum, int notifdest); const char *QCC_GetSRefName(QCC_sref_t ref) { if (ref.sym && ref.sym->name && !ref.ofs) + { + if (ref.sym->temp) + return ref.cast->name; return ref.sym->name; + } return "TEMP"; } @@ -1504,7 +1522,7 @@ static void QCC_ClobberDef(QCC_def_t *def) a->nextlocal = NULL; if (a->refcount) { - tmp = QCC_GetTemp(a->type); + tmp = QCC_GetTemp(a->type->type==ev_variant?type_vector:a->type); for (st = a->fromstatement; st < numstatements; st++) { if (statements[st].a.sym == a) @@ -1522,7 +1540,7 @@ static void QCC_ClobberDef(QCC_def_t *def) a->ofs = tmp.sym->ofs; tmp.sym = a; tmp.sym->refcount = a->refcount; - if (a->type->type == ev_vector) + if (a->type->type==ev_variant || a->type->type == ev_vector) QCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_V], from, tmp, NULL, STFL_PRESERVEB)); else QCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_F], from, tmp, NULL, STFL_PRESERVEB)); @@ -1830,7 +1848,7 @@ static void QCC_fprintfLocals(FILE *f, QCC_def_t *locals) { if (!tempsinfo[u].locked) { - fprintf(f, "local %s temp_%i;\n", (tempsinfo[u].size == 1)?"float":"vector", u); + fprintf(f, "local %s temp_%u;\n", (tempsinfo[u].size == 1)?"float":"vector", (unsigned)u); } } } @@ -2805,43 +2823,64 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ } else { - QCC_PR_ParseWarning(0, "string(string,float) AddStringFloat: emulation depends upon denormals"); + QCC_PR_ParseWarning(0, "OP_ADD_SI: string+float may be unsafe"); var_b = QCC_SupplyConversion(var_b, ev_integer, true); //FIXME: this should be an unconditional float->int conversion - var_c = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_F], var_a, var_b, NULL, 0); + var_c = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_I], var_a, var_b, NULL, 0); } var_c.cast = type_string; return var_c; case OP_ADD_SI: - QCC_PR_ParseWarning(0, "OP_ADD_SI: denormals may be unsafe"); - var_c = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_F], var_a, var_b, NULL, 0); + case OP_ADD_IS: + QCC_PR_ParseWarning(0, "OP_ADD_SI: string+int may be unsafe"); + var_c = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_I], var_a, var_b, NULL, 0); var_c.cast = type_string; return var_c; case OP_ADD_PF: case OP_ADD_FP: case OP_ADD_PI: case OP_ADD_IP: - var_c = (op == &pr_opcodes[OP_ADD_PF] || op == &pr_opcodes[OP_ADD_PI])?var_a:var_b; - var_b = (op == &pr_opcodes[OP_ADD_PF] || op == &pr_opcodes[OP_ADD_PI])?var_b:var_a; - if (op == &pr_opcodes[OP_ADD_FP] || op == &pr_opcodes[OP_ADD_PF]) - var_b = QCC_SupplyConversion(var_b, ev_integer, true); //FIXME: this should be an unconditional float->int conversion - var_b = QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_I], var_b, QCC_MakeIntConst(var_c.cast->size), NULL, 0); - return QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_PIW], var_c, var_b, NULL, 0); + { + QCC_type_t *t; + var_c = (op == &pr_opcodes[OP_ADD_PF] || op == &pr_opcodes[OP_ADD_PI])?var_a:var_b; + t = var_c.cast; + var_b = (op == &pr_opcodes[OP_ADD_PF] || op == &pr_opcodes[OP_ADD_PI])?var_b:var_a; + if (op == &pr_opcodes[OP_ADD_FP] || op == &pr_opcodes[OP_ADD_PF]) + var_b = QCC_SupplyConversion(var_b, ev_integer, true); //FIXME: this should be an unconditional float->int conversion + if (var_c.cast->aux_type->type == ev_void) //void* is treated as a byte type. + var_c = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_I], var_c, var_b, NULL, 0); + else + { + var_b = QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_I], var_b, QCC_MakeIntConst(var_c.cast->aux_type->size), NULL, 0); + var_c = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_PIW], var_c, var_b, NULL, 0); + } + var_c.cast = t; + } + return var_c; case OP_SUB_PF: case OP_SUB_PI: var_c = var_a; var_b = var_b; if (op == &pr_opcodes[OP_SUB_PF]) var_b = QCC_SupplyConversion(var_b, ev_integer, true); //FIXME: this should be an unconditional float->int conversion - //fixme: word size - var_b = QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_I], var_b, QCC_MakeIntConst(var_c.cast->size*4), NULL, 0); - return QCC_PR_StatementFlags(&pr_opcodes[OP_SUB_I], var_c, var_b, NULL, 0); + if (var_c.cast->aux_type->type == ev_void) //void* is treated as a byte type. + var_c = QCC_PR_StatementFlags(&pr_opcodes[OP_SUB_I], var_c, var_b, NULL, 0); + else + { + //fixme: word size + var_b = QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_I], var_b, QCC_MakeIntConst(var_c.cast->aux_type->size*4), NULL, 0); + var_c = QCC_PR_StatementFlags(&pr_opcodes[OP_SUB_I], var_c, var_b, NULL, 0); + } + var_c.cast = var_a.cast; + return var_c; case OP_SUB_PP: if (typecmp(var_a.cast, var_b.cast)) QCC_PR_ParseError(0, "incompatible pointer types"); //determine byte offset var_c = QCC_PR_StatementFlags(&pr_opcodes[OP_SUB_I], var_a, var_b, NULL, 0); + if (var_a.cast->aux_type->type == ev_void) + return var_c; //we're done if we're using void/bytes //determine divisor (fixme: word size) - var_b = QCC_MakeIntConst(var_c.cast->size*4); + var_b = QCC_MakeIntConst(var_a.cast->aux_type->size*4); //divide the result return QCC_PR_StatementFlags(&pr_opcodes[OP_DIV_I], var_c, var_b, NULL, 0); @@ -2983,9 +3022,12 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ var_c = QCC_PR_GetSRef(NULL, "itof", NULL, false, 0, 0); if (!var_c.cast) { - QCC_PR_ParseError(0, "itof function not defined: cannot emulate int -> float conversions"); + //with denormals, 5 * 1i -> 5i + QCC_PR_ParseWarning(0, "itof emulation: denormals have limited precision"); + var_a = QCC_PR_StatementFlags(&pr_opcodes[OP_DIV_F], var_a, QCC_MakeIntConst(1), NULL, 0); } - var_a = QCC_PR_GenerateFunctionCall(nullsref, var_c, &var_a, &type_integer, 1); + else + var_a = QCC_PR_GenerateFunctionCall(nullsref, var_c, &var_a, &type_integer, 1); var_a.cast = type_float; } if (var_b.cast) @@ -3024,17 +3066,18 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ } } return var_a; + case OP_STORE_P: case OP_STORE_I: op = pr_opcodes+OP_STORE_F; break; case OP_BITXOR_F: -// a = (a & ~b) | (b & ~a); +// r = (a & ~b) | (b & ~a); var_c = QCC_PR_StatementFlags(&pr_opcodes[OP_BITNOT_F], var_b, nullsref, NULL, STFL_PRESERVEA); var_c = QCC_PR_StatementFlags(&pr_opcodes[OP_BITAND_F], var_a, var_c, NULL, STFL_PRESERVEA); var_a = QCC_PR_StatementFlags(&pr_opcodes[OP_BITNOT_F], var_a, nullsref, NULL, 0); var_a = QCC_PR_StatementFlags(&pr_opcodes[OP_BITAND_F], var_b, var_a, NULL, 0); - return QCC_PR_StatementFlags(&pr_opcodes[OP_BITOR_F], var_c, var_a, NULL, STFL_PRESERVEA); + return QCC_PR_StatementFlags(&pr_opcodes[OP_BITOR_F], var_c, var_a, NULL, 0); case OP_IF_S: tmp = QCC_MakeFloatConst(0); @@ -3211,6 +3254,10 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ var_a = tmp; break; + case OP_LOAD_P: + case OP_LOAD_I: + op = &pr_opcodes[OP_LOAD_F]; + break; case OP_STOREP_P: op = &pr_opcodes[OP_STOREP_I]; break; @@ -3619,6 +3666,32 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ else op = &pr_opcodes[OP_OR_F]; //generally works. if there's no other choice then meh. break; + +// case OP_LOADP_V: +// break; + case OP_LOADP_F: + case OP_LOADP_S: + case OP_LOADP_ENT: + case OP_LOADP_FLD: + case OP_LOADP_FNC: + case OP_LOADP_I: + { + QCC_type_t *argt[2] = {type_pointer, type_float}; + QCC_sref_t fnc = QCC_PR_GetSRef(NULL, "memgetval", NULL, false, 0, 0); + QCC_sref_t arg[2];arg[0] = var_a;arg[1]=QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_ITOF], var_b, nullsref, NULL, (flags&STFL_PRESERVEB)?STFL_PRESERVEA:0); //conversion return valuevar_b; //MSVC sucks when arg is a struct array. its fine when its a def_t ptr array + if (!fnc.cast) + QCC_PR_ParseError(0, "memgetval function not defined: cannot emulate OP_LOADP_*"); + var_c = QCC_PR_GenerateFunctionCall(nullsref, fnc, arg, argt, 2); + var_c.cast = *op->type_c; + return var_c; + } + break; + case OP_ADD_PIW: + var_b = QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_I], QCC_MakeIntConst(4), var_b, NULL, flags&STFL_PRESERVEB); + var_c = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_I], var_a, var_b, NULL, flags&~STFL_PRESERVEB); + var_c.cast = var_a.cast; + return var_c; + default: { int oldtarg = qcc_targetformat; @@ -3873,17 +3946,25 @@ void QCC_PrecacheSound (const char *n, int ch) if (!*n) return; + if (ch >= '1' && ch <= '9') + ch -= '0'; + else + ch = 1; + for (i=0 ; i ch) + precache_sound[i].block = ch; return; + } if (numsounds == QCC_MAX_SOUNDS) return; // QCC_Error ("PrecacheSound: numsounds == MAX_SOUNDS"); strcpy (precache_sound[i].name, n); - if (ch >= '1' && ch <= '9') - precache_sound[i].block = ch - '0'; - else - precache_sound[i].block = 1; + precache_sound[i].block = ch; + precache_sound[i].filename = strings+s_file; + precache_sound[i].fileline = pr_source_line; numsounds++; } @@ -3940,6 +4021,28 @@ void QCC_SetModel (const char *n) precache_model[i].fileline = pr_source_line; nummodels++; } +void QCC_SoundUsed (const char *n) +{ + int i; + + if (!*n) + return; + for (i=0 ; igeneratedfor == &def_ret && arglist[i].sym->refcount == 1) + { + QCC_FreeTemp(arglist[i]); + arglist[i].sym = &def_ret; + QCC_ForceUnFreeDef(arglist[i].sym); + break; + } + } + QCC_ClobberDef(&def_ret); /*can free temps used for arguments now*/ @@ -4548,6 +4664,76 @@ QCC_sref_t QCC_PR_GenerateFunctionCall (QCC_sref_t newself, QCC_sref_t func, QCC QCC_FreeTemp(oself); QCC_FreeTemp(self); + + if (func.cast->type == ev_function && func.cast->params) + { + for (i = 0; i < argcount && i < func.cast->num_parms; i++) + { + if (!func.cast->params[i].out) + continue; + if (arglist[i].sym->constant) + { + QCC_PR_ParseWarning(ERR_TYPEMISMATCHPARM, "Constant passed as out argument\n"); + continue; + } + if (arglist[i].sym->temp) + { + QCC_PR_ParseWarning(ERR_TYPEMISMATCHPARM, "Temp passed as out argument\n"); + continue; + } + + if (i>=MAX_PARMS) + { + d = extra_parms[i - MAX_PARMS]; + if (!d.cast) + { + char name[128]; + QC_snprintfz(name, sizeof(name), "$parm%u", i); + d = extra_parms[i - MAX_PARMS] = QCC_PR_GetSRef(type_vector, name, NULL, true, 0, GDF_STRIP); + } + else + QCC_ForceUnFreeDef(d.sym); + } + else + { + d.sym = &def_parms[i]; + d.ofs = 0; + d.cast = type_vector; + } + if (argtypelist && argtypelist[i]) + d.cast = argtypelist[i]; + else + d.cast = arglist[i].cast; + + QCC_ForceUnFreeDef(arglist[i].sym); + + //FIXME: if the def is a temp with only one reference, we can update the statement that generated the temp to directly store to the parm + if (d.cast->size == 3 || !opt_nonvec_parms) + QCC_FreeTemp(QCC_PR_StatementFlags (&pr_opcodes[OP_STORE_V], d, arglist[i], NULL, 0)); + else + { + switch(d.cast->type) + { + case ev_entity: + QCC_FreeTemp(QCC_PR_StatementFlags (&pr_opcodes[OP_STORE_ENT], d, arglist[i], NULL, 0)); + break; + case ev_string: + QCC_FreeTemp(QCC_PR_StatementFlags (&pr_opcodes[OP_STORE_S], d, arglist[i], NULL, 0)); + break; + case ev_field: + QCC_FreeTemp(QCC_PR_StatementFlags (&pr_opcodes[OP_STORE_FLD], d, arglist[i], NULL, 0)); + break; + case ev_function: + QCC_FreeTemp(QCC_PR_StatementFlags (&pr_opcodes[OP_STORE_FNC], d, arglist[i], NULL, 0)); + break; + default: + QCC_FreeTemp(QCC_PR_StatementFlags (&pr_opcodes[OP_STORE_F], d, arglist[i], NULL, 0)); + break; + } + optres_nonvec_parms++; + } + } + } return retval; } @@ -4623,7 +4809,8 @@ QCC_sref_t QCC_PR_ParseFunctionCall (QCC_ref_t *funcref) //warning, the func cou int oldstcount = numstatements; #if 1 QCC_ref_t refbuf, *r; - r = QCC_PR_ParseRefValue(&refbuf, pr_classtype, false, false, false); + r = QCC_PR_RefExpression(&refbuf, TOP_PRIORITY, 0); +// r = QCC_PR_ParseRefValue(&refbuf, pr_classtype, false, false, false); if (r->type == REF_ARRAYHEAD && !r->index.cast) { e = r->base; @@ -4933,6 +5120,32 @@ QCC_sref_t QCC_PR_ParseFunctionCall (QCC_ref_t *funcref) //warning, the func cou return result; } + else if (!strcmp(funcname, "used_sound")) + { + e = QCC_PR_Expression(TOP_PRIORITY, EXPR_DISALLOW_COMMA); + QCC_PR_Expect(")"); + if (e.cast->type == ev_string && e.sym->constant && !e.sym->temp) + { + const char *value = &strings[e.sym->symboldata[e.ofs].string]; + QCC_SoundUsed(value); + } + else + QCC_PR_ParseWarning(ERR_BADIMMEDIATETYPE, "Argument to used_sound intrinsic was not a string immediate."); + return e; + } + else if (!strcmp(funcname, "used_model")) + { + e = QCC_PR_Expression(TOP_PRIORITY, EXPR_DISALLOW_COMMA); + QCC_PR_Expect(")"); + if (e.cast->type == ev_string && e.sym->constant && !e.sym->temp) + { + const char *value = &strings[e.sym->symboldata[e.ofs].string]; + QCC_SetModel(value); + } + else + QCC_PR_ParseWarning(ERR_BADIMMEDIATETYPE, "Argument to used_model intrinsic was not a string immediate."); + return e; + } else if (!strcmp(funcname, "autocvar") && !QCC_PR_CheckToken(")")) { char autocvarname[256]; @@ -5153,6 +5366,11 @@ QCC_sref_t QCC_PR_ParseFunctionCall (QCC_ref_t *funcref) //warning, the func cou const char *value = &strings[e.sym->symboldata[e.ofs].string]; QCC_SetModel(value); } + if (arg == 2 && !STRCMP(QCC_GetSRefName(func), "sound") && e.cast->type == ev_string && e.sym->constant && !e.sym->temp) + { + const char *value = &strings[e.sym->symboldata[e.ofs].string]; + QCC_SoundUsed(value); + } param[arg] = e; paramtypes[arg] = p; @@ -5749,15 +5967,12 @@ static QCC_sref_t QCC_PR_ExpandField(QCC_sref_t ent, QCC_sref_t field, QCC_type_ break; case ev_pointer: r = QCC_PR_StatementFlags(&pr_opcodes[OP_LOAD_P], ent, field, NULL, preserveflags); - r.cast = fieldtype; break; case ev_field: r = QCC_PR_StatementFlags(&pr_opcodes[OP_LOAD_FLD], ent, field, NULL, preserveflags); - r.cast = fieldtype; break; case ev_variant: r = QCC_PR_StatementFlags(&pr_opcodes[OP_LOAD_FLD], ent, field, NULL, preserveflags); - r.cast = fieldtype; break; case ev_float: r = QCC_PR_StatementFlags(&pr_opcodes[OP_LOAD_F], ent, field, NULL, preserveflags); @@ -5770,12 +5985,12 @@ static QCC_sref_t QCC_PR_ExpandField(QCC_sref_t ent, QCC_sref_t field, QCC_type_ break; case ev_function: r = QCC_PR_StatementFlags(&pr_opcodes[OP_LOAD_FNC], ent, field, NULL, preserveflags); - r.cast = fieldtype; break; case ev_entity: r = QCC_PR_StatementFlags(&pr_opcodes[OP_LOAD_ENT], ent, field, NULL, preserveflags); break; } + r.cast = fieldtype; return r; } @@ -5797,7 +6012,7 @@ static QCC_ref_t *QCC_PR_ParseField(QCC_ref_t *refbuf, QCC_ref_t *lhs) } else field = QCC_PR_ParseRefValue(&fieldbuf, t, false, false, true); - if (field->cast->type == ev_field || field->cast->type == ev_variant) + if (field->type != REF_ARRAYHEAD && (field->cast->type == ev_field || field->cast->type == ev_variant)) { //fields are generally always readonly. that refers to the field def itself, rather than products of said field. //entities, like 'world' might also be consts. just ignore that fact. the def itself is not assigned, but the fields of said def. @@ -6324,6 +6539,8 @@ QCC_ref_t *QCC_PR_ParseRefValue (QCC_ref_t *refbuf, QCC_type_t *assumeclass, pbo (!strcmp(name, "sizeof")) || (!strcmp(name, "entnum")) || (!strcmp(name, "autocvar")) || + (!strcmp(name, "used_model")) || + (!strcmp(name, "used_sound")) || (!strcmp(name, "va_arg")) || (!strcmp(name, "...")) || //for compat. otherwise wtf? (!strcmp(name, "_")) ) //intrinsics, any old function with no args will do. @@ -6427,7 +6644,14 @@ QCC_ref_t *QCC_PR_ParseRefValue (QCC_ref_t *refbuf, QCC_type_t *assumeclass, pbo else t = QCC_PR_GetSRef(NULL, "self", NULL, true, 0, false); - d = QCC_PR_ParseArrayPointer(d, allowarrayassign, makearraypointers); //opportunistic vecmember[0] handling + if (d.sym->arraysize) + { + QCC_DefToRef(refbuf, d); + refbuf->type = REF_ARRAYHEAD; + d = QCC_RefToDef(QCC_PR_ParseRefArrayPointer(refbuf, refbuf, allowarrayassign, makearraypointers), true); + } + else + d = QCC_PR_ParseArrayPointer(d, allowarrayassign, makearraypointers); //opportunistic vecmember[0] handling //then return a reference to this.field QCC_PR_BuildRef(refbuf, REF_FIELD, t, d, d.cast->aux_type, false); @@ -6974,6 +7198,22 @@ void QCC_StoreToPointer(QCC_sref_t dest, QCC_sref_t source, QCC_type_t *type) { default: QCC_PR_ParseErrorPrintSRef(ERR_INTERNAL, dest, "QCC_StoreToPointer doesn't know how to store to that type"); + case ev_struct: + case ev_union: + { + int i; + for (i = 0; i+2 < type->size; i+=3) + { + QCC_PR_SimpleStatement(&pr_opcodes[OP_STOREP_V], source, dest, QCC_MakeIntConst(i), false); + source.ofs += 3; + } + for (i = 0; i < type->size; i++) + { + QCC_PR_SimpleStatement(&pr_opcodes[OP_STOREP_F], source, dest, QCC_MakeIntConst(i), false); + source.ofs += 1; + } + } + break; case ev_float: QCC_PR_SimpleStatement(&pr_opcodes[OP_STOREP_F], source, dest, nullsref, false); break; @@ -6993,7 +7233,10 @@ void QCC_StoreToPointer(QCC_sref_t dest, QCC_sref_t source, QCC_type_t *type) QCC_PR_SimpleStatement(&pr_opcodes[OP_STOREP_FLD], source, dest, nullsref, false); break; case ev_integer: - QCC_PR_SimpleStatement(&pr_opcodes[OP_STOREP_I], source, dest, nullsref, false); + if (!QCC_OPCodeValid(&pr_opcodes[OP_STOREP_I])) + QCC_PR_SimpleStatement(&pr_opcodes[OP_STOREP_FLD], source, dest, nullsref, false); + else + QCC_PR_SimpleStatement(&pr_opcodes[OP_STOREP_I], source, dest, nullsref, false); break; case ev_pointer: if (!QCC_OPCodeValid(&pr_opcodes[OP_STOREP_P])) @@ -7003,44 +7246,35 @@ void QCC_StoreToPointer(QCC_sref_t dest, QCC_sref_t source, QCC_type_t *type) break; } } -void QCC_LoadFromPointer(QCC_sref_t dest, QCC_sref_t source, QCC_sref_t idx, QCC_type_t *type) +QCC_sref_t QCC_LoadFromPointer(QCC_sref_t source, QCC_sref_t idx, QCC_type_t *type) { + QCC_sref_t ret; + int op; + while (type->type == ev_accessor) type = type->parentclass; //fixme: we should probably handle entire structs or something switch(type->type) { - case ev_float: - QCC_PR_SimpleStatement (&pr_opcodes[OP_LOADP_F], source, idx, dest, false); - break; - case ev_string: - QCC_PR_SimpleStatement (&pr_opcodes[OP_LOADP_S], source, idx, dest, false); - break; - case ev_vector: - QCC_PR_SimpleStatement (&pr_opcodes[OP_LOADP_V], source, idx, dest, false); - break; - case ev_entity: - QCC_PR_SimpleStatement (&pr_opcodes[OP_LOADP_ENT], source, idx, dest, false); - break; - case ev_field: - QCC_PR_SimpleStatement (&pr_opcodes[OP_LOADP_FLD], source, idx, dest, false); - break; - case ev_function: - QCC_PR_SimpleStatement (&pr_opcodes[OP_LOADP_FNC], source, idx, dest, false); - break; - case ev_integer: - QCC_PR_SimpleStatement (&pr_opcodes[OP_LOADP_I], source, idx, dest, false); - break; + case ev_float: op = OP_LOADP_F; break; + case ev_string: op = OP_LOADP_S; break; + case ev_vector: op = OP_LOADP_V; break; + case ev_entity: op = OP_LOADP_ENT; break; + case ev_field: op = OP_LOADP_FLD; break; + case ev_function: op = OP_LOADP_FNC; break; + case ev_integer: op = OP_LOADP_I; break; default: - QCC_PR_ParseErrorPrintSRef(ERR_INTERNAL, dest, "QCC_LoadFromPointer doesn't know how to load from that type"); + QCC_PR_ParseError(ERR_INTERNAL, "QCC_LoadFromPointer doesn't know how to load from that type"); case ev_pointer: - if (!QCC_OPCodeValid(&pr_opcodes[OP_LOADP_P])) - QCC_PR_SimpleStatement (&pr_opcodes[OP_LOADP_I], source, idx, dest, false); - else - QCC_PR_SimpleStatement (&pr_opcodes[OP_LOADP_P], source, idx, dest, false); + op = OP_LOADP_P; + if (!QCC_OPCodeValid(&pr_opcodes[op])) + op = OP_LOADP_I; break; } + ret = QCC_PR_StatementFlags(&pr_opcodes[op], source, idx, NULL, STFL_PRESERVEA|STFL_PRESERVEB); //get pointer to precise def. + ret.cast = type; + return ret; } void QCC_StoreToArray(QCC_sref_t base, QCC_sref_t index, QCC_sref_t source, QCC_type_t *t) { @@ -7413,7 +7647,6 @@ QCC_sref_t QCC_RefToDef(QCC_ref_t *ref, pbool freetemps) QCC_UnFreeTemp(ret); break; case REF_POINTER: - tmp = QCC_GetTemp(ref->cast); if (ref->index.cast) { // if (!freetemps) @@ -7422,7 +7655,7 @@ QCC_sref_t QCC_RefToDef(QCC_ref_t *ref, pbool freetemps) } else idx = nullsref; - QCC_LoadFromPointer(tmp, ref->base, idx, ref->cast); + tmp = QCC_LoadFromPointer(ref->base, idx, ref->cast); QCC_FreeTemp(idx); if (freetemps) QCC_PR_DiscardRef(ref); @@ -8359,6 +8592,28 @@ QCC_statement_t *QCC_Generate_OP_GOTO(void) return st; } +void PR_GenerateReturnOuts(void) +{ + int i; + QCC_sref_t p; + QCC_def_t *local; + + for (i = 0, local = pr.local_head.nextlocal; i < pr_scope->type->num_parms; i++, local = local->deftail->nextlocal) + { + if (!pr_scope->type->params[i].out) + continue; + + if (i > MAX_PARMS) + p = extra_parms[i-MAX_PARMS]; + else + { + p.sym = &def_parms[i]; + p.ofs = 0; + p.cast = type_vector; + } + QCC_StoreToSRef(p, QCC_MakeSRefForce(local, 0, local->type), local->type, false, false); + } +} /* ============ PR_ParseStatement @@ -8410,6 +8665,7 @@ void QCC_PR_ParseStatement (void) if (QCC_PR_CheckToken (";")) { + PR_GenerateReturnOuts(); if (pr_scope->type->aux_type->type != ev_void) QCC_PR_ParseWarning(WARN_MISSINGRETURNVALUE, "\'%s\' returned nothing, expected %s", pr_scope->name, pr_scope->type->aux_type->name); if (opt_return_only) @@ -8440,12 +8696,14 @@ void QCC_PR_ParseStatement (void) e = QCC_SupplyConversion(e, pr_scope->type->aux_type->type, true); // QCC_PR_ParseWarning(WARN_WRONGRETURNTYPE, "\'%s\' returned %s, expected %s", pr_scope->name, e->type->name, pr_scope->type->aux_type->name); } + PR_GenerateReturnOuts(); QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_RETURN], e, nullsref, NULL)); QCC_PR_Expect (";"); return; } if (QCC_PR_CheckKeyword(keyword_exit, "exit")) { + PR_GenerateReturnOuts(); QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_DONE], nullsref, nullsref, NULL)); QCC_PR_Expect (";"); return; @@ -8973,6 +9231,8 @@ void QCC_PR_ParseStatement (void) else patch1->a.ofs = &statements[numstatements] - patch1; //the goto start part + oldst = numstatements; + QCC_ForceUnFreeDef(e.sym); //in the following code, e should still be live for (i = cases; i < num_cases; i++) { @@ -9099,6 +9359,10 @@ void QCC_PR_ParseStatement (void) } num_breaks = breaks; } + + //update the jumptable statements to hide as part of the switch itself. + while (oldst < numstatements) + statements[oldst++].linenum = patch1->linenum; return; } @@ -10641,6 +10905,8 @@ QCC_function_t *QCC_PR_ParseImmediateStatements (QCC_def_t *def, QCC_type_t *typ QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_ENT], e, QCC_PR_DummyDef(pr_classtype, "self", pr_scope, 0, e2->ofs, false), NULL)); }*/ + if (needsdone) + PR_GenerateReturnOuts(); QCC_PR_Statement (&pr_opcodes[OP_DONE], nullsref, nullsref, NULL); } else @@ -12564,7 +12830,7 @@ void QCC_PR_ParseDefs (char *classname) gd_flags = 0; if (isstatic) gd_flags |= GDF_STATIC; - if (isconstant || (type->type == ev_function && !isvar)) + if (isconstant || (type->type == ev_function && !isvar && !pr_scope)) gd_flags |= GDF_CONST; if (!nosave) gd_flags |= GDF_SAVED; diff --git a/engine/qclib/qcc_pr_lex.c b/engine/qclib/qcc_pr_lex.c index 3f22387b..559e470b 100644 --- a/engine/qclib/qcc_pr_lex.c +++ b/engine/qclib/qcc_pr_lex.c @@ -1935,7 +1935,7 @@ void QCC_PR_LexVector (void) if (*pr_file_p == '\'' && i == 1) { if (i < 2) - QCC_PR_ParseWarning (WARN_FTE_SPECIFIC, "Bad vector"); + QCC_PR_ParseWarning (WARN_FTE_SPECIFIC, "2d vector"); for (i++ ; i<3 ; i++) pr_immediate.vector[i] = 0; @@ -3369,10 +3369,14 @@ pbool VARGS QCC_PR_PrintWarning (int type, const char *file, int line, const cha if (!wnam) wnam = ""; - QCC_PR_PrintScope(); + if (string) + QCC_PR_PrintScope(); + if (type >= ERR_PARSEERRORS) { - if (!file || !*file) + if (!string) + ; + else if (!file || !*file) printf (":: error%s: %s\n", wnam, string); else if (flag_msvcstyle) printf ("%s(%i) : error%s: %s\n", file, line, wnam, string); @@ -3382,7 +3386,9 @@ pbool VARGS QCC_PR_PrintWarning (int type, const char *file, int line, const cha } else if (qccwarningaction[type] == 2) { //-werror - if (!file || !*file) + if (!string) + ; + else if (!file || !*file) printf (": werror%s: %s\n", wnam, string); else if (flag_msvcstyle) printf ("%s(%i) : werror%s: %s\n", file, line, wnam, string); @@ -3392,7 +3398,9 @@ pbool VARGS QCC_PR_PrintWarning (int type, const char *file, int line, const cha } else { - if (!file || !*file) + if (!string) + ; + else if (!file || !*file) printf (": warning%s: %s\n", wnam, string); else if (flag_msvcstyle) printf ("%s(%i) : warning%s: %s\n", file, line, wnam, string); @@ -3410,6 +3418,9 @@ pbool VARGS QCC_PR_Warning (int type, const char *file, int line, const char *er if (!qccwarningaction[type]) return false; + if (!error) + return QCC_PR_PrintWarning(type, file, line, NULL); + va_start (argptr,error); QC_vsnprintf (string,sizeof(string)-1, error,argptr); va_end (argptr); @@ -3643,17 +3654,28 @@ pbool QCC_PR_CheckName(const char *string) pbool QCC_PR_CheckKeyword(int keywordenabled, const char *string) { - if (!keywordenabled) - return false; - if (flag_caseinsensitive) + if (pr_token[0] == '_' && pr_token[1] == '_') { - if (stricmp (string, pr_token)) + //lets just always go insensitive with a leading underscore pair. + if (stricmp(string, pr_token+2)) return false; + QCC_PR_Lex (); + return true; } else { - if (STRCMP(string, pr_token)) + if (!keywordenabled) return false; + if (flag_caseinsensitive) + { + if (stricmp (string, pr_token)) + return false; + } + else + { + if (STRCMP(string, pr_token)) + return false; + } } QCC_PR_Lex (); return true; @@ -3833,6 +3855,8 @@ int typecmp_lax(QCC_type_t *a, QCC_type_t *b) { if (a->params[t].type->type != b->params[t].type->type) return 1; + if (a->params[t].out != b->params[t].out) + return 1; //classes/structs/unions are matched on class names rather than the contents of the class //it gets too painful otherwise, with recursive definitions. if (a->params[t].type->type == ev_entity || a->params[t].type->type == ev_struct || a->params[t].type->type == ev_union) @@ -3936,6 +3960,8 @@ char *TypeName(QCC_type_t *type, char *buffer, int buffersize) Q_strlcat(buffer, "(", buffersize); for (i = 0; i < type->num_parms; ) { + if (type->params[i].out) + Q_strlcat(buffer, "inout ", buffersize); if (type->params[i].optional) Q_strlcat(buffer, "optional ", buffersize); args--; @@ -4111,6 +4137,11 @@ QCC_type_t *QCC_PR_ParseFunctionType (int newtype, QCC_type_t *returntype) break; } + if (QCC_PR_CheckKeyword(keyword_inout, "inout")) + paramlist[numparms].out = true; + else + paramlist[numparms].out = false; + if (QCC_PR_CheckKeyword(keyword_optional, "optional")) { paramlist[numparms].optional = true; @@ -4232,7 +4263,7 @@ QCC_type_t *QCC_PR_ParseFunctionTypeReacc (int newtype, QCC_type_t *returntype) break; // type->name = "FUNC PARAMETER"; - + paramlist[numparms].out = false; paramlist[numparms].optional = false; paramlist[numparms].ofs = 0; paramlist[numparms].arraysize = 0; @@ -4306,6 +4337,7 @@ QCC_type_t *QCC_PR_GenFunctionType (QCC_type_t *rettype, QCC_type_t **args, char p->paramname = qccHunkAlloc(strlen(argnames[i])+1); strcpy(p->paramname, argnames[i]); p->type = args[i]; + p->out = 0; p->optional = false; p->ofs = 0; p->arraysize = 0; @@ -4356,8 +4388,6 @@ QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail) } if (QCC_PR_CheckToken (".")) { - newt = QCC_PR_NewType("FIELD_TYPE", ev_field, false); - //.float *foo; is annoying. //technically it is a pointer to a .float //most people will want a .(float*) foo; @@ -4366,14 +4396,19 @@ QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail) //this is pretty much an evil syntax hack. if (QCC_PR_CheckToken ("*")) { - newt->aux_type = QCC_PR_NewType("POINTER TYPE", ev_pointer, false); - newt->aux_type->aux_type = QCC_PR_ParseType (false, false); - - newt->aux_type->size = newt->aux_type->aux_type->size; + type = QCC_PR_NewType("POINTER TYPE", ev_pointer, false); + type->aux_type = QCC_PR_ParseType (false, false); + type->size = type->aux_type->size; } else - newt->aux_type = QCC_PR_ParseType (false, false); + type = QCC_PR_ParseType (false, false); + name = qccHunkAlloc(strlen(type->name)+2); + *name = '.'; + strcpy(name+1, type->name); + + newt = QCC_PR_NewType(name, ev_field, false); + newt->aux_type = type; newt->size = newt->aux_type->size; if (newtype) @@ -4878,6 +4913,7 @@ QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail) parms = realloc(parms, sizeof(*parms) * (numparms+1)); parms[numparms].ofs = 0; + parms[numparms].out = false; parms[numparms].optional = false; parms[numparms].paramname = parmname; parms[numparms].arraysize = arraysize; @@ -4918,8 +4954,8 @@ QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail) found = true; break; } - if ((unsigned int)basicindex < pp[i].ofs+1) //if we found one with the index - basicindex = pp[i].ofs+1; //make sure we don't union it. + if ((unsigned int)basicindex < pp[i].ofs+(pp[i].arraysize?pp[i].arraysize:1)) //if we found one with the index + basicindex = pp[i].ofs+(pp[i].arraysize?pp[i].arraysize:1); //make sure we don't union it. } } } @@ -4940,13 +4976,15 @@ QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail) d = QCC_PR_GetDef(NULL, membername, NULL, 0, 0, GDF_CONST); if (!d) { - d = QCC_PR_GetDef(QCC_PR_FieldType(*basictypes[newparm->type]), membername, NULL, 2, 0, GDF_CONST); - for (i = 0; (unsigned int)i < newparm->size; i++) + d = QCC_PR_GetDef(QCC_PR_FieldType(*basictypes[newparm->type]), membername, NULL, 2, arraysize, GDF_CONST); + for (i = 0; (unsigned int)i < newparm->size*(arraysize?arraysize:1); i++) d->symboldata[i]._int = pr.size_fields+i; pr.size_fields += i; d->referenced = true; //always referenced, so you can inherit safely. } + else if (d->arraysize != arraysize) + QCC_PR_ParseError(ERR_INTERNAL, "array members are kinda limited, sorry. try rearranging them or adding padding for alignment\n"); //FIXME: add relocs to cope with this all of a type can then be contiguous and thus allow arrays. } QCC_FreeDef(d); @@ -4954,7 +4992,7 @@ QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail) //actually, that seems pointless. QC_snprintfz(membername, sizeof(membername), "%s::"MEMBERFIELDNAME, classname, parmname); // printf("define %s -> %s\n", membername, d->name); - d = QCC_PR_DummyDef(fieldtype, membername, pr_scope, 0, d, 0, true, (isnull?0:GDF_CONST)|(opt_classfields?GDF_STRIP:0)); + d = QCC_PR_DummyDef(fieldtype, membername, pr_scope, arraysize, d, 0, true, (isnull?0:GDF_CONST)|(opt_classfields?GDF_STRIP:0)); d->referenced = true; //always referenced, so you can inherit safely. } @@ -5050,6 +5088,9 @@ QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail) arraysize = 0; + while (QCC_PR_CheckToken("*")) + newparm = QCC_PointerTypeTo(newparm); + if (!QCC_PR_CheckToken(";")) { parmname = qccHunkAlloc(strlen(pr_token)+1); @@ -5067,6 +5108,12 @@ QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail) else parmname = ""; + if (newparm == newt || ((newparm->type == ev_struct || newparm->type == ev_union) && !newparm->size)) + { + QCC_PR_ParseWarning(ERR_NOTANAME, "type %s not fully defined yet", newparm->name); + continue; + } + parms = realloc(parms, sizeof(*parms) * (numparms+1)); if (structtype == ev_union) @@ -5081,6 +5128,7 @@ QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail) newt->size += newparm->size*(arraysize?arraysize:1); } parms[numparms].arraysize = arraysize; + parms[numparms].out = false; parms[numparms].optional = false; parms[numparms].paramname = parmname; parms[numparms].type = newparm; diff --git a/engine/qclib/qccgui.c b/engine/qclib/qccgui.c index f0e18153..6ad6aca1 100644 --- a/engine/qclib/qccgui.c +++ b/engine/qclib/qccgui.c @@ -1378,6 +1378,7 @@ char *GetTooltipText(editor_t *editor, int pos, pbool dwell) QCC_def_t *def; int fno; int line; + int best, bestline; char *macro = QCC_PR_CheckCompConstTooltip(defname, buffer, buffer + sizeof(buffer)); if (macro && *macro) return macro; @@ -1393,6 +1394,81 @@ char *GetTooltipText(editor_t *editor, int pos, pbool dwell) line = SendMessage(editor->editpane, SCI_LINEFROMPOSITION, pos, 0); + for (best = 0,bestline=0, fno = 1; fno < numfunctions; fno++) + { + if (line > functions[fno].line && bestline < functions[fno].line) + { + if (!strcmp(editor->filename, functions[fno].file)) + { + best = fno; + bestline = functions[fno].line; + } + } + } + if (best) + { + if (strstr(functions[best].name, "::")) + { + QCC_type_t *type; + char tmp[256]; + char *c; + QC_strlcpy(tmp, functions[best].name, sizeof(tmp)); + c = strstr(tmp, "::"); + if (c) + *c = 0; + type = QCC_TypeForName(tmp); + + if (type->type == ev_entity) + { + QCC_def_t *def; + QC_snprintfz(tmp, sizeof(tmp), "%s::__m%s", type->name, term); + + for (fno = 0, def = NULL; fno < sourcefilesnumdefs && !def; fno++) + { + for (def = sourcefilesdefs[fno]; def; def = def->next) + { + if (def->scope && def->scope != &functions[best]) + continue; +// OutputDebugString(def->name); +// OutputDebugString("\n"); + if (!strcmp(def->name, tmp)) + { + //FIXME: look at the scope's function to find the start+end of the function and filter based upon that, to show locals + break; + } + } + } + + if (def && def->type->type == ev_field) + { +// QC_strlcpy(tmp, term, sizeof(tmp)); + QC_snprintfz(term, sizeof(term), "self.%s", tmp); + } + else + { + for (fno = 0, def = NULL; fno < sourcefilesnumdefs && !def; fno++) + { + for (def = sourcefilesdefs[fno]; def; def = def->next) + { + if (def->scope && def->scope != &functions[best]) + continue; + if (!strcmp(def->name, term)) + { + //FIXME: look at the scope's function to find the start+end of the function and filter based upon that, to show locals + break; + } + } + } + if (def && def->type->type == ev_field) + { + QC_strlcpy(tmp, term, sizeof(tmp)); + QC_snprintfz(term, sizeof(term), "self.%s", tmp); + } + } + } + } + } + //FIXME: we may need to display types too for (fno = 0, def = NULL; fno < sourcefilesnumdefs && !def; fno++) { @@ -4407,7 +4483,7 @@ int GrepSubFiles(HTREEITEM node, char *string) { HTREEITEM ch, p; char fullname[1024]; - char parentstring[256], *sl; + char parentstring[256]; int pl, nl; TV_ITEM parent; int found = 0; @@ -4430,14 +4506,17 @@ int GrepSubFiles(HTREEITEM node, char *string) pl = strlen(parent.pszText); if (nl + 1 + pl + 1 > sizeof(fullname)) return found; + p = TreeView_GetParent(projecttree, p); + if (!p && *fullname) + break; + + //ignore the root node, unless we're actually querying that root node. memmove(fullname+pl+1, fullname, nl+1); memcpy(fullname, parent.pszText, pl); fullname[pl] = nl?'/':'\0'; - p = TreeView_GetParent(projecttree, p); } //skip the leading progs.src/ if its there, because that's an abstraction and does not match the filesystem. - sl = strchr(fullname, '/'); - found += Grep(sl?sl+1:fullname, string); + found += Grep(fullname, string); ch = TreeView_GetChild(projecttree, node); found += GrepSubFiles(ch, string); diff --git a/engine/qclib/qccmain.c b/engine/qclib/qccmain.c index 0288cd4e..80e9a2e3 100644 --- a/engine/qclib/qccmain.c +++ b/engine/qclib/qccmain.c @@ -196,6 +196,11 @@ struct { {" F308", WARN_TYPEMISMATCHREDECOPTIONAL}, {" F309", WARN_IGNORECOMMANDLINE}, {" F310", WARN_MISUSEDAUTOCVAR}, + {" F311", WARN_FTE_SPECIFIC}, + + {" F208", WARN_NOTREFERENCEDCONST}, + {" F209", WARN_EXTRAPRECACHE}, + {" F210", WARN_NOTPRECACHED}, //frikqcc errors //Q608: PrecacheSound: numsounds @@ -300,6 +305,7 @@ compiler_flag_t compiler_flag[] = { {&keyword_shared, defaultkeyword, "shared", "Keyword: shared", "Disables the 'shared' keyword."}, //mark global to be copied over when progs changes (part of FTE_MULTIPROGS) {&keyword_state, nondefaultkeyword,"state", "Keyword: state", "Disables the 'state' keyword."}, {&keyword_optional, defaultkeyword,"optional", "Keyword: optional", "Disables the 'optional' keyword."}, + {&keyword_inout, nondefaultkeyword,"inout", "Keyword: inout", "Disables the 'inout' keyword."}, {&keyword_string, defaultkeyword, "string", "Keyword: string", "Disables the 'string' keyword."}, {&keyword_struct, defaultkeyword, "struct", "Keyword: struct", "Disables the 'struct' keyword."}, {&keyword_switch, defaultkeyword, "switch", "Keyword: switch", "Disables the 'switch' keyword."}, @@ -749,7 +755,7 @@ int WriteBodylessFuncs (int handle) { if (d->type->type == ev_function && !d->scope)// function parms are ok { - if (!(d->initialized & 1) && d->referenced) + if ((d->initialized == 2) && d->referenced) { SafeWrite(handle, d->name, strlen(d->name)+1); ret++; @@ -817,13 +823,13 @@ void QCC_FinaliseDef(QCC_def_t *def) for (prev = def, sub = prev->next; prev != def->deftail; sub = (prev=sub)->next) { if (sub->referenced) - def->referenced=true; + def->referenced=true; //if one child is referenced, the composite is referenced else if (!sub->referenced && ignoreone) ignoreone = false; else sub->referenced |= def->referenced; } - if (!def->referenced) + if (!def->referenced) //okay, at least one child was refrenced, lets just flag all as referenced to silence warnings about vec_z being unused for (prev = def, sub = prev->next; prev != def->deftail; sub = (prev=sub)->next) sub->referenced = true; } @@ -834,8 +840,8 @@ void QCC_FinaliseDef(QCC_def_t *def) sub->referenced |= def->referenced; } } - else if (def->symbolheader) //if a child symbol is referenced, mark the entire parent as referenced too. this avoids vec_x+vec_y with no vec or vec_z from generating warnings about vec being unreferenced - def->symbolheader->referenced |= def->referenced; +// else if (def->symbolheader) //if a child symbol is referenced, mark the entire parent as referenced too. this avoids vec_x+vec_y with no vec or vec_z from generating warnings about vec being unreferenced +// def->symbolheader->referenced |= def->referenced; if (!def->symbolheader->used) { @@ -940,11 +946,12 @@ pbool QCC_WriteData (int crc) pbool debugtarget = false; pbool types = false; int outputsttype = PST_DEFAULT; - int warnedunref = 0; + int dupewarncount = 0; int *statement_linenums; void *funcdata; size_t funcdatasize; + extern char *basictypenames[]; if (numstatements==1 && numfunctions==1 && numglobaldefs==1 && numfielddefs==1) @@ -1136,14 +1143,27 @@ pbool QCC_WriteData (int crc) } else if (functions[i].firstlocal) { - funcs[i].parm_start = functions[i].firstlocal->ofs; - for (local = functions[i].firstlocal, p = 0; local && p < MAX_PARMS && p < functions[i].type->num_parms; local = local->deftail->nextlocal, p++) + funcs[i].parm_start = 0; + for (local = functions[i].firstlocal, p = 0; local && p < MAX_PARMS && p < functions[i].type->num_parms; local = local->deftail->nextlocal) { + if (!local->used) + { //all params should have been assigned space. logically we could have safely omitted the last ones, but blurgh. + QCC_PR_Warning(ERR_INTERNAL, strings + local->s_file, local->s_line, "Argument %s was not marked used.\n", local->name); + continue; + } + + if (!p) + funcs[i].parm_start = local->ofs; funcs[i].locals += local->type->size; - funcs[i].parm_size[p] = local->type->size; + funcs[i].parm_size[p++] = local->type->size; } + for (; local && !local->used; local = local->nextlocal) + ; + if (!p && local) + funcs[i].parm_start = local->ofs; for (; local; local = local->nextlocal) - funcs[i].locals += local->type->size; + if (local->used) + funcs[i].locals += local->type->size; funcs[i].numparms = p; } else @@ -1160,6 +1180,9 @@ pbool QCC_WriteData (int crc) funcs[i].locals = PRLittleLong(funcs[i].locals); funcs[i].numparms = PRLittleLong(funcs[i].numparms); + if (funcs[i].locals && !funcs[i].parm_start) + QCC_PR_Warning(0, strings + funcs[i].s_file, functions[i].line, "%s:%i: func %s @%i locals@%i+%i, %i parms\n", strings+funcs[i].s_file, 0, strings+funcs[i].s_name, funcs[i].first_statement, funcs[i].parm_start, funcs[i].locals, funcs[i].numparms); + #ifdef DEBUG_DUMP printf("code: %s:%i: func %s @%i locals@%i+%i, %i parms\n", strings+funcs[i].s_file, 0, strings+funcs[i].s_name, funcs[i].first_statement, funcs[i].parm_start, funcs[i].locals, funcs[i].numparms); #endif @@ -1172,7 +1195,7 @@ pbool QCC_WriteData (int crc) Sys_Error("structtype error"); } - for (warnedunref = 0, def = pr.def_head.next ; def ; def = def->next) + for (dupewarncount = 0, def = pr.def_head.next ; def ; def = def->next) { if ((def->type->type == ev_struct || def->type->type == ev_union || def->arraysize) && def->deftail) { @@ -1221,13 +1244,12 @@ pbool QCC_WriteData (int crc) { int wt = def->constant?WARN_NOTREFERENCEDCONST:WARN_NOTREFERENCED; pr_scope = def->scope; - if (strcmp(def->name, "IMMEDIATE") && qccwarningaction[wt]) + if (!strncmp(def->name, "spawnfunc_", 10)) + ; //no warnings from unreferenced entry points. + else if (strcmp(def->name, "IMMEDIATE") && qccwarningaction[wt] && !(def->type->type == ev_function && def->symbolheader->timescalled) && !def->symbolheader->used) { char typestr[256]; - if (warnedunref++ >= 10 && !verbose) - pr_warning_count++; - else - QCC_PR_Warning(wt, strings + def->s_file, def->s_line, "%s %s no references.", TypeName(def->type, typestr, sizeof(typestr)), def->name); + QCC_PR_Warning(wt, strings + def->s_file, def->s_line, (dupewarncount++ >= 10 && !verbose)?NULL:"%s %s no references.", TypeName(def->type, typestr, sizeof(typestr)), def->name); } pr_scope = NULL; @@ -1351,8 +1373,8 @@ pbool QCC_WriteData (int crc) #endif } - if (warnedunref > 10 && !verbose) - QCC_PR_Note(WARN_NOTREFERENCED, NULL, 0, "suppressed %i more warnings about unreferenced variables, as you clearly don't care about the first 10.", warnedunref-10); + if (dupewarncount > 10 && !verbose) + QCC_PR_Note(WARN_NOTREFERENCED, NULL, 0, "suppressed %i more warnings about unreferenced variables, as you clearly don't care about the first 10.", dupewarncount-10); for (i = 0; i < numglobaldefs; i++) { @@ -1405,13 +1427,26 @@ pbool QCC_WriteData (int crc) QCC_Error(ERR_TOOMANYGLOBALS, "Too many globals - %i\nAdd \"MAX_GLOBALS\" \"%i\" to qcc.cfg", numglobaldefs, (numglobaldefs+32768)&~32767); + dupewarncount = 0; for (i = 0; i < nummodels; i++) { if (!precache_model[i].used) - QCC_PR_Warning(WARN_EXTRAPRECACHE, precache_model[i].filename, precache_model[i].fileline, "Model \"%s\" was precached but not directly used", precache_model[i].name); + dupewarncount+=QCC_PR_Warning(WARN_EXTRAPRECACHE, precache_model[i].filename, precache_model[i].fileline, (dupewarncount>10&&!verbose)?NULL:"Model \"%s\" was precached but not directly used%s", precache_model[i].name, dupewarncount?"":" (annotate the usage with the used_model intrinsic to silence this warning)"); else if (!precache_model[i].block) - QCC_PR_Warning(WARN_NOTPRECACHED, precache_model[i].filename, precache_model[i].fileline, "Model \"%s\" was used but not precached", precache_model[i].name); + dupewarncount+=QCC_PR_Warning(WARN_NOTPRECACHED, precache_model[i].filename, precache_model[i].fileline, (dupewarncount>10&&!verbose)?NULL:"Model \"%s\" was used but not precached", precache_model[i].name); } + + for (i = 0; i < numsounds; i++) + { + if (!precache_sound[i].used) + dupewarncount+=QCC_PR_Warning(WARN_EXTRAPRECACHE, precache_sound[i].filename, precache_sound[i].fileline, (dupewarncount>10&&!verbose)?NULL:"Sound \"%s\" was precached but not directly used", precache_sound[i].name, dupewarncount?"":" (annotate the usage with the used_sound intrinsic to silence this warning)"); + else if (!precache_sound[i].block) + dupewarncount+=QCC_PR_Warning(WARN_NOTPRECACHED, precache_sound[i].filename, precache_sound[i].fileline, (dupewarncount>10&&!verbose)?NULL:"Sound \"%s\" was used but not precached", precache_sound[i].name); + } + + if (dupewarncount > 10 && !verbose) + QCC_PR_Note(WARN_NOTREFERENCED, NULL, 0, "suppressed %i more warnings about precaches.", dupewarncount-10); + //PrintStrings (); //PrintFunctions (); //PrintFields (); @@ -1801,6 +1836,10 @@ strofs = (strofs+3)&~3; progs.ofs_types = 0; progs.numtypes = 0; + + progs.ofsbodylessfuncs = SafeSeek (h, 0, SEEK_CUR); + progs.numbodylessfuncs = WriteBodylessFuncs(h); + switch(qcc_targetformat) { case QCF_QTEST: @@ -1824,9 +1863,6 @@ strofs = (strofs+3)&~3; else progs.secondaryversion = PROG_SECONDARYVERSION16; - progs.ofsbodylessfuncs = SafeSeek (h, 0, SEEK_CUR); - progs.numbodylessfuncs = WriteBodylessFuncs(h); - if (debugtarget && statement_linenums) { progs.ofslinenums = SafeSeek (h, 0, SEEK_CUR); @@ -1874,6 +1910,10 @@ strofs = (strofs+3)&~3; break; } + if (progs.version != PROG_EXTENDEDVERSION && progs.numbodylessfuncs) + printf ("WARNING: progs format cannot handle extern functions\n"); + + if (verbose) printf ("%6i TOTAL SIZE\n", (int)SafeSeek (h, 0, SEEK_CUR)); @@ -3253,7 +3293,10 @@ void QCC_PR_CommandLinePrecompilerOptions (void) for (j = 0; j < ERR_PARSEERRORS; j++) if (qccwarningaction[j] == WA_IGNORE) { - qccwarningaction[j] = WA_WARN; + if (j != WARN_FTE_SPECIFIC && //kinda annoying when its actually valid code. + j != WARN_NOTREFERENCEDCONST && //warning about every single constant is annoying as heck. note that this includes both stuff like MOVETYPE_ and builtins. + j != WARN_EXTRAPRECACHE) //we can't guarentee that we can parse this correctly. this warning is thus a common false positive. its available with -Wextra, and there's intrinsics to reduce false positives. + qccwarningaction[j] = WA_WARN; } } else if (!stricmp(myargv[i]+2, "extra")) @@ -3427,7 +3470,7 @@ void QCC_SetDefaultProperties (void) qccwarningaction[WARN_EVILPREPROCESSOR] = WA_WARN;//FIXME: make into WA_ERROR; if (qcc_targetformat == QCF_HEXEN2 || qcc_targetformat == QCF_FTEH2) - qccwarningaction[WARN_CASEINSENSITIVEFRAMEMACRO] = WA_IGNORE; + qccwarningaction[WARN_CASEINSENSITIVEFRAMEMACRO] = WA_IGNORE; //hexenc consides these fair game. //Check the command line QCC_PR_CommandLinePrecompilerOptions(); @@ -4187,7 +4230,7 @@ void QCC_FinishCompile(void) if (optres_test2) printf("optres_test2 %i\n", optres_test2); - printf("numtemps %i\n", tempsused); + printf("numtemps %u\n", (unsigned)tempsused); } if (!flag_msvcstyle) printf("Done. %i warnings\n", pr_warning_count); diff --git a/engine/server/net_preparse.c b/engine/server/net_preparse.c index 8c707974..117ee62c 100644 --- a/engine/server/net_preparse.c +++ b/engine/server/net_preparse.c @@ -629,7 +629,6 @@ void NPP_NQFlush(void) if (!bufferlen) return; - switch(majortype) { case svc_cdtrack: @@ -1543,6 +1542,36 @@ void NPP_NQWriteEntity(int dest, int data) //replacement write func (nq to qw) #ifdef NQPROT +float NPP_ReadFloat(qbyte *buf) +{ + union + { + float f; + qbyte b[4]; + } u; + memcpy(u.b, buf, sizeof(u.f)); + return LittleFloat(u.f); +} +short NPP_ReadShort(qbyte *buf) +{ + union + { + short s; + qbyte b[2]; + } u; + memcpy(u.b, buf, sizeof(u.s)); + return LittleShort(u.s); +} +unsigned short NPP_ReadUShort(qbyte *buf) +{ + union + { + unsigned short s; + qbyte b[2]; + } u; + memcpy(u.b, buf, sizeof(u.s)); + return LittleShort(u.s); +} //qw to nq translation is only useful if we allow nq clients to connect. @@ -1568,7 +1597,9 @@ void NPP_QWFlush(void) Con_Printf("QWFlush: svc_cdtrack wasn't the right length\n"); else { - b = 0; + //qw cdtracks have only a loop byte. + //nq has initial+loop values. + b = (bufferlen==2)?buffer[1]:0; NPP_AddData(&b, sizeof(qbyte)); } break; @@ -1596,13 +1627,36 @@ void NPP_QWFlush(void) ClientReliableCheckBlock(cl, 1); ClientReliableWrite_Byte(cl, svc_intermission); - org[0] = (*(short*)&buffer[1])/8.0f; - org[1] = (*(short*)&buffer[1+2])/8.0f; - org[2] = (*(short*)&buffer[1+4])/8.0f; + i = 1; + if (destprim->coordsize == 4) + { + org[0] = NPP_ReadFloat(buffer+i+0); + org[1] = NPP_ReadFloat(buffer+i+4); + org[2] = NPP_ReadFloat(buffer+i+8); + i += 12; + } + else + { + org[0] = NPP_ReadShort(buffer+i+0) / 8.0; + org[1] = NPP_ReadShort(buffer+i+2) / 8.0; + org[2] = NPP_ReadShort(buffer+i+4) / 8.0; + i += 6; + } - ang[0] = (*(qbyte*)&buffer[7])*360.0/255; - ang[1] = (*(qbyte*)&buffer[7+1])*360.0/255; - ang[2] = (*(qbyte*)&buffer[7+2])*360.0/255; + if (destprim->anglesize == 2) + { + ang[0] = NPP_ReadUShort(buffer+i+0)*360.0/0xffff; + ang[1] = NPP_ReadUShort(buffer+i+2)*360.0/0xffff; + ang[2] = NPP_ReadUShort(buffer+i+4)*360.0/0xffff; + i += 6; + } + else + { + ang[0] = (*(qbyte*)&buffer[i+0])*360.0/0xff; + ang[1] = (*(qbyte*)&buffer[i+1])*360.0/0xff; + ang[2] = (*(qbyte*)&buffer[i+2])*360.0/0xff; + i += 3; + } //move nq players to origin + angle VectorCopy(org, cl->edict->v->origin); @@ -1913,7 +1967,7 @@ void NPP_QWWriteByte(int dest, qbyte data) //replacement write func (nq to qw) protocollen = 1; break; case svc_intermission: - protocollen = 10; + protocollen = 1 + destprim->coordsize*3 + destprim->anglesize*3; break; case svc_finale: protocollen = 2; diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index 8f9468a0..fa8f1063 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -1392,7 +1392,7 @@ void Q_InitProgs(void) for (j = 0; j < maps; j++) { f = COM_Parse(f); - if (!Q_strcasecmp(sv.name, com_token)) + if (!Q_strcasecmp(svs.name, com_token)) { f = COM_Parse(f); strcpy(addons, com_token); @@ -1592,7 +1592,7 @@ void Q_InitProgs(void) } //progs depended on by maps. - a = as = COM_LoadStackFile(va("maps/%s.inf", sv.name), addons, sizeof(addons), NULL); + a = as = COM_LoadStackFile(va("maps/%s.inf", svs.name), addons, sizeof(addons), NULL); if (a) { as = strstr(a, "qwprogs="); @@ -3033,6 +3033,7 @@ static void QCBUILTIN PF_sound (pubprogfuncs_t *prinst, struct globalvars_s *pr_ float attenuation; int pitchadj; int flags; + float timeofs; entity = G_EDICT(prinst, OFS_PARM0); channel = G_FLOAT(OFS_PARM1); @@ -3045,7 +3046,7 @@ static void QCBUILTIN PF_sound (pubprogfuncs_t *prinst, struct globalvars_s *pr_ pitchadj = 0; if (svprogfuncs->callargc > 6) { - flags = G_FLOAT(OFS_PARM5); + flags = G_FLOAT(OFS_PARM6); if (channel < 0) channel = 0; } @@ -3056,6 +3057,7 @@ static void QCBUILTIN PF_sound (pubprogfuncs_t *prinst, struct globalvars_s *pr_ //demangle it so the upper bits are still useful. channel = (channel & 7) | ((channel & 0x1f0) >> 1); } + timeofs = (svprogfuncs->callargc>7)?G_FLOAT(OFS_PARM7):0; if (volume < 0) //erm... return; @@ -3063,11 +3065,11 @@ static void QCBUILTIN PF_sound (pubprogfuncs_t *prinst, struct globalvars_s *pr_ if (volume > 255) volume = 255; + //should probably be an argument instead, but whatever. if (flags & 1) channel |= 256; - //shift the reliable flag to 256 instead. - SVQ1_StartSound (NULL, (wedict_t*)entity, channel, sample, volume, attenuation, pitchadj); + SVQ1_StartSound (NULL, (wedict_t*)entity, channel, sample, volume, attenuation, pitchadj, timeofs); } static void QCBUILTIN PF_pointsound(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) @@ -3087,7 +3089,7 @@ static void QCBUILTIN PF_pointsound(pubprogfuncs_t *prinst, struct globalvars_s else pitchpct = 0; - SVQ1_StartSound (origin, sv.world.edicts, 0, sample, volume, attenuation, pitchpct); + SVQ1_StartSound (origin, sv.world.edicts, 0, sample, volume, attenuation, pitchpct, 0); } //an evil one from telejano. @@ -6087,7 +6089,6 @@ string readmcmd (string str) static void QCBUILTIN PF_readcmd (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { const char *s; - static char output[8000]; extern char outputbuf[]; extern redirect_t sv_redirected; extern int sv_redirectedlang; @@ -6106,14 +6107,12 @@ static void QCBUILTIN PF_readcmd (pubprogfuncs_t *prinst, struct globalvars_s *p SV_BeginRedirect(RD_OBLIVION, TL_FindLanguage("")); Cbuf_Execute(); - Q_strncpyz(output, outputbuf, sizeof(output)); + Con_Printf("PF_readcmd: %s\n%s", s, outputbuf); + G_INT(OFS_RETURN) = (int)PR_TempString(prinst, outputbuf); SV_EndRedirect(); if (old != RD_NONE) SV_BeginRedirect(old, oldl); - -Con_Printf("PF_readcmd: %s\n%s", s, output); - G_INT(OFS_RETURN) = (int)PR_SetString(prinst, output); } /* @@ -6647,11 +6646,18 @@ void SV_AddDebugPolygons(void) int i; if (gfuncs.AddDebugPolygons) { +#ifdef PROGS_DAT + extern qboolean csqc_dp_lastwas3d; + csqc_dp_lastwas3d = true; +#endif pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv.world.edicts); for (i = 0; i < sv.allocated_client_slots; i++) if (svs.clients[i].netchan.remote_address.type == NA_LOOPBACK) pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, svs.clients[i].edict); PR_ExecuteProgram (svprogfuncs, gfuncs.AddDebugPolygons); +#ifdef PROGS_DAT + csqc_dp_lastwas3d = false; +#endif } } @@ -7564,7 +7570,7 @@ static void QCBUILTIN PF_h2StopSound(pubprogfuncs_t *prinst, struct globalvars_s entity = G_EDICT(prinst, OFS_PARM0); channel = G_FLOAT(OFS_PARM1); - SVQ1_StartSound (NULL, (wedict_t*)entity, channel, "", 1, 0, 0); + SVQ1_StartSound (NULL, (wedict_t*)entity, channel, "", 1, 0, 0, 0); } static void QCBUILTIN PF_h2updatesoundpos(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) @@ -9227,7 +9233,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"qtest_setabssize",PF_setsize, 5, 0, 0, 0, D("void(entity e, vector min, vector max)", "qtest"), true}, {"breakpoint", PF_break, 6, 6, 6, 0, D("void()", "Trigger a debugging event. FTE will break into the qc debugger. Other engines may crash with a debug execption.")}, {"random", PF_random, 7, 7, 7, 0, D("float()", "Returns a random value between 0 and 1. Be warned, this builtin can return 1 in most engines, which can break arrays.")}, - {"sound", PF_sound, 8, 8, 8, 0, D("void(entity e, float chan, string samp, float vol, float atten, optional float speedpct, optional float flags)", "Starts a sound centered upon the given entity.\nchan is the entity sound channel to use, channel 0 will allow you to mix many samples at once, others will replace the old sample\n'samp' must have been precached first\nif specified, 'speedpct' should normally be around 100 (or =0), 200 for double speed or 50 for half speed.\nflags&1 means the sound should be sent reliably.")}, + {"sound", PF_sound, 8, 8, 8, 0, D("void(entity e, float chan, string samp, float vol, float atten, optional float speedpct, optional float flags, optional float timeofs)", "Starts a sound centered upon the given entity.\nchan is the entity sound channel to use, channel 0 will allow you to mix many samples at once, others will replace the old sample\n'samp' must have been precached first\nif specified, 'speedpct' should normally be around 100 (or =0), 200 for double speed or 50 for half speed.\nIf flags is specified, the reliable flag in the channels argument is used for additional channels. Flags should be made from SOUNDFLAG_* constants\ntimeofs should be negative in order to provide a delay before the sound actually starts.")}, {"normalize", PF_normalize, 9, 9, 9, 0, D("vector(vector v)", "Shorten or lengthen a direction vector such that it is only one quake unit long.")}, {"error", PF_error, 10, 10, 10, 0, D("void(string e)", "Ends the game with an easily readable error message.")}, {"objerror", PF_objerror, 11, 11, 11, 0, D("void(string e)", "Displays a non-fatal easily readable error message concerning the self entity, including a field dump. self will be removed!")}, @@ -9537,6 +9543,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"strcasecmp", PF_strncasecmp, 0, 0, 0, 229, D("float(string s1, string s2)", "Compares the two strings without case sensitivity.\nReturns 0 if they are equal. The sign of the return value may be significant, but should not be depended upon.")}, {"strncasecmp", PF_strncasecmp, 0, 0, 0, 230, D("float(string s1, string s2, float len, optional float s1ofs, optional float s2ofs)", "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.\nReturns 0 if they are equal. The sign of the return value may be significant, but should not be depended upon.")}, //END FTE_STRINGS + {"strtrim", PF_strtrim, 0, 0, 0, 0, D("string(string s)", "Trims the whitespace from the start+end of the string.")}, //FTE_CALLTIMEOFDAY {"calltimeofday", PF_calltimeofday, 0, 0, 0, 231, D("void()", "Asks the engine to instantly call the qc's 'timeofday' function, before returning. For compatibility with mvdsv.\ntimeofday should have the prototype: void(float secs, float mins, float hour, float day, float mon, float year, string strvalue)\nThe strftime builtin is more versatile and less weird.")}, @@ -9579,10 +9586,12 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"sqlversion", PF_sqlversion, 0, 0, 0, 257, "string(float serveridx)"}, // sqlversion (FTE_SQL) {"sqlreadfloat", PF_sqlreadfloat, 0, 0, 0, 258, "float(float serveridx, float queryidx, float row, float column)"}, // sqlreadfloat (FTE_SQL) - {"stoi", PF_stoi, 0, 0, 0, 259, D("int(string)", "Converts the given string into an integer. Base 8, 10, or 16 is determined based upon the format of the string.")}, - {"itos", PF_itos, 0, 0, 0, 260, D("string(int)", "Converts the passed integer into a base10 string.")}, + {"stoi", PF_stoi, 0, 0, 0, 259, D("int(string)", "Converts the given string into a true integer. Base 8, 10, or 16 is determined based upon the format of the string.")}, + {"itos", PF_itos, 0, 0, 0, 260, D("string(int)", "Converts the passed true integer into a base10 string.")}, {"stoh", PF_stoh, 0, 0, 0, 261, D("int(string)", "Reads a base-16 string (with or without 0x prefix) as an integer. Bugs out if given a base 8 or base 10 string. :P")}, {"htos", PF_htos, 0, 0, 0, 262, D("string(int)", "Formats an integer as a base16 string, with leading 0s and no prefix. Always returns 8 characters.")}, + {"ftoi", PF_ftoi, 0, 0, 0, 0, D("int(float)", "Converts the given float into a true integer without depending on extended qcvm instructions.")}, + {"itof", PF_itof, 0, 0, 0, 0, D("float(int)", "Converts the given true integer into a float without depending on extended qcvm instructions.")}, {"skel_create", PF_skel_create, 0, 0, 0, 263, D("float(float modlindex, optional float useabstransforms)", "Allocates a new uninitiaised skeletal object, with enough bone info to animate the given model.\neg: self.skeletonobject = skel_create(self.modelindex);")}, // (FTE_CSQC_SKELETONOBJECTS) {"skel_build", PF_skel_build, 0, 0, 0, 264, D("float(float skel, entity ent, float modelindex, float retainfrac, float firstbone, float lastbone, optional float addfrac)", "Animation data (according to the entity's frame info) is pulled from the specified model and blended into the specified skeletal object.\nIf retainfrac is set to 0 on the first call and 1 on the others, you can blend multiple animations together according to the addfrac value. The final weight should be 1. Other values will result in scaling and/or other weirdness. You can use firstbone and lastbone to update only part of the skeletal object, to allow legs to animate separately from torso, use 0 for both arguments to specify all, as bones are 1-based.")}, // (FTE_CSQC_SKELETONOBJECTS) @@ -9654,7 +9663,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"dynamiclight_add",PF_Fixme, 0, 0, 0, 305, D("float(vector org, float radius, vector lightcolours, optional float style, optional string cubemapname, optional float pflags)", "Adds a temporary dlight, ready to be drawn via addscene. Cubemap orientation will be read from v_forward/v_right/v_up.")},// (EXT_CSQC) //gonna expose these to ssqc as a debugging extension - {"R_BeginPolygon", PF_R_PolygonBegin,0,0, 0, 306, D("void(string texturename, optional float flags)", "Specifies the shader to use for the following polygons, along with optional flags.\nIf flags&4, the polygon will be drawn as soon as the EndPolygon call is made, rather than waiting for renderscene. This allows complex 2d effects.")},// (EXT_CSQC_???) + {"R_BeginPolygon", PF_R_PolygonBegin,0,0, 0, 306, D("void(string texturename, optional float flags, optional float is2d)", "Specifies the shader to use for the following polygons, along with optional flags.\nIf is2d, the polygon will be drawn as soon as the EndPolygon call is made, rather than waiting for renderscene. This allows complex 2d effects.")},// (EXT_CSQC_???) {"R_PolygonVertex", PF_R_PolygonVertex,0,0, 0, 307, D("void(vector org, vector texcoords, vector rgb, float alpha)", "Specifies a polygon vertex with its various properties.")},// (EXT_CSQC_???) {"R_EndPolygon", PF_R_PolygonEnd,0, 0, 0, 308, D("void()", "Ends the current polygon. At least 3 verticies must have been specified. You do not need to call beginpolygon if you wish to draw another polygon with the same shader.")}, @@ -9763,6 +9772,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"memgetval", PF_memgetval, 0, 0, 0, 388, D("__variant(__variant *dst, float ofs)", "Looks up the 32bit value stored at a pointer-with-offset.")}, {"memsetval", PF_memsetval, 0, 0, 0, 389, D("void(__variant *dst, float ofs, __variant val)", "Changes the 32bit value stored at the specified pointer-with-offset.")}, {"memptradd", PF_memptradd, 0, 0, 0, 390, D("__variant*(__variant *base, float ofs)", "Perform some pointer maths. Woo.")}, + {"memstrsize", PF_memstrsize, 0, 0, 0, 0, D("float(string s)", "strlen, except ignores utf-8")}, {"con_getset", PF_Fixme, 0, 0, 0, 391, D("string(string conname, string field, optional string newvalue)", "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")}, {"con_printf", PF_Fixme, 0, 0, 0, 392, D("void(string conname, string messagefmt, ...)", "Prints onto a named console.")}, @@ -9957,7 +9967,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"gettime", PF_gettime, 0, 0, 0, 519, "float(optional float timetype)"}, {"keynumtostring_omgwtf",PF_Fixme, 0, 0, 0, 520, "string(float keynum)"}, //excessive third version in dp's csqc. {"findkeysforcommand",PF_Fixme, 0, 0, 0, 521, D("string(string command, optional float bindmap)", "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.")}, - {"findkeysforcommandex",PF_Fixme, 0, 0, 0, 0, D("string(string command)", "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.")}, + {"findkeysforcommandex",PF_Fixme, 0, 0, 0, 0, D("string(string command, optional float bindmap)", "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.")}, // {"initparticlespawner",PF_Fixme, 0, 0, 0, 522, "void(float max_themes)"}, // {"resetparticle", PF_Fixme, 0, 0, 0, 523, "void()"}, // {"particletheme", PF_Fixme, 0, 0, 0, 524, "void(float theme)"}, @@ -9974,8 +9984,9 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs //end mvdsv extras //restart dp extras // {"log", PF_Fixme, 0, 0, 0, 532, "float(string mname)", true}, - {"getsoundtime", PF_Ignore, 0, 0, 0, 533, "float(entity e, float channel)"}, - {"soundlength", PF_Ignore, 0, 0, 0, 534, "float(string sample)"}, + {"soundupdate", PF_Fixme, 0, 0, 0, 0, D("float(entity e, float channel, string newsample, float volume, float attenuation, float pitchpct, float flags, float timeoffset)", "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.")}, + {"getsoundtime", PF_Ignore, 0, 0, 0, 533, D("float(entity e, float channel)", "Returns the current playback time of the sample on the given entity's channel. Beware CHAN_AUTO (in csqc, channels are not limited by network protocol).")}, + {"soundlength", PF_Ignore, 0, 0, 0, 534, D("float(string sample)", "Provides a way to query the duration of a sound sample, allowing you to set up a timer to chain samples.")}, {"buf_loadfile", PF_buf_loadfile, 0, 0, 0, 535, D("float(string filename, strbuf bufhandle)", "Appends the named file into a string buffer (which must have been created in advance). The return value merely says whether the file was readable.")}, {"buf_writefile", PF_buf_writefile, 0, 0, 0, 536, D("float(filestream filehandle, strbuf bufhandle, optional float startpos, optional float numstrings)", "Writes the contents of a string buffer onto the end of the supplied filehandle (you must have already used fopen). Additional optional arguments permit you to constrain the writes to a subsection of the stringbuffer.")}, // {"bufstr_find", PF_Fixme, 0, 0, 0, 537, "float(float bufhandle, string match, float matchrule, float startpos)"}, @@ -10021,8 +10032,8 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"getsurfacenumtriangles",PF_getsurfacenumtriangles,0,0,0, 628, "float(entity e, float s)"}, {"getsurfacetriangle",PF_getsurfacetriangle,0, 0, 0, 629, "vector(entity e, float s, float n)"}, // {"setkeybind", PF_Fixme, 0, 0, 0, 630, "float(float key, string bind, optional float bindmap)"}, - {"getbindmaps", PF_Fixme, 0, 0, 0, 631, "vector()" STUB}, - {"setbindmaps", PF_Fixme, 0, 0, 0, 632, "float(vector bm)" STUB}, + {"getbindmaps", PF_Fixme, 0, 0, 0, 631, "vector()"}, + {"setbindmaps", PF_Fixme, 0, 0, 0, 632, "float(vector bm)"}, {"crypto_getkeyfp", PF_Fixme, 0, 0, 0, 633, "string(string addr)" STUB}, {"crypto_getidfp", PF_Fixme, 0, 0, 0, 634, "string(string addr)" STUB}, {"crypto_getencryptlevel",PF_Fixme, 0, 0, 0, 635, "string(string addr)" STUB}, @@ -10703,12 +10714,12 @@ void PR_DumpPlatform_f(void) {"CSQC_Parse_Print", "void(string printmsg, float printlvl)", CS, "Gives the CSQC a chance to intercept sprint/bprint builtin calls. CSQC should filter by the client's current msg setting and then pass the message on to the print command, or handle them itself."}, {"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", "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_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_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}, {"CSQC_Ent_Remove", "void()", CS}, - {"CSQC_Event_Sound", "float(float entnum, float channel, string soundname, float vol, float attenuation, vector pos, float pitchmod)", CS}, + {"CSQC_Event_Sound", "float(float entnum, float channel, string soundname, float vol, float attenuation, vector pos, float pitchmod, float flags"/*", float timeofs*/")", CS}, // {"CSQC_ServerSound", "//void()", CS}, {"CSQC_LoadResource", "float(string resname, string restype)", CS, "Called each time some resource is being loaded. CSQC can invoke various draw calls to provide a loading screen, until WorldLoaded is called."}, {"CSQC_Parse_TempEntity", "float()", CS, "Please don't use this. Use CSQC_Parse_Event and multicasts instead."}, @@ -10730,7 +10741,7 @@ void PR_DumpPlatform_f(void) {"parm49, parm50, parm51, parm52, parm53, parm54, parm55, parm56, parm57, parm58, parm59, parm60, parm61, parm62, parm63, parm64", "float", QW|NQ}, {"dimension_send", "var float", QW|NQ, "Used by multicast functionality. Multicasts (and related builtins that multicast internally) will only be sent to players where (player.dimension_see & dimension_send) is non-zero."}, {"dimension_default", "//var float", QW|NQ, "Default dimension bitmask", 255}, - {"physics_mode", "var float", QW|NQ|CS, "0: original csqc - physics are not run\n1: DP-compat. Thinks occur, but not true movetypes.\n2: movetypes occur just as they do in ssqc.", 2}, + {"physics_mode", "__used var float", QW|NQ|CS, "0: original csqc - physics are not run\n1: DP-compat. Thinks occur, but not true movetypes.\n2: movetypes occur just as they do in ssqc.", 2}, {"gamespeed", "float", CS, "Set by the engine, this is the value of the sv_gamespeed cvar"}, {"numclientseats", "float", CS, "This is the number of splitscreen clients currently running on this client."}, {"drawfontscale", "var vector", CS|MENU, "Specifies a scaler for all text rendering. There are other ways to implement this.", 0, "'1 1 0'"}, @@ -10826,6 +10837,10 @@ void PR_DumpPlatform_f(void) {"CHAN_VOICE", "const float", QW|NQ|CS, NULL, CHAN_VOICE}, {"CHAN_ITEM", "const float", QW|NQ|CS, NULL, CHAN_ITEM}, {"CHAN_BODY", "const float", QW|NQ|CS, NULL, CHAN_BODY}, + {"CHANF_RELIABLE", "const float", QW, NULL, 8}, + + {"SOUNDFLAG_RELIABLE", "const float", QW|NQ, NULL, 1}, +// {"SOUNDFLAG_ABSOLUTEVOL", "const float", CS, NULL, 256}, {"ATTN_NONE", "const float", QW|NQ|CS, "Sounds with this attenuation can be heard throughout the map", ATTN_NONE}, {"ATTN_NORM", "const float", QW|NQ|CS, "Standard attenuation", ATTN_NORM}, @@ -10901,6 +10916,7 @@ void PR_DumpPlatform_f(void) {"SERVERKEY_PAUSESTATE","const string", CS, "1 if the server claimed to be paused. 0 otherwise", 0, "\"pausestate\""}, {"SERVERKEY_DLSTATE", "const string", CS, "The progress of any current downloads. Empty string if no download is active, otherwise a tokenizable string containing this info:\nfiles-remaining, total-size, unknown-sizes-flag, file-localname, file-remotename, file-percent, file-rate, file-received-bytes, file-total-bytes\nIf the current file info is omitted, then we are waiting for a download to start.", 0, "\"dlstate\""}, {"SERVERKEY_PROTOCOL", "const string", CS, "The protocol we are connected to the server with.", 0, "\"protocol\""}, + {"SERVERKEY_MAXPLAYERS","const string", CS, "The protocol we are connected to the server with.", 0, "\"maxplayers\""}, // edict.flags {"FL_FLY", "const float", QW|NQ|CS, NULL, FL_FLY}, @@ -10950,6 +10966,7 @@ void PR_DumpPlatform_f(void) {"EF_ADDITIVE", "const float", NQ|CS, NULL, NQEF_ADDITIVE}, {"EF_BLUE", "const float", QW|NQ|CS, NULL, EF_BLUE}, {"EF_RED", "const float", QW|NQ|CS, NULL, EF_RED}, + {"EF_GREEN", "const float", QW|NQ|CS, NULL, EF_GREEN}, {"EF_FULLBRIGHT", "const float", QW|NQ|CS, NULL, EF_FULLBRIGHT}, {"EF_NODEPTHTEST", "const float", QW|NQ|CS, NULL, EF_NODEPTHTEST}, diff --git a/engine/server/pr_q1qvm.c b/engine/server/pr_q1qvm.c index 64a2b3b7..7cde1607 100755 --- a/engine/server/pr_q1qvm.c +++ b/engine/server/pr_q1qvm.c @@ -685,7 +685,7 @@ static qintptr_t QVM_AmbientSound (void *offset, quintptr_t mask, const qintptr_ static qintptr_t QVM_Sound (void *offset, quintptr_t mask, const qintptr_t *arg) { // ( int edn, int channel, char *samp, float vol, float att ) - SVQ1_StartSound (NULL, (wedict_t*)Q1QVMPF_EdictNum(svprogfuncs, VM_LONG(arg[0])), VM_LONG(arg[1]), VM_POINTER(arg[2]), VM_FLOAT(arg[3])*255, VM_FLOAT(arg[4]), 0); + SVQ1_StartSound (NULL, (wedict_t*)Q1QVMPF_EdictNum(svprogfuncs, VM_LONG(arg[0])), VM_LONG(arg[1]), VM_POINTER(arg[2]), VM_FLOAT(arg[3])*255, VM_FLOAT(arg[4]), 0, 0); return 0; } static qintptr_t QVM_TraceLine (void *offset, quintptr_t mask, const qintptr_t *arg) @@ -1412,19 +1412,19 @@ static qintptr_t QVM_uri_query (void *offset, quintptr_t mask, const qintptr_t * if (!pr_enable_uriget.ival) { - Con_Printf("QVM_uri_query(\"%s\",%x): %s disabled\n", url, (int)cb_context, pr_enable_uriget.name); + Con_Printf("QVM_uri_query(\"%s\","fPRIp"): %s disabled\n", url, cb_context, pr_enable_uriget.name); return 0; } if (mimetype && *mimetype) { VALIDATEPOINTER(arg[4],datasize); - Con_DPrintf("QVM_uri_query(%s,%x)\n", url, (int)cb_context); + Con_DPrintf("QVM_uri_query(%s,"fPRIp")\n", url, cb_context); dl = HTTP_CL_Put(url, mimetype, data, datasize, QVM_uri_query_callback); } else { - Con_DPrintf("QVM_uri_query(%s,%x)\n", url, (int)cb_context); + Con_DPrintf("QVM_uri_query(%s,"fPRIp")\n", url, cb_context); dl = HTTP_CL_Get(url, NULL, QVM_uri_query_callback); } if (dl) @@ -1614,7 +1614,7 @@ static qintptr_t QVM_Map_Extension (void *offset, quintptr_t mask, const qintptr //============== general Quake services ================== -#if __WORDSIZE == 64 +#if FTE_WORDSIZE == 64 static int syscallqvm (void *offset, quintptr_t mask, int fn, const int *arg) { qintptr_t args[13]; @@ -1910,9 +1910,9 @@ qboolean PR_LoadQ1QVM(void) sv.world.usesolidcorpse = true; if ((unsigned)gd->global->mapname && (unsigned)gd->global->mapname+MAPNAME_LEN < VM_MemoryMask(q1qvm)) - Q_strncpyz((char*)VM_MemoryBase(q1qvm) + gd->global->mapname, sv.mapname, MAPNAME_LEN); + Q_strncpyz((char*)VM_MemoryBase(q1qvm) + gd->global->mapname, svs.name, MAPNAME_LEN); else - gd->global->mapname = Q1QVMPF_StringToProgs(sv.world.progs, sv.mapname); + gd->global->mapname = Q1QVMPF_StringToProgs(sv.world.progs, svs.name); PR_SV_FillWorldGlobals(&sv.world); return true; diff --git a/engine/server/savegame.c b/engine/server/savegame.c index a0cd7ada..935b247e 100644 --- a/engine/server/savegame.c +++ b/engine/server/savegame.c @@ -447,7 +447,7 @@ void SV_LegacySavegame_f (void) VFS_PRINTF(f, "%f\n", coop.value); VFS_PRINTF(f, "%f\n", teamplay.value); } - VFS_PRINTF(f, "%s\n", sv.name); + VFS_PRINTF(f, "%s\n", svs.name); VFS_PRINTF(f, "%f\n",sv.time); // write the light styles (only 64 are saved in legacy saved games) @@ -868,16 +868,16 @@ void SV_SaveLevelCache(char *savedir, qboolean dontharmgame) cache = svs.levcache; while(cache) { - if (!strcmp(cache->mapname, sv.name)) + if (!strcmp(cache->mapname, svs.name)) break; cache = cache->next; } if (!cache) //not visited yet. Let us know that we went there. { - cache = Z_Malloc(sizeof(levelcache_t)+strlen(sv.name)+1); + cache = Z_Malloc(sizeof(levelcache_t)+strlen(svs.name)+1); cache->mapname = (char *)(cache+1); - strcpy(cache->mapname, sv.name); + strcpy(cache->mapname, svs.name); cache->gametype = svs.gametype; cache->next = svs.levcache; @@ -886,9 +886,9 @@ void SV_SaveLevelCache(char *savedir, qboolean dontharmgame) } if (savedir) - Q_snprintfz (name, sizeof(name), "saves/%s/%s", savedir, sv.name); + Q_snprintfz (name, sizeof(name), "saves/%s/%s", savedir, svs.name); else - Q_snprintfz (name, sizeof(name), "saves/%s", sv.name); + Q_snprintfz (name, sizeof(name), "saves/%s", svs.name); COM_DefaultExtension (name, ".lvc", sizeof(name)); FS_CreatePath(name, FS_GAMEONLY); @@ -935,7 +935,9 @@ void SV_SaveLevelCache(char *savedir, qboolean dontharmgame) //probably this should happen elsewhere. for (cl = svs.clients, clnum=0; clnum < sv.allocated_client_slots; cl++,clnum++)//fake dropping { - if (cl->state < cs_spawned && !cl->istobeloaded) //don't drop if they are still connecting + if (progstype == PROG_H2) + cl->edict->isfree = true; //hexen2 has some annoying prints. it never formally dropped clients on map changes. + else if (cl->state < cs_spawned && !cl->istobeloaded) //don't drop if they are still connecting { cl->edict->v->solid = 0; } @@ -976,7 +978,7 @@ void SV_SaveLevelCache(char *savedir, qboolean dontharmgame) VFS_PRINTF (f, "deathmatch %s\n", COM_QuotedString(deathmatch.string, buf, sizeof(buf), false)); VFS_PRINTF (f, "coop %s\n", COM_QuotedString(coop.string, buf, sizeof(buf), false)); VFS_PRINTF (f, "teamplay %s\n", COM_QuotedString(teamplay.string, buf, sizeof(buf), false)); - VFS_PRINTF (f, "map %s\n", COM_QuotedString(sv.name, buf, sizeof(buf), false)); + VFS_PRINTF (f, "map %s\n", COM_QuotedString(svs.name, buf, sizeof(buf), false)); VFS_PRINTF (f, "time %f\n", sv.time); for (i=0 ; iparms->memfree(s); VFS_CLOSE (f); + + + if (!dontharmgame) + { + for (cl = svs.clients, clnum=0; clnum < sv.allocated_client_slots; cl++,clnum++) + cl->edict->isfree = false; + } } #define FTESAVEGAME_VERSION 25000 @@ -1167,7 +1176,7 @@ void SV_Savegame (char *savename) while(cache) { VFS_PRINTF(f, "%s\n", cache->mapname); - if (strcmp(cache->mapname, sv.name)) + if (strcmp(cache->mapname, svs.name)) { FS_Copy(va("saves/%s.lvc", cache->mapname), va("saves/%s/%s.lvc", savename, cache->mapname), FS_GAME, FS_GAME); } @@ -1175,7 +1184,7 @@ void SV_Savegame (char *savename) } VFS_PRINTF(f, "}\n"); - VFS_PRINTF (f, "%s\n", sv.name); + VFS_PRINTF (f, "%s\n", svs.name); VFS_PRINTF (f, "%g\n", (float)svs.serverflags); diff --git a/engine/server/server.h b/engine/server/server.h index 8baced32..2a3f5e45 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -128,8 +128,8 @@ typedef struct unsigned model_player_checksum; unsigned eyes_player_checksum; - char name[64]; // file map name - char mapname[256]; +// char name[64]; // file map name (moved to svs, for restart) + char mapname[256]; // text description of the map char modelname[MAX_QPATH]; // maps/.bsp, for model_precache[0] world_t world; @@ -870,6 +870,7 @@ typedef struct laggedpacket_t *free_lagged_packet; packet_entities_t entstatebuffer; /*just a temp buffer*/ + char name[64]; // map name (base filename). static because of restart command after disconnecting. levelcache_t *levcache; } server_static_t; @@ -1137,8 +1138,8 @@ void VARGS SV_Multicast (vec3_t origin, multicast_t to); #define FULLDIMENSIONMASK 0xffffffff void SV_MulticastProtExt(vec3_t origin, multicast_t to, int dimension_mask, int with, int without); -void SV_StartSound (int ent, vec3_t origin, int seenmask, int channel, const char *sample, int volume, float attenuation, int pitchadj); -void SVQ1_StartSound (float *origin, wedict_t *entity, int channel, const char *sample, int volume, float attenuation, int pitchadj); +void SV_StartSound (int ent, vec3_t origin, int seenmask, int channel, const char *sample, int volume, float attenuation, int pitchadj, float timeofs); +void SVQ1_StartSound (float *origin, wedict_t *entity, int channel, const char *sample, int volume, float attenuation, int pitchadj, float timeofs); void SV_PrintToClient(client_t *cl, int level, const char *string); void SV_TPrintToClient(client_t *cl, int level, const char *string); void SV_StuffcmdToClient(client_t *cl, const char *string); diff --git a/engine/server/sv_ccmds.c b/engine/server/sv_ccmds.c index 3fc96d41..75428940 100644 --- a/engine/server/sv_ccmds.c +++ b/engine/server/sv_ccmds.c @@ -469,7 +469,7 @@ void SV_Map_f (void) if (!strcmp(level, ".")) //restart current { //grab the current map name - Q_strncpyz(level, sv.name, sizeof(level)); + Q_strncpyz(level, svs.name, sizeof(level)); isrestart = true; flushparms = false; newunit = false; @@ -628,7 +628,15 @@ void SV_Map_f (void) if (host_client->controller == NULL) { if (ISNQCLIENT(host_client)) - SV_StuffcmdToClient(host_client, va("reconnect \"%s\"\n", level)); + { + if (ISDPCLIENT(host_client)) + { + //DP clients cannot cope with being told the next map's name + SV_StuffcmdToClient(host_client, "reconnect\n"); + } + else + SV_StuffcmdToClient(host_client, va("reconnect \"%s\"\n", level)); + } else SV_StuffcmdToClient(host_client, va("changing \"%s\"\n", level)); } @@ -678,7 +686,10 @@ void SV_Map_f (void) continue; if (ISNQCLIENT(host_client)) + { SVNQ_New_f(); + host_client->send_message = true; + } else SV_New_f(); } @@ -1183,7 +1194,7 @@ static void SV_BanList_f (void) { *middlebit = 0; if (nb->expiretime) - Q_strncatz(middlebit, va(",\t+%llu", (unsigned long long)nb->expiretime - bantime), sizeof(middlebit)); + Q_strncatz(middlebit, va(",\t+"fPRIllu, (unsigned long long)nb->expiretime - bantime), sizeof(middlebit)); if (nb->reason[0]) Q_strncatz(middlebit, ",\t", sizeof(middlebit)); Con_Printf("%s%s%s\n", NET_AdrToStringMasked(adr, sizeof(adr), &nb->adr, &nb->adrmask), middlebit, nb->reason); @@ -1234,7 +1245,7 @@ static void SV_FilterList_f (void) if (nb->expiretime) { time_t secs = nb->expiretime - curtime; - Con_Printf("%s %s +%llu:%02llu\n", NET_AdrToStringMasked(adr, sizeof(adr), &nb->adr, &nb->adrmask), banflags, (unsigned long long)(secs/60), (unsigned long long)(secs%60)); + Con_Printf("%s %s +"fPRIllu":%02u\n", NET_AdrToStringMasked(adr, sizeof(adr), &nb->adr, &nb->adrmask), banflags, (unsigned long long)(secs/60), (unsigned int)(secs%60)); } else Con_Printf("%s %s\n", NET_AdrToStringMasked(adr, sizeof(adr), &nb->adr, &nb->adrmask), banflags); @@ -1441,9 +1452,9 @@ static void SV_WriteIP_f (void) } } if (bi->reason[0]) - s = va("addip %s %s %llu \"%s\"\n", NET_AdrToStringMasked(adr, sizeof(adr), &bi->adr, &bi->adrmask), banflags, (unsigned long long) bi->expiretime, bi->reason); + s = va("addip %s %s "fPRIllu" \"%s\"\n", NET_AdrToStringMasked(adr, sizeof(adr), &bi->adr, &bi->adrmask), banflags, (unsigned long long) bi->expiretime, bi->reason); else if (bi->expiretime) - s = va("addip %s %s %llu\n", NET_AdrToStringMasked(adr, sizeof(adr), &bi->adr, &bi->adrmask), banflags, (unsigned long long) bi->expiretime); + s = va("addip %s %s "fPRIllu"\n", NET_AdrToStringMasked(adr, sizeof(adr), &bi->adr, &bi->adrmask), banflags, (unsigned long long) bi->expiretime); else s = va("addip %s %s\n", NET_AdrToStringMasked(adr, sizeof(adr), &bi->adr, &bi->adrmask), banflags); VFS_WRITE(f, s, strlen(s)); @@ -1659,10 +1670,10 @@ SV_Status_f */ static void SV_Status_f (void) { - int i, j, l; + int i; client_t *cl; float cpu, avg, pak; - char *s; + char *s, *p; char adr[MAX_ADR_SIZE]; float pi, po, bi, bo; @@ -1711,9 +1722,9 @@ static void SV_Status_f (void) Con_Printf("map uptime : %s\n", ShowTime(sv.world.physicstime)); //show the current map+name (but hide name if its too long or would be ugly) if (columns >= 80 && *sv.mapname && strlen(sv.mapname) < 45 && !strchr(sv.mapname, '\n')) - Con_Printf ("current map : %s (%s)\n", sv.name, sv.mapname); + Con_Printf ("current map : %s (%s)\n", svs.name, sv.mapname); else - Con_Printf ("current map : %s\n", sv.name); + Con_Printf ("current map : %s\n", svs.name); if (svs.gametype == GT_PROGS) { @@ -1789,16 +1800,55 @@ static void SV_Status_f (void) } else { - Con_Printf ("frags userid address name rate ping drop qport dl%% dls\n"); - Con_Printf ("----- ------ --------------- --------------- ---- ---- ----- ----- --- ----\n"); +#define COLUMNS C_FRAGS C_USERID C_ADDRESS C_NAME C_RATE C_PING C_DROP C_DLP C_DLS C_PROT C_ADDRESS2 +#define C_FRAGS COLUMN(0, "frags", Con_Printf("%5i ", (int)cl->old_frags)) +#define C_USERID COLUMN(1, "userid", Con_Printf("%6i ", (int)cl->userid)) +#define C_ADDRESS COLUMN(2, "address ", Con_Printf("%-16.16s", s)) +#define C_NAME COLUMN(3, "name ", Con_Printf("%-16.16s", cl->name)) +#define C_RATE COLUMN(4, "rate", Con_Printf("%4i ", (int)(1/cl->netchan.frame_rate))) +#define C_PING COLUMN(5, "ping", Con_Printf("%4i ", (int)SV_CalcPing (cl, false))) +#define C_DROP COLUMN(6, "drop", Con_Printf("%4.1f ", 100.0*cl->netchan.drop_count / cl->netchan.incoming_sequence)) +#define C_DLP COLUMN(7, "dl ", if (!cl->download)Con_Printf(" ");else Con_Printf("%3g ", (cl->downloadcount*100.0)/cl->downloadsize)) +#define C_DLS COLUMN(8, "dls", if (!cl->download)Con_Printf(" ");else Con_Printf("%3u ", (unsigned int)(cl->downloadsize/1024))) +#define C_PROT COLUMN(9, "prot", Con_Printf("%-5.5s", p)) +#define C_ADDRESS2 COLUMN(10, "address ", Con_Printf("%s", s)) + + int columns = (1<<6)-1; + + for (i=0,cl=svs.clients ; inetchan.drop_count) + columns |= 1<<6; + if (cl->download) + { + columns |= 1<<7; + columns |= 1<<8; + } + if (cl->protocol != SCP_QUAKEWORLD || cl->spectator || !(cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)) + columns |= 1<<9; + if (cl->netchan.remote_address.type == NA_IPV6 || cl->netchan.remote_address.type == NA_TCPV6 || cl->netchan.remote_address.type == NA_TLSV6) + columns = (columns & ~(1<<2)) | (1<<10); + } + +#define COLUMN(f,t,v) if (columns&(1<state) continue; - Con_Printf ("%5i %6i ", (int)cl->old_frags, cl->userid); + if (cl->state == cs_loadzombie) - { + { //loadzombies have no specific address if (cl->istobeloaded) s = "LoadZombie"; else @@ -1812,39 +1862,40 @@ static void SV_Status_f (void) s = "bot"; else s = NET_BaseAdrToString (adr, sizeof(adr), &cl->netchan.remote_address); - Con_Printf ("%s", s); - l = 16 - strlen(s); - for (j=0 ; jname); - l = 16 - strlen(cl->name); - for (j=0 ; jprotocol) + { + default: + case SCP_BAD: + p = ""; + break; + case SCP_QUAKEWORLD: + if (cl->spectator) + p = "s"; + else if (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS) + p = "fte"; + else + p = "qw"; + break; + case SCP_QUAKE2: p = "q2"; break; + case SCP_QUAKE3: p = "q3"; break; + case SCP_NETQUAKE: p = "nq"; break; + case SCP_PROQUAKE: p = "pq"; break; + case SCP_FITZ666: p = "fq"; break; + case SCP_DARKPLACES6: p = "dp6"; break; + case SCP_DARKPLACES7: p = "dp7"; break; + } if (cl->state == cs_connected) - { - Con_Printf ("CONNECTING "); - } + p = "con"; else if (cl->state == cs_zombie || cl->state == cs_loadzombie) - { - Con_Printf ("ZOMBIE "); - } - else - Con_Printf ("%4i %4i %5.1f %4i" - , (int)(1000*cl->netchan.frame_rate) - , (int)SV_CalcPing (cl, false) - , 100.0*cl->netchan.drop_count / cl->netchan.incoming_sequence - , cl->netchan.qport); - if (cl->download) - { - Con_Printf (" %3g %4u", (cl->downloadcount*100.0)/cl->downloadsize, (unsigned int)(cl->downloadsize/1024)); - } - if (cl->spectator) - Con_Printf(" (s)\n"); - else - Con_Printf("\n"); + p = "zom"; +#define COLUMN(f,t,v) if (columns&(1<csqcentversions) * cl->max_net_ents; - Con_Printf("%i minping=%i frame=%i, csqc=%i\n", sizeof(svs.clients[i]), sz, fr, csfr); + Con_Printf(fPRIzu" minping=%i frame=%i, csqc=%i\n", sizeof(svs.clients[i]), sz, fr, csfr); } } + + //FIXME: report vm memory } /* diff --git a/engine/server/sv_ents.c b/engine/server/sv_ents.c index f06967fa..5f9d4415 100644 --- a/engine/server/sv_ents.c +++ b/engine/server/sv_ents.c @@ -807,8 +807,8 @@ void SVQW_WriteDelta (entity_state_t *from, entity_state_t *to, sizebuf_t *msg, #define UF_REMOVE UF_16BIT /*says we removed the entity in this frame*/ #define UF_MOVETYPE UF_EFFECTS2 /*this flag isn't present in the header itself*/ #define UF_RESET2 UF_EXTEND1 /*so new ents are reset 3 times to avoid weird baselines*/ -#define UF_UNUSED UF_EXTEND2 /**/ -#define UF_WEAPONFRAME_OLD UF_EXTEND3 +//#define UF_UNUSED UF_EXTEND2 /**/ +#define UF_WEAPONFRAME_OLD UF_EXTEND2 #define UF_VIEWANGLES UF_EXTEND3 /**/ static unsigned int SVFTE_DeltaPredCalcBits(entity_state_t *from, entity_state_t *to) @@ -944,6 +944,11 @@ static void SVFTE_WriteUpdate(unsigned int bits, entity_state_t *state, sizebuf_ } else { + if (bits & UF_VIEWANGLES) + { + bits &= ~UF_VIEWANGLES; + bits |= UF_PREDINFO; + } if (bits & UF_WEAPONFRAME_OLD) { bits &= ~UF_WEAPONFRAME_OLD; @@ -1552,20 +1557,10 @@ void SVQW_EmitPacketEntities (client_t *client, packet_entities_t *to, sizebuf_t void SVDP_EmitEntityDelta(entity_state_t *from, entity_state_t *to, sizebuf_t *msg, qboolean isnew) { int bits; -// if (!isnew && !memcmp(from, to, sizeof(entity_state_t))) -// { -// return; //didn't change -// } bits = 0; if (isnew) - { bits |= E5_FULLUPDATE; - } - - bits |= E5_MODEL; - bits |= E5_ORIGIN; - bits |= E5_FRAME; if (!VectorEquals(from->origin, to->origin)) bits |= E5_ORIGIN; @@ -1596,9 +1591,9 @@ void SVDP_EmitEntityDelta(entity_state_t *from, entity_state_t *to, sizebuf_t *m if (from->colormod[0] != to->colormod[0] || from->colormod[1] != to->colormod[1] || from->colormod[2] != to->colormod[2]) bits |= E5_COLORMOD; - if ((bits & E5_ORIGIN) && (/*!(to->flags & RENDER_LOWPRECISION) ||*/ to->origin[0] < -4096 || to->origin[0] >= 4096 || to->origin[1] < -4096 || to->origin[1] >= 4096 || to->origin[2] < -4096 || to->origin[2] >= 4096)) + if ((bits & E5_ORIGIN) && (!(to->dpflags & RENDER_LOWPRECISION) || to->origin[0] < -4096 || to->origin[0] >= 4096 || to->origin[1] < -4096 || to->origin[1] >= 4096 || to->origin[2] < -4096 || to->origin[2] >= 4096)) bits |= E5_ORIGIN32; - if ((bits & E5_ANGLES)/* && !(to->flags & RENDER_LOWPRECISION)*/) + if ((bits & E5_ANGLES) && !(to->dpflags & RENDER_LOWPRECISION)) bits |= E5_ANGLES16; if ((bits & E5_MODEL) && to->modelindex >= 256) bits |= E5_MODEL16; @@ -1728,19 +1723,19 @@ void SVDP_EmitEntitiesUpdate (client_t *client, packet_entities_t *to, sizebuf_t int oldnum, newnum; int oldmax; - client->netchan.incoming_sequence++; - // this is the frame that we are going to delta update from - fromframe = &client->frameunion.frames[client->delta_sequence & UPDATE_MASK]; + fromframe = &client->frameunion.frames[client->netchan.incoming_sequence-1 & UPDATE_MASK]; from = &fromframe->entities; oldmax = from->num_entities; // Con_Printf ("frame %i\n", client->netchan.incoming_sequence); MSG_WriteByte(msg, svcdp_entities); - MSG_WriteLong(msg, client->netchan.incoming_sequence); + MSG_WriteLong(msg, client->netchan.incoming_sequence); //sequence for the client to ack (any bits sent in unacked frames will be re-queued) if (client->protocol == SCP_DARKPLACES7) - MSG_WriteLong(msg, client->last_sequence); + MSG_WriteLong(msg, client->last_sequence); //movement sequence that we are acking. + + client->netchan.incoming_sequence++; //add in the bitmasks of dropped packets. @@ -1772,6 +1767,7 @@ void SVDP_EmitEntitiesUpdate (client_t *client, packet_entities_t *to, sizebuf_t if (newnum > oldnum) { // the old entity isn't present in the new message +// Con_Printf("sRemove %i\n", oldnum); MSG_WriteShort(msg, oldnum | 0x8000); oldindex++; continue; @@ -3044,9 +3040,9 @@ void SV_Snapshot_BuildStateQ1(entity_state_t *state, edict_t *ent, client_t *cli state->tagentity = ent->xv->tag_entity; state->tagindex = ent->xv->tag_index; - state->light[0] = ent->xv->color[0]*255; - state->light[1] = ent->xv->color[1]*255; - state->light[2] = ent->xv->color[2]*255; + state->light[0] = ent->xv->color[0]*1024; + state->light[1] = ent->xv->color[1]*1024; + state->light[2] = ent->xv->color[2]*1024; state->light[3] = ent->xv->light_lev; state->lightstyle = ent->xv->style; state->lightpflags = ent->xv->pflags; @@ -3082,53 +3078,83 @@ void SV_Snapshot_BuildStateQ1(entity_state_t *state, edict_t *ent, client_t *cli if (state->effects & EF_FULLBRIGHT) //wrap the field for fte clients (this is horrible) state->hexen2flags |= MLS_FULLBRIGHT; - if (progstype != PROG_QW && state->effects && client && ISQWCLIENT(client)) //don't send extra nq effects to a qw client. + if (progstype != PROG_QW) { - //EF_NODRAW doesn't draw the model. - //The client still needs to know about it though, as it might have other effects on it. - if (progstype == PROG_H2) - { - if (state->effects == H2EF_NODRAW) - { - //actually, H2 is pretty lame about this - state->effects = 0; - state->modelindex = 0; - state->frame = 0; - state->colormap = 0; - state->abslight = 0; - state->skinnum = 0; - state->hexen2flags = 0; - } - } - else if (progstype == PROG_UNKNOWN) + if (progstype == PROG_UNKNOWN) { //unknown progs crc. things here are basically hacks. - if (state->effects & 16) //tenebrae's EF_FULLDYNAMIC + //tenebrae has some hideous hacks + if (!strcmp(sv.strings.model_precache[state->modelindex], "progs/w_light.spr") || + !strcmp(sv.strings.model_precache[state->modelindex], "progs/b_light.spr") || + !strcmp(sv.strings.model_precache[state->modelindex], "progs/s_light.spr") || + !strcmp(sv.strings.model_precache[state->modelindex], "progs/flame.mdl") || + !strcmp(sv.strings.model_precache[state->modelindex], "progs/flame2.mdl")) { - state->effects &= ~16; + //fixme: add some default colours state->lightpflags |= PFLAGS_FULLDYNAMIC; + if (!state->light[3]) + state->light[3] = 350; } - } - else - { - if (state->effects & NQEF_NODRAW) - state->modelindex = 0; - } - if (state->number <= sv.allocated_client_slots) // clear only client ents - state->effects &= ~ (QWEF_FLAG1|QWEF_FLAG2); - - if ((state->effects & EF_DIMLIGHT) && !(state->effects & (EF_RED|EF_BLUE))) + if (!strcmp (sv.strings.model_precache[state->modelindex], "progs/lavaball.mdl")) + { + state->lightpflags |= PFLAGS_FULLDYNAMIC; + state->skinnum = 17; + state->light[3] = 270; + } + } + if (state->effects && client && ISQWCLIENT(client)) //don't send extra nq effects to a qw client. { - int it = ent->v->items; - state->effects &= ~EF_DIMLIGHT; - if ((it & (IT_INVULNERABILITY|IT_QUAD)) == (IT_INVULNERABILITY|IT_QUAD)) - state->effects |= EF_RED|EF_BLUE; - else if (it & IT_INVULNERABILITY) - state->effects |= EF_RED; - else if (it & IT_QUAD) - state->effects |= EF_BLUE; + //EF_NODRAW doesn't draw the model. + //The client still needs to know about it though, as it might have other effects on it. + if (progstype == PROG_H2) + { + if (state->effects == H2EF_NODRAW) + { + //actually, H2 is pretty lame about this + state->effects = 0; + state->modelindex = 0; + state->frame = 0; + state->colormap = 0; + state->abslight = 0; + state->skinnum = 0; + state->hexen2flags = 0; + } + } + else if (progstype == PROG_UNKNOWN) + { //unknown progs crc. things here are basically hacks. + if (state->effects & 16) //tenebrae's EF_FULLDYNAMIC + { + state->effects &= ~16; + state->lightpflags |= PFLAGS_FULLDYNAMIC; + } + if (state->effects & 32) + { + state->effects &= ~32; + state->effects |= EF_GREEN; + } + } else - state->effects |= EF_DIMLIGHT; + { + if (state->effects & NQEF_NODRAW) + state->modelindex = 0; + } + + if (state->number <= sv.allocated_client_slots) // clear only client ents + state->effects &= ~ (QWEF_FLAG1|QWEF_FLAG2); + + if ((state->effects & EF_DIMLIGHT) && !(state->effects & (EF_RED|EF_BLUE))) + { + int it = ent->v->items; + state->effects &= ~EF_DIMLIGHT; + if ((it & (IT_INVULNERABILITY|IT_QUAD)) == (IT_INVULNERABILITY|IT_QUAD)) + state->effects |= EF_RED|EF_BLUE; + else if (it & IT_INVULNERABILITY) + state->effects |= EF_RED; + else if (it & IT_QUAD) + state->effects |= EF_BLUE; + else + state->effects |= EF_DIMLIGHT; + } } } @@ -3637,7 +3663,6 @@ void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qboolean ignore return; // put other visible entities into either a packet_entities or a nails message - #ifdef SERVER_DEMO_PLAYBACK if (sv.demostatevalid) //generate info from demo stats { diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index 3d53ad53..682e9402 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -491,7 +491,7 @@ void SV_CalcPHS (void) if (rowbytes*num >= 0x100000) { char hdr[8]; - vfsfile_t *f = FS_OpenVFS(va("maps/%s.phs", sv.name), "rb", FS_GAME); + vfsfile_t *f = FS_OpenVFS(va("maps/%s.phs", svs.name), "rb", FS_GAME); if (f) { VFS_READ(f, hdr, sizeof(hdr)); @@ -544,7 +544,7 @@ void SV_CalcPHS (void) if (rowbytes*num >= 0x100000) { - vfsfile_t *f = FS_OpenVFS(va("maps/%s.phs", sv.name), "wb", FS_GAMEONLY); + vfsfile_t *f = FS_OpenVFS(va("maps/%s.phs", svs.name), "wb", FS_GAMEONLY); if (f) { VFS_WRITE(f, "QPHS\1\0\0\0", 8); @@ -610,7 +610,6 @@ void SV_UnspawnServer (void) //terminate the running server. #endif sv.world.worldmodel = NULL; sv.state = ss_dead; - *sv.name = '\0'; if (sv.csqcentversion) { BZ_Free(sv.csqcentversion); @@ -869,6 +868,9 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us #endif Mod_ClearAll (); +#ifndef SERVERONLY + r_regsequence++; +#endif PR_Deinit(); @@ -884,7 +886,7 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us if (allow_download_refpackages.ival) FS_ReferenceControl(1, 1); - Q_strncpyz (sv.name, server, sizeof(sv.name)); + Q_strncpyz (svs.name, server, sizeof(svs.name)); #ifndef SERVERONLY current_loading_size+=10; //SCR_BeginLoadingPlaque(); @@ -931,7 +933,7 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us { qboolean QDECL Mod_LoadQ2BrushModel (model_t *mod, void *buffer, size_t fsize); - Q_strncpyz (sv.name, server, sizeof(sv.name)); + Q_strncpyz (svs.name, server, sizeof(svs.name)); Q_strncpyz (sv.modelname, "", sizeof(sv.modelname)); sv.world.worldmodel = Mod_FindName (sv.modelname); @@ -947,7 +949,7 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us //if you want to load a .map, just use 'map foo.map' instead. char *exts[] = {"maps/%s", "maps/%s.bsp", "maps/%s.cm", "maps/%s.hmp", /*"maps/%s.map",*/ NULL}; int depth, bestdepth; - Q_strncpyz (sv.name, server, sizeof(sv.name)); + Q_strncpyz (svs.name, server, sizeof(svs.name)); Q_snprintfz (sv.modelname, sizeof(sv.modelname), exts[0], server); bestdepth = COM_FDepthFile(sv.modelname, false); for (i = 1; exts[i]; i++) @@ -1372,7 +1374,7 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us #ifdef VM_Q1 if (svs.gametype != GT_Q1QVM) //we cannot do this with qvm #endif - svprogfuncs->SetStringField(svprogfuncs, NULL, &pr_global_struct->mapname, sv.name, true); + svprogfuncs->SetStringField(svprogfuncs, NULL, &pr_global_struct->mapname, svs.name, true); // serverflags are for cross level information (sigils) pr_global_struct->serverflags = svs.serverflags; @@ -1500,7 +1502,7 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us break; #ifdef Q2SERVER case GT_QUAKE2: - ge->SpawnEntities(sv.name, file, startspot?startspot:""); + ge->SpawnEntities(svs.name, file, startspot?startspot:""); break; #endif case GT_QUAKE3: @@ -1518,7 +1520,7 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us SCR_ImageName(server); #endif - Q_strncpyz(sv.mapname, sv.name, sizeof(sv.mapname)); + Q_strncpyz(sv.mapname, svs.name, sizeof(sv.mapname)); if (svprogfuncs) { eval_t *val; @@ -1535,7 +1537,7 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us snprintf(sv.mapname, sizeof(sv.mapname), "%s", PR_GetString(svprogfuncs, val->string)); } else - snprintf(sv.mapname, sizeof(sv.mapname), "%s", sv.name); + snprintf(sv.mapname, sizeof(sv.mapname), "%s", svs.name); if (Cvar_Get("sv_readonlyworld", "1", 0, "DP compatability")->value) { ent->readonly = true; //lock it down! @@ -1600,7 +1602,7 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us SV_GibFilterInit(); SV_FilterImpulseInit(); - Info_SetValueForKey (svs.info, "map", sv.name, MAX_SERVERINFO_STRING); + Info_SetValueForKey (svs.info, "map", svs.name, MAX_SERVERINFO_STRING); if (sv.allocated_client_slots != 1) Con_TPrintf ("Server spawned.\n"); //misc filenotfounds can be misleading. diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 98a697b1..b8199f0a 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -109,7 +109,7 @@ cvar_t sv_maxdrate = CVARAF("sv_maxdrate", "100000", "sv_maxdownloadrate", 0); cvar_t sv_minping = CVARF("sv_minping", "", CVAR_SERVERINFO); -cvar_t sv_bigcoords = CVARFD("sv_bigcoords", "", CVAR_SERVERINFO, "Uses floats for coordinates instead of 16bit values. Affects clients thusly:\nQW: enforces a mandatory protocol extension\nDP: enables DPP7 protocol support\nNQ: uses RMQ protocol (protocol 999)."); +cvar_t sv_bigcoords = CVARFD("sv_bigcoords", "1", 0, "Uses floats for coordinates instead of 16bit values.\nAlso boosts angle precision, so can be useful even on small maps.\nAffects clients thusly:\nQW: enforces a mandatory protocol extension\nDP: enables DPP7 protocol support\nNQ: uses RMQ protocol (protocol 999)."); cvar_t sv_calcphs = CVARFD("sv_calcphs", "2", CVAR_LATCH, "Enables culling of sound effects. 0=always skip phs. Sounds are globally broadcast. 1=always generate phs. Sounds are always culled. On large maps the phs will be dumped to disk. 2=On large single-player maps, generation of phs is skipped. Otherwise like option 1."); cvar_t sv_showconnectionlessmessages = CVARD("sv_showconnectionlessmessages", "0", "Display a line describing each connectionless message that arrives on the server. Primarily a debugging feature, but also potentially useful to admins."); @@ -117,7 +117,7 @@ cvar_t sv_cullplayers_trace = CVARFD("sv_cullplayers_trace", "", CVAR_SERVERINFO cvar_t sv_cullentities_trace = CVARFD("sv_cullentities_trace", "", CVAR_SERVERINFO, "Attempt to cull non-player entities using tracelines as an extreeme anti-wallhack."); cvar_t sv_phs = CVARD("sv_phs", "1", "If 1, do not use the phs. It is generally better to use sv_calcphs instead, and leave this as 1."); cvar_t sv_resetparms = CVAR("sv_resetparms", "0"); -cvar_t sv_pupglow = CVARF("sv_pupglow", "", CVAR_SERVERINFO); +cvar_t sv_pupglow = CVARFD("sv_pupglow", "", CVAR_SERVERINFO, "Instructs clients to enable hexen2-style powerup pulsing."); cvar_t sv_master = CVAR("sv_master", "0"); cvar_t sv_masterport = CVAR("sv_masterport", "0"); @@ -1264,7 +1264,7 @@ void SVC_InfoQ2 (void) if (svs.clients[i].state >= cs_connected) count++; - snprintf (string, sizeof(string), "%16s %8s %2i/%2i\n", hostname.string, sv.name, count, (int)maxclients.value); + snprintf (string, sizeof(string), "%16s %8s %2i/%2i\n", hostname.string, svs.name, count, (int)maxclients.value); } Netchan_OutOfBandPrint (NS_SERVER, &net_from, "info\n%s", string); @@ -1386,7 +1386,10 @@ int SV_NewChallenge (void) for (i = 0 ; i < MAX_CHALLENGES ; i++) { if (NET_CompareBaseAdr (&net_from, &svs.challenges[i].adr)) + { + svs.challenges[i].time = realtime; return svs.challenges[i].challenge; + } if (svs.challenges[i].time < oldestTime) { oldestTime = svs.challenges[i].time; @@ -1418,136 +1421,109 @@ void SVC_GetChallenge (void) #ifdef HUFFNETWORK int compressioncrc; #endif - int i; - int oldest; - int oldestTime; + int challenge; + char *buf; + int lng; + char *over; if (!sv_listen_qw.value && !sv_listen_dp.value && !sv_listen_q3.ival) return; - oldest = 0; - oldestTime = 0x7fffffff; - // see if we already have a challenge for this ip - for (i = 0 ; i < MAX_CHALLENGES ; i++) - { - if (NET_CompareBaseAdr (&net_from, &svs.challenges[i].adr)) - break; - if (svs.challenges[i].time < oldestTime) - { - oldestTime = svs.challenges[i].time; - oldest = i; - } - } - - if (i == MAX_CHALLENGES) - { - // overwrite the oldest - svs.challenges[oldest].challenge = (rand() << 16) ^ rand(); - svs.challenges[oldest].adr = net_from; - svs.challenges[oldest].time = realtime; - i = oldest; - } + challenge = SV_NewChallenge(); // send it back - { - char *buf; - int lng; - char *over; - #ifdef Q3SERVER - if (svs.gametype == GT_QUAKE3) //q3 servers - buf = va("challengeResponse %i", svs.challenges[i].challenge); - else + if (svs.gametype == GT_QUAKE3) //q3 servers + buf = va("challengeResponse %i", challenge); + else #endif #ifdef Q2SERVER - if (svs.gametype == GT_QUAKE2) //quake 2 servers give a different challenge responce - buf = va("challenge %i", svs.challenges[i].challenge); - else + if (svs.gametype == GT_QUAKE2) //quake 2 servers give a different challenge responce + buf = va("challenge %i", challenge); + else #endif - buf = va("%c%i", S2C_CHALLENGE, svs.challenges[i].challenge); + buf = va("%c%i", S2C_CHALLENGE, challenge); - over = buf + strlen(buf) + 1; + over = buf + strlen(buf) + 1; - if (svs.gametype == GT_PROGS || svs.gametype == GT_Q1QVM) - { + if (svs.gametype == GT_PROGS || svs.gametype == GT_Q1QVM) + { #ifdef PROTOCOL_VERSION_FTE - unsigned int mask; - //tell the client what fte extensions we support - mask = Net_PextMask(1, false); - if (mask) - { - lng = LittleLong(PROTOCOL_VERSION_FTE); - memcpy(over, &lng, sizeof(lng)); - over+=sizeof(lng); + unsigned int mask; + //tell the client what fte extensions we support + mask = Net_PextMask(1, false); + if (mask) + { + lng = LittleLong(PROTOCOL_VERSION_FTE); + memcpy(over, &lng, sizeof(lng)); + over+=sizeof(lng); - lng = LittleLong(mask); - memcpy(over, &lng, sizeof(lng)); - over+=sizeof(lng); - } - //tell the client what fte extensions we support - mask = Net_PextMask(2, false); - if (mask) - { - lng = LittleLong(PROTOCOL_VERSION_FTE2); - memcpy(over, &lng, sizeof(lng)); - over+=sizeof(lng); + lng = LittleLong(mask); + memcpy(over, &lng, sizeof(lng)); + over+=sizeof(lng); + } + //tell the client what fte extensions we support + mask = Net_PextMask(2, false); + if (mask) + { + lng = LittleLong(PROTOCOL_VERSION_FTE2); + memcpy(over, &lng, sizeof(lng)); + over+=sizeof(lng); - lng = LittleLong(mask); - memcpy(over, &lng, sizeof(lng)); - over+=sizeof(lng); - } + lng = LittleLong(mask); + memcpy(over, &lng, sizeof(lng)); + over+=sizeof(lng); + } #endif - mask = net_mtu.ival&~7; - if (mask > 64) - { - lng = LittleLong(PROTOCOL_VERSION_FRAGMENT); - memcpy(over, &lng, sizeof(lng)); - over+=sizeof(lng); + mask = net_mtu.ival&~7; + if (mask > 64) + { + lng = LittleLong(PROTOCOL_VERSION_FRAGMENT); + memcpy(over, &lng, sizeof(lng)); + over+=sizeof(lng); - lng = LittleLong(mask); - memcpy(over, &lng, sizeof(lng)); - over+=sizeof(lng); - } + lng = LittleLong(mask); + memcpy(over, &lng, sizeof(lng)); + over+=sizeof(lng); + } #ifdef HUFFNETWORK - compressioncrc = Huff_PreferedCompressionCRC(); - if (compressioncrc) - { - lng = LittleLong(PROTOCOL_VERSION_HUFFMAN); - memcpy(over, &lng, sizeof(lng)); - over+=sizeof(lng); - - lng = LittleLong(compressioncrc); - memcpy(over, &lng, sizeof(lng)); - over+=sizeof(lng); - } -#endif - } - if (sv_listen_qw.value || (svs.gametype != GT_PROGS && svs.gametype != GT_Q1QVM)) - Netchan_OutOfBand(NS_SERVER, &net_from, over-buf, buf); - - if (sv_listen_dp.value && (sv_listen_nq.value || sv_bigcoords.value || !sv_listen_qw.value)) + compressioncrc = Huff_PreferedCompressionCRC(); + if (compressioncrc) { - //dp (protocol6 upwards) can respond to this (and fte won't get confused because the challenge will be wrong) - buf = va("challenge "DISTRIBUTION"%i", svs.challenges[i].challenge); - Netchan_OutOfBand(NS_SERVER, &net_from, strlen(buf)+1, buf); - } -#ifdef Q3SERVER - if (svs.gametype == GT_PROGS || svs.gametype == GT_Q1QVM) - { - if (sv_listen_q3.ival) - { - buf = va("challengeResponse %i", svs.challenges[i].challenge); - Netchan_OutOfBand(NS_SERVER, &net_from, strlen(buf), buf); - } + lng = LittleLong(PROTOCOL_VERSION_HUFFMAN); + memcpy(over, &lng, sizeof(lng)); + over+=sizeof(lng); + + lng = LittleLong(compressioncrc); + memcpy(over, &lng, sizeof(lng)); + over+=sizeof(lng); } #endif } -// Netchan_OutOfBandPrint (net_from, "%c%i", S2C_CHALLENGE, -// svs.challenges[i].challenge); + if (sv_listen_dp.value && (sv_listen_nq.value || sv_bigcoords.value || !sv_listen_qw.value)) + { + //dp (protocol6 upwards) can respond to this (and fte won't get confused because the challenge will be wrong) + char *dp = va("challenge "DISTRIBUTION"%i", challenge); + Netchan_OutOfBand(NS_SERVER, &net_from, strlen(dp)+1, dp); + } + + if (sv_listen_qw.value || (svs.gametype != GT_PROGS && svs.gametype != GT_Q1QVM)) + Netchan_OutOfBand(NS_SERVER, &net_from, over-buf, buf); + +#ifdef Q3SERVER + if (svs.gametype == GT_PROGS || svs.gametype == GT_Q1QVM) + { + if (sv_listen_q3.ival) + { + buf = va("challengeResponse %i", challenge); + Netchan_OutOfBand(NS_SERVER, &net_from, strlen(buf), buf); + } + } +#endif } void VARGS SV_OutOfBandPrintf (int q2, netadr_t *adr, char *format, ...) @@ -1610,6 +1586,25 @@ qboolean SV_ChallengePasses(int challenge) return false; } +//DP sends us a getchallenge followed by a CCREQ_CONNECT at about the same time. +//this means that DP clients tend to connect as generic NQ clients. +//and because DP _REQUIRES_ sv_bigcoords, they tend to end up being given fitz/rmq protocols +//thus we don't respond to the connect if sv_listen_dp is 1, and we had a recent getchallenge request. recent is 2 secs. +qboolean SV_ChallengeRecent(void) +{ + int curtime = realtime; //yeah, evil. sue me. consitent with challenges. + int i; + for (i=0 ; i curtime - 2) + return true; + } + } + return false; +} + void VARGS SV_RejectMessage(int protocol, char *format, ...) { va_list argptr; @@ -1890,7 +1885,7 @@ void SV_UserDNSResolved(void *ctx, void *data, size_t idx, size_t uid) return; } } - Con_DPrintf("stale dns lookup result: %s\n", data); + Con_DPrintf("stale dns lookup result: %s\n", (char*)data); Z_Free(data); } @@ -2046,8 +2041,7 @@ client_t *SVC_DirectConnect(void) //this is used by q3 (note, we already decrypted the huffman connection packet in a hack) if (!sv_listen_q3.ival) { - if (!sv_listen_nq.value) - SV_RejectMessage (SCP_DARKPLACES6, "Server is not accepting quake3 clients at this time.\n", version_string()); + SV_RejectMessage (SCP_QUAKE3, "Server is not accepting quake3 clients at this time.\n", version_string()); Con_TPrintf ("* rejected connect from q3 client\n"); return NULL; } @@ -2084,7 +2078,7 @@ client_t *SVC_DirectConnect(void) } else if (*(Cmd_Argv(0)+7) == '\\') { //DP has the userinfo attached directly to the end of the connect command - if (!sv_listen_dp.value) + if (!sv_listen_dp.value && net_from.type != NA_LOOPBACK) { if (!sv_listen_nq.value) SV_RejectMessage (SCP_DARKPLACES6, "Server is not accepting darkplaces clients at this time.\n", version_string()); @@ -2390,13 +2384,15 @@ client_t *SVC_DirectConnect(void) if (cl->state == cs_free || cl->state == cs_loadzombie) continue; if (NET_CompareBaseAdr (&adr, &cl->netchan.remote_address) - && ( cl->netchan.qport == qport - || adr.port == cl->netchan.remote_address.port )) + && ((protocol == SCP_QUAKEWORLD && cl->netchan.qport == qport) || adr.port == cl->netchan.remote_address.port )) { if (cl->state == cs_connected) { if (cl->protocol != protocol) + { Con_TPrintf("%s: diff prot connect\n", NET_AdrToString (adrbuf, sizeof(adrbuf), &adr)); + return NULL; + } else Con_TPrintf("%s:dup connect\n", NET_AdrToString (adrbuf, sizeof(adrbuf), &adr)); } @@ -2405,8 +2401,9 @@ client_t *SVC_DirectConnect(void) Con_Printf ("%s:reconnect\n", NET_AdrToString (adrbuf, sizeof(adrbuf), &adr)); }*/ else - Con_TPrintf ("%s:%s:reconnect\n", sv.name, NET_AdrToString (adrbuf, sizeof(adrbuf), &adr)); + Con_TPrintf ("%s:%s:reconnect\n", svs.name, NET_AdrToString (adrbuf, sizeof(adrbuf), &adr)); //silently drop the old connection, without causing the old client to get a disconnect or anything stupid like that. + return NULL; cl->protocol = SCP_BAD; SV_DropClient (cl); cl->protocol = protocol; @@ -2477,9 +2474,9 @@ client_t *SVC_DirectConnect(void) if (((!strcmp(cl->name, name) || !*cl->name) && (!*cl->guid || !strcmp(guid, cl->guid))) || sv.allocated_client_slots <= 1) //named, or first come first serve. { if (cl->istobeloaded) - Con_DPrintf("%s:Using loadzombie\n", sv.name); + Con_DPrintf("%s:Using loadzombie\n", svs.name); else - Con_DPrintf("%s:Using parmzombie\n", sv.name); + Con_DPrintf("%s:Using parmzombie\n", svs.name); newcl = cl; preserveparms = true; temp.istobeloaded = cl->istobeloaded; @@ -2497,6 +2494,7 @@ client_t *SVC_DirectConnect(void) if (SSV_IsSubServer()) { SV_RejectMessage (protocol, "Direct connections are not permitted.\n"); + Con_TPrintf ("* rejected direct connection\n"); return NULL; } @@ -2558,12 +2556,18 @@ client_t *SVC_DirectConnect(void) if (spectator && spectators >= maxspectators.ival) { SV_RejectMessage (protocol, "\nserver is full (%i of %i spectators)\n\n", spectators, maxspectators.ival); - Con_TPrintf ("%s:full connect\n", NET_AdrToString (adrbuf, sizeof(adrbuf), &adr)); + Con_TPrintf ("%s:full connect (spectators)\n", NET_AdrToString (adrbuf, sizeof(adrbuf), &adr)); } else if (!spectator && clients >= maxclients.ival) + { SV_RejectMessage (protocol, "\nserver is full (%i of %i players)\n\n", clients, maxclients.ival); + Con_TPrintf ("%s:full connect (players)\n", NET_AdrToString (adrbuf, sizeof(adrbuf), &adr)); + } else + { SV_RejectMessage (protocol, "\nserver is full (%i of %i connections)\n\n", clients+spectators, sv.allocated_client_slots); + Con_TPrintf ("%s:full connect\n", NET_AdrToString (adrbuf, sizeof(adrbuf), &adr)); + } } return NULL; } @@ -2581,6 +2585,7 @@ client_t *SVC_DirectConnect(void) if (!SVHL_ClientConnect(newcl, adr, reject)) { SV_RejectMessage(protocol, "%s", reject); + Con_TPrintf ("%s:gamecode reject\n", NET_AdrToString (adrbuf, sizeof(adrbuf), &adr)); return NULL; } } @@ -3432,6 +3437,8 @@ qboolean SVNQ_ConnectionlessPacket(void) if (msg_readcount+17 <= net_message.cursize && !strncmp("challengeconnect ", &net_message.data[msg_readcount], 17)) { client_t *newcl; + if (sv_showconnectionlessmessages.ival) + Con_Printf("CCREQ_CONNECT_COOKIE\n"); Cmd_TokenizeString(MSG_ReadStringLine(), false, false); /*okay, so this is a reliable packet from a client, containing a 'cmd challengeconnect $challenge' response*/ str = va("connect %i %i %s \"\\name\\unconnected\\mod\\%s\\modver\\%s\\flags\\%s\\password\\%s\"", NQ_NETCHAN_VERSION, 0, Cmd_Argv(1), Cmd_Argv(2), Cmd_Argv(3), Cmd_Argv(4), Cmd_Argv(5)); @@ -3499,6 +3506,9 @@ qboolean SVNQ_ConnectionlessPacket(void) switch(MSG_ReadByte()) { case CCREQ_CONNECT: + if (sv_showconnectionlessmessages.ival) + Con_Printf("CCREQ_CONNECT\n"); + sb.maxsize = sizeof(buffer); sb.data = buffer; if (strcmp(MSG_ReadString(), NQ_NETCHAN_GAMENAME)) @@ -3545,6 +3555,8 @@ qboolean SVNQ_ConnectionlessPacket(void) /*dual-stack client, supporting either DP or QW protocols*/ SVC_GetChallenge (); } + else if (SV_ChallengeRecent()) + return true; else { if (sv_listen_nq.ival == 2) @@ -3581,6 +3593,8 @@ qboolean SVNQ_ConnectionlessPacket(void) } return true; case CCREQ_SERVER_INFO: + if (sv_showconnectionlessmessages.ival) + Con_Printf("CCREQ_SERVER_INFO\n"); if (sv_public.ival < 0) return false; if (SV_BannedReason (&net_from)) @@ -3603,7 +3617,7 @@ qboolean SVNQ_ConnectionlessPacket(void) if (svs.clients[i].state) active++; MSG_WriteString (&sb, hostname.string); - MSG_WriteString (&sb, sv.name); + MSG_WriteString (&sb, svs.name); MSG_WriteByte (&sb, active); MSG_WriteByte (&sb, maxclients.value); MSG_WriteByte (&sb, NQ_NETCHAN_VERSION); @@ -3611,6 +3625,8 @@ qboolean SVNQ_ConnectionlessPacket(void) NET_SendPacket(NS_SERVER, sb.cursize, sb.data, &net_from); return true; case CCREQ_PLAYER_INFO: + if (sv_showconnectionlessmessages.ival) + Con_Printf("CCREQ_PLAYER_INFO\n"); if (sv_public.ival < 0) return false; if (SV_BannedReason (&net_from)) @@ -3643,6 +3659,8 @@ qboolean SVNQ_ConnectionlessPacket(void) NET_SendPacket(NS_SERVER, sb.cursize, sb.data, &net_from); return true; case CCREQ_RULE_INFO: + if (sv_showconnectionlessmessages.ival) + Con_Printf("CCREQ_RULE_INFO\n"); if (sv_public.ival < 0) return false; if (SV_BannedReason (&net_from)) @@ -5001,6 +5019,9 @@ void SV_ExtractFromUserinfo (client_t *cl, qboolean verbose) else cl->rate = ISNQCLIENT(cl)?10000:2500; //an nq client cannot cope with quakeworld's default rate, and typically doesn't have rate set either. + val = Info_ValueForKey (cl->userinfo, "dupe"); + cl->netchan.dupe = atoi(val); + val = Info_ValueForKey (cl->userinfo, "drate"); if (strlen(val)) cl->drate = atoi(val); diff --git a/engine/server/sv_mvd.c b/engine/server/sv_mvd.c index 09ce3961..d34afce5 100644 --- a/engine/server/sv_mvd.c +++ b/engine/server/sv_mvd.c @@ -1419,7 +1419,7 @@ mvddest_t *SV_InitRecordFile (char *name) COM_TimeOfDay(&date); - snprintf(buf, sizeof(buf), "date %s\nmap %s\nteamplay %d\ndeathmatch %d\ntimelimit %d\n%s",date.str, sv.name, (int)teamplay.value, (int)deathmatch.value, (int)timelimit.value, SV_PrintTeams()); + snprintf(buf, sizeof(buf), "date %s\nmap %s\nteamplay %d\ndeathmatch %d\ntimelimit %d\n%s",date.str, svs.name, (int)teamplay.value, (int)deathmatch.value, (int)timelimit.value, SV_PrintTeams()); VFS_WRITE(f, buf, strlen(buf)); VFS_FLUSH(f); VFS_CLOSE(f); @@ -2347,19 +2347,19 @@ void SV_MVDEasyRecord_f (void) Q_strncatz (name, va("[%s]_%s_vs_[%s]_%s_%s", Dem_Team(1), Dem_PlayerNameTeam(Dem_Team(1)), Dem_Team(2), Dem_PlayerNameTeam(Dem_Team(2)), - sv.name), sizeof(name)); + svs.name), sizeof(name)); } else - Q_strncatz (name, va("%s_vs_%s_%s", Dem_Team(1), Dem_Team(2), sv.name), sizeof(name)); + Q_strncatz (name, va("%s_vs_%s_%s", Dem_Team(1), Dem_Team(2), svs.name), sizeof(name)); } else { if (i == 2) { // Duel snprintf (name, sizeof(name), "duel_%s_vs_%s_%s", Dem_PlayerName(1), Dem_PlayerName(2), - sv.name); + svs.name); } else { // FFA - snprintf (name, sizeof(name), "ffa_%s(%d)", sv.name, i); + snprintf (name, sizeof(name), "ffa_%s(%d)", svs.name, i); } } } diff --git a/engine/server/sv_phys.c b/engine/server/sv_phys.c index c62fc976..1261737a 100644 --- a/engine/server/sv_phys.c +++ b/engine/server/sv_phys.c @@ -1254,7 +1254,7 @@ static void WPhys_CheckWaterTransition (world_t *w, wedict_t *ent) { if (ent->v->watertype == Q1CONTENTS_EMPTY && *sv_sound_watersplash.string) { // just crossed into water - w->Event_Sound(NULL, ent, 0, sv_sound_watersplash.string, 255, 1, 0); + w->Event_Sound(NULL, ent, 0, sv_sound_watersplash.string, 255, 1, 0, 0); } ent->v->watertype = cont; ent->v->waterlevel = 1; @@ -1263,7 +1263,7 @@ static void WPhys_CheckWaterTransition (world_t *w, wedict_t *ent) { if (ent->v->watertype != Q1CONTENTS_EMPTY && *sv_sound_watersplash.string) { // just crossed into open - w->Event_Sound(NULL, ent, 0, sv_sound_watersplash.string, 255, 1, 0); + w->Event_Sound(NULL, ent, 0, sv_sound_watersplash.string, 255, 1, 0, 0); } ent->v->watertype = Q1CONTENTS_EMPTY; ent->v->waterlevel = cont; @@ -1449,7 +1449,7 @@ static void WPhys_Physics_Step (world_t *w, wedict_t *ent) { if (hitsound && *sv_sound_land.string) { - w->Event_Sound(NULL, ent, 0, sv_sound_land.string, 255, 1, 0); + w->Event_Sound(NULL, ent, 0, sv_sound_land.string, 255, 1, 0, 0); } } } diff --git a/engine/server/sv_send.c b/engine/server/sv_send.c index 59c760c0..ce6e862d 100644 --- a/engine/server/sv_send.c +++ b/engine/server/sv_send.c @@ -979,7 +979,7 @@ Larger attenuations will drop off. (max 4 attenuation) ================== */ -void SV_StartSound (int ent, vec3_t origin, int seenmask, int channel, const char *sample, int volume, float attenuation, int pitchadj) +void SV_StartSound (int ent, vec3_t origin, int seenmask, int channel, const char *sample, int volume, float attenuation, int pitchadj, float timeofs) { int sound_num; int extfield_mask; @@ -1074,19 +1074,23 @@ void SV_StartSound (int ent, vec3_t origin, int seenmask, int channel, const cha extfield_mask |= DPSND_LARGESOUND; if (pitchadj && (pitchadj != 100)) extfield_mask |= FTESND_PITCHADJ; + if (timeofs != 0) + extfield_mask |= FTESND_TIMEOFS; #ifdef PEXT_SOUNDDBL - if (channel >= 8 || ent >= 2048 || sound_num > 0xff || pitchadj) + if (channel >= 8 || ent >= 2048 || sound_num > 0xff || (pitchadj && pitchadj != 100)) { //if any of the above conditions evaluates to true, then we can't use standard qw protocols MSG_WriteByte (&sv.multicast, svcfte_soundextended); MSG_WriteByte (&sv.multicast, extfield_mask); if (extfield_mask & NQSND_VOLUME) - MSG_WriteByte (&sv.multicast, volume); + MSG_WriteByte (&sv.multicast, bound(0, volume, 255)); if (extfield_mask & NQSND_ATTENUATION) MSG_WriteByte (&sv.multicast, bound(0, attenuation*64, 255)); if (extfield_mask & FTESND_PITCHADJ) - MSG_WriteByte (&sv.multicast, pitchadj); + MSG_WriteByte (&sv.multicast, bound(1, pitchadj, 255)); + if (extfield_mask & FTESND_TIMEOFS) + MSG_WriteShort (&sv.multicast, bound(-32768, timeofs*1000, 32767)); if (extfield_mask & DPSND_LARGEENTITY) { MSG_WriteEntity (&sv.multicast, ent); @@ -1142,6 +1146,8 @@ void SV_StartSound (int ent, vec3_t origin, int seenmask, int channel, const cha MSG_WriteByte (&sv.nqmulticast, bound(0, attenuation*64, 255)); if (extfield_mask & FTESND_PITCHADJ) MSG_WriteByte (&sv.nqmulticast, pitchadj); + if (extfield_mask & FTESND_TIMEOFS) + MSG_WriteShort (&sv.nqmulticast, bound(-32768, timeofs*1000, 32767)); if (extfield_mask & DPSND_LARGEENTITY) { MSG_WriteEntity (&sv.nqmulticast, ent); @@ -1162,7 +1168,7 @@ void SV_StartSound (int ent, vec3_t origin, int seenmask, int channel, const cha SV_MulticastProtExt(origin, reliable ? MULTICAST_ALL_R : MULTICAST_ALL, seenmask, requiredextensions, 0); } -void SVQ1_StartSound (float *origin, wedict_t *wentity, int channel, const char *sample, int volume, float attenuation, int pitchadj) +void SVQ1_StartSound (float *origin, wedict_t *wentity, int channel, const char *sample, int volume, float attenuation, int pitchadj, float timeofs) { edict_t *entity = (edict_t*)wentity; int i; @@ -1193,7 +1199,7 @@ void SVQ1_StartSound (float *origin, wedict_t *wentity, int channel, const char } } - SV_StartSound(NUM_FOR_EDICT(svprogfuncs, entity), origin, entity->xv->dimension_seen, channel, sample, volume, attenuation, pitchadj); + SV_StartSound(NUM_FOR_EDICT(svprogfuncs, entity), origin, entity->xv->dimension_seen, channel, sample, volume, attenuation, pitchadj, timeofs); } /* @@ -1429,6 +1435,12 @@ void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg) if (client->fteprotocolextensions2 & PEXT2_PREDINFO) return; + + if (client->protocol == SCP_DARKPLACES6 || client->protocol == SCP_DARKPLACES7) + nqjunk = false; + else + nqjunk = true; + bits = 0; if (ent->v->view_ofs[2] != DEFAULT_VIEWHEIGHT) @@ -1447,7 +1459,8 @@ void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg) items = (int)ent->v->items | ((int)pr_global_struct->serverflags << 28); - bits |= SU_ITEMS; + if (nqjunk) + bits |= SU_ITEMS; if ( (int)ent->v->flags & FL_ONGROUND) bits |= SU_ONGROUND; @@ -1463,12 +1476,7 @@ void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg) bits |= (SU_VELOCITY1<protocol == SCP_DARKPLACES6 || client->protocol == SCP_DARKPLACES7) - { - //bits &= ~SU_ITEMS; - nqjunk = false; - } - else + if (nqjunk) { nqjunk = true; @@ -1560,7 +1568,7 @@ void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg) MSG_WriteByte (msg, ent->v->armorvalue); } if (bits & SU_WEAPONMODEL) - MSG_WriteByte (msg, weaponmodelindex); + MSG_WriteByte (msg, weaponmodelindex&0xff); if (nqjunk) { @@ -2665,7 +2673,7 @@ void SV_SendClientMessages (void) //if they're running too slowly, FORCE them to run //this little check is to guard against people using msecs=0 to hover in mid-air. also keeps players animating/moving/etc when timing c->msecs += msecs; - while (c->msecs > 1000) + while (c->state >= cs_spawned && c->msecs > 1000) { if (c->msecs > 1200) c->msecs = 1200; @@ -2677,6 +2685,8 @@ void SV_SendClientMessages (void) sv_player = c->edict; SV_PreRunCmd(); cmd.msec = msecs;//25; + if (msecs > 1000) + msecs = 1000; //really? I blame the debugger. VectorCopy(c->lastcmd.angles, cmd.angles); cmd.buttons = c->lastcmd.buttons; SV_RunCmd (&cmd, true); @@ -2773,7 +2783,8 @@ void SV_SendClientMessages (void) c->nextservertimeupdate = 0; c->netchan.cleartime = realtime - 100; - c->netchan.nqunreliableonly = !c->send_message; + if (c->netchan.nqunreliableonly == 1) + c->netchan.nqunreliableonly = !c->send_message; c->datagram.cursize = 0; if (!c->send_message && c->nextservertimeupdate < pt) { diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index 3f131cf4..663908d7 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -34,6 +34,7 @@ edict_t *sv_player; usercmd_t cmd; +extern cvar_t dpcompat_nopreparse; #ifdef SERVERONLY cvar_t cl_rollspeed = SCVAR("cl_rollspeed", "200"); cvar_t cl_rollangle = SCVAR("cl_rollangle", "2.0"); @@ -245,6 +246,14 @@ void SV_New_f (void) } } + if (dpcompat_nopreparse.ival && progstype != PROG_QW) + { + SV_PrintToClient(host_client, PRINT_HIGH, "This server now has network preparsing disabled, and thus only supports NetQuake clients\n"); + Con_Printf("%s was not using NQ protocols\n"); + host_client->drop = true; + return; + } + /* splitt delay host_client->state = cs_connected; host_client->connection_started = realtime; @@ -278,6 +287,7 @@ void SV_New_f (void) { SV_ClientPrintf(host_client, 2, "\n\n\n\nSorry, but your client does not appear to support FTE's bigcoords\nFTE users will need to set cl_nopext to 0 and then reconnect, or to upgrade\n"); Con_Printf("%s does not support bigcoords\n", host_client->name); + host_client->drop = true; return; } @@ -462,6 +472,13 @@ void SVNQ_New_f (void) return; } + if (dpcompat_nopreparse.ival && progstype == PROG_QW) + { + SV_PrintToClient(host_client, PRINT_HIGH, "This server now has network preparsing disabled, and thus only supports QuakeWorld clients\n"); + Con_Printf("%s was not using QW protocols\n"); + host_client->drop = true; + return; + } Z_Free(host_client->csqcentversions); host_client->csqcentversions = NULL; @@ -508,7 +525,7 @@ void SVNQ_New_f (void) } else { - host_client->protocol = (host_client->protocol==SCP_PROQUAKE)?SCP_PROQUAKE:SCP_NETQUAKE; //identical other than the client->server angles + host_client->protocol = (host_client->protocol!=SCP_NETQUAKE)?SCP_PROQUAKE:SCP_NETQUAKE; //identical other than the client->server angles protmain = NQ_PROTOCOL_VERSION; protoname = "NQ"; } @@ -590,9 +607,7 @@ void SVNQ_New_f (void) else MSG_WriteByte (&host_client->netchan.message, GAME_COOP); - strcpy (message, sv.mapname); - - MSG_WriteString (&host_client->netchan.message,message); + MSG_WriteString (&host_client->netchan.message, sv.mapname); //fixme: don't send too many models. @@ -617,6 +632,8 @@ void SVNQ_New_f (void) host_client->prespawn_stage = PRESPAWN_SERVERINFO; host_client->prespawn_idx = 0; + + host_client->netchan.nqunreliableonly = 2; } @@ -7457,7 +7474,7 @@ void SVNQ_ReadClientMove (usercmd_t *move) else host_client->edict->v->v_angle[i] = MSG_ReadAngle (); - move->angles[i] = (host_client->edict->v->v_angle[i] * 256*256)/360; + move->angles[i] = ANGLE2SHORT(host_client->edict->v->v_angle[i]); } // read movement diff --git a/engine/server/svq2_game.c b/engine/server/svq2_game.c index 11b2146c..27835608 100644 --- a/engine/server/svq2_game.c +++ b/engine/server/svq2_game.c @@ -261,7 +261,7 @@ static void VARGS PFQ2_Configstring (int i, char *val) strcpy(sv.strings.configstring[i], val); if (i == Q2CS_NAME) - Q_strncpyz(sv.mapname, val, sizeof(sv.name)); + Q_strncpyz(sv.mapname, val, sizeof(sv.mapname)); /* //work out range diff --git a/engine/server/svq3_game.c b/engine/server/svq3_game.c index f382a77b..99d2d133 100644 --- a/engine/server/svq3_game.c +++ b/engine/server/svq3_game.c @@ -4,7 +4,6 @@ //requires qvm implementation and existing q3 client stuff (or at least the overlapping stuff in q3common.c). #ifdef Q3SERVER -float RadiusFromBounds (vec3_t mins, vec3_t maxs); #define USEBOTLIB @@ -1791,7 +1790,7 @@ qboolean SVQ3_InitGame(void) //q3 needs mapname (while qw has map serverinfo) { cvar_t *mapname = Cvar_Get("mapname", "", CVAR_SERVERINFO, "Q3 compatability"); - Cvar_Set(mapname, sv.name); + Cvar_Set(mapname, svs.name); } SV_InitBotLib(); @@ -1804,7 +1803,7 @@ qboolean SVQ3_InitGame(void) strcpy(buffer, svs.info); Info_SetValueForKey(buffer, "map", "", sizeof(buffer)); Info_SetValueForKey(buffer, "maxclients", "", sizeof(buffer)); - Info_SetValueForKey(buffer, "mapname", sv.name, sizeof(buffer)); + Info_SetValueForKey(buffer, "mapname", svs.name, sizeof(buffer)); Info_SetValueForKey(buffer, "sv_maxclients", "32", sizeof(buffer)); Info_SetValueForKey(buffer, "sv_pure", "", sizeof(buffer)); SVQ3_SetConfigString(0, buffer); diff --git a/plugins/jabber/jabberclient.c b/plugins/jabber/jabberclient.c index 85e16fb9..62585453 100644 --- a/plugins/jabber/jabberclient.c +++ b/plugins/jabber/jabberclient.c @@ -2308,7 +2308,7 @@ struct stringprep_range stringprep_A1[] = {0x05C5,0x05CF}, {0x05EB,0x05EF}, {0x05F5,0x060B}, - {0x0600,~0}, //FIXME rest of A.1 + {0x0600,~0}, //FIXME rest of A.1 (utf) }; struct stringprep_range stringprep_B1[] = { @@ -2355,7 +2355,7 @@ struct stringprep_range stringprep_C1[] = {0x0020}, {0x00A0}, {0x1680}, - //FIXME... + //FIXME... utf }; struct stringprep_range stringprep_C2[] = { @@ -2364,7 +2364,7 @@ struct stringprep_range stringprep_C2[] = {0x007F, 0x007F}, //C.2.1 {0x0080, 0x009F}, //C.2.2 - //FIXME... + //FIXME... utf {0x06DD, ~0}, }; struct stringprep_range stringprep_C3[] =