From 3d3b7695a0f51411325cf580587d0a1fd500d84f Mon Sep 17 00:00:00 2001 From: Mario Date: Sat, 30 May 2020 23:36:13 +1000 Subject: [PATCH 01/23] Implement "forcenearest" particle effect key, ported from wrath-darkplaces --- cl_main.c | 9 ++++----- cl_parse.c | 8 ++++---- cl_particles.c | 43 ++++++++++++++++++++++++++++--------------- client.h | 2 +- clvm_cmds.c | 6 +++--- gl_rmain.c | 11 +++++++++-- 6 files changed, 49 insertions(+), 30 deletions(-) diff --git a/cl_main.c b/cl_main.c index 5d22ba7b6..9eb39ec89 100644 --- a/cl_main.c +++ b/cl_main.c @@ -701,7 +701,7 @@ void CL_Effect(vec3_t org, int modelindex, int startframe, int framecount, float } } -void CL_AllocLightFlash(entity_render_t *ent, matrix4x4_t *matrix, float radius, float red, float green, float blue, float decay, float lifetime, int cubemapnum, int style, int shadowenable, vec_t corona, vec_t coronasizescale, vec_t ambientscale, vec_t diffusescale, vec_t specularscale, int flags) +void CL_AllocLightFlash(entity_render_t *ent, matrix4x4_t *matrix, float radius, float red, float green, float blue, float decay, float lifetime, char *cubemapname, int style, int shadowenable, vec_t corona, vec_t coronasizescale, vec_t ambientscale, vec_t diffusescale, vec_t specularscale, int flags) { int i; dlight_t *dl; @@ -738,10 +738,9 @@ void CL_AllocLightFlash(entity_render_t *ent, matrix4x4_t *matrix, float radius, dl->die = cl.time + lifetime; else dl->die = 0; - if (cubemapnum > 0) - dpsnprintf(dl->cubemapname, sizeof(dl->cubemapname), "cubemaps/%i", cubemapnum); - else - dl->cubemapname[0] = 0; + dl->cubemapname[0] = 0; + if (cubemapname && cubemapname[0]) + strlcpy(dl->cubemapname, cubemapname, sizeof(dl->cubemapname)); dl->style = style; dl->shadow = shadowenable; dl->corona = corona; diff --git a/cl_parse.c b/cl_parse.c index 45bd0137d..13d1479a7 100644 --- a/cl_parse.c +++ b/cl_parse.c @@ -2797,7 +2797,7 @@ static void CL_ParseTempEntity(void) color[2] = MSG_ReadCoord(&cl_message, cls.protocol) * (2.0f / 1.0f); CL_ParticleExplosion(pos); Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]); - CL_AllocLightFlash(NULL, &tempmatrix, 350, color[0], color[1], color[2], 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); + CL_AllocLightFlash(NULL, &tempmatrix, 350, color[0], color[1], color[2], 700, 0.5, NULL, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1); break; @@ -2810,7 +2810,7 @@ static void CL_ParseTempEntity(void) color[1] = MSG_ReadByte(&cl_message) * (2.0f / 255.0f); color[2] = MSG_ReadByte(&cl_message) * (2.0f / 255.0f); Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]); - CL_AllocLightFlash(NULL, &tempmatrix, 350, color[0], color[1], color[2], 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); + CL_AllocLightFlash(NULL, &tempmatrix, 350, color[0], color[1], color[2], 700, 0.5, NULL, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1); break; @@ -2837,7 +2837,7 @@ static void CL_ParseTempEntity(void) color[1] = MSG_ReadByte(&cl_message) * (2.0f / 255.0f); color[2] = MSG_ReadByte(&cl_message) * (2.0f / 255.0f); Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]); - CL_AllocLightFlash(NULL, &tempmatrix, radius, color[0], color[1], color[2], radius / velspeed, velspeed, 0, -1, true, 1, 0.25, 1, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); + CL_AllocLightFlash(NULL, &tempmatrix, radius, color[0], color[1], color[2], radius / velspeed, velspeed, NULL, -1, true, 1, 0.25, 1, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); break; case TE_FLAMEJET: @@ -2898,7 +2898,7 @@ static void CL_ParseTempEntity(void) color[1] = tempcolor[1] * (2.0f / 255.0f); color[2] = tempcolor[2] * (2.0f / 255.0f); Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]); - CL_AllocLightFlash(NULL, &tempmatrix, 350, color[0], color[1], color[2], 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); + CL_AllocLightFlash(NULL, &tempmatrix, 350, color[0], color[1], color[2], 700, 0.5, NULL, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1); break; diff --git a/cl_particles.c b/cl_particles.c index 2093bcb5c..e5d02e450 100644 --- a/cl_particles.c +++ b/cl_particles.c @@ -44,6 +44,7 @@ particletype_t particletype[pt_total] = #define PARTICLEEFFECT_UNDERWATER 1 #define PARTICLEEFFECT_NOTUNDERWATER 2 +#define PARTICLEEFFECT_FORCENEAREST 4 #define PARTICLEEFFECT_DEFINED 2147483648U typedef struct particleeffectinfo_s @@ -465,6 +466,7 @@ static void CL_Particles_ParseEffectInfo(const char *textstart, const char *text else if (!strcmp(argv[0], "staintex")) {readints(info->staintex, 2);} else if (!strcmp(argv[0], "stainless")) {info->staintex[0] = -2; info->staincolor[0] = (unsigned int)-1; info->staincolor[1] = (unsigned int)-1; info->stainalpha[0] = 1; info->stainalpha[1] = 1; info->stainsize[0] = 2; info->stainsize[1] = 2; } else if (!strcmp(argv[0], "rotate")) {readfloats(info->rotate, 4);} + else if (!strcmp(argv[0], "forcenearest")) {checkparms(1);info->flags |= PARTICLEEFFECT_FORCENEAREST;} else Con_Printf("%s:%i: skipping unknown command %s\n", filename, linenumber, argv[0]); #undef checkparms @@ -875,6 +877,17 @@ void CL_SpawnDecalParticleForPoint(const vec3_t org, float maxdist, float size, CL_SpawnDecalParticleForSurface(besthitent, bestorg, bestnormal, color1, color2, texnum, size, alpha); } +// generates a cubemap name with prefix flags based on info flags (for now only `!`) +static char *LightCubemapNumToName(char *vabuf, size_t vasize, int lightcubemapnum, int flags) +{ + if (lightcubemapnum <= 0) + return NULL; + // `!` is prepended if the cubemap must be nearest-filtered + if (flags & PARTICLEEFFECT_FORCENEAREST) + return va(vabuf, vasize, "!cubemaps/%i", lightcubemapnum); + return va(vabuf, vasize, "cubemaps/%i", lightcubemapnum); +} + static void CL_Sparks(const vec3_t originmins, const vec3_t originmaxs, const vec3_t velocitymins, const vec3_t velocitymaxs, float sparkcount); static void CL_Smoke(const vec3_t originmins, const vec3_t originmaxs, const vec3_t velocitymins, const vec3_t velocitymaxs, float smokecount); static void CL_NewParticlesFromEffectinfo(int effectnameindex, float pcount, const vec3_t originmins, const vec3_t originmaxs, const vec3_t velocitymins, const vec3_t velocitymaxs, entity_t *ent, int palettecolor, qboolean spawndlight, qboolean spawnparticles, float tintmins[4], float tintmaxs[4], float fade, qboolean wanttrail); @@ -949,7 +962,7 @@ static void CL_ParticleEffect_Fallback(int effectnameindex, float count, const v // bullet hole R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64); CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF); - CL_AllocLightFlash(NULL, &lightmatrix, 100, 0.15f, 0.15f, 1.5f, 500, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); + CL_AllocLightFlash(NULL, &lightmatrix, 100, 0.15f, 0.15f, 1.5f, 500, 0.2, NULL, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); } else if (effectnameindex == EFFECT_TE_SUPERSPIKE) { @@ -990,7 +1003,7 @@ static void CL_ParticleEffect_Fallback(int effectnameindex, float count, const v // bullet hole R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64); CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF); - CL_AllocLightFlash(NULL, &lightmatrix, 100, 0.15f, 0.15f, 1.5f, 500, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); + CL_AllocLightFlash(NULL, &lightmatrix, 100, 0.15f, 0.15f, 1.5f, 500, 0.2, NULL, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); } else if (effectnameindex == EFFECT_TE_BLOOD) { @@ -1022,7 +1035,7 @@ static void CL_ParticleEffect_Fallback(int effectnameindex, float count, const v // plasma scorch mark R_Stain(center, 40, 40, 40, 40, 64, 88, 88, 88, 64); CL_SpawnDecalParticleForPoint(center, 6, 6, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF); - CL_AllocLightFlash(NULL, &lightmatrix, 200, 1, 1, 1, 1000, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); + CL_AllocLightFlash(NULL, &lightmatrix, 200, 1, 1, 1, 1000, 0.2, NULL, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); } else if (effectnameindex == EFFECT_TE_GUNSHOT) { @@ -1057,17 +1070,17 @@ static void CL_ParticleEffect_Fallback(int effectnameindex, float count, const v // bullet hole R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64); CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF); - CL_AllocLightFlash(NULL, &lightmatrix, 100, 0.15f, 0.15f, 1.5f, 500, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); + CL_AllocLightFlash(NULL, &lightmatrix, 100, 0.15f, 0.15f, 1.5f, 500, 0.2, NULL, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); } else if (effectnameindex == EFFECT_TE_EXPLOSION) { CL_ParticleExplosion(center); - CL_AllocLightFlash(NULL, &lightmatrix, 350, 4.0f, 2.0f, 0.50f, 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); + CL_AllocLightFlash(NULL, &lightmatrix, 350, 4.0f, 2.0f, 0.50f, 700, 0.5, NULL, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); } else if (effectnameindex == EFFECT_TE_EXPLOSIONQUAD) { CL_ParticleExplosion(center); - CL_AllocLightFlash(NULL, &lightmatrix, 350, 2.5f, 2.0f, 4.0f, 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); + CL_AllocLightFlash(NULL, &lightmatrix, 350, 2.5f, 2.0f, 4.0f, 700, 0.5, NULL, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); } else if (effectnameindex == EFFECT_TE_TAREXPLOSION) { @@ -1084,10 +1097,10 @@ static void CL_ParticleEffect_Fallback(int effectnameindex, float count, const v } else CL_ParticleExplosion(center); - CL_AllocLightFlash(NULL, &lightmatrix, 600, 1.6f, 0.8f, 2.0f, 1200, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); + CL_AllocLightFlash(NULL, &lightmatrix, 600, 1.6f, 0.8f, 2.0f, 1200, 0.5, NULL, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); } else if (effectnameindex == EFFECT_TE_SMALLFLASH) - CL_AllocLightFlash(NULL, &lightmatrix, 200, 2, 2, 2, 1000, 0.2, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); + CL_AllocLightFlash(NULL, &lightmatrix, 200, 2, 2, 2, 1000, 0.2, NULL, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); else if (effectnameindex == EFFECT_TE_FLAMEJET) { count *= cl_particles_quality.value; @@ -1142,7 +1155,7 @@ static void CL_ParticleEffect_Fallback(int effectnameindex, float count, const v } if (!cl_particles_quake.integer) CL_NewParticle(center, pt_static, 0xffffff, 0xffffff, tex_particle, 30, 0, 256, 512, 0, 0, center[0], center[1], center[2], 0, 0, 0, 0, 0, 0, 0, false, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1, 0, 0, NULL); - CL_AllocLightFlash(NULL, &lightmatrix, 200, 2.0f, 2.0f, 2.0f, 400, 99.0f, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); + CL_AllocLightFlash(NULL, &lightmatrix, 200, 2.0f, 2.0f, 2.0f, 400, 99.0f, NULL, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); } else if (effectnameindex == EFFECT_TE_TEI_G3) CL_NewParticle(center, pt_beam, 0xFFFFFF, 0xFFFFFF, tex_beam, 8, 0, 256, 256, 0, 0, originmins[0], originmins[1], originmins[2], originmaxs[0], originmaxs[1], originmaxs[2], 0, 0, 0, 0, false, 0, 1, PBLEND_ADD, PARTICLE_HBEAM, -1, -1, -1, 1, 1, 0, 0, NULL); @@ -1158,7 +1171,7 @@ static void CL_ParticleEffect_Fallback(int effectnameindex, float count, const v else if (effectnameindex == EFFECT_TE_TEI_BIGEXPLOSION) { CL_ParticleExplosion(center); - CL_AllocLightFlash(NULL, &lightmatrix, 500, 2.5f, 2.0f, 1.0f, 500, 9999, 0, -1, true, 1, 0.25, 0.5, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); + CL_AllocLightFlash(NULL, &lightmatrix, 500, 2.5f, 2.0f, 1.0f, 500, 9999, NULL, -1, true, 1, 0.25, 0.5, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); } else if (effectnameindex == EFFECT_TE_TEI_PLASMAHIT) { @@ -1171,7 +1184,7 @@ static void CL_ParticleEffect_Fallback(int effectnameindex, float count, const v if (cl_particles_sparks.integer) for (f = 0;f < count;f += 1.0f / cl_particles_quality.value) CL_NewParticle(center, pt_spark, 0x2030FF, 0x80C0FF, tex_particle, 2.0f, 0, lhrandom(64, 255), 512, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 0, 0, 0, 465, true, 0, 1, PBLEND_ADD, PARTICLE_SPARK, -1, -1, -1, 1, 1, 0, 0, NULL); - CL_AllocLightFlash(NULL, &lightmatrix, 500, 0.6f, 1.2f, 2.0f, 2000, 9999, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); + CL_AllocLightFlash(NULL, &lightmatrix, 500, 0.6f, 1.2f, 2.0f, 2000, 9999, NULL, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); } else if (effectnameindex == EFFECT_EF_FLAME) { @@ -1180,7 +1193,7 @@ static void CL_ParticleEffect_Fallback(int effectnameindex, float count, const v count *= 300 * cl_particles_quality.value; while (count-- > 0) CL_NewParticle(center, pt_smoke, 0x6f0f00, 0xe3974f, tex_particle, 4, 0, lhrandom(64, 128), 384, -1, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 1, 4, 16, 128, true, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1, 0, 0, NULL); - CL_AllocLightFlash(NULL, &lightmatrix, 200, 2.0f, 1.5f, 0.5f, 0, 0, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); + CL_AllocLightFlash(NULL, &lightmatrix, 200, 2.0f, 1.5f, 0.5f, 0, 0, NULL, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); } else if (effectnameindex == EFFECT_EF_STARDUST) { @@ -1189,7 +1202,7 @@ static void CL_ParticleEffect_Fallback(int effectnameindex, float count, const v count *= 200 * cl_particles_quality.value; while (count-- > 0) CL_NewParticle(center, pt_static, 0x903010, 0xFFD030, tex_particle, 4, 0, lhrandom(64, 128), 128, 1, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 0.2, 0.8, 16, 128, true, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1, 0, 0, NULL); - CL_AllocLightFlash(NULL, &lightmatrix, 200, 1.0f, 0.7f, 0.3f, 0, 0, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); + CL_AllocLightFlash(NULL, &lightmatrix, 200, 1.0f, 0.7f, 0.3f, 0, 0, NULL, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); } else if (!strncmp(particleeffectname[effectnameindex], "TR_", 3)) { @@ -1479,7 +1492,7 @@ static void CL_NewParticlesFromEffectinfo(int effectnameindex, float pcount, con { // light flash (explosion, etc) // called when effect starts - CL_AllocLightFlash(NULL, &tempmatrix, info->lightradiusstart, info->lightcolor[0]*avgtint[0]*avgtint[3], info->lightcolor[1]*avgtint[1]*avgtint[3], info->lightcolor[2]*avgtint[2]*avgtint[3], info->lightradiusfade, info->lighttime, info->lightcubemapnum, -1, info->lightshadow, info->lightcorona[0], info->lightcorona[1], 0, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); + CL_AllocLightFlash(NULL, &tempmatrix, info->lightradiusstart, info->lightcolor[0]*avgtint[0]*avgtint[3], info->lightcolor[1]*avgtint[1]*avgtint[3], info->lightcolor[2]*avgtint[2]*avgtint[3], info->lightradiusfade, info->lighttime, LightCubemapNumToName(vabuf, sizeof(vabuf), info->lightcubemapnum, info->flags), -1, info->lightshadow, info->lightcorona[0], info->lightcorona[1], 0, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); } else if (r_refdef.scene.numlights < MAX_DLIGHTS) { @@ -1489,7 +1502,7 @@ static void CL_NewParticlesFromEffectinfo(int effectnameindex, float pcount, con rvec[0] = info->lightcolor[0]*avgtint[0]*avgtint[3]; rvec[1] = info->lightcolor[1]*avgtint[1]*avgtint[3]; rvec[2] = info->lightcolor[2]*avgtint[2]*avgtint[3]; - R_RTLight_Update(&r_refdef.scene.templights[r_refdef.scene.numlights], false, &tempmatrix, rvec, -1, info->lightcubemapnum > 0 ? va(vabuf, sizeof(vabuf), "cubemaps/%i", info->lightcubemapnum) : NULL, info->lightshadow, info->lightcorona[0], info->lightcorona[1], 0, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); + R_RTLight_Update(&r_refdef.scene.templights[r_refdef.scene.numlights], false, &tempmatrix, rvec, -1, LightCubemapNumToName(vabuf, sizeof(vabuf), info->lightcubemapnum, info->flags), info->lightshadow, info->lightcorona[0], info->lightcorona[1], 0, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); r_refdef.scene.lights[r_refdef.scene.numlights] = &r_refdef.scene.templights[r_refdef.scene.numlights];r_refdef.scene.numlights++; } } diff --git a/client.h b/client.h index 435299c4d..e7684bb43 100644 --- a/client.h +++ b/client.h @@ -1518,7 +1518,7 @@ extern cvar_t cl_locs_enable; extern client_state_t cl; -extern void CL_AllocLightFlash (entity_render_t *ent, matrix4x4_t *matrix, float radius, float red, float green, float blue, float decay, float lifetime, int cubemapnum, int style, int shadowenable, vec_t corona, vec_t coronasizescale, vec_t ambientscale, vec_t diffusescale, vec_t specularscale, int flags); +extern void CL_AllocLightFlash (entity_render_t *ent, matrix4x4_t *matrix, float radius, float red, float green, float blue, float decay, float lifetime, char *cubemapname, int style, int shadowenable, vec_t corona, vec_t coronasizescale, vec_t ambientscale, vec_t diffusescale, vec_t specularscale, int flags); cl_locnode_t *CL_Locs_FindNearest(const vec3_t point); void CL_Locs_FindLocationName(char *buffer, size_t buffersize, vec3_t point); diff --git a/clvm_cmds.c b/clvm_cmds.c index 56e4517fb..d4f7ffabc 100644 --- a/clvm_cmds.c +++ b/clvm_cmds.c @@ -1925,7 +1925,7 @@ static void VM_CL_te_explosionrgb (prvm_prog_t *prog) CL_FindNonSolidLocation(pos, pos2, 10); CL_ParticleExplosion(pos2); Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]); - CL_AllocLightFlash(NULL, &tempmatrix, 350, PRVM_G_VECTOR(OFS_PARM1)[0], PRVM_G_VECTOR(OFS_PARM1)[1], PRVM_G_VECTOR(OFS_PARM1)[2], 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); + CL_AllocLightFlash(NULL, &tempmatrix, 350, PRVM_G_VECTOR(OFS_PARM1)[0], PRVM_G_VECTOR(OFS_PARM1)[1], PRVM_G_VECTOR(OFS_PARM1)[2], 700, 0.5, NULL, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); } // #408 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color, float gravityflag, float randomveljitter) te_particlecube (DP_TE_PARTICLECUBE) @@ -2070,7 +2070,7 @@ static void VM_CL_te_customflash (prvm_prog_t *prog) VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos); CL_FindNonSolidLocation(pos, pos2, 4); Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]); - CL_AllocLightFlash(NULL, &tempmatrix, PRVM_G_FLOAT(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM3)[0], PRVM_G_VECTOR(OFS_PARM3)[1], PRVM_G_VECTOR(OFS_PARM3)[2], PRVM_G_FLOAT(OFS_PARM1) / PRVM_G_FLOAT(OFS_PARM2), PRVM_G_FLOAT(OFS_PARM2), 0, -1, true, 1, 0.25, 1, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); + CL_AllocLightFlash(NULL, &tempmatrix, PRVM_G_FLOAT(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM3)[0], PRVM_G_VECTOR(OFS_PARM3)[1], PRVM_G_VECTOR(OFS_PARM3)[2], PRVM_G_FLOAT(OFS_PARM1) / PRVM_G_FLOAT(OFS_PARM2), PRVM_G_FLOAT(OFS_PARM2), NULL, -1, true, 1, 0.25, 1, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); } // #418 void(vector org) te_gunshot (DP_TE_STANDARDEFFECTBUILTINS) @@ -2221,7 +2221,7 @@ static void VM_CL_te_explosion2 (prvm_prog_t *prog) color[1] = tempcolor[1] * (2.0f / 255.0f); color[2] = tempcolor[2] * (2.0f / 255.0f); Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]); - CL_AllocLightFlash(NULL, &tempmatrix, 350, color[0], color[1], color[2], 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); + CL_AllocLightFlash(NULL, &tempmatrix, 350, color[0], color[1], color[2], 700, 0.5, NULL, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); S_StartSound(-1, 0, cl.sfx_r_exp3, pos2, 1, 1); } diff --git a/gl_rmain.c b/gl_rmain.c index 6c6e2c1ab..22e28b753 100644 --- a/gl_rmain.c +++ b/gl_rmain.c @@ -2950,9 +2950,16 @@ static int componentorder[4] = {0, 1, 2, 3}; static rtexture_t *R_LoadCubemap(const char *basename) { - int i, j, cubemapsize; + int i, j, cubemapsize, forcefilter; unsigned char *cubemappixels, *image_buffer; rtexture_t *cubemaptexture; + // HACK: if the cubemap name starts with a !, the cubemap is nearest-filtered + forcefilter = TEXF_FORCELINEAR; + if (basename && basename[0] == '!') + { + basename++; + forcefilter = TEXF_FORCENEAREST; + } char name[256]; // must start 0 so the first loadimagepixels has no requested width/height cubemapsize = 0; @@ -2996,7 +3003,7 @@ static rtexture_t *R_LoadCubemap(const char *basename) if (developer_loading.integer) Con_Printf("loading cubemap \"%s\"\n", basename); - cubemaptexture = R_LoadTextureCubeMap(r_main_texturepool, basename, cubemapsize, cubemappixels, vid.sRGB3D ? TEXTYPE_SRGB_BGRA : TEXTYPE_BGRA, (gl_texturecompression_lightcubemaps.integer && gl_texturecompression.integer ? TEXF_COMPRESS : 0) | TEXF_FORCELINEAR | TEXF_CLAMP, -1, NULL); + cubemaptexture = R_LoadTextureCubeMap(r_main_texturepool, basename, cubemapsize, cubemappixels, vid.sRGB3D ? TEXTYPE_SRGB_BGRA : TEXTYPE_BGRA, (gl_texturecompression_lightcubemaps.integer && gl_texturecompression.integer ? TEXF_COMPRESS : 0) | forcefilter | TEXF_CLAMP, -1, NULL); Mem_Free(cubemappixels); } else From 5f55f8321ff61b9d7e17c8b55ff4e3ae74d37265 Mon Sep 17 00:00:00 2001 From: Mario Date: Sat, 30 May 2020 23:52:03 +1000 Subject: [PATCH 02/23] Add support for CACHEPICFLAG_LINEAR (currently unused), ported from wrath-darkplaces --- draw.h | 3 ++- gl_draw.c | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/draw.h b/draw.h index 5e023da8a..2ab338976 100644 --- a/draw.h +++ b/draw.h @@ -35,7 +35,8 @@ typedef enum cachepicflags_e CACHEPICFLAG_NEWPIC = 16, // disables matching texflags check, because a pic created with Draw_NewPic should not be subject to that CACHEPICFLAG_MIPMAP = 32, CACHEPICFLAG_NEAREST = 64, // force nearest filtering instead of linear - CACHEPICFLAG_FAILONMISSING = 128 // return NULL if the pic has no texture + CACHEPICFLAG_FAILONMISSING = 128, // return NULL if the pic has no texture + CACHEPICFLAG_LINEAR = 256 // force linear filtering even if nearest_2d is enabled } cachepicflags_t; diff --git a/gl_draw.c b/gl_draw.c index 18e38754d..38508d775 100644 --- a/gl_draw.c +++ b/gl_draw.c @@ -94,7 +94,9 @@ cachepic_t *Draw_CachePic_Flags(const char *path, unsigned int cachepicflags) texflags |= TEXF_MIPMAP; if (!(cachepicflags & CACHEPICFLAG_NOCOMPRESSION) && gl_texturecompression_2d.integer && gl_texturecompression.integer) texflags |= TEXF_COMPRESS; - if ((cachepicflags & CACHEPICFLAG_NEAREST) || r_nearest_2d.integer) + if (cachepicflags & CACHEPICFLAG_LINEAR) + texflags |= TEXF_FORCELINEAR; + else if ((cachepicflags & CACHEPICFLAG_NEAREST) || r_nearest_2d.integer) texflags |= TEXF_FORCENEAREST; // check whether the picture has already been cached From 0cb0479b136a275d6c31a7bc68d9634564503433 Mon Sep 17 00:00:00 2001 From: Mario Date: Sun, 31 May 2020 00:11:05 +1000 Subject: [PATCH 03/23] Don't crash the game if a "nasty" -game name is rejected, matches behavior of missing -game name --- fs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs.c b/fs.c index 590a336c6..72004d797 100644 --- a/fs.c +++ b/fs.c @@ -2159,7 +2159,7 @@ void FS_Init (void) i++; p = FS_CheckGameDir(com_argv[i]); if(!p) - Sys_Error("Nasty -game name rejected: %s", com_argv[i]); + Con_Printf("WARNING: Nasty -game name rejected: %s\n", com_argv[i]); if(p == fs_checkgamedir_missing) Con_Warnf("WARNING: -game %s%s/ not found!\n", fs_basedir, com_argv[i]); // add the gamedir to the list of active gamedirs From bcaf48e8aaeb685b12f192e043a7620c575e650e Mon Sep 17 00:00:00 2001 From: Mario Date: Sun, 31 May 2020 00:17:06 +1000 Subject: [PATCH 04/23] Save cl_maxfps_alwayssleep changes to config --- host.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/host.c b/host.c index 46453fd14..1ee2b3434 100644 --- a/host.c +++ b/host.c @@ -75,7 +75,7 @@ cvar_t cl_minfps_qualityhysteresis = {CVAR_CLIENT | CVAR_SAVE, "cl_minfps_qualit cvar_t cl_minfps_qualitystepmax = {CVAR_CLIENT | CVAR_SAVE, "cl_minfps_qualitystepmax", "0.1", "maximum quality change in a single frame"}; cvar_t cl_minfps_force = {CVAR_CLIENT, "cl_minfps_force", "0", "also apply quality reductions in timedemo/capturevideo"}; cvar_t cl_maxfps = {CVAR_CLIENT | CVAR_SAVE, "cl_maxfps", "0", "maximum fps cap, 0 = unlimited, if game is running faster than this it will wait before running another frame (useful to make cpu time available to other programs)"}; -cvar_t cl_maxfps_alwayssleep = {CVAR_CLIENT, "cl_maxfps_alwayssleep","1", "gives up some processing time to other applications each frame, value in milliseconds, disabled if cl_maxfps is 0"}; +cvar_t cl_maxfps_alwayssleep = {CVAR_CLIENT | CVAR_SAVE, "cl_maxfps_alwayssleep","1", "gives up some processing time to other applications each frame, value in milliseconds, disabled if cl_maxfps is 0"}; cvar_t cl_maxidlefps = {CVAR_CLIENT | CVAR_SAVE, "cl_maxidlefps", "20", "maximum fps cap when the game is not the active window (makes cpu time available to other programs"}; cvar_t developer = {CVAR_CLIENT | CVAR_SERVER | CVAR_SAVE, "developer","0", "shows debugging messages and information (recommended for all developers and level designers); the value -1 also suppresses buffering and logging these messages"}; From 6f684f2b930f14862e02901c854cbb39c2ee68e5 Mon Sep 17 00:00:00 2001 From: Mario Date: Sun, 31 May 2020 00:27:31 +1000 Subject: [PATCH 05/23] Increase MAX_CACHED_PICS to 2048, as per wrath-darkplaces --- quakedef.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/quakedef.h b/quakedef.h index d07a93a5a..75a018dbf 100644 --- a/quakedef.h +++ b/quakedef.h @@ -95,7 +95,7 @@ extern char engineversion[128]; #define MAX_CUBEMAPS 1024 #define MAX_EXPLOSIONS 8 #define MAX_DLIGHTS 16 -#define MAX_CACHED_PICS 1024 // this is 144 bytes each (or 152 on 64bit) +#define MAX_CACHED_PICS 2048 // this is 144 bytes each (or 152 on 64bit) #define CACHEPICHASHSIZE 256 #define MAX_PARTICLEEFFECTNAME 256 #define MAX_PARTICLEEFFECTINFO 1024 @@ -162,7 +162,7 @@ extern char engineversion[128]; #define MAX_CUBEMAPS 1024 ///< max number of cubemap textures loaded for light filters #define MAX_EXPLOSIONS 64 ///< max number of explosion shell effects active at once (not particle related) #define MAX_DLIGHTS 256 ///< max number of dynamic lights (rocket flashes, etc) in scene at once -#define MAX_CACHED_PICS 1024 ///< max number of 2D pics loaded at once +#define MAX_CACHED_PICS 2048 ///< max number of 2D pics loaded at once #define CACHEPICHASHSIZE 256 ///< number of hash buckets for accelerating 2D pic name lookups #define MAX_PARTICLEEFFECTNAME 4096 ///< maximum number of unique names of particle effects (for particleeffectnum) #define MAX_PARTICLEEFFECTINFO 8192 ///< maximum number of unique particle effects (each name may associate with several of these) From c5b1865a79f27308df9b0317adda8287f0cb13e0 Mon Sep 17 00:00:00 2001 From: Mario Date: Sun, 31 May 2020 00:34:40 +1000 Subject: [PATCH 06/23] Implement pausesound command, ported from wrath-darkplaces --- snd_main.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/snd_main.c b/snd_main.c index a677b174e..c7bd33aed 100644 --- a/snd_main.c +++ b/snd_main.c @@ -359,6 +359,14 @@ static void S_SoundInfo_f(cmd_state_t *cmd) Con_Printf("%5u total_channels\n", total_channels); } +void S_PauseSound_f(cmd_state_t *cmd) +{ + if( Cmd_Argc(cmd) != 2 ) { + Con_Print("pausesound \n"); + return; + } + S_PauseGameSounds(atoi( Cmd_Argv(cmd, 1 ) ) != 0); +} int S_GetSoundRate(void) { @@ -773,6 +781,7 @@ void S_Init(void) Cmd_AddCommand(&cmd_client, "play2", S_Play2_f, "play a sound globally throughout the level (not heard by anyone else)"); Cmd_AddCommand(&cmd_client, "playvol", S_PlayVol_f, "play a sound at the specified volume level at your current location (not heard by anyone else)"); Cmd_AddCommand(&cmd_client, "stopsound", S_StopAllSounds_f, "silence"); + Cmd_AddCommand(&cmd_client, "pausesound", S_PauseSound_f, "temporary silence"); Cmd_AddCommand(&cmd_client, "soundlist", S_SoundList_f, "list loaded sounds"); Cmd_AddCommand(&cmd_client, "soundinfo", S_SoundInfo_f, "print sound system information (such as channels and speed)"); Cmd_AddCommand(&cmd_client, "snd_restart", S_Restart_f, "restart sound system"); From 9204771c3a759dcdf194679af29b70e458ba7ed4 Mon Sep 17 00:00:00 2001 From: Mario Date: Sun, 31 May 2020 00:37:23 +1000 Subject: [PATCH 07/23] Fix a potential memory leak with wavefront sounds --- snd_wav.c | 1 + 1 file changed, 1 insertion(+) diff --git a/snd_wav.c b/snd_wav.c index 6f8619133..b4d014b34 100644 --- a/snd_wav.c +++ b/snd_wav.c @@ -344,5 +344,6 @@ qboolean S_LoadWavFile (const char *filename, sfx_t *sfx) sfx->loopstart = min(sfx->loopstart, sfx->total_length); sfx->flags &= ~SFXFLAG_STREAMED; + Mem_Free(data); // we already got a copy of this in fetcher_data return true; } From 775cf78cdb3076abab68092c73f1ca8c92ad085a Mon Sep 17 00:00:00 2001 From: Mario Date: Sun, 31 May 2020 00:58:42 +1000 Subject: [PATCH 08/23] Don't show beam list overflow spam warnings normally --- cl_parse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cl_parse.c b/cl_parse.c index 13d1479a7..98e5edbf0 100644 --- a/cl_parse.c +++ b/cl_parse.c @@ -2385,7 +2385,7 @@ void CL_NewBeam (int ent, vec3_t start, vec3_t end, dp_model_t *m, int lightning VectorCopy (end, b->end); } else - Con_Print("beam list overflow!\n"); + Con_DPrint("beam list overflow!\n"); } static void CL_ParseBeam (dp_model_t *m, int lightning) From 85d4aaa026e61e1239dc25f4c4253e86c7391bae Mon Sep 17 00:00:00 2001 From: Mario Date: Sun, 31 May 2020 02:18:40 +1000 Subject: [PATCH 09/23] Some minor tweaks to CACHEPICFLAG_LINEAR and the nasty -game name rejection message to ease the processes of backporting and modernization --- draw.h | 4 ++-- fs.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/draw.h b/draw.h index 2ab338976..abc520da4 100644 --- a/draw.h +++ b/draw.h @@ -35,8 +35,8 @@ typedef enum cachepicflags_e CACHEPICFLAG_NEWPIC = 16, // disables matching texflags check, because a pic created with Draw_NewPic should not be subject to that CACHEPICFLAG_MIPMAP = 32, CACHEPICFLAG_NEAREST = 64, // force nearest filtering instead of linear - CACHEPICFLAG_FAILONMISSING = 128, // return NULL if the pic has no texture - CACHEPICFLAG_LINEAR = 256 // force linear filtering even if nearest_2d is enabled + CACHEPICFLAG_LINEAR = 128, // force linear filtering even if nearest_2d is enabled + CACHEPICFLAG_FAILONMISSING = 256 // return NULL if the pic has no texture } cachepicflags_t; diff --git a/fs.c b/fs.c index 72004d797..1608c3990 100644 --- a/fs.c +++ b/fs.c @@ -2159,7 +2159,7 @@ void FS_Init (void) i++; p = FS_CheckGameDir(com_argv[i]); if(!p) - Con_Printf("WARNING: Nasty -game name rejected: %s\n", com_argv[i]); + Con_Warnf("WARNING: Nasty -game name rejected: %s\n", com_argv[i]); if(p == fs_checkgamedir_missing) Con_Warnf("WARNING: -game %s%s/ not found!\n", fs_basedir, com_argv[i]); // add the gamedir to the list of active gamedirs From aeaaffcf07f0394ff77b4164383a1e5895cfebd3 Mon Sep 17 00:00:00 2001 From: Mario Date: Mon, 1 Jun 2020 00:50:15 +1000 Subject: [PATCH 10/23] Implement wrath's workaround for #152: only apply nogravityonground while not moving --- cl_input.c | 8 ++++++-- sv_phys.c | 7 +++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/cl_input.c b/cl_input.c index ed7c44c3c..a3ed56da9 100644 --- a/cl_input.c +++ b/cl_input.c @@ -1312,6 +1312,7 @@ static void CL_ClientMovement_Physics_Walk(cl_clientmovement_state_t *s) vec3_t wishdir; vec3_t yawangles; trace_t trace; + qboolean moving; // jump if on ground with jump button pressed but only if it has been // released at least once since the last jump @@ -1378,6 +1379,7 @@ static void CL_ClientMovement_Physics_Walk(cl_clientmovement_state_t *s) VectorMA(s->velocity, accelspeed, wishdir, s->velocity); } gravity = cl.movevars_gravity * cl.movevars_entgravity * s->cmd.frametime; + moving = s->velocity[0] || s->velocity[1]; if(!(cl.moveflags & MOVEFLAG_NOGRAVITYONGROUND)) { if(cl.moveflags & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE) @@ -1387,9 +1389,10 @@ static void CL_ClientMovement_Physics_Walk(cl_clientmovement_state_t *s) } if (cls.protocol == PROTOCOL_QUAKEWORLD) s->velocity[2] = 0; + moving = s->velocity[0] || s->velocity[1]; if (VectorLength2(s->velocity)) CL_ClientMovement_Move(s); - if(!(cl.moveflags & MOVEFLAG_NOGRAVITYONGROUND) || !s->onground) + if(!(cl.moveflags & MOVEFLAG_NOGRAVITYONGROUND) || !s->onground || (s->onground && moving)) { if(cl.moveflags & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE) s->velocity[2] -= gravity * 0.5f; @@ -1448,8 +1451,9 @@ static void CL_ClientMovement_Physics_Walk(cl_clientmovement_state_t *s) s->velocity[2] -= gravity * 0.5f; else s->velocity[2] -= gravity; + moving = s->velocity[0] || s->velocity[1]; CL_ClientMovement_Move(s); - if(!(cl.moveflags & MOVEFLAG_NOGRAVITYONGROUND) || !s->onground) + if(!(cl.moveflags & MOVEFLAG_NOGRAVITYONGROUND) || !s->onground || (s->onground && moving)) { if(cl.moveflags & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE) s->velocity[2] -= gravity * 0.5f; diff --git a/sv_phys.c b/sv_phys.c index 9964c362f..3c58d62d9 100644 --- a/sv_phys.c +++ b/sv_phys.c @@ -1207,6 +1207,7 @@ static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, flo vec3_t end; #endif trace_t trace; + qboolean moving; if (time <= 0) return 0; gravity = 0; @@ -1216,7 +1217,7 @@ static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, flo if(applygravity) { gravity = SV_Gravity(ent); - + moving = PRVM_serveredictvector(ent, velocity)[0] || PRVM_serveredictvector(ent, velocity)[1]; if(!sv_gameplayfix_nogravityonground.integer || !((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)) { if (sv_gameplayfix_gravityunaffectedbyticrate.integer) @@ -1420,9 +1421,11 @@ static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, flo if (sv_gameplayfix_easierwaterjump.integer && ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP) && !(blocked & 8)) VectorCopy(primal_velocity, PRVM_serveredictvector(ent, velocity)); + // Mario: this workaround introduces a new bug: sliding down ramps occurs when moving sideways + moving = PRVM_serveredictvector(ent, velocity)[0] || PRVM_serveredictvector(ent, velocity)[1]; if(applygravity) { - if(!sv_gameplayfix_nogravityonground.integer || !((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)) + if(!sv_gameplayfix_nogravityonground.integer || !((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND) || (((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND) && moving)) { if (sv_gameplayfix_gravityunaffectedbyticrate.integer) PRVM_serveredictvector(ent, velocity)[2] -= gravity * 0.5f; From d3deefd7b62634d4a714ee7d6abd25b9c049d01c Mon Sep 17 00:00:00 2001 From: Mario Date: Mon, 1 Jun 2020 00:55:35 +1000 Subject: [PATCH 11/23] Implement wrath's workaround for #136: don't clear the onground flag from view smoothing and bobbing for a short time --- client.h | 1 + view.c | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/client.h b/client.h index e7684bb43..686ec947e 100644 --- a/client.h +++ b/client.h @@ -1168,6 +1168,7 @@ typedef struct client_state_s qboolean oldonground; double lastongroundtime; double hitgroundtime; + double bobongroundtime; float bob2_smooth; float bobfall_speed; float bobfall_swing; diff --git a/view.c b/view.c index 7bea5a969..dd2afe13b 100644 --- a/view.c +++ b/view.c @@ -507,6 +507,7 @@ void V_CalcRefdefUsing (const matrix4x4_t *entrendermatrix, const vec3_t clviewa float vieworg[3], viewangles[3], smoothtime; float gunorg[3], gunangles[3]; matrix4x4_t tmpmatrix; + qboolean realonground; static float viewheightavg; float viewheight; @@ -518,6 +519,15 @@ void V_CalcRefdefUsing (const matrix4x4_t *entrendermatrix, const vec3_t clviewa #endif trace_t trace; + // save the "true" onground time + if (clonground) + cl.bobongroundtime = cl.movecmd[0].time; + realonground = clonground; + + // if nogravityonground is enabled, use a "delayed" onground flag + if ((cl.moveflags & MOVEFLAG_NOGRAVITYONGROUND) && !clonground && (cl.time - cl.bobongroundtime < 0.2)) + clonground = true; + // react to clonground state changes (for gun bob) if (clonground) { @@ -572,7 +582,7 @@ void V_CalcRefdefUsing (const matrix4x4_t *entrendermatrix, const vec3_t clviewa else { // smooth stair stepping, but only if clonground and enabled - if (!clonground || cl_stairsmoothspeed.value <= 0 || teleported) + if (!realonground || cl_stairsmoothspeed.value <= 0 || teleported) cl.stairsmoothz = vieworg[2]; else { From abe995d61281bbc18f57babc6cd02948a9e73295 Mon Sep 17 00:00:00 2001 From: Mario Date: Mon, 1 Jun 2020 01:24:57 +1000 Subject: [PATCH 12/23] Remove the linear flag from some particle textures and adjust an off by 1 check (may introduce division by zero warnings), as per wrath-darkplaces --- cl_particles.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/cl_particles.c b/cl_particles.c index e5d02e450..ba2c2ab6f 100644 --- a/cl_particles.c +++ b/cl_particles.c @@ -2141,7 +2141,7 @@ static void R_InitParticleTexture (void) // we invert it again during the blendfunc to make it work... #ifndef DUMPPARTICLEFONT - decalskinframe = R_SkinFrame_LoadExternal("particles/particlefont.tga", TEXF_ALPHA | TEXF_FORCELINEAR | TEXF_RGBMULTIPLYBYALPHA, false, false); + decalskinframe = R_SkinFrame_LoadExternal("particles/particlefont.tga", TEXF_ALPHA | TEXF_RGBMULTIPLYBYALPHA, false, false); if (decalskinframe) { particlefonttexture = decalskinframe->base; @@ -2286,7 +2286,7 @@ static void R_InitParticleTexture (void) Image_WriteTGABGRA ("particles/particlefont.tga", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata); #endif - decalskinframe = R_SkinFrame_LoadInternalBGRA("particlefont", TEXF_ALPHA | TEXF_FORCELINEAR | TEXF_RGBMULTIPLYBYALPHA, particletexturedata, PARTICLEFONTSIZE, PARTICLEFONTSIZE, 0, 0, 0, false); + decalskinframe = R_SkinFrame_LoadInternalBGRA("particlefont", TEXF_ALPHA | TEXF_RGBMULTIPLYBYALPHA, particletexturedata, PARTICLEFONTSIZE, PARTICLEFONTSIZE, 0, 0, 0, false); particlefonttexture = decalskinframe->base; Mem_Free(particletexturedata); @@ -2298,14 +2298,14 @@ static void R_InitParticleTexture (void) { CL_Particle_PixelCoordsForTexnum(i, &basex, &basey, &w, &h); particletexture[i].texture = particlefonttexture; - particletexture[i].s1 = (basex + 1) / (float)particlefontwidth; - particletexture[i].t1 = (basey + 1) / (float)particlefontheight; - particletexture[i].s2 = (basex + w - 1) / (float)particlefontwidth; - particletexture[i].t2 = (basey + h - 1) / (float)particlefontheight; + particletexture[i].s1 = (basex + 0) / (float)particlefontwidth; + particletexture[i].t1 = (basey + 0) / (float)particlefontheight; + particletexture[i].s2 = (basex + w - 0) / (float)particlefontwidth; + particletexture[i].t2 = (basey + h - 0) / (float)particlefontheight; } #ifndef DUMPPARTICLEFONT - particletexture[tex_beam].texture = loadtextureimage(particletexturepool, "particles/nexbeam.tga", false, TEXF_ALPHA | TEXF_FORCELINEAR | TEXF_RGBMULTIPLYBYALPHA, true, vid.sRGB3D); + particletexture[tex_beam].texture = loadtextureimage(particletexturepool, "particles/nexbeam.tga", false, TEXF_ALPHA | TEXF_RGBMULTIPLYBYALPHA, true, vid.sRGB3D); if (!particletexture[tex_beam].texture) #endif { @@ -2328,7 +2328,7 @@ static void R_InitParticleTexture (void) #ifdef DUMPPARTICLEFONT Image_WriteTGABGRA ("particles/nexbeam.tga", 64, 64, &data2[0][0][0]); #endif - particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "nexbeam", 16, 64, &data2[0][0][0], TEXTYPE_BGRA, TEXF_ALPHA | TEXF_FORCELINEAR | TEXF_RGBMULTIPLYBYALPHA, -1, NULL); + particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "nexbeam", 16, 64, &data2[0][0][0], TEXTYPE_BGRA, TEXF_ALPHA | TEXF_RGBMULTIPLYBYALPHA, -1, NULL); } particletexture[tex_beam].s1 = 0; particletexture[tex_beam].t1 = 0; @@ -2388,7 +2388,7 @@ static void R_InitParticleTexture (void) Con_Printf("particles/particlefont.txt: texnum %i outside valid range (0 to %i)\n", i, MAX_PARTICLETEXTURES); continue; } - sf = R_SkinFrame_LoadExternal(texturename, TEXF_ALPHA | TEXF_FORCELINEAR | TEXF_RGBMULTIPLYBYALPHA, true, true); // note: this loads as sRGB if sRGB is active! + sf = R_SkinFrame_LoadExternal(texturename, TEXF_ALPHA | TEXF_RGBMULTIPLYBYALPHA, true, true); // note: this loads as sRGB if sRGB is active! particletexture[i].texture = sf->base; particletexture[i].s1 = s1; particletexture[i].t1 = t1; From 8ed9fddc68e1e60d2edf5230e815f0e3aa02569b Mon Sep 17 00:00:00 2001 From: Mario Date: Wed, 3 Jun 2020 00:08:20 +1000 Subject: [PATCH 13/23] Don't save scr_loadingscreen_picture to config (matches other loading screen cvars), as per wrath-darkplaces --- cl_screen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cl_screen.c b/cl_screen.c index f6ca6e1c0..e8df2b343 100644 --- a/cl_screen.c +++ b/cl_screen.c @@ -42,7 +42,7 @@ cvar_t scr_loadingscreen_background = {CVAR_CLIENT, "scr_loadingscreen_backgroun cvar_t scr_loadingscreen_scale = {CVAR_CLIENT, "scr_loadingscreen_scale","1", "scale factor of the background"}; cvar_t scr_loadingscreen_scale_base = {CVAR_CLIENT, "scr_loadingscreen_scale_base","0", "0 = console pixels, 1 = video pixels"}; cvar_t scr_loadingscreen_scale_limit = {CVAR_CLIENT, "scr_loadingscreen_scale_limit","0", "0 = no limit, 1 = until first edge hits screen edge, 2 = until last edge hits screen edge, 3 = until width hits screen width, 4 = until height hits screen height"}; -cvar_t scr_loadingscreen_picture = {CVAR_CLIENT | CVAR_SAVE, "scr_loadingscreen_picture", "gfx/loading", "picture shown during loading"}; +cvar_t scr_loadingscreen_picture = {CVAR_CLIENT, "scr_loadingscreen_picture", "gfx/loading", "picture shown during loading"}; cvar_t scr_loadingscreen_count = {CVAR_CLIENT, "scr_loadingscreen_count","1", "number of loading screen files to use randomly (named loading.tga, loading2.tga, loading3.tga, ...)"}; cvar_t scr_loadingscreen_firstforstartup = {CVAR_CLIENT, "scr_loadingscreen_firstforstartup","0", "remove loading.tga from random scr_loadingscreen_count selection and only display it on client startup, 0 = normal, 1 = firstforstartup"}; cvar_t scr_loadingscreen_barcolor = {CVAR_CLIENT, "scr_loadingscreen_barcolor", "0 0 1", "rgb color of loadingscreen progress bar"}; From bb4f48e07356072fcf810f82cb6d3dd3bb9766f6 Mon Sep 17 00:00:00 2001 From: Mario Date: Wed, 3 Jun 2020 01:24:16 +1000 Subject: [PATCH 14/23] Implement texture lookup table GLSL shader, ported from wrath-darkplaces --- gl_rmain.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++ shader_glsl.h | 1 + 2 files changed, 61 insertions(+) diff --git a/gl_rmain.c b/gl_rmain.c index 22e28b753..8f3445057 100644 --- a/gl_rmain.c +++ b/gl_rmain.c @@ -182,6 +182,7 @@ cvar_t r_glsl_offsetmapping_scale = {CVAR_CLIENT | CVAR_SAVE, "r_glsl_offsetmapp cvar_t r_glsl_offsetmapping_lod = {CVAR_CLIENT | CVAR_SAVE, "r_glsl_offsetmapping_lod", "0", "apply distance-based level-of-detail correction to number of offsetmappig steps, effectively making it render faster on large open-area maps"}; cvar_t r_glsl_offsetmapping_lod_distance = {CVAR_CLIENT | CVAR_SAVE, "r_glsl_offsetmapping_lod_distance", "32", "first LOD level distance, second level (-50% steps) is 2x of this, third (33%) - 3x etc."}; cvar_t r_glsl_postprocess = {CVAR_CLIENT | CVAR_SAVE, "r_glsl_postprocess", "0", "use a GLSL postprocessing shader"}; +cvar_t r_glsl_postprocess_color_lut = {CVAR_CLIENT | CVAR_SAVE, "r_glsl_postprocess_color_lut", "", "color lookup table for post, empty string for default"}; cvar_t r_glsl_postprocess_uservec1 = {CVAR_CLIENT | CVAR_SAVE, "r_glsl_postprocess_uservec1", "0 0 0 0", "a 4-component vector to pass as uservec1 to the postprocessing shader (only useful if default.glsl has been customized)"}; cvar_t r_glsl_postprocess_uservec2 = {CVAR_CLIENT | CVAR_SAVE, "r_glsl_postprocess_uservec2", "0 0 0 0", "a 4-component vector to pass as uservec2 to the postprocessing shader (only useful if default.glsl has been customized)"}; cvar_t r_glsl_postprocess_uservec3 = {CVAR_CLIENT | CVAR_SAVE, "r_glsl_postprocess_uservec3", "0 0 0 0", "a 4-component vector to pass as uservec3 to the postprocessing shader (only useful if default.glsl has been customized)"}; @@ -276,6 +277,9 @@ rtexture_t *r_texture_normalizationcube; rtexture_t *r_texture_fogattenuation; rtexture_t *r_texture_fogheighttexture; rtexture_t *r_texture_gammaramps; +rtexture_t *r_texture_lut; +rtexture_t *r_texture_lut_default; +cachepic_t *r_texture_lut_pic; unsigned int r_texture_gammaramps_serial; //rtexture_t *r_texture_fogintensity; rtexture_t *r_texture_reflectcube; @@ -611,6 +615,32 @@ static void R_BuildFogHeightTexture(void) r_texture_fogheighttexture = R_LoadTexture2D(r_main_texturepool, "fogheighttable", size, size, r_refdef.fog_height_table2d, TEXTYPE_BGRA, TEXF_ALPHA | TEXF_CLAMP, -1, NULL); } +#define LUT_SLICE_SIZE 64 +#define LUT_FACTOR (256 / LUT_SLICE_SIZE) +#define LUT_SLICES_X 8 +#define LUT_SLICES_Y 8 +#define LUT_WIDTH (LUT_SLICE_SIZE * LUT_SLICES_X) +#define LUT_HEIGHT (LUT_SLICE_SIZE * LUT_SLICES_Y) + +static void R_BuildDefaultLUT(void) +{ + unsigned char *p, *data; + int x, y; + data = (unsigned char *)Mem_Alloc(r_main_mempool, LUT_WIDTH * LUT_HEIGHT * 4); + p = data; + for (y = 0; y < LUT_HEIGHT; ++y) + for (x = 0; x < LUT_WIDTH; ++x) + { + p[3] = 255; + p[2] = (x % LUT_SLICE_SIZE) * LUT_FACTOR; + p[1] = (y % LUT_SLICE_SIZE) * LUT_FACTOR; + p[0] = ((y / LUT_SLICE_SIZE) * LUT_SLICES_X + x / LUT_SLICE_SIZE) * LUT_FACTOR; + p += 4; + } + r_texture_lut_default = R_LoadTexture2D(r_main_texturepool, "lutdefault", LUT_WIDTH, LUT_HEIGHT, data, TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_PERSISTENT, -1, NULL); + r_texture_lut = r_texture_lut_default; +} + //======================================================================================================================================================= static const char *builtinshaderstrings[] = @@ -746,6 +776,7 @@ typedef struct r_glsl_permutation_s int tex_Texture_ReflectMask; int tex_Texture_ReflectCube; int tex_Texture_BounceGrid; + int tex_Texture_LUT; /// locations of detected uniforms in program object, or -1 if not found int loc_Texture_First; int loc_Texture_Second; @@ -777,6 +808,7 @@ typedef struct r_glsl_permutation_s int loc_Texture_ReflectMask; int loc_Texture_ReflectCube; int loc_Texture_BounceGrid; + int loc_Texture_LUT; int loc_Alpha; int loc_BloomBlur_Parameters; int loc_ClientTime; @@ -1212,6 +1244,7 @@ static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode p->loc_Texture_ReflectMask = qglGetUniformLocation(p->program, "Texture_ReflectMask"); p->loc_Texture_ReflectCube = qglGetUniformLocation(p->program, "Texture_ReflectCube"); p->loc_Texture_BounceGrid = qglGetUniformLocation(p->program, "Texture_BounceGrid"); + p->loc_Texture_LUT = qglGetUniformLocation(p->program, "Texture_LUT"); p->loc_Alpha = qglGetUniformLocation(p->program, "Alpha"); p->loc_BloomBlur_Parameters = qglGetUniformLocation(p->program, "BloomBlur_Parameters"); p->loc_ClientTime = qglGetUniformLocation(p->program, "ClientTime"); @@ -1303,6 +1336,7 @@ static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode p->tex_Texture_ReflectMask = -1; p->tex_Texture_ReflectCube = -1; p->tex_Texture_BounceGrid = -1; + p->tex_Texture_LUT = -1; // bind the texture samplers in use sampler = 0; if (p->loc_Texture_First >= 0) {p->tex_Texture_First = sampler;qglUniform1i(p->loc_Texture_First , sampler);sampler++;} @@ -1335,6 +1369,7 @@ static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode if (p->loc_Texture_ReflectMask >= 0) {p->tex_Texture_ReflectMask = sampler;qglUniform1i(p->loc_Texture_ReflectMask , sampler);sampler++;} if (p->loc_Texture_ReflectCube >= 0) {p->tex_Texture_ReflectCube = sampler;qglUniform1i(p->loc_Texture_ReflectCube , sampler);sampler++;} if (p->loc_Texture_BounceGrid >= 0) {p->tex_Texture_BounceGrid = sampler;qglUniform1i(p->loc_Texture_BounceGrid , sampler);sampler++;} + if (p->loc_Texture_LUT >= 0) {p->tex_Texture_LUT = sampler;qglUniform1i(p->loc_Texture_LUT , sampler);sampler++;} // get the uniform block indices so we can bind them p->ubiloc_Skeletal_Transform12_UniformBlock = -1; #ifndef USE_GLES2 /* FIXME: GLES3 only */ @@ -1496,6 +1531,8 @@ void R_SetupShader_Generic(rtexture_t *t, qboolean usegamma, qboolean notrippy, R_Mesh_TexBind(r_glsl_permutation->tex_Texture_First, t); if (r_glsl_permutation->tex_Texture_GammaRamps >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_GammaRamps, r_texture_gammaramps); + if (r_glsl_permutation->tex_Texture_LUT >= 0) + R_Mesh_TexBind(r_glsl_permutation->tex_Texture_LUT, r_texture_lut); break; } } @@ -2057,6 +2094,7 @@ void R_SetupShader_Surface(const float rtlightambient[3], const float rtlightdif if (r_glsl_permutation->tex_Texture_First >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_First , r_texture_white ); if (r_glsl_permutation->tex_Texture_Second >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Second , r_texture_white ); if (r_glsl_permutation->tex_Texture_GammaRamps >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_GammaRamps , r_texture_gammaramps ); + if (r_glsl_permutation->tex_Texture_LUT >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_LUT , r_texture_lut ); if (r_glsl_permutation->tex_Texture_Normal >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Normal , t->nmaptexture ); if (r_glsl_permutation->tex_Texture_Color >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Color , t->basetexture ); if (r_glsl_permutation->tex_Texture_Gloss >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Gloss , t->glosstexture ); @@ -3100,6 +3138,8 @@ static void gl_main_start(void) r_texture_normalizationcube = NULL; r_texture_fogattenuation = NULL; r_texture_fogheighttexture = NULL; + r_texture_lut = NULL; + r_texture_lut_default = NULL; r_texture_gammaramps = NULL; r_texture_numcubemaps = 0; r_uniformbufferalignment = 32; @@ -3147,6 +3187,7 @@ static void gl_main_start(void) R_BuildNoTexture(); R_BuildWhiteCube(); R_BuildNormalizationCube(); + R_BuildDefaultLUT(); r_texture_fogattenuation = NULL; r_texture_fogheighttexture = NULL; r_texture_gammaramps = NULL; @@ -3232,6 +3273,8 @@ static void gl_main_shutdown(void) r_texture_normalizationcube = NULL; r_texture_fogattenuation = NULL; r_texture_fogheighttexture = NULL; + r_texture_lut = NULL; + r_texture_lut_default = NULL; r_texture_gammaramps = NULL; r_texture_numcubemaps = 0; //r_texture_fogintensity = NULL; @@ -3396,6 +3439,7 @@ void GL_Main_Init(void) Cvar_RegisterVariable(&r_glsl_offsetmapping_lod); Cvar_RegisterVariable(&r_glsl_offsetmapping_lod_distance); Cvar_RegisterVariable(&r_glsl_postprocess); + Cvar_RegisterVariable(&r_glsl_postprocess_color_lut); Cvar_RegisterVariable(&r_glsl_postprocess_uservec1); Cvar_RegisterVariable(&r_glsl_postprocess_uservec2); Cvar_RegisterVariable(&r_glsl_postprocess_uservec3); @@ -5457,6 +5501,7 @@ static void R_BlendView(int viewfbo, rtexture_t *viewdepthtexture, rtexture_t *v if (r_glsl_permutation->tex_Texture_First >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_First , viewtexture); if (r_glsl_permutation->tex_Texture_Second >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Second , bloomtexture); if (r_glsl_permutation->tex_Texture_GammaRamps >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_GammaRamps, r_texture_gammaramps ); + if (r_glsl_permutation->tex_Texture_LUT >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_LUT, r_texture_lut ); if (r_glsl_permutation->loc_ViewTintColor >= 0) qglUniform4f(r_glsl_permutation->loc_ViewTintColor , r_refdef.viewblend[0], r_refdef.viewblend[1], r_refdef.viewblend[2], r_refdef.viewblend[3]); if (r_glsl_permutation->loc_PixelSize >= 0) qglUniform2f(r_glsl_permutation->loc_PixelSize , 1.0/r_fb.screentexturewidth, 1.0/r_fb.screentextureheight); if (r_glsl_permutation->loc_UserVec1 >= 0) qglUniform4f(r_glsl_permutation->loc_UserVec1 , uservecs[0][0], uservecs[0][1], uservecs[0][2], uservecs[0][3]); @@ -5602,6 +5647,21 @@ void R_UpdateVariables(void) case RENDERPATH_GL32: r_gpuskeletal = r_glsl_skeletal.integer && !r_showsurfaces.integer; case RENDERPATH_GLES2: + if (r_glsl_postprocess.integer) + { + if (r_glsl_postprocess_color_lut.string[0]) + { + r_texture_lut_pic = Draw_CachePic_Flags(r_glsl_postprocess_color_lut.string, CACHEPICFLAG_NOTPERSISTENT | CACHEPICFLAG_LINEAR); + if (r_texture_lut_pic) + r_texture_lut = Draw_GetPicTexture(r_texture_lut_pic); + else + r_texture_lut = r_texture_lut_default; + } + else + { + r_texture_lut = r_texture_lut_default; + } + } if(!vid_gammatables_trivial) { if(!r_texture_gammaramps || vid_gammatables_serial != r_texture_gammaramps_serial) diff --git a/shader_glsl.h b/shader_glsl.h index 11ec83dcd..fff9af5fe 100644 --- a/shader_glsl.h +++ b/shader_glsl.h @@ -249,6 +249,7 @@ "// uniform mediump vec4 UserVec4;\n", "uniform mediump float ColorFringe;\n", "// uniform highp float ClientTime;\n", +"// uniform sampler2D Texture_LUT;\n", "uniform mediump vec2 PixelSize;\n", "\n", "#ifdef USEFXAA\n", From d76626e8562b378d23f92341c37750a66279b1fc Mon Sep 17 00:00:00 2001 From: Mario Date: Wed, 3 Jun 2020 01:26:45 +1000 Subject: [PATCH 15/23] Remove forced linear flag from motion blur texture, as per wrath-darkplaces --- gl_rmain.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gl_rmain.c b/gl_rmain.c index 8f3445057..79d7240b0 100644 --- a/gl_rmain.c +++ b/gl_rmain.c @@ -5258,7 +5258,7 @@ static void R_Bloom_StartFrame(void) if (r_fb.screentexturewidth && r_fb.screentextureheight) { if (r_motionblur.value > 0 || r_damageblur.value > 0) - r_fb.ghosttexture = R_LoadTexture2D(r_main_texturepool, "framebuffermotionblur", r_fb.screentexturewidth, r_fb.screentextureheight, NULL, r_fb.textype, TEXF_RENDERTARGET | TEXF_FORCELINEAR | TEXF_CLAMP, -1, NULL); + r_fb.ghosttexture = R_LoadTexture2D(r_main_texturepool, "framebuffermotionblur", r_fb.screentexturewidth, r_fb.screentextureheight, NULL, r_fb.textype, TEXF_RENDERTARGET | TEXF_CLAMP, -1, NULL); r_fb.ghosttexture_valid = false; } } From 4ff05ac24b2d355834393106423823e85a3fd244 Mon Sep 17 00:00:00 2001 From: Mario Date: Wed, 3 Jun 2020 01:44:57 +1000 Subject: [PATCH 16/23] Increase maximum beams from 256 to 4096, as per wrath-darkplaces --- quakedef.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quakedef.h b/quakedef.h index 75a018dbf..916235502 100644 --- a/quakedef.h +++ b/quakedef.h @@ -187,7 +187,7 @@ extern char engineversion[128]; #define MAX_ENITIES_INITIAL 256 ///< initial size of cl.entities #define MAX_STATICENTITIES 1024 ///< limit on size of cl.static_entities #define MAX_EFFECTS 256 ///< limit on size of cl.effects -#define MAX_BEAMS 256 ///< limit on size of cl.beams +#define MAX_BEAMS 4096 ///< limit on size of cl.beams #define MAX_TEMPENTITIES 4096 ///< max number of temporary models visible per frame (certain sprite effects, certain types of CSQC entities also use this) #define SERVERLIST_TOTALSIZE 2048 ///< max servers in the server list #define SERVERLIST_ANDMASKCOUNT 16 ///< max items in server list AND mask From ac6cc772c0e06bdd434fab030c96c700f44f6070 Mon Sep 17 00:00:00 2001 From: Mario Date: Wed, 10 Jun 2020 03:48:05 +1000 Subject: [PATCH 17/23] Add GLSL implementation of texture lookup tables (LUT), ported from Wrath --- shader_glsl.h | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/shader_glsl.h b/shader_glsl.h index fff9af5fe..ed74055a1 100644 --- a/shader_glsl.h +++ b/shader_glsl.h @@ -249,7 +249,7 @@ "// uniform mediump vec4 UserVec4;\n", "uniform mediump float ColorFringe;\n", "// uniform highp float ClientTime;\n", -"// uniform sampler2D Texture_LUT;\n", +"uniform sampler2D Texture_LUT;\n", "uniform mediump vec2 PixelSize;\n", "\n", "#ifdef USEFXAA\n", @@ -299,6 +299,35 @@ "}\n", "#endif\n", "\n", +"#ifdef USEPOSTPROCESSING\n", +"// https://github.com/mattdesl/glsl-lut\n", +"vec4 sampleLUT(sampler2D lookupTable, vec3 textureColor) {\n", +" mediump float blueColor = textureColor.b * 63.0;\n", +"\n", +" mediump vec2 quad1;\n", +" quad1.y = floor(floor(blueColor) / 8.0);\n", +" quad1.x = floor(blueColor) - (quad1.y * 8.0);\n", +"\n", +" mediump vec2 quad2;\n", +" quad2.y = floor(ceil(blueColor) / 8.0);\n", +" quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n", +"\n", +" highp vec2 texPos1;\n", +" texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n", +" texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g);\n", +"\n", +" highp vec2 texPos2;\n", +" texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n", +" texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g);\n", +"\n", +" lowp vec4 newColor1 = texture2D(lookupTable, texPos1);\n", +" lowp vec4 newColor2 = texture2D(lookupTable, texPos2);\n", +"\n", +" lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n", +" return newColor;\n", +"}\n", +"#endif\n", +"\n", "void main(void)\n", "{\n", " float fringe = ColorFringe;//.0033f;\n", @@ -312,6 +341,13 @@ "#endif\n", "\n", "#ifdef USEPOSTPROCESSING\n", +" // color grading using LUTs\n", +" vec4 oldColor = dp_texture2D(Texture_First, TexCoord1);\n", +" dp_FragColor.rgb = sampleLUT(Texture_LUT, oldColor.rgb).rgb;\n", +" dp_FragColor.a = oldColor.a;\n", +"#endif\n", +"\n", +"#ifdef USEPOSTPROCESSING\n", "// do r_glsl_dumpshader, edit glsl/default.glsl, and replace this by your own postprocessing if you want\n", "// this code does a blur with the radius specified in the first component of r_glsl_postprocess_uservec1 and blends it using the second component\n", "#if defined(USERVEC1) || defined(USERVEC2)\n", From b6ef825999616f85f824f7adfbbe7abecd2a0df9 Mon Sep 17 00:00:00 2001 From: Cloudwalk Date: Wed, 10 Jun 2020 09:44:06 -0400 Subject: [PATCH 18/23] Fix compile warnings --- gl_rmain.c | 2 +- snd_main.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gl_rmain.c b/gl_rmain.c index 0a0ce0671..d81512b18 100644 --- a/gl_rmain.c +++ b/gl_rmain.c @@ -2951,6 +2951,7 @@ static int componentorder[4] = {0, 1, 2, 3}; static rtexture_t *R_LoadCubemap(const char *basename) { int i, j, cubemapsize, forcefilter; + char name[256]; unsigned char *cubemappixels, *image_buffer; rtexture_t *cubemaptexture; // HACK: if the cubemap name starts with a !, the cubemap is nearest-filtered @@ -2960,7 +2961,6 @@ static rtexture_t *R_LoadCubemap(const char *basename) basename++; forcefilter = TEXF_FORCENEAREST; } - char name[256]; // must start 0 so the first loadimagepixels has no requested width/height cubemapsize = 0; cubemappixels = NULL; diff --git a/snd_main.c b/snd_main.c index c7bd33aed..7c5ca3d8e 100644 --- a/snd_main.c +++ b/snd_main.c @@ -359,7 +359,7 @@ static void S_SoundInfo_f(cmd_state_t *cmd) Con_Printf("%5u total_channels\n", total_channels); } -void S_PauseSound_f(cmd_state_t *cmd) +static void S_PauseSound_f(cmd_state_t *cmd) { if( Cmd_Argc(cmd) != 2 ) { Con_Print("pausesound \n"); From b61e9d017e9ed03c69e26300abd35f276d5b4eb3 Mon Sep 17 00:00:00 2001 From: Cloudwalk Date: Wed, 10 Jun 2020 10:41:19 -0400 Subject: [PATCH 19/23] Implement EXT_WRATH Wrath can now at least enter a map. Performance is horrid by next level. More work remains. --- clvm_cmds.c | 108 +++++ fs.c | 63 +++ mvm_cmds.c | 107 +++++ prvm_cmds.c | 247 ++++++++++- prvm_cmds.h | 7 + svvm_cmds.c | 176 +++++++- vsdirent.h | 1160 +++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 1865 insertions(+), 3 deletions(-) create mode 100644 vsdirent.h diff --git a/clvm_cmds.c b/clvm_cmds.c index d4f7ffabc..bf9153317 100644 --- a/clvm_cmds.c +++ b/clvm_cmds.c @@ -4806,6 +4806,114 @@ VM_digest_hex, // #639 VM_CL_V_CalcRefdef, // #640 void(entity e) V_CalcRefdef (DP_CSQC_V_CALCREFDEF) NULL, // #641 VM_coverage, // #642 +NULL, // #643 +NULL, // #644 +NULL, // #645 +NULL, // #646 +NULL, // #647 +NULL, // #648 +NULL, // #649 +// WRATH range (#650-#???) +VM_fcopy, // #650 float(string fnfrom, string fnto) fcopy (EXT_WRATH) +VM_frename, // #651 float (string fnold, string fnnew) frename (EXT_WRATH) +VM_fremove, // #652 float (string fname) fremove (EXT_WRATH) +VM_fexists, // #653 float (string fname) fexists (EXT_WRATH) +VM_rmtree, // #654 float (string path) rmtree (EXT_WRATH) +NULL, // #655 +NULL, // #656 +NULL, // #657 +NULL, // #658 +NULL, // #659 +NULL, // #660 +NULL, // #661 +NULL, // #662 +NULL, // #663 +NULL, // #664 +NULL, // #665 +NULL, // #666 +NULL, // #667 +NULL, // #668 +NULL, // #669 +NULL, // #670 +NULL, // #671 +NULL, // #672 +NULL, // #673 +NULL, // #674 +NULL, // #675 +NULL, // #676 +NULL, // #677 +NULL, // #678 +NULL, // #679 +NULL, // #680 +NULL, // #681 +NULL, // #682 +NULL, // #683 +NULL, // #684 +NULL, // #685 +NULL, // #686 +NULL, // #687 +NULL, // #688 +NULL, // #689 +NULL, // #690 +NULL, // #691 +NULL, // #692 +NULL, // #693 +NULL, // #694 +NULL, // #695 +NULL, // #696 +NULL, // #697 +NULL, // #698 +NULL, // #699 +NULL, // #700 +NULL, // #701 +NULL, // #702 +NULL, // #703 +NULL, // #704 +NULL, // #705 +NULL, // #706 +NULL, // #707 +NULL, // #708 +NULL, // #709 +NULL, // #710 +NULL, // #711 +NULL, // #712 +NULL, // #713 +NULL, // #714 +NULL, // #715 +NULL, // #716 +NULL, // #717 +NULL, // #718 +NULL, // #719 +NULL, // #720 +NULL, // #721 +NULL, // #722 +NULL, // #723 +NULL, // #724 +NULL, // #725 +NULL, // #726 +NULL, // #727 +NULL, // #728 +NULL, // #729 +NULL, // #730 +NULL, // #731 +NULL, // #732 +NULL, // #733 +NULL, // #734 +NULL, // #735 +NULL, // #736 +NULL, // #737 +NULL, // #738 +NULL, // #739 +NULL, // #740 +NULL, // #741 +NULL, // #742 +NULL, // #743 +NULL, // #744 +NULL, // #745 +NULL, // #746 +NULL, // #747 +NULL, // #748 +NULL, // #749 NULL }; diff --git a/fs.c b/fs.c index 8290c4a7c..c7f6ad7ec 100644 --- a/fs.c +++ b/fs.c @@ -24,6 +24,15 @@ #include #include +#include + +#if defined(_MSC_VER) +// visual studio does not have a dirent.h, so we use +// https://github.com/tronkko/dirent +# include "vsdirent.h" +#else +# include +#endif #ifdef WIN32 # include @@ -33,6 +42,7 @@ # include #else # include +# include # include # include #endif @@ -944,6 +954,59 @@ static void FS_mkdir (const char *path) } } +int FS_rmtree(const char *dir) +{ + struct dirent *ep; + struct stat st; + DIR *dp; + char vabuf[1024] = { 0 }; + int ret = 0; + + dp = opendir(dir); + if (!dp) + { + Con_DPrintf("FS_rmtree(): can't open dir `%s`: %s\n", dir, strerror(errno)); + return -1; + } + + while ((ep = readdir(dp))) + { + if (!strcmp(".", ep->d_name) || !strcmp("..", ep->d_name)) + continue; // NB: do these even appear in readdir() output? + + if (dpsnprintf(vabuf, sizeof(vabuf), "%s/%s", dir, ep->d_name) >= (int)sizeof(vabuf)) + { + // this is some wacky shit, better abort lest we fuck some poor sod's files up + Con_DPrintf("FS_rmtree(): path `%s` is longer than the path buffer, aborting\n", vabuf); + ret = -2; + break; + } + + if (stat(vabuf, &st) < 0) + { + Con_DPrintf("FS_rmtree(): can't stat `%s`: %s\n", vabuf, strerror(errno)); + ret = -3; + break; + } + + ret = (S_ISDIR(st.st_mode)) ? FS_rmtree(vabuf) : remove(vabuf); + if (ret) + { + Con_DPrintf("FS_rmtree(): can't remove `%s`: %s\n", vabuf, strerror(errno)); + break; + } + } + + closedir(dp); + + if (!ret) + { + ret = rmdir(dir); + if (ret) Con_DPrintf("FS_rmtree(): can't remove `%s`: %s\n", dir, strerror(errno)); + } + + return ret; +} /* ============ diff --git a/mvm_cmds.c b/mvm_cmds.c index 58e089a0b..429c5d7a3 100644 --- a/mvm_cmds.c +++ b/mvm_cmds.c @@ -1602,6 +1602,113 @@ NULL, // #640 VM_M_crypto_getmyidstatus, // #641 float(float i) crypto_getmyidstatus VM_coverage, // #642 VM_M_crypto_getidstatus, // #643 float(string addr) crypto_getidstatus +NULL, // #644 +NULL, // #645 +NULL, // #646 +NULL, // #647 +NULL, // #648 +NULL, // #649 +// WRATH range (#650-#???) +VM_fcopy, // #650 float(string fnfrom, string fnto) fcopy (EXT_WRATH) +VM_frename, // #651 float (string fnold, string fnnew) frename (EXT_WRATH) +VM_fremove, // #652 float (string fname) fremove (EXT_WRATH) +VM_fexists, // #653 float (string fname) fexists (EXT_WRATH) +VM_rmtree, // #654 float (string path) rmtree (EXT_WRATH) +NULL, // #655 +NULL, // #656 +NULL, // #657 +NULL, // #658 +NULL, // #659 +NULL, // #660 +NULL, // #661 +NULL, // #662 +NULL, // #663 +NULL, // #664 +NULL, // #665 +NULL, // #666 +NULL, // #667 +NULL, // #668 +NULL, // #669 +NULL, // #670 +NULL, // #671 +NULL, // #672 +NULL, // #673 +NULL, // #674 +NULL, // #675 +NULL, // #676 +NULL, // #677 +NULL, // #678 +NULL, // #679 +NULL, // #680 +NULL, // #681 +NULL, // #682 +NULL, // #683 +NULL, // #684 +NULL, // #685 +NULL, // #686 +NULL, // #687 +NULL, // #688 +NULL, // #689 +NULL, // #690 +NULL, // #691 +NULL, // #692 +NULL, // #693 +NULL, // #694 +NULL, // #695 +NULL, // #696 +NULL, // #697 +NULL, // #698 +NULL, // #699 +NULL, // #700 +NULL, // #701 +NULL, // #702 +NULL, // #703 +NULL, // #704 +NULL, // #705 +NULL, // #706 +NULL, // #707 +NULL, // #708 +NULL, // #709 +NULL, // #710 +NULL, // #711 +NULL, // #712 +NULL, // #713 +NULL, // #714 +NULL, // #715 +NULL, // #716 +NULL, // #717 +NULL, // #718 +NULL, // #719 +NULL, // #720 +NULL, // #721 +NULL, // #722 +NULL, // #723 +NULL, // #724 +NULL, // #725 +NULL, // #726 +NULL, // #727 +NULL, // #728 +NULL, // #729 +NULL, // #730 +NULL, // #731 +NULL, // #732 +NULL, // #733 +NULL, // #734 +NULL, // #735 +NULL, // #736 +NULL, // #737 +NULL, // #738 +NULL, // #739 +NULL, // #740 +NULL, // #741 +NULL, // #742 +NULL, // #743 +NULL, // #744 +NULL, // #745 +NULL, // #746 +NULL, // #747 +NULL, // #748 +NULL, // #749 NULL }; diff --git a/prvm_cmds.c b/prvm_cmds.c index f947766ae..36a6488dd 100644 --- a/prvm_cmds.c +++ b/prvm_cmds.c @@ -1997,6 +1997,244 @@ void VM_fputs(prvm_prog_t *prog) Con_DPrintf("fputs: %s: %s\n", prog->name, string); } +// EXT_WRATH file stuff + +/* +========= +VM_fcopy + +float fcopy(string fnfrom, string fnto) +========= +*/ +// float(string fnfrom, string fnto) fcopy = #650; +// copies quake/gamedir/data/$fnfrom to quake/gamedir/data/$fnto +// returns 0 on success, <0 on failure +void VM_fcopy(prvm_prog_t *prog) +{ + char vabuf[1024]; + char fbuf[VM_STRINGTEMP_LENGTH]; + const char *fname1, *fname2; + qfile_t *f1, *f2; + fs_offset_t rx = 0, wx = 0; + + VM_SAFEPARMCOUNT(2, VM_fcopy); + PRVM_G_FLOAT(OFS_RETURN) = 0; + + fname1 = PRVM_G_STRING(OFS_PARM0); + fname2 = PRVM_G_STRING(OFS_PARM1); + + f1 = FS_OpenVirtualFile(va(vabuf, sizeof(vabuf), "data/%s", fname1), false); + if (f1 == NULL) + f1 = FS_OpenVirtualFile(va(vabuf, sizeof(vabuf), "%s", fname1), false); + if (f1 == NULL) + { + PRVM_G_FLOAT(OFS_RETURN) = -1; + VM_Warning(prog, "VM_fcopy: %s could not open file %s for reading\n", prog->name, fname1); + return; + } + + f2 = FS_OpenRealFile(va(vabuf, sizeof(vabuf), "data/%s", fname2), "wb", false); + if (f2 == NULL) + { + FS_Close(f1); + PRVM_G_FLOAT(OFS_RETURN) = -2; + VM_Warning(prog, "VM_fcopy: %s could not open file %s for writing\n", prog->name, fname2); + return; + } + + while ((rx = FS_Read(f1, fbuf, sizeof(fbuf)-1)) > 0) { + wx = FS_Write(f2, fbuf, rx); + if (wx != rx) + { + FS_Close(f1); + FS_Close(f2); + PRVM_G_FLOAT(OFS_RETURN) = -3; + VM_Warning(prog, "VM_fcopy: %s read %I64d from %s but wrote %I64d to %s\n", prog->name, rx, fname1, wx, fname2); + return; + } + } + + if (wx == 0) + VM_Warning(prog, "VM_fcopy: %s wrote 0 bytes to %s\n", prog->name, fname2); + FS_Close(f1); + FS_Close(f2); +} + +/* +========= +VM_frename + +float frename(string fnold, string fnnew) +========= +*/ +// float(string fnold, string fnnew) frename = #651; +// renames quake/gamedir/data/$fnold to quake/gamedir/data/$fnnew +// returns 0 on success, <0 on failure +void VM_frename(prvm_prog_t *prog) +{ + char vabuf1[1024], vabuf2[1024]; + const char *fname1, *fname2; + int err = 0; + + VM_SAFEPARMCOUNT(2, VM_frename); + fname1 = PRVM_G_STRING(OFS_PARM0); + fname2 = PRVM_G_STRING(OFS_PARM1); + + if (FS_CheckNastyPath(fname1, true)) + { + PRVM_G_FLOAT(OFS_RETURN) = -1; + VM_Warning(prog, "VM_frename: %s rejects nasty path %s\n", prog->name, vabuf1); + return; + } + + if (FS_CheckNastyPath(fname2, true)) + { + PRVM_G_FLOAT(OFS_RETURN) = -2; + VM_Warning(prog, "VM_frename: %s rejects nasty path %s\n", prog->name, vabuf2); + return; + } + + dpsnprintf(vabuf1, sizeof(vabuf1), "%s/data/%s", fs_gamedir, fname1); + dpsnprintf(vabuf2, sizeof(vabuf2), "%s/data/%s", fs_gamedir, fname2); + + if (!FS_SysFileExists(vabuf1)) + { + PRVM_G_FLOAT(OFS_RETURN) = -3; + VM_Warning(prog, "VM_frename: %s can't find %s\n", prog->name, vabuf1); + return; + } + + if (FS_SysFileExists(vabuf2)) + { + PRVM_G_FLOAT(OFS_RETURN) = -4; + VM_Warning(prog, "VM_frename: %s file %s already exists\n", prog->name, vabuf2); + return; + } + + if ((err = rename(vabuf1, vabuf2)) != 0) + { + PRVM_G_FLOAT(OFS_RETURN) = -5; + VM_Warning(prog, "VM_frename: %s could not rename %s to %s\n", prog->name, vabuf1, vabuf2); + return; + } + + PRVM_G_FLOAT(OFS_RETURN) = 0; +} + +/* +========= +VM_fremove + +float fremove(string fname) +========= +*/ +// float(string fname) fremove = #652; +// removes quake/gamedir/data/$fname +// returns 0 on success, <0 on failure +void VM_fremove(prvm_prog_t *prog) +{ + const char *fname; + char vabuf[1024] = { 0 }; + int err = 0; + + VM_SAFEPARMCOUNT(1, VM_fremove); + fname = PRVM_G_STRING(OFS_PARM0); + + if (FS_CheckNastyPath(fname, true)) + { + PRVM_G_FLOAT(OFS_RETURN) = -1; + VM_Warning(prog, "VM_fremove: %s rejects nasty path %s\n", prog->name, vabuf); + return; + } + + if (dpsnprintf(vabuf, sizeof(vabuf), "%s/data/%s", fs_gamedir, fname) >= (int)sizeof(vabuf)) + { + PRVM_G_FLOAT(OFS_RETURN) = -2; + VM_Warning(prog, "VM_fremove: %s rejects path %s as too long\n", prog->name, vabuf); + return; + } + + if (!FS_SysFileExists(vabuf)) + { + PRVM_G_FLOAT(OFS_RETURN) = -3; + VM_Warning(prog, "VM_fremove: %s can't find %s\n", prog->name, vabuf); + return; + } + + if ((err = remove(vabuf)) != 0) + { + PRVM_G_FLOAT(OFS_RETURN) = -4; + VM_Warning(prog, "VM_fremove: %s could not remove %s\n", prog->name, vabuf); + return; + } + + PRVM_G_FLOAT(OFS_RETURN) = 0; +} + +/* +========= +VM_fexists + +float fexists(string filename) +========= +*/ +// float(string filename) fexists = #653; +// checks if quake/gamedir/$fname exists without opening it +// returns not 0 if it exists, 0 if it does not +void VM_fexists(prvm_prog_t *prog) +{ + const char *fname; + char vabuf[1024] = { 0 }; + + VM_SAFEPARMCOUNT(1, VM_fexists); + fname = PRVM_G_STRING(OFS_PARM0); + + if (FS_CheckNastyPath(fname, true)) + { + PRVM_G_FLOAT(OFS_RETURN) = 0; + VM_Warning(prog, "VM_fexists: %s rejects nasty path %s\n", prog->name, vabuf); + return; + } + + dpsnprintf(vabuf, sizeof(vabuf), "%s/%s", fs_gamedir, fname); + PRVM_G_FLOAT(OFS_RETURN) = FS_SysFileExists(vabuf); +} + +/* +========= +VM_rmtree + +float rmtree(string path) +========= +*/ +// float(string path) rmtree = #654; +// removes the directory "data/$path" and all of its contents +// returns 0 on success, !=0 on failure +void VM_rmtree(prvm_prog_t *prog) +{ + const char *fname; + char vabuf[1024] = { 0 }; + + VM_SAFEPARMCOUNT(1, VM_rmtree); + fname = PRVM_G_STRING(OFS_PARM0); + + if (FS_CheckNastyPath(fname, true)) + { + PRVM_G_FLOAT(OFS_RETURN) = -1; + VM_Warning(prog, "VM_rmtree: %s rejects nasty path %s\n", prog->name, vabuf); + return; + } + + if (dpsnprintf(vabuf, sizeof(vabuf), "%s/data/%s", fs_gamedir, fname) >= (int)sizeof(vabuf)) + { + PRVM_G_FLOAT(OFS_RETURN) = -2; + VM_Warning(prog, "VM_fremove: %s rejects path %s as too long\n", prog->name, vabuf); + return; + } + + PRVM_G_FLOAT(OFS_RETURN) = FS_rmtree(vabuf); +} + /* ========= VM_writetofile @@ -7200,19 +7438,21 @@ void VM_getsurfacetexture(prvm_prog_t *prog) PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, surface->texture->name); } //PF_getsurfacenearpoint, // #438 float(entity e, vector p) getsurfacenearpoint = #438; +// [EXT_WRATH] #438 float(entity e, vector p[, float skipmask]) getsurfacenearpoint = #438; void VM_getsurfacenearpoint(prvm_prog_t *prog) { - int surfacenum, best; + int surfacenum, best, skipmask; vec3_t clipped, p; vec_t dist, bestdist; prvm_edict_t *ed; dp_model_t *model; msurface_t *surface; vec3_t point; - VM_SAFEPARMCOUNT(2, VM_getsurfacenearpoint); + VM_SAFEPARMCOUNTRANGE(2, 3, VM_getsurfacenearpoint); PRVM_G_FLOAT(OFS_RETURN) = -1; ed = PRVM_G_EDICT(OFS_PARM0); VectorCopy(PRVM_G_VECTOR(OFS_PARM1), point); + skipmask = (prog->argc > 2) ? (int)PRVM_G_FLOAT(OFS_PARM2) : 0; if (!ed || ed->priv.server->free) return; @@ -7228,6 +7468,9 @@ void VM_getsurfacenearpoint(prvm_prog_t *prog) for (surfacenum = 0;surfacenum < model->nummodelsurfaces;surfacenum++) { surface = model->data_surfaces + surfacenum + model->firstmodelsurface; + // [EXT_WRATH] skip surfaces that match the surfparm skip mask + if (skipmask && (!surface->texture || (surface->texture->surfaceflags & skipmask))) + continue; // first see if the nearest point on the surface's box is closer than the previous match clipped[0] = bound(surface->mins[0], p[0], surface->maxs[0]) - p[0]; clipped[1] = bound(surface->mins[1], p[1], surface->maxs[1]) - p[1]; diff --git a/prvm_cmds.h b/prvm_cmds.h index c4ac87c8c..f1a8553ab 100644 --- a/prvm_cmds.h +++ b/prvm_cmds.h @@ -491,4 +491,11 @@ void VM_physics_addtorque(prvm_prog_t *prog); void VM_coverage(prvm_prog_t *prog); +// EXT_WRATH +void VM_fcopy(prvm_prog_t *prog); +void VM_frename(prvm_prog_t *prog); +void VM_fremove(prvm_prog_t *prog); +void VM_fexists(prvm_prog_t *prog); +void VM_rmtree(prvm_prog_t *prog); + #endif diff --git a/svvm_cmds.c b/svvm_cmds.c index 015c6b9da..2ae68c1ab 100644 --- a/svvm_cmds.c +++ b/svvm_cmds.c @@ -227,6 +227,7 @@ const char *vm_sv_extensions = "TENEBRAE_GFX_DLIGHTS " "TW_SV_STEPCONTROL " "ZQ_PAUSE " +"EXT_WRATH " "DP_RM_CLIPGROUP " //"EXT_CSQC " // not ready yet ; @@ -1120,6 +1121,71 @@ static void VM_SV_walkmove(prvm_prog_t *prog) PRVM_serverglobaledict(self) = oldself; } +/* +=============== +VM_SV_walkmovedist + +float(float yaw, float dist[, settrace]) walkmovedist (EXT_WRATH) +=============== +*/ +static void VM_SV_walkmovedist(prvm_prog_t *prog) +{ + prvm_edict_t *ent; + float yaw, dist; + vec3_t move, oldorg, neworg; + mfunction_t *oldf; + int oldself; + qboolean settrace; + + VM_SAFEPARMCOUNTRANGE(2, 3, VM_SV_walkmovedist); + + // assume failure if it returns early + PRVM_G_FLOAT(OFS_RETURN) = 0; + + ent = PRVM_PROG_TO_EDICT(PRVM_serverglobaledict(self)); + if (ent == prog->edicts) + { + VM_Warning(prog, "walkmove: can not modify world entity\n"); + return; + } + if (ent->priv.server->free) + { + VM_Warning(prog, "walkmove: can not modify free entity\n"); + return; + } + yaw = PRVM_G_FLOAT(OFS_PARM0); + dist = PRVM_G_FLOAT(OFS_PARM1); + settrace = prog->argc >= 3 && PRVM_G_FLOAT(OFS_PARM2); + + if ( !( (int)PRVM_serveredictfloat(ent, flags) & (FL_ONGROUND|FL_FLY|FL_SWIM) ) ) + return; + + yaw = yaw*M_PI*2 / 360; + + move[0] = cos(yaw)*dist; + move[1] = sin(yaw)*dist; + move[2] = 0; + +// save origin before move + VectorCopy (PRVM_serveredictvector(ent, origin), oldorg); + +// save program state, because SV_movestep may call other progs + oldf = prog->xfunction; + oldself = PRVM_serverglobaledict(self); + + SV_movestep(ent, move, true, false, settrace); + +// restore program state + prog->xfunction = oldf; + PRVM_serverglobaledict(self) = oldself; + +// save origin after move + VectorCopy (PRVM_serveredictvector(ent, origin), neworg); + +// return distance traveled + PRVM_G_FLOAT(OFS_RETURN) = VectorDistance(oldorg, neworg); +} + /* =============== VM_SV_droptofloor @@ -3841,7 +3907,115 @@ VM_digest_hex, // #639 NULL, // #640 NULL, // #641 VM_coverage, // #642 -NULL, // #643 +NULL, // #643 +NULL, // #644 +NULL, // #645 +NULL, // #646 +NULL, // #647 +NULL, // #648 +NULL, // #649 +// WRATH range (#650-#???) +VM_fcopy, // #650 float(string fnfrom, string fnto) fcopy (EXT_WRATH) +VM_frename, // #651 float (string fnold, string fnnew) frename (EXT_WRATH) +VM_fremove, // #652 float (string fname) fremove (EXT_WRATH) +VM_fexists, // #653 float (string fname) fexists (EXT_WRATH) +VM_rmtree, // #654 float (string path) rmtree (EXT_WRATH) +VM_SV_walkmovedist, // #655 float (float yaw, float dist[, float settrace]) walkmovedist (EXT_WRATH) +NULL, // #656 +NULL, // #657 +NULL, // #658 +NULL, // #659 +NULL, // #660 +NULL, // #661 +NULL, // #662 +NULL, // #663 +NULL, // #664 +NULL, // #665 +NULL, // #666 +NULL, // #667 +NULL, // #668 +NULL, // #669 +NULL, // #670 +NULL, // #671 +NULL, // #672 +NULL, // #673 +NULL, // #674 +NULL, // #675 +NULL, // #676 +NULL, // #677 +NULL, // #678 +NULL, // #679 +NULL, // #680 +NULL, // #681 +NULL, // #682 +NULL, // #683 +NULL, // #684 +NULL, // #685 +NULL, // #686 +NULL, // #687 +NULL, // #688 +NULL, // #689 +NULL, // #690 +NULL, // #691 +NULL, // #692 +NULL, // #693 +NULL, // #694 +NULL, // #695 +NULL, // #696 +NULL, // #697 +NULL, // #698 +NULL, // #699 +NULL, // #700 +NULL, // #701 +NULL, // #702 +NULL, // #703 +NULL, // #704 +NULL, // #705 +NULL, // #706 +NULL, // #707 +NULL, // #708 +NULL, // #709 +NULL, // #710 +NULL, // #711 +NULL, // #712 +NULL, // #713 +NULL, // #714 +NULL, // #715 +NULL, // #716 +NULL, // #717 +NULL, // #718 +NULL, // #719 +NULL, // #720 +NULL, // #721 +NULL, // #722 +NULL, // #723 +NULL, // #724 +NULL, // #725 +NULL, // #726 +NULL, // #727 +NULL, // #728 +NULL, // #729 +NULL, // #730 +NULL, // #731 +NULL, // #732 +NULL, // #733 +NULL, // #734 +NULL, // #735 +NULL, // #736 +NULL, // #737 +NULL, // #738 +NULL, // #739 +NULL, // #740 +NULL, // #741 +NULL, // #742 +NULL, // #743 +NULL, // #744 +NULL, // #745 +NULL, // #746 +NULL, // #747 +NULL, // #748 +NULL, // #749 +NULL }; const int vm_sv_numbuiltins = sizeof(vm_sv_builtins) / sizeof(prvm_builtin_t); diff --git a/vsdirent.h b/vsdirent.h new file mode 100644 index 000000000..332c798ad --- /dev/null +++ b/vsdirent.h @@ -0,0 +1,1160 @@ +/* + * Dirent interface for Microsoft Visual Studio + * Version 1.23.1 + * + * Copyright (C) 2006-2012 Toni Ronkko + * This file is part of dirent. Dirent may be freely distributed + * under the MIT license. For all details and documentation, see + * https://github.com/tronkko/dirent + */ +#ifndef DIRENT_H +#define DIRENT_H + +/* + * Include windows.h without Windows Sockets 1.1 to prevent conflicts with + * Windows Sockets 2.0. + */ +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Indicates that d_type field is available in dirent structure */ +#define _DIRENT_HAVE_D_TYPE + +/* Indicates that d_namlen field is available in dirent structure */ +#define _DIRENT_HAVE_D_NAMLEN + +/* Entries missing from MSVC 6.0 */ +#if !defined(FILE_ATTRIBUTE_DEVICE) +# define FILE_ATTRIBUTE_DEVICE 0x40 +#endif + +/* File type and permission flags for stat(), general mask */ +#if !defined(S_IFMT) +# define S_IFMT _S_IFMT +#endif + +/* Directory bit */ +#if !defined(S_IFDIR) +# define S_IFDIR _S_IFDIR +#endif + +/* Character device bit */ +#if !defined(S_IFCHR) +# define S_IFCHR _S_IFCHR +#endif + +/* Pipe bit */ +#if !defined(S_IFFIFO) +# define S_IFFIFO _S_IFFIFO +#endif + +/* Regular file bit */ +#if !defined(S_IFREG) +# define S_IFREG _S_IFREG +#endif + +/* Read permission */ +#if !defined(S_IREAD) +# define S_IREAD _S_IREAD +#endif + +/* Write permission */ +#if !defined(S_IWRITE) +# define S_IWRITE _S_IWRITE +#endif + +/* Execute permission */ +#if !defined(S_IEXEC) +# define S_IEXEC _S_IEXEC +#endif + +/* Pipe */ +#if !defined(S_IFIFO) +# define S_IFIFO _S_IFIFO +#endif + +/* Block device */ +#if !defined(S_IFBLK) +# define S_IFBLK 0 +#endif + +/* Link */ +#if !defined(S_IFLNK) +# define S_IFLNK 0 +#endif + +/* Socket */ +#if !defined(S_IFSOCK) +# define S_IFSOCK 0 +#endif + +/* Read user permission */ +#if !defined(S_IRUSR) +# define S_IRUSR S_IREAD +#endif + +/* Write user permission */ +#if !defined(S_IWUSR) +# define S_IWUSR S_IWRITE +#endif + +/* Execute user permission */ +#if !defined(S_IXUSR) +# define S_IXUSR 0 +#endif + +/* Read group permission */ +#if !defined(S_IRGRP) +# define S_IRGRP 0 +#endif + +/* Write group permission */ +#if !defined(S_IWGRP) +# define S_IWGRP 0 +#endif + +/* Execute group permission */ +#if !defined(S_IXGRP) +# define S_IXGRP 0 +#endif + +/* Read others permission */ +#if !defined(S_IROTH) +# define S_IROTH 0 +#endif + +/* Write others permission */ +#if !defined(S_IWOTH) +# define S_IWOTH 0 +#endif + +/* Execute others permission */ +#if !defined(S_IXOTH) +# define S_IXOTH 0 +#endif + +/* Maximum length of file name */ +#if !defined(PATH_MAX) +# define PATH_MAX MAX_PATH +#endif +#if !defined(FILENAME_MAX) +# define FILENAME_MAX MAX_PATH +#endif +#if !defined(NAME_MAX) +# define NAME_MAX FILENAME_MAX +#endif + +/* File type flags for d_type */ +#define DT_UNKNOWN 0 +#define DT_REG S_IFREG +#define DT_DIR S_IFDIR +#define DT_FIFO S_IFIFO +#define DT_SOCK S_IFSOCK +#define DT_CHR S_IFCHR +#define DT_BLK S_IFBLK +#define DT_LNK S_IFLNK + +/* Macros for converting between st_mode and d_type */ +#define IFTODT(mode) ((mode) & S_IFMT) +#define DTTOIF(type) (type) + +/* + * File type macros. Note that block devices, sockets and links cannot be + * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are + * only defined for compatibility. These macros should always return false + * on Windows. + */ +#if !defined(S_ISFIFO) +# define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) +#endif +#if !defined(S_ISDIR) +# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) +# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISLNK) +# define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) +#endif +#if !defined(S_ISSOCK) +# define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) +#endif +#if !defined(S_ISCHR) +# define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) +#endif +#if !defined(S_ISBLK) +# define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) +#endif + +/* Return the exact length of the file name without zero terminator */ +#define _D_EXACT_NAMLEN(p) ((p)->d_namlen) + +/* Return the maximum size of a file name */ +#define _D_ALLOC_NAMLEN(p) ((PATH_MAX)+1) + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Wide-character version */ +struct _wdirent { + /* Always zero */ + long d_ino; + + /* File position within stream */ + long d_off; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + wchar_t d_name[PATH_MAX+1]; +}; +typedef struct _wdirent _wdirent; + +struct _WDIR { + /* Current directory entry */ + struct _wdirent ent; + + /* Private file data */ + WIN32_FIND_DATAW data; + + /* True if data is valid */ + int cached; + + /* Win32 search handle */ + HANDLE handle; + + /* Initial directory name */ + wchar_t *patt; +}; +typedef struct _WDIR _WDIR; + +/* Multi-byte character version */ +struct dirent { + /* Always zero */ + long d_ino; + + /* File position within stream */ + long d_off; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + char d_name[PATH_MAX+1]; +}; +typedef struct dirent dirent; + +struct DIR { + struct dirent ent; + struct _WDIR *wdirp; +}; +typedef struct DIR DIR; + + +/* Dirent functions */ +static DIR *opendir (const char *dirname); +static _WDIR *_wopendir (const wchar_t *dirname); + +static struct dirent *readdir (DIR *dirp); +static struct _wdirent *_wreaddir (_WDIR *dirp); + +static int readdir_r( + DIR *dirp, struct dirent *entry, struct dirent **result); +static int _wreaddir_r( + _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result); + +static int closedir (DIR *dirp); +static int _wclosedir (_WDIR *dirp); + +static void rewinddir (DIR* dirp); +static void _wrewinddir (_WDIR* dirp); + +static int scandir (const char *dirname, struct dirent ***namelist, + int (*filter)(const struct dirent*), + int (*compare)(const struct dirent**, const struct dirent**)); + +static int alphasort (const struct dirent **a, const struct dirent **b); + +static int versionsort (const struct dirent **a, const struct dirent **b); + + +/* For compatibility with Symbian */ +#define wdirent _wdirent +#define WDIR _WDIR +#define wopendir _wopendir +#define wreaddir _wreaddir +#define wclosedir _wclosedir +#define wrewinddir _wrewinddir + + +/* Internal utility functions */ +static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp); +static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp); + +static int dirent_mbstowcs_s( + size_t *pReturnValue, + wchar_t *wcstr, + size_t sizeInWords, + const char *mbstr, + size_t count); + +static int dirent_wcstombs_s( + size_t *pReturnValue, + char *mbstr, + size_t sizeInBytes, + const wchar_t *wcstr, + size_t count); + +static void dirent_set_errno (int error); + + +/* + * Open directory stream DIRNAME for read and return a pointer to the + * internal working area that is used to retrieve individual directory + * entries. + */ +static _WDIR* +_wopendir( + const wchar_t *dirname) +{ + _WDIR *dirp = NULL; + int error; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno (ENOENT); + return NULL; + } + + /* Allocate new _WDIR structure */ + dirp = (_WDIR*) malloc (sizeof (struct _WDIR)); + if (dirp != NULL) { + DWORD n; + + /* Reset _WDIR structure */ + dirp->handle = INVALID_HANDLE_VALUE; + dirp->patt = NULL; + dirp->cached = 0; + + /* Compute the length of full path plus zero terminator + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume it is an absolute path. + */ +# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) + n = wcslen(dirname); +# else + n = GetFullPathNameW (dirname, 0, NULL, NULL); +# endif + + /* Allocate room for absolute directory name and search pattern */ + dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16); + if (dirp->patt) { + + /* + * Convert relative directory name to an absolute one. This + * allows rewinddir() to function correctly even when current + * working directory is changed between opendir() and rewinddir(). + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume it is an absolute path. + */ +# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) + wcsncpy_s(dirp->patt, n+1, dirname, n); +# else + n = GetFullPathNameW (dirname, n, dirp->patt, NULL); +# endif + if (n > 0) { + wchar_t *p; + + /* Append search pattern \* to the directory name */ + p = dirp->patt + n; + if (dirp->patt < p) { + switch (p[-1]) { + case '\\': + case '/': + case ':': + /* Directory ends in path separator, e.g. c:\temp\ */ + /*NOP*/; + break; + + default: + /* Directory name doesn't end in path separator */ + *p++ = '\\'; + } + } + *p++ = '*'; + *p = '\0'; + + /* Open directory stream and retrieve the first entry */ + if (dirent_first (dirp)) { + /* Directory stream opened successfully */ + error = 0; + } else { + /* Cannot retrieve first entry */ + error = 1; + dirent_set_errno (ENOENT); + } + + } else { + /* Cannot retrieve full path name */ + dirent_set_errno (ENOENT); + error = 1; + } + + } else { + /* Cannot allocate memory for search pattern */ + error = 1; + } + + } else { + /* Cannot allocate _WDIR structure */ + error = 1; + } + + /* Clean up in case of error */ + if (error && dirp) { + _wclosedir (dirp); + dirp = NULL; + } + + return dirp; +} + +/* + * Read next directory entry. + * + * Returns pointer to static directory entry which may be overwritten by + * subsequent calls to _wreaddir(). + */ +static struct _wdirent* +_wreaddir( + _WDIR *dirp) +{ + struct _wdirent *entry; + + /* + * Read directory entry to buffer. We can safely ignore the return value + * as entry will be set to NULL in case of error. + */ + (void) _wreaddir_r (dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry. + * + * Returns zero on success. If end of directory stream is reached, then sets + * result to NULL and returns zero. + */ +static int +_wreaddir_r( + _WDIR *dirp, + struct _wdirent *entry, + struct _wdirent **result) +{ + WIN32_FIND_DATAW *datap; + + /* Read next directory entry */ + datap = dirent_next (dirp); + if (datap) { + size_t n; + DWORD attr; + + /* + * Copy file name as wide-character string. If the file name is too + * long to fit in to the destination buffer, then truncate file name + * to PATH_MAX characters and zero-terminate the buffer. + */ + n = 0; + while (n < PATH_MAX && datap->cFileName[n] != 0) { + entry->d_name[n] = datap->cFileName[n]; + n++; + } + entry->d_name[n] = 0; + + /* Length of file name excluding zero terminator */ + entry->d_namlen = n; + + /* File type */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entry->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entry->d_type = DT_DIR; + } else { + entry->d_type = DT_REG; + } + + /* Reset dummy fields */ + entry->d_ino = 0; + entry->d_off = 0; + entry->d_reclen = sizeof (struct _wdirent); + + /* Set result address */ + *result = entry; + + } else { + + /* Return NULL to indicate end of directory */ + *result = NULL; + + } + + return /*OK*/0; +} + +/* + * Close directory stream opened by opendir() function. This invalidates the + * DIR structure as well as any directory entry read previously by + * _wreaddir(). + */ +static int +_wclosedir( + _WDIR *dirp) +{ + int ok; + if (dirp) { + + /* Release search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose (dirp->handle); + dirp->handle = INVALID_HANDLE_VALUE; + } + + /* Release search pattern */ + if (dirp->patt) { + free (dirp->patt); + dirp->patt = NULL; + } + + /* Release directory structure */ + free (dirp); + ok = /*success*/0; + + } else { + + /* Invalid directory stream */ + dirent_set_errno (EBADF); + ok = /*failure*/-1; + + } + return ok; +} + +/* + * Rewind directory stream such that _wreaddir() returns the very first + * file name again. + */ +static void +_wrewinddir( + _WDIR* dirp) +{ + if (dirp) { + /* Release existing search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose (dirp->handle); + } + + /* Open new search handle */ + dirent_first (dirp); + } +} + +/* Get first directory entry (internal) */ +static WIN32_FIND_DATAW* +dirent_first( + _WDIR *dirp) +{ + WIN32_FIND_DATAW *datap; + + /* Open directory and retrieve the first entry */ + dirp->handle = FindFirstFileExW( + dirp->patt, FindExInfoStandard, &dirp->data, + FindExSearchNameMatch, NULL, 0); + if (dirp->handle != INVALID_HANDLE_VALUE) { + + /* a directory entry is now waiting in memory */ + datap = &dirp->data; + dirp->cached = 1; + + } else { + + /* Failed to re-open directory: no directory entry in memory */ + dirp->cached = 0; + datap = NULL; + + } + return datap; +} + +/* + * Get next directory entry (internal). + * + * Returns + */ +static WIN32_FIND_DATAW* +dirent_next( + _WDIR *dirp) +{ + WIN32_FIND_DATAW *p; + + /* Get next directory entry */ + if (dirp->cached != 0) { + + /* A valid directory entry already in memory */ + p = &dirp->data; + dirp->cached = 0; + + } else if (dirp->handle != INVALID_HANDLE_VALUE) { + + /* Get the next directory entry from stream */ + if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) { + /* Got a file */ + p = &dirp->data; + } else { + /* The very last entry has been processed or an error occurred */ + FindClose (dirp->handle); + dirp->handle = INVALID_HANDLE_VALUE; + p = NULL; + } + + } else { + + /* End of directory stream reached */ + p = NULL; + + } + + return p; +} + +/* + * Open directory stream using plain old C-string. + */ +static DIR* +opendir( + const char *dirname) +{ + struct DIR *dirp; + int error; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno (ENOENT); + return NULL; + } + + /* Allocate memory for DIR structure */ + dirp = (DIR*) malloc (sizeof (struct DIR)); + if (dirp) { + wchar_t wname[PATH_MAX + 1]; + size_t n; + + /* Convert directory name to wide-character string */ + error = dirent_mbstowcs_s( + &n, wname, PATH_MAX + 1, dirname, PATH_MAX + 1); + if (!error) { + + /* Open directory stream using wide-character name */ + dirp->wdirp = _wopendir (wname); + if (dirp->wdirp) { + /* Directory stream opened */ + error = 0; + } else { + /* Failed to open directory stream */ + error = 1; + } + + } else { + /* + * Cannot convert file name to wide-character string. This + * occurs if the string contains invalid multi-byte sequences or + * the output buffer is too small to contain the resulting + * string. + */ + error = 1; + } + + } else { + /* Cannot allocate DIR structure */ + error = 1; + } + + /* Clean up in case of error */ + if (error && dirp) { + free (dirp); + dirp = NULL; + } + + return dirp; +} + +/* + * Read next directory entry. + */ +static struct dirent* +readdir( + DIR *dirp) +{ + struct dirent *entry; + + /* + * Read directory entry to buffer. We can safely ignore the return value + * as entry will be set to NULL in case of error. + */ + (void) readdir_r (dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry into called-allocated buffer. + * + * Returns zero on success. If the end of directory stream is reached, then + * sets result to NULL and returns zero. + */ +static int +readdir_r( + DIR *dirp, + struct dirent *entry, + struct dirent **result) +{ + WIN32_FIND_DATAW *datap; + + /* Read next directory entry */ + datap = dirent_next (dirp->wdirp); + if (datap) { + size_t n; + int error; + + /* Attempt to convert file name to multi-byte string */ + error = dirent_wcstombs_s( + &n, entry->d_name, PATH_MAX + 1, datap->cFileName, PATH_MAX + 1); + + /* + * If the file name cannot be represented by a multi-byte string, + * then attempt to use old 8+3 file name. This allows traditional + * Unix-code to access some file names despite of unicode + * characters, although file names may seem unfamiliar to the user. + * + * Be ware that the code below cannot come up with a short file + * name unless the file system provides one. At least + * VirtualBox shared folders fail to do this. + */ + if (error && datap->cAlternateFileName[0] != '\0') { + error = dirent_wcstombs_s( + &n, entry->d_name, PATH_MAX + 1, + datap->cAlternateFileName, PATH_MAX + 1); + } + + if (!error) { + DWORD attr; + + /* Length of file name excluding zero terminator */ + entry->d_namlen = n - 1; + + /* File attributes */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entry->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entry->d_type = DT_DIR; + } else { + entry->d_type = DT_REG; + } + + /* Reset dummy fields */ + entry->d_ino = 0; + entry->d_off = 0; + entry->d_reclen = sizeof (struct dirent); + + } else { + + /* + * Cannot convert file name to multi-byte string so construct + * an erroneous directory entry and return that. Note that + * we cannot return NULL as that would stop the processing + * of directory entries completely. + */ + entry->d_name[0] = '?'; + entry->d_name[1] = '\0'; + entry->d_namlen = 1; + entry->d_type = DT_UNKNOWN; + entry->d_ino = 0; + entry->d_off = -1; + entry->d_reclen = 0; + + } + + /* Return pointer to directory entry */ + *result = entry; + + } else { + + /* No more directory entries */ + *result = NULL; + + } + + return /*OK*/0; +} + +/* + * Close directory stream. + */ +static int +closedir( + DIR *dirp) +{ + int ok; + if (dirp) { + + /* Close wide-character directory stream */ + ok = _wclosedir (dirp->wdirp); + dirp->wdirp = NULL; + + /* Release multi-byte character version */ + free (dirp); + + } else { + + /* Invalid directory stream */ + dirent_set_errno (EBADF); + ok = /*failure*/-1; + + } + return ok; +} + +/* + * Rewind directory stream to beginning. + */ +static void +rewinddir( + DIR* dirp) +{ + /* Rewind wide-character string directory stream */ + _wrewinddir (dirp->wdirp); +} + +/* + * Scan directory for entries. + */ +static int +scandir( + const char *dirname, + struct dirent ***namelist, + int (*filter)(const struct dirent*), + int (*compare)(const struct dirent**, const struct dirent**)) +{ + struct dirent **files = NULL; + size_t size = 0; + size_t allocated = 0; + const size_t init_size = 1; + DIR *dir = NULL; + struct dirent *entry; + struct dirent *tmp = NULL; + size_t i; + int result = 0; + + /* Open directory stream */ + dir = opendir (dirname); + if (dir) { + + /* Read directory entries to memory */ + while (1) { + + /* Enlarge pointer table to make room for another pointer */ + if (size >= allocated) { + void *p; + size_t num_entries; + + /* Compute number of entries in the enlarged pointer table */ + if (size < init_size) { + /* Allocate initial pointer table */ + num_entries = init_size; + } else { + /* Double the size */ + num_entries = size * 2; + } + + /* Allocate first pointer table or enlarge existing table */ + p = realloc (files, sizeof (void*) * num_entries); + if (p != NULL) { + /* Got the memory */ + files = (dirent**) p; + allocated = num_entries; + } else { + /* Out of memory */ + result = -1; + break; + } + + } + + /* Allocate room for temporary directory entry */ + if (tmp == NULL) { + tmp = (struct dirent*) malloc (sizeof (struct dirent)); + if (tmp == NULL) { + /* Cannot allocate temporary directory entry */ + result = -1; + break; + } + } + + /* Read directory entry to temporary area */ + if (readdir_r (dir, tmp, &entry) == /*OK*/0) { + + /* Did we get an entry? */ + if (entry != NULL) { + int pass; + + /* Determine whether to include the entry in result */ + if (filter) { + /* Let the filter function decide */ + pass = filter (tmp); + } else { + /* No filter function, include everything */ + pass = 1; + } + + if (pass) { + /* Store the temporary entry to pointer table */ + files[size++] = tmp; + tmp = NULL; + + /* Keep up with the number of files */ + result++; + } + + } else { + + /* + * End of directory stream reached => sort entries and + * exit. + */ + qsort (files, size, sizeof (void*), + (int (*) (const void*, const void*)) compare); + break; + + } + + } else { + /* Error reading directory entry */ + result = /*Error*/ -1; + break; + } + + } + + } else { + /* Cannot open directory */ + result = /*Error*/ -1; + } + + /* Release temporary directory entry */ + if (tmp) { + free (tmp); + } + + /* Release allocated memory on error */ + if (result < 0) { + for (i = 0; i < size; i++) { + free (files[i]); + } + free (files); + files = NULL; + } + + /* Close directory stream */ + if (dir) { + closedir (dir); + } + + /* Pass pointer table to caller */ + if (namelist) { + *namelist = files; + } + return result; +} + +/* Alphabetical sorting */ +static int +alphasort( + const struct dirent **a, const struct dirent **b) +{ + return strcoll ((*a)->d_name, (*b)->d_name); +} + +/* Sort versions */ +static int +versionsort( + const struct dirent **a, const struct dirent **b) +{ + /* FIXME: implement strverscmp and use that */ + return alphasort (a, b); +} + + +/* Convert multi-byte string to wide character string */ +static int +dirent_mbstowcs_s( + size_t *pReturnValue, + wchar_t *wcstr, + size_t sizeInWords, + const char *mbstr, + size_t count) +{ + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to wide-character string (or count characters) */ + n = mbstowcs (wcstr, mbstr, sizeInWords); + if (!wcstr || n < count) { + + /* Zero-terminate output buffer */ + if (wcstr && sizeInWords) { + if (n >= sizeInWords) { + n = sizeInWords - 1; + } + wcstr[n] = 0; + } + + /* Length of resulting multi-byte string WITH zero terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + + /* Could not convert string */ + error = 1; + + } + +#endif + + return error; +} + +/* Convert wide-character string to multi-byte string */ +static int +dirent_wcstombs_s( + size_t *pReturnValue, + char *mbstr, + size_t sizeInBytes, /* max size of mbstr */ + const wchar_t *wcstr, + size_t count) +{ + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to multi-byte string (or count the number of bytes needed) */ + n = wcstombs (mbstr, wcstr, sizeInBytes); + if (!mbstr || n < count) { + + /* Zero-terminate output buffer */ + if (mbstr && sizeInBytes) { + if (n >= sizeInBytes) { + n = sizeInBytes - 1; + } + mbstr[n] = '\0'; + } + + /* Length of resulting multi-bytes string WITH zero-terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + + /* Cannot convert string */ + error = 1; + + } + +#endif + + return error; +} + +/* Set errno variable */ +static void +dirent_set_errno( + int error) +{ +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 and later */ + _set_errno (error); + +#else + + /* Non-Microsoft compiler or older Microsoft compiler */ + errno = error; + +#endif +} + + +#ifdef __cplusplus +} +#endif +#endif /*DIRENT_H*/ + From e7fb44e96f8acf32c6814165e2dc44f30e51986d Mon Sep 17 00:00:00 2001 From: Cloudwalk Date: Wed, 10 Jun 2020 11:27:53 -0400 Subject: [PATCH 20/23] Implement GAME_WRATH and its codepaths The game works now. Some console spam related to VM_fgets that I'll need to look into. Other than that, it doesn't have horrid framerates anymore in e1m1. --- cl_parse.c | 18 ++++- cl_screen.c | 194 +++++++++++++++++++++++++++++++++++++-------------- cmd.c | 24 +++++++ common.c | 1 + common.h | 1 + draw.h | 1 + gl_draw.c | 11 +++ host_cmd.c | 8 +++ keys.c | 12 ++++ menu.c | 35 ++++++++++ screen.h | 7 ++ sv_main.c | 1 + vid_shared.c | 7 ++ 13 files changed, 267 insertions(+), 53 deletions(-) diff --git a/cl_parse.c b/cl_parse.c index c86581a34..cdfb12016 100644 --- a/cl_parse.c +++ b/cl_parse.c @@ -331,7 +331,7 @@ void CL_KeepaliveMessage (qboolean readmessages) { if(cls.state != ca_dedicated) { - if(countdownupdate <= 0) // check if time stepped backwards + if(countdownupdate <= 0 || gamemode == GAME_WRATH) // check if time stepped backwards { SCR_UpdateLoadingScreenIfShown(); countdownupdate = 2; @@ -484,6 +484,7 @@ static void CL_SetupWorldModel(void) strlcpy(cl.worldname, cl.worldmodel->name, sizeof(cl.worldname)); FS_StripExtension(cl.worldname, cl.worldnamenoextension, sizeof(cl.worldnamenoextension)); strlcpy(cl.worldbasename, !strncmp(cl.worldnamenoextension, "maps/", 5) ? cl.worldnamenoextension + 5 : cl.worldnamenoextension, sizeof(cl.worldbasename)); + SCR_SetLoadingSplash(cl.worldbasename); // set the loading splash once we know the map name for sure Cvar_SetQuick(&cl_worldmessage, cl.worldmessage); Cvar_SetQuick(&cl_worldname, cl.worldname); Cvar_SetQuick(&cl_worldnamenoextension, cl.worldnamenoextension); @@ -1350,7 +1351,8 @@ static void CL_BeginDownloads(qboolean aborteddownload) // finished loading sounds } - SCR_PopLoadingScreen(false); + if (gamemode != GAME_WRATH) // this will be done after the "press any key" screen + SCR_ClearLoadingScreen(false); if (!cl.loadfinished) { @@ -1659,6 +1661,17 @@ static void CL_SignonReply (void) Con_ClearNotify(); if (COM_CheckParm("-profilegameonly")) Sys_AllowProfiling(true); + + if (gamemode == GAME_WRATH) + { + // HACK: pause the game and display "loading ended, press any key" screen + SCR_ClearLoadingScreen(false); + if (cl.islocalgame) + { + SCR_PushLoadingScreen("$", 1); + sv.paused = true; + } + } break; } } @@ -1685,6 +1698,7 @@ static void CL_ParseServerInfo (void) // if server is active, we already began a loading plaque if (!sv.active) { + SCR_SetLoadingSplash(NULL); SCR_BeginLoadingPlaque(false); S_StopAllSounds(); // free q3 shaders so that any newly downloaded shaders will be active diff --git a/cl_screen.c b/cl_screen.c index 5810f31d2..fa0245fdc 100644 --- a/cl_screen.c +++ b/cl_screen.c @@ -53,6 +53,7 @@ cvar_t vid_conwidthauto = {CVAR_CLIENT | CVAR_SAVE, "vid_conwidthauto", "1", "au cvar_t vid_conwidth = {CVAR_CLIENT | CVAR_SAVE, "vid_conwidth", "640", "virtual width of 2D graphics system (note: changes may be overwritten, see vid_conwidthauto)"}; cvar_t vid_conheight = {CVAR_CLIENT | CVAR_SAVE, "vid_conheight", "480", "virtual height of 2D graphics system"}; cvar_t vid_pixelheight = {CVAR_CLIENT | CVAR_SAVE, "vid_pixelheight", "1", "adjusts vertical field of vision to account for non-square pixels (1280x1024 on a CRT monitor for example)"}; +cvar_t scr_aspectname = {CVAR_CLIENT, "scr_aspectname", "16-9", "string name for the current aspect ratio; use a dash instead of a colon, e. g. 16-9"}; cvar_t scr_screenshot_jpeg = {CVAR_CLIENT | CVAR_SAVE, "scr_screenshot_jpeg","1", "save jpeg instead of targa"}; cvar_t scr_screenshot_jpeg_quality = {CVAR_CLIENT | CVAR_SAVE, "scr_screenshot_jpeg_quality","0.9", "image quality of saved jpeg"}; cvar_t scr_screenshot_png = {CVAR_CLIENT | CVAR_SAVE, "scr_screenshot_png","0", "save png instead of targa"}; @@ -2415,8 +2416,11 @@ static float SCR_DrawLoadingStack_r(loadingscreenstack_t *s, float y, float size len = strlen(s->msg); x = (vid_conwidth.integer - DrawQ_TextWidth(s->msg, len, size, size, true, FONT_INFOBAR)) / 2; y -= size; - DrawQ_Fill(0, y, vid_conwidth.integer, size, 0, 0, 0, 1, 0); - DrawQ_String(x, y, s->msg, len, size, size, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR); + if (gamemode != GAME_WRATH) + { + DrawQ_Fill(0, y, vid_conwidth.integer, size, 0, 0, 0, 1, 0); + DrawQ_String(x, y, s->msg, len, size, size, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR); + } total += size; } #endif @@ -2429,7 +2433,7 @@ static void SCR_DrawLoadingStack(void) float colors[16]; loadingscreenheight = SCR_DrawLoadingStack_r(loadingscreenstack, vid_conheight.integer, scr_loadingscreen_barheight.value); - if(loadingscreenstack) + if(gamemode != GAME_WRATH && loadingscreenstack) { // height = 32; // sorry, using the actual one is ugly GL_BlendFunc(GL_SRC_ALPHA, GL_ONE); @@ -2465,8 +2469,47 @@ static void SCR_DrawLoadingStack(void) } static cachepic_t *loadingscreenpic; +static cachepic_t *loadingscreenind; static float loadingscreenpic_vertex3f[12]; static float loadingscreenpic_texcoord2f[8]; +static float loadingscreenind_vertex3f[12]; +static float loadingscreenind_texcoord2f[8]; +static float loadingscreenind_angle; +static float loadingscreenind_pos[2]; +static qboolean loadingscreenind_show; + +void SCR_SetLoadingSplash (const char *mapname) +{ + char vabuf[1024] = { 0 }; + + if (gamemode != GAME_WRATH) + return; // let god sort em out + + // if we're connecting somewhere, the mapname is unknown until the server sends it to us, + // so the pic is set once we get the server info, + // otherwise the appropriate function just calls this with a known mapname. + // to reset to black screen, just call this with NULL mapname + + if (mapname && mapname[0]) { + // try the current aspect ratio + dpsnprintf(vabuf, sizeof(vabuf), "gfx/splashes/%s_%s", mapname, scr_aspectname.string); + if (!Draw_PicExists(vabuf)) { + // try the default aspect ratio (16:9) + dpsnprintf(vabuf, sizeof(vabuf), "gfx/splashes/%s_16-9", mapname); + if (!Draw_PicExists(vabuf)) { + // try without aspect ratio + dpsnprintf(vabuf, sizeof(vabuf), "gfx/splashes/%s", mapname); + if (!Draw_PicExists(vabuf)) + vabuf[0] = 0; // give up + } + } + } + + if (!vabuf[0]) + strlcpy(vabuf, "gfx/splashes/_blank", sizeof(vabuf)); + + Cvar_SetQuick(&scr_loadingscreen_picture, vabuf); +} static void SCR_DrawLoadingScreen_SharedSetup (qboolean clear) { @@ -2488,59 +2531,103 @@ static void SCR_DrawLoadingScreen_SharedSetup (qboolean clear) R_Textures_Frame(); R_Mesh_Start(); R_EntityMatrix(&identitymatrix); - // draw the loading plaque - loadingscreenpic = Draw_CachePic_Flags (loadingscreenpic_number ? va(vabuf, sizeof(vabuf), "%s%d", scr_loadingscreen_picture.string, loadingscreenpic_number+1) : scr_loadingscreen_picture.string, loadingscreenpic_number ? CACHEPICFLAG_NOTPERSISTENT : 0); + + if (gamemode == GAME_WRATH) + { + // setup the splash + loadingscreenpic = Draw_CachePic_Flags(scr_loadingscreen_picture.string, 0); + loadingscreenpic_vertex3f[2] = loadingscreenpic_vertex3f[5] = loadingscreenpic_vertex3f[8] = loadingscreenpic_vertex3f[11] = 0; + loadingscreenpic_vertex3f[0] = loadingscreenpic_vertex3f[9] = 0; + loadingscreenpic_vertex3f[1] = loadingscreenpic_vertex3f[4] = 0; + loadingscreenpic_vertex3f[3] = loadingscreenpic_vertex3f[6] = vid_conwidth.integer; + loadingscreenpic_vertex3f[7] = loadingscreenpic_vertex3f[10] = vid_conheight.integer; + loadingscreenpic_texcoord2f[0] = 0;loadingscreenpic_texcoord2f[1] = 0; + loadingscreenpic_texcoord2f[2] = 1;loadingscreenpic_texcoord2f[3] = 0; + loadingscreenpic_texcoord2f[4] = 1;loadingscreenpic_texcoord2f[5] = 1; + loadingscreenpic_texcoord2f[6] = 0;loadingscreenpic_texcoord2f[7] = 1; + + // spinning circle/arrow thing + if (SCR_LoadingScreenWaiting()) + { + loadingscreenind = loadingscreenind_show ? Draw_CachePic_Flags ("gfx/splashes/arrow", 0) : NULL; + loadingscreenind_angle = 0.f; + } + else + { + loadingscreenind = Draw_CachePic_Flags ("gfx/splashes/loading_ring", 0); + } - w = Draw_GetPicWidth(loadingscreenpic); - h = Draw_GetPicHeight(loadingscreenpic); + if (loadingscreenind) + { + loadingscreenind_pos[0] = vid_conwidth.integer - 20; + loadingscreenind_pos[1] = vid_conheight.integer - 20; + loadingscreenind_vertex3f[2] = loadingscreenind_vertex3f[5] = loadingscreenind_vertex3f[8] = loadingscreenind_vertex3f[11] = 0; + loadingscreenind_vertex3f[0] = loadingscreenind_vertex3f[9] = -Draw_GetPicWidth(loadingscreenind) / 2; + loadingscreenind_vertex3f[1] = loadingscreenind_vertex3f[4] = -Draw_GetPicHeight(loadingscreenind) / 2; + loadingscreenind_vertex3f[3] = loadingscreenind_vertex3f[6] = Draw_GetPicWidth(loadingscreenind) / 2; + loadingscreenind_vertex3f[7] = loadingscreenind_vertex3f[10] = Draw_GetPicHeight(loadingscreenind) / 2; + loadingscreenind_texcoord2f[0] = 0;loadingscreenind_texcoord2f[1] = 0; + loadingscreenind_texcoord2f[2] = 1;loadingscreenind_texcoord2f[3] = 0; + loadingscreenind_texcoord2f[4] = 1;loadingscreenind_texcoord2f[5] = 1; + loadingscreenind_texcoord2f[6] = 0;loadingscreenind_texcoord2f[7] = 1; + } + } + else + { + // draw the loading plaque + loadingscreenpic = Draw_CachePic_Flags (loadingscreenpic_number ? va(vabuf, sizeof(vabuf), "%s%d", scr_loadingscreen_picture.string, loadingscreenpic_number+1) : scr_loadingscreen_picture.string, loadingscreenpic_number ? CACHEPICFLAG_NOTPERSISTENT : 0); - // apply scale - w *= scr_loadingscreen_scale.value; - h *= scr_loadingscreen_scale.value; + w = Draw_GetPicWidth(loadingscreenpic); + h = Draw_GetPicHeight(loadingscreenpic); - // apply scale base - if(scr_loadingscreen_scale_base.integer) - { - w *= vid_conwidth.integer / (float) vid.width; - h *= vid_conheight.integer / (float) vid.height; - } + // apply scale + w *= scr_loadingscreen_scale.value; + h *= scr_loadingscreen_scale.value; - // apply scale limit - sw = w / vid_conwidth.integer; - sh = h / vid_conheight.integer; - f = 1; - switch(scr_loadingscreen_scale_limit.integer) - { - case 1: - f = max(sw, sh); - break; - case 2: - f = min(sw, sh); - break; - case 3: - f = sw; - break; - case 4: - f = sh; - break; - } - if(f > 1) - { - w /= f; - h /= f; - } + // apply scale base + if(scr_loadingscreen_scale_base.integer) + { + w *= vid_conwidth.integer / (float) vid.width; + h *= vid_conheight.integer / (float) vid.height; + } + + // apply scale limit + sw = w / vid_conwidth.integer; + sh = h / vid_conheight.integer; + f = 1; + switch(scr_loadingscreen_scale_limit.integer) + { + case 1: + f = max(sw, sh); + break; + case 2: + f = min(sw, sh); + break; + case 3: + f = sw; + break; + case 4: + f = sh; + break; + } + if(f > 1) + { + w /= f; + h /= f; + } - x = (vid_conwidth.integer - w)/2; - y = (vid_conheight.integer - h)/2; - loadingscreenpic_vertex3f[2] = loadingscreenpic_vertex3f[5] = loadingscreenpic_vertex3f[8] = loadingscreenpic_vertex3f[11] = 0; - loadingscreenpic_vertex3f[0] = loadingscreenpic_vertex3f[9] = x; - loadingscreenpic_vertex3f[1] = loadingscreenpic_vertex3f[4] = y; - loadingscreenpic_vertex3f[3] = loadingscreenpic_vertex3f[6] = x + w; - loadingscreenpic_vertex3f[7] = loadingscreenpic_vertex3f[10] = y + h; - loadingscreenpic_texcoord2f[0] = 0;loadingscreenpic_texcoord2f[1] = 0; - loadingscreenpic_texcoord2f[2] = 1;loadingscreenpic_texcoord2f[3] = 0; - loadingscreenpic_texcoord2f[4] = 1;loadingscreenpic_texcoord2f[5] = 1; - loadingscreenpic_texcoord2f[6] = 0;loadingscreenpic_texcoord2f[7] = 1; + x = (vid_conwidth.integer - w)/2; + y = (vid_conheight.integer - h)/2; + loadingscreenpic_vertex3f[2] = loadingscreenpic_vertex3f[5] = loadingscreenpic_vertex3f[8] = loadingscreenpic_vertex3f[11] = 0; + loadingscreenpic_vertex3f[0] = loadingscreenpic_vertex3f[9] = x; + loadingscreenpic_vertex3f[1] = loadingscreenpic_vertex3f[4] = y; + loadingscreenpic_vertex3f[3] = loadingscreenpic_vertex3f[6] = x + w; + loadingscreenpic_vertex3f[7] = loadingscreenpic_vertex3f[10] = y + h; + loadingscreenpic_texcoord2f[0] = 0;loadingscreenpic_texcoord2f[1] = 0; + loadingscreenpic_texcoord2f[2] = 1;loadingscreenpic_texcoord2f[3] = 0; + loadingscreenpic_texcoord2f[4] = 1;loadingscreenpic_texcoord2f[5] = 1; + loadingscreenpic_texcoord2f[6] = 0;loadingscreenpic_texcoord2f[7] = 1; + } } static void SCR_DrawLoadingScreen (void) @@ -2654,6 +2741,11 @@ void SCR_UpdateLoadingScreen (qboolean clear, qboolean startup) key_consoleactive = old_key_consoleactive; } +qboolean SCR_LoadingScreenWaiting(void) +{ + return cl.islocalgame && (loadingscreenstack && loadingscreenstack->msg[0] == '$' && loadingscreenstack->msg[1] == '\0'); +} + qboolean R_Stereo_ColorMasking(void) { return r_stereo_redblue.integer || r_stereo_redgreen.integer || r_stereo_redcyan.integer; diff --git a/cmd.c b/cmd.c index d23691572..5b68b6c9e 100644 --- a/cmd.c +++ b/cmd.c @@ -695,6 +695,30 @@ static void Cmd_Exec(cmd_state_t *cmd, const char *filename) "csqc_polygons_defaultmaterial_nocullface 1\n" ); break; + case GAME_WRATH: + Cbuf_InsertText(cmd, "\n" +"sv_gameplayfix_blowupfallenzombies 1\n" +"sv_gameplayfix_findradiusdistancetobox 1\n" +"sv_gameplayfix_grenadebouncedownslopes 1\n" +"sv_gameplayfix_slidemoveprojectiles 1\n" +"sv_gameplayfix_upwardvelocityclearsongroundflag 1\n" +"sv_gameplayfix_setmodelrealbox 1\n" +"sv_gameplayfix_droptofloorstartsolid 1\n" +"sv_gameplayfix_droptofloorstartsolid_nudgetocorrect 1\n" +"sv_gameplayfix_noairborncorpse 1\n" +"sv_gameplayfix_noairborncorpse_allowsuspendeditems 0\n" +"sv_gameplayfix_easierwaterjump 1\n" +"sv_gameplayfix_delayprojectiles 1\n" +"sv_gameplayfix_multiplethinksperframe 1\n" +"sv_gameplayfix_fixedcheckwatertransition 1\n" +"sv_gameplayfix_q1bsptracelinereportstexture 1\n" +"sv_gameplayfix_swiminbmodels 1\n" +"sv_gameplayfix_downtracesupportsongroundflag 0\n" +"sv_gameplayfix_stepmultipletimes 1\n" +"sv_gameplayfix_nogravityonground 1\n" +"sys_ticrate 0.01388889\n" + ); + break; default: Cbuf_InsertText(cmd, "\n" "sv_gameplayfix_blowupfallenzombies 1\n" diff --git a/common.c b/common.c index 090f09708..8be0e0dd6 100644 --- a/common.c +++ b/common.c @@ -1480,6 +1480,7 @@ static const gamemode_info_t gamemode_info [GAME_COUNT] = { GAME_STRAPBOMB, GAME_STRAPBOMB, "strapbomb", "-strapbomb", "Strap-on-bomb Car", "Strap-on-bomb_Car", "id1", NULL, "strap", "strapbomb" }, // COMMANDLINEOPTION: Game: -strapbomb runs the game Strap-on-bomb Car { GAME_MOONHELM, GAME_MOONHELM, "moonhelm", "-moonhelm", "MoonHelm", "MoonHelm", "data", NULL, "mh", "moonhelm" }, // COMMANDLINEOPTION: Game: -moonhelm runs the game MoonHelm { GAME_VORETOURNAMENT, GAME_VORETOURNAMENT, "voretournament", "-voretournament", "Vore Tournament", "Vore_Tournament", "data", NULL, "voretournament", "voretournament" }, // COMMANDLINEOPTION: Game: -voretournament runs the multiplayer game Vore Tournament +{ GAME_WRATH, GAME_WRATH, "wrath", "-wrath", "WRATH", "WRATH", "kp1", NULL, "wrath", "WRATH" }, // COMMANDLINEOPTION: Game: -wrath runs WRATH }; static void COM_SetGameType(int index); diff --git a/common.h b/common.h index a490ede2d..44f49a1fd 100644 --- a/common.h +++ b/common.h @@ -298,6 +298,7 @@ typedef enum gamemode_e GAME_STRAPBOMB, // added by motorsep for Urre GAME_MOONHELM, GAME_VORETOURNAMENT, + GAME_WRATH, GAME_COUNT } gamemode_t; diff --git a/draw.h b/draw.h index abc520da4..bbb5009c5 100644 --- a/draw.h +++ b/draw.h @@ -47,6 +47,7 @@ cachepic_t *Draw_CachePic (const char *path); // standard function with no optio cachepic_t *Draw_NewPic(const char *picname, int width, int height, unsigned char *pixels, textype_t textype, int texflags); // free the texture memory used by a pic (the cachepic_t itself is eternal) void Draw_FreePic(const char *picname); +qboolean Draw_PicExists(const char *picname); // a triangle mesh.. // each vertex is 3 floats diff --git a/gl_draw.c b/gl_draw.c index e488e2af0..c62a297e5 100644 --- a/gl_draw.c +++ b/gl_draw.c @@ -323,6 +323,17 @@ void Draw_FreePic(const char *picname) } } +qboolean Draw_PicExists(const char *name) { + char vabuf[1024] = { 0 }; + const char *checkfmt[] = { "%s.tga", "%s.png", "%s.jpg", "%s.pcx" }; + int i; + // TODO: actually use the gfx format list for this + for (i = 0; i < sizeof(checkfmt) / sizeof(checkfmt[0]); ++i) + if (FS_FileExists(va(vabuf, sizeof(vabuf), checkfmt[i], name))) + return true; + return false; +} + static float snap_to_pixel_x(float x, float roundUpAt); extern int con_linewidth; // to force rewrapping void LoadFont(qboolean override, const char *name, dp_font_t *fnt, float scale, float voffset) diff --git a/host_cmd.c b/host_cmd.c index bcf349dc7..3c26c1b62 100644 --- a/host_cmd.c +++ b/host_cmd.c @@ -382,6 +382,8 @@ static void Host_Map_f(cmd_state_t *cmd) return; } + SCR_SetLoadingSplash(NULL); // clear splash + // GAME_DELUXEQUAKE - clear warpmark (used by QC) if (gamemode == GAME_DELUXEQUAKE) Cvar_Set(&cvars_all, "warpmark", ""); @@ -435,6 +437,8 @@ static void Host_Changelevel_f(cmd_state_t *cmd) return; } + SCR_SetLoadingSplash(NULL); // clear splash + #ifdef CONFIG_MENU // remove menu if (key_dest == key_menu || key_dest == key_menu_grabbed) @@ -471,6 +475,8 @@ static void Host_Restart_f(cmd_state_t *cmd) return; } + SCR_SetLoadingSplash(NULL); // clear splash + #ifdef CONFIG_MENU // remove menu if (key_dest == key_menu || key_dest == key_menu_grabbed) @@ -801,6 +807,8 @@ static void Host_Loadgame_f(cmd_state_t *cmd) return; } + SCR_SetLoadingSplash(NULL); // clear splash + strlcpy (filename, Cmd_Argv(cmd, 1), sizeof(filename)); FS_DefaultExtension (filename, ".sav", sizeof (filename)); diff --git a/keys.c b/keys.c index cb050819e..65d232546 100644 --- a/keys.c +++ b/keys.c @@ -1795,10 +1795,22 @@ Key_Event (int key, int ascii, qboolean down) qboolean q; keydest_t keydest = key_dest; char vabuf[1024]; + long curtime; + static long pausetime = 0; // HACK: prevent double unpause if (key < 0 || key >= MAX_KEYS) return; + // HACK: allow unpause by any key for the "press any key" screen + if (SCR_LoadingScreenWaiting() && sv.paused && down && (curtime = Sys_DirtyTime()) > pausetime) + { + key_consoleactive &= ~KEY_CONSOLEACTIVE_USER; // close the console if opened + Cbuf_InsertText(cmd,"pause\n"); // unpause + SCR_ClearLoadingScreen(false); + pausetime = curtime + 2; + return; // eat the key + } + if(events_blocked) { Key_EventQueue_Add(key, ascii, down); diff --git a/menu.c b/menu.c index 25fd8acb8..25e29979d 100644 --- a/menu.c +++ b/menu.c @@ -2451,6 +2451,36 @@ static const char *goodvsbad2bindnames[][2] = {"+movedown", "swim down"} }; +static const char *wrathbindnames[][2] = +{ +{"+attack", "primary fire"}, +{"+button3", "secondary fire"}, +{"impulse 10", "next weapon"}, +{"impulse 12", "previous weapon"}, +{"impulse 15", "use artifact"}, +{"+button6", "artifact inventory"}, +{"+jump", "jump / swim up"}, +{"+forward", "walk forward"}, +{"+back", "backpedal"}, +{"+left", "turn left"}, +{"+right", "turn right"}, +{"+speed", "walk"}, +{"+button4", "crouch"}, +{"+button5", "use"}, +{"+moveleft", "step left"}, +{"+moveright", "step right"}, +{"+moveup", "swim up"}, +{"+movedown", "swim down"}, +{"impulse 35", "toggle journal"}, +// guns +{"impulse 1", "select gauntlet"}, +{"impulse 2", "select coachgun"}, +{"impulse 3", "select shotgun"}, +{"impulse 4", "select fang spitter"}, +{"impulse 5", "select retcher"}, +{"impulse 6", "select slag cannon"} +}; + static int numcommands; static const char *(*bindnames)[2]; @@ -2564,6 +2594,11 @@ void M_Menu_Keys_f(cmd_state_t *cmd) numcommands = sizeof(goodvsbad2bindnames) / sizeof(goodvsbad2bindnames[0]); bindnames = goodvsbad2bindnames; } + else if (gamemode == GAME_WRATH) + { + numcommands = sizeof(wrathbindnames) / sizeof(wrathbindnames[0]); + bindnames = wrathbindnames; + } else { numcommands = sizeof(quakebindnames) / sizeof(quakebindnames[0]); diff --git a/screen.h b/screen.h index 55b0e3f0a..268c55d64 100644 --- a/screen.h +++ b/screen.h @@ -26,6 +26,8 @@ void CL_Screen_Init (void); void CL_UpdateScreen (void); void SCR_CenterPrint(const char *str); +void SCR_SetLoadingSplash (const char *mapname); + void SCR_BeginLoadingPlaque (qboolean startup); void SCR_EndLoadingPlaque (void); @@ -38,6 +40,9 @@ void SCR_PushLoadingScreen (const char *msg, float len_in_parent); void SCR_PopLoadingScreen (qboolean redraw); void SCR_ClearLoadingScreen (qboolean redraw); +// returns true if the loading screen is waiting for a key press +qboolean SCR_LoadingScreenWaiting(void); + extern float scr_con_current; // current height of displayed console extern int sb_lines; @@ -64,6 +69,8 @@ extern cvar_t scr_conscroll3_y; extern cvar_t scr_conbrightness; extern cvar_t r_letterbox; +extern cvar_t scr_aspectname; + extern cvar_t scr_refresh; extern cvar_t scr_stipple; diff --git a/sv_main.c b/sv_main.c index 8ac96a112..7dceb7df6 100644 --- a/sv_main.c +++ b/sv_main.c @@ -3314,6 +3314,7 @@ void SV_SpawnServer (const char *server) if (cls.state != ca_dedicated) { + SCR_SetLoadingSplash(NULL); // clear splash SCR_BeginLoadingPlaque(false); S_StopAllSounds(); } diff --git a/vid_shared.c b/vid_shared.c index ec7eba115..11fd84030 100644 --- a/vid_shared.c +++ b/vid_shared.c @@ -1432,6 +1432,12 @@ static int VID_Mode(int fullscreen, int width, int height, int bpp, float refres in_windowmouse_y = vid_height.value / 2.f; } + // TODO: a more fine-grained calculation + if (((float)vid.mode.width / (float)vid.mode.height) > 1.501f) + Cvar_SetQuick(&scr_aspectname, "16-9"); + else + Cvar_SetQuick(&scr_aspectname, "4-3"); + return true; } else @@ -1465,6 +1471,7 @@ void VID_Restart_f(cmd_state_t *cmd) if (!vid_opened) { + SCR_SetLoadingSplash(NULL); SCR_BeginLoadingPlaque(false); return; } From a1c2de7f75e8eb39906bb11e357ad75e61c80cf5 Mon Sep 17 00:00:00 2001 From: Cloudwalk Date: Wed, 10 Jun 2020 11:56:38 -0400 Subject: [PATCH 21/23] Implement EXT_NODEGRAPH --- makefile.inc | 1 + nodegraph.c | 1238 ++++++++++++++++++++++++++++++++++++++++++++++++++ nodegraph.h | 50 ++ svvm_cmds.c | 424 ++++++++++++++++- 4 files changed, 1688 insertions(+), 25 deletions(-) create mode 100644 nodegraph.c create mode 100644 nodegraph.h diff --git a/makefile.inc b/makefile.inc index 6d9826fbb..2abd83d33 100644 --- a/makefile.inc +++ b/makefile.inc @@ -130,6 +130,7 @@ OBJ_COMMON= \ model_shared.o \ model_sprite.o \ netconn.o \ + nodegraph.o \ palette.o \ polygon.o \ portals.o \ diff --git a/nodegraph.c b/nodegraph.c new file mode 100644 index 000000000..ee8372aaa --- /dev/null +++ b/nodegraph.c @@ -0,0 +1,1238 @@ +#include "quakedef.h" +#include "nodegraph.h" + +// ============================================================================ +#define NODEGRAPH_NODES_COUNT_LIMIT 4096 +#define NODEGRAPH_QUERY_ENTRIES_LIMIT NODEGRAPH_NODES_COUNT_LIMIT +#define NODEGRAPH_QUERIES_COUNT_LIMIT 128 + +// ============================================================================ +#define NODEGRAPH_NODES_DATA_LENGTH (NODEGRAPH_NODES_COUNT_LIMIT * 3) +#define NODEGRAPH_LINKS_DATA_LENGTH (NODEGRAPH_NODES_COUNT_LIMIT * NODEGRAPH_NODES_COUNT_LIMIT / 8) + +// ============================================================================ +#define GRAPH_MATRIX_ELEMENT_INDEX(i, j) GRAPH_MATRIX_ELEMENT_INDEX_SIZED(i, j, NODEGRAPH_NODES_COUNT_LIMIT) +#define GRAPH_MATRIX_ELEMENT_INDEX_SIZED(i, j, size) (i * size + j) + +// ============================================================================ +typedef struct nodegraph_s +{ + vec_t nodes[NODEGRAPH_NODES_DATA_LENGTH]; + char links[NODEGRAPH_LINKS_DATA_LENGTH]; + int nodes_count; +} +nodegraph_t; + +typedef struct nodegraph_query_s +{ + short entries[NODEGRAPH_QUERY_ENTRIES_LIMIT]; + short graphid; + short entries_count; +} +nodegraph_query_t; + +typedef struct nodegraph_floyd_warshall_matrix_s +{ + short indexes[NODEGRAPH_NODES_COUNT_LIMIT * NODEGRAPH_NODES_COUNT_LIMIT]; +} +nodegraph_floyd_warshall_matrix_t; + +// ============================================================================ +typedef struct nodegraph_query_sort_data_s +{ + short queryid; + vec3_t point; +} +nodegraph_query_sort_data_t; + +// ============================================================================ +static nodegraph_t g_nodegraph_set[NODEGRAPH_GRAPHSET_SIZE_LIMIT]; +static nodegraph_query_t g_nodegraph_queries[NODEGRAPH_QUERIES_COUNT_LIMIT]; +static nodegraph_floyd_warshall_matrix_t g_nodegraph_floyd_warshall_matrices[NODEGRAPH_GRAPHSET_SIZE_LIMIT]; + +// ============================================================================ +static nodegraph_query_sort_data_t g_nodegraph_query_sort_data; + +// ============================================================================ +static int nodegraph_query_sort_function(const void *left, const void *right) +{ + const short queryid = g_nodegraph_query_sort_data.queryid; + + const short leftid = *(const short *)left; + const short rightid = *(const short *)right; + + vec3_t pointleft; + vec3_t pointright; + + float distanceleft; + float distanceright; + + nodegraph_query_t *query; + nodegraph_t *nodegraph; + + if (queryid < 0 || queryid >= NODEGRAPH_QUERIES_COUNT_LIMIT) + { + Con_DPrintf("%s, queryid is out of bounds: %d\n", __FUNCTION__, queryid); + return 0; + } + + query = &g_nodegraph_queries[queryid]; + nodegraph = &g_nodegraph_set[query->graphid]; + + if (leftid < 0 || leftid >= nodegraph->nodes_count) + { + Con_DPrintf("%s, leftid is out of bounds: %d\n", __FUNCTION__, leftid); + return 0; + } + + if (rightid < 0 || rightid >= nodegraph->nodes_count) + { + Con_DPrintf("%s, rightid is out of bounds: %d\n", __FUNCTION__, rightid); + return 0; + } + + nodegraph_graph_get_node(query->graphid, leftid, pointleft); + nodegraph_graph_get_node(query->graphid, rightid, pointright); + + distanceleft = VectorDistance(pointleft, g_nodegraph_query_sort_data.point); + distanceright = VectorDistance(pointright, g_nodegraph_query_sort_data.point); + + return distanceleft - distanceright; +} + +// ============================================================================ +static qboolean nodegraph_graph_queries_clear(short graphid) +{ + short i; + + if (graphid < 0 || graphid >= NODEGRAPH_GRAPHSET_SIZE_LIMIT) + { + Con_DPrintf("%s, graphid is out of bounds: %d\n", __FUNCTION__, graphid); + return false; + } + + for (i = 0; i < NODEGRAPH_QUERIES_COUNT_LIMIT; i++) + { + nodegraph_query_t *query = &g_nodegraph_queries[i]; + + if (query->graphid == graphid) + { + nodegraph_query_release(i); + } + } + + return true; +} + +// ============================================================================ +static qboolean nodegraph_graph_rebuild_floyd_warshall_matrices(void) +{ + short graphid, i, j, k; + + float *floyd_matrix_measures = (float *)Mem_Alloc(tempmempool, NODEGRAPH_NODES_COUNT_LIMIT * NODEGRAPH_NODES_COUNT_LIMIT * sizeof(float)); + + if (!floyd_matrix_measures) + { + return false; + } + + for (graphid = 0; graphid < NODEGRAPH_GRAPHSET_SIZE_LIMIT; graphid++) + { + nodegraph_t *nodegraph = &g_nodegraph_set[graphid]; + nodegraph_floyd_warshall_matrix_t *floyd_matrix = &g_nodegraph_floyd_warshall_matrices[graphid]; + + for (i = 0; i < nodegraph->nodes_count; i++) + { + for (j = 0; j < nodegraph->nodes_count; j++) + { + floyd_matrix_measures[GRAPH_MATRIX_ELEMENT_INDEX(i, j)] = 16777216.0f; + floyd_matrix->indexes[GRAPH_MATRIX_ELEMENT_INDEX(i, j)] = -1; + + if (nodegraph_graph_does_link_exist(graphid, i, j)) + { + vec3_t nodefrom; + vec3_t nodeto; + + float distance; + + nodegraph_graph_get_node(graphid, i, nodefrom); + nodegraph_graph_get_node(graphid, j, nodeto); + + distance = VectorDistance(nodefrom, nodeto); + + floyd_matrix_measures[GRAPH_MATRIX_ELEMENT_INDEX(i, j)] = distance; + floyd_matrix->indexes[GRAPH_MATRIX_ELEMENT_INDEX(i, j)] = j; + } + } + } + + for (i = 0; i < nodegraph->nodes_count; i++) + { + floyd_matrix_measures[GRAPH_MATRIX_ELEMENT_INDEX(i, i)] = 0.0f; + floyd_matrix->indexes[GRAPH_MATRIX_ELEMENT_INDEX(i, i)] = i; + } + + for (k = 0; k < nodegraph->nodes_count; k++) + { + for (i = 0; i < nodegraph->nodes_count; i++) + { + for (j = 0; j < nodegraph->nodes_count; j++) + { + float distance = floyd_matrix_measures[GRAPH_MATRIX_ELEMENT_INDEX(i, k)] + floyd_matrix_measures[GRAPH_MATRIX_ELEMENT_INDEX(k, j)]; + + if (floyd_matrix_measures[GRAPH_MATRIX_ELEMENT_INDEX(i, j)] > distance) + { + floyd_matrix_measures[GRAPH_MATRIX_ELEMENT_INDEX(i, j)] = distance; + floyd_matrix->indexes[GRAPH_MATRIX_ELEMENT_INDEX(i, j)] = floyd_matrix->indexes[GRAPH_MATRIX_ELEMENT_INDEX(i, k)]; + } + } + } + } + } + + Mem_Free(floyd_matrix_measures); + + return true; +} + +// ============================================================================ +qboolean nodegraph_graphset_clear(void) +{ + short i; + + for (i = 0; i < NODEGRAPH_GRAPHSET_SIZE_LIMIT; i++) + { + nodegraph_graph_clear(i); + } + + return true; +} + +// ============================================================================ +qboolean nodegraph_graphset_load(void) +{ + char vabuf[1024]; + char *graphset_data; + + qboolean nodegraph_graphset_has_been_loaded; + + nodegraph_graphset_has_been_loaded = (graphset_data = (char *)FS_LoadFile(va(vabuf, sizeof(vabuf), "%s.qng", sv.worldnamenoextension), tempmempool, true, NULL)) != NULL; + + if (nodegraph_graphset_has_been_loaded) + { + short graphid; + short graphset_nodes_count[NODEGRAPH_GRAPHSET_SIZE_LIMIT]; + + size_t offset, length; + + Con_Printf("Loaded %s.qng\n", sv.worldnamenoextension); + + nodegraph_graphset_clear(); + + offset = 0; + + length = sizeof(short) * NODEGRAPH_GRAPHSET_SIZE_LIMIT; + memcpy((void *)graphset_nodes_count, (const void *)(graphset_data + offset), length); + + offset += length; + + for (graphid = 0; graphid < NODEGRAPH_GRAPHSET_SIZE_LIMIT; graphid++) + { + nodegraph_t *nodegraph = &g_nodegraph_set[graphid]; + nodegraph->nodes_count = graphset_nodes_count[graphid]; + + if (nodegraph->nodes_count > 0) + { + short i, j; + char *nodegraph_links_sub_matrix; + + length = sizeof(float) * 3 * nodegraph->nodes_count; + memcpy((void *)nodegraph->nodes, (const void *)(graphset_data + offset), length); + + offset += length; + + nodegraph_links_sub_matrix = graphset_data + offset; + + for (i = 0; i < nodegraph->nodes_count; i++) + { + for (j = 0; j < nodegraph->nodes_count; j++) + { + int entryindex = GRAPH_MATRIX_ELEMENT_INDEX_SIZED(i, j, nodegraph->nodes_count); + qboolean does_link_exist = ((nodegraph_links_sub_matrix[entryindex / 8] & (1 << (entryindex % 8))) != 0); + + if (does_link_exist) + { + nodegraph_graph_add_link(graphid, i, j); + } + } + } + + length = (nodegraph->nodes_count * nodegraph->nodes_count - 1) / 8 + 1; + offset += length; + } + } + + for (graphid = 0; graphid < NODEGRAPH_GRAPHSET_SIZE_LIMIT; graphid++) + { + nodegraph_t *nodegraph = &g_nodegraph_set[graphid]; + nodegraph_floyd_warshall_matrix_t *floyd_matrix = &g_nodegraph_floyd_warshall_matrices[graphid]; + + short i, j; + short *floyd_sub_matrix_indexes = (short *)(graphset_data + offset); + + for (i = 0; i < nodegraph->nodes_count; i++) + { + for (j = 0; j < nodegraph->nodes_count; j++) + { + floyd_matrix->indexes[GRAPH_MATRIX_ELEMENT_INDEX(i, j)] = floyd_sub_matrix_indexes[GRAPH_MATRIX_ELEMENT_INDEX_SIZED(i, j, nodegraph->nodes_count)]; + } + } + + offset += sizeof(short) * nodegraph->nodes_count * nodegraph->nodes_count; + } + + Mem_Free(graphset_data); + + return true; + } + + return false; +} + +// ============================================================================ +qboolean nodegraph_graphset_save(void) +{ + char vabuf[1024]; + + char *graphset_data; + size_t graphset_data_size; + + qboolean nodegraph_graphset_has_been_saved; + + short graphid; + short graphset_nodes_count[NODEGRAPH_GRAPHSET_SIZE_LIMIT]; + + size_t offset, length; + + nodegraph_graph_rebuild_floyd_warshall_matrices(); + + graphset_data_size = sizeof(short) * NODEGRAPH_GRAPHSET_SIZE_LIMIT + sizeof(g_nodegraph_set) + sizeof(g_nodegraph_floyd_warshall_matrices); + graphset_data = (char *)Mem_Alloc(tempmempool, graphset_data_size); + + if (!graphset_data) + { + return false; + } + + memset((void *)graphset_data, 0, graphset_data_size); + + for (graphid = 0; graphid < NODEGRAPH_GRAPHSET_SIZE_LIMIT; graphid++) + { + nodegraph_t *nodegraph = &g_nodegraph_set[graphid]; + graphset_nodes_count[graphid] = nodegraph->nodes_count; + } + + offset = 0; + + length = sizeof(short) * NODEGRAPH_GRAPHSET_SIZE_LIMIT; + memcpy((void *)(graphset_data + offset), (const void *)graphset_nodes_count, length); + + offset += length; + + for (graphid = 0; graphid < NODEGRAPH_GRAPHSET_SIZE_LIMIT; graphid++) + { + nodegraph_t *nodegraph = &g_nodegraph_set[graphid]; + + if (nodegraph->nodes_count > 0) + { + short i, j; + char *nodegraph_links_sub_matrix; + + length = sizeof(float) * 3 * nodegraph->nodes_count; + memcpy((void *)(graphset_data + offset), (const void *)nodegraph->nodes, length); + + offset += length; + + nodegraph_links_sub_matrix = graphset_data + offset; + + for (i = 0; i < nodegraph->nodes_count; i++) + { + for (j = 0; j < nodegraph->nodes_count; j++) + { + if (nodegraph_graph_does_link_exist(graphid, i, j)) + { + int entryindex = GRAPH_MATRIX_ELEMENT_INDEX_SIZED(i, j, nodegraph->nodes_count); + nodegraph_links_sub_matrix[entryindex / 8] |= 1 << (entryindex % 8); + } + } + } + + length = (nodegraph->nodes_count * nodegraph->nodes_count - 1) / 8 + 1; + offset += length; + } + } + + for (graphid = 0; graphid < NODEGRAPH_GRAPHSET_SIZE_LIMIT; graphid++) + { + nodegraph_t *nodegraph = &g_nodegraph_set[graphid]; + nodegraph_floyd_warshall_matrix_t *floyd_matrix = &g_nodegraph_floyd_warshall_matrices[graphid]; + + short i, j; + short *floyd_sub_matrix_indexes = (short *)(graphset_data + offset); + + for (i = 0; i < nodegraph->nodes_count; i++) + { + for (j = 0; j < nodegraph->nodes_count; j++) + { + floyd_sub_matrix_indexes[GRAPH_MATRIX_ELEMENT_INDEX_SIZED(i, j, nodegraph->nodes_count)] = floyd_matrix->indexes[GRAPH_MATRIX_ELEMENT_INDEX(i, j)]; + } + } + + offset += sizeof(short) * nodegraph->nodes_count * nodegraph->nodes_count; + } + + graphset_data_size = offset; + + nodegraph_graphset_has_been_saved = FS_WriteFile(va(vabuf, sizeof(vabuf), "%s.qng", sv.worldnamenoextension), (const void *)graphset_data, (fs_offset_t)graphset_data_size); + + Mem_Free(graphset_data); + + if (nodegraph_graphset_has_been_saved) + { + Con_Printf("Saved %s.qng\n", sv.worldnamenoextension); + } + + return nodegraph_graphset_has_been_saved; +} + +// ============================================================================ +qboolean nodegraph_graph_clear(short graphid) +{ + nodegraph_t *nodegraph; + nodegraph_floyd_warshall_matrix_t *floyd_matrix; + + if (graphid < 0 || graphid >= NODEGRAPH_GRAPHSET_SIZE_LIMIT) + { + Con_DPrintf("%s, graphid is out of bounds: %d\n", __FUNCTION__, graphid); + return false; + } + + nodegraph = &g_nodegraph_set[graphid]; + memset((void *)nodegraph, 0, sizeof(nodegraph_t)); + + nodegraph_graph_queries_clear(graphid); + + floyd_matrix = &g_nodegraph_floyd_warshall_matrices[graphid]; + memset((void *)floyd_matrix, 0, sizeof(nodegraph_floyd_warshall_matrix_t)); + + return true; +} + +// ============================================================================ +short nodegraph_graph_nodes_count(short graphid) +{ + nodegraph_t *nodegraph; + + if (graphid < 0 || graphid >= NODEGRAPH_GRAPHSET_SIZE_LIMIT) + { + Con_DPrintf("%s, graphid is out of bounds: %d\n", __FUNCTION__, graphid); + return -1; + } + + nodegraph = &g_nodegraph_set[graphid]; + + return nodegraph->nodes_count; +} + +// ============================================================================ +qboolean nodegraph_graph_add_node(short graphid, const vec3_t node) +{ + nodegraph_t *nodegraph; + + if (graphid < 0 || graphid >= NODEGRAPH_GRAPHSET_SIZE_LIMIT) + { + Con_DPrintf("%s, graphid is out of bounds: %d\n", __FUNCTION__, graphid); + return false; + } + + nodegraph = &g_nodegraph_set[graphid]; + + if (nodegraph->nodes_count >= NODEGRAPH_NODES_COUNT_LIMIT) + { + Con_DPrintf("%s, the number of nodes exceeds the limit: %d\n", __FUNCTION__, NODEGRAPH_NODES_COUNT_LIMIT); + return false; + } + + VectorCopy(node, &nodegraph->nodes[nodegraph->nodes_count * 3]); + nodegraph->nodes_count++; + + nodegraph_graph_queries_clear(graphid); + + return true; +} + +// ============================================================================ +qboolean nodegraph_graph_remove_node(short graphid, short nodeid) +{ + nodegraph_t *nodegraph; + + short i, j; + + if (graphid < 0 || graphid >= NODEGRAPH_GRAPHSET_SIZE_LIMIT) + { + Con_DPrintf("%s, graphid is out of bounds: %d\n", __FUNCTION__, graphid); + return false; + } + + nodegraph = &g_nodegraph_set[graphid]; + + if (nodeid < 0 || nodeid >= nodegraph->nodes_count) + { + Con_DPrintf("%s, nodeid is out of bounds: %d\n", __FUNCTION__, nodeid); + return false; + } + + for (i = nodeid; i < nodegraph->nodes_count - 1; i++) + { + VectorCopy(&nodegraph->nodes[(i + 1) * 3], &nodegraph->nodes[i * 3]); + + for (j = 0; j < nodegraph->nodes_count; j++) + { + nodegraph_graph_does_link_exist(graphid, i + 1, j) ? nodegraph_graph_add_link(graphid, i, j) : nodegraph_graph_remove_link(graphid, i, j); + nodegraph_graph_does_link_exist(graphid, j, i + 1) ? nodegraph_graph_add_link(graphid, j, i) : nodegraph_graph_remove_link(graphid, j, i); + } + } + + VectorSet(&nodegraph->nodes[(nodegraph->nodes_count - 1) * 3], 0.0f, 0.0f, 0.0f); + nodegraph->nodes_count--; + + nodegraph_graph_queries_clear(graphid); + + return true; +} + +// ============================================================================ +qboolean nodegraph_graph_is_node_valid(short graphid, short nodeid) +{ + nodegraph_t *nodegraph; + + if (graphid < 0 || graphid >= NODEGRAPH_GRAPHSET_SIZE_LIMIT) + { + Con_DPrintf("%s, graphid is out of bounds: %d\n", __FUNCTION__, graphid); + return false; + } + + nodegraph = &g_nodegraph_set[graphid]; + + if (nodeid < 0 || nodeid >= nodegraph->nodes_count) + { + return false; + } + + return true; +} + +// ============================================================================ +qboolean nodegraph_graph_get_node(short graphid, short nodeid, vec3_t outnode) +{ + nodegraph_t *nodegraph; + + VectorSet(outnode, NAN, NAN, NAN); + + if (graphid < 0 || graphid >= NODEGRAPH_GRAPHSET_SIZE_LIMIT) + { + Con_DPrintf("%s, graphid is out of bounds: %d\n", __FUNCTION__, graphid); + return false; + } + + nodegraph = &g_nodegraph_set[graphid]; + + if (nodeid < 0 || nodeid >= nodegraph->nodes_count) + { + Con_DPrintf("%s, nodeid is out of bounds: %d\n", __FUNCTION__, nodeid); + return false; + } + + VectorCopy(&nodegraph->nodes[nodeid * 3], outnode); + + return true; +} + +// ============================================================================ +qboolean nodegraph_graph_add_link(short graphid, short nodeidfrom, short nodeidto) +{ + nodegraph_t *nodegraph; + + int entryindex; + + if (graphid < 0 || graphid >= NODEGRAPH_GRAPHSET_SIZE_LIMIT) + { + Con_DPrintf("%s, graphid is out of bounds: %d\n", __FUNCTION__, graphid); + return false; + } + + nodegraph = &g_nodegraph_set[graphid]; + + if (nodeidfrom < 0 || nodeidfrom >= nodegraph->nodes_count) + { + Con_DPrintf("%s, nodeidfrom is out of bounds: %d\n", __FUNCTION__, nodeidfrom); + return false; + } + + if (nodeidto < 0 || nodeidto >= nodegraph->nodes_count) + { + Con_DPrintf("%s, nodeidto is out of bounds: %d\n", __FUNCTION__, nodeidto); + return false; + } + + entryindex = GRAPH_MATRIX_ELEMENT_INDEX(nodeidfrom, nodeidto); + nodegraph->links[entryindex / 8] |= 1 << (entryindex % 8); + + return true; +} + +// ============================================================================ +qboolean nodegraph_graph_remove_link(short graphid, short nodeidfrom, short nodeidto) +{ + nodegraph_t *nodegraph; + + int entryindex; + + if (graphid < 0 || graphid >= NODEGRAPH_GRAPHSET_SIZE_LIMIT) + { + Con_DPrintf("%s, graphid is out of bounds: %d\n", __FUNCTION__, graphid); + return false; + } + + nodegraph = &g_nodegraph_set[graphid]; + + if (nodeidfrom < 0 || nodeidfrom >= nodegraph->nodes_count) + { + Con_DPrintf("%s, nodeidfrom is out of bounds: %d\n", __FUNCTION__, nodeidfrom); + return false; + } + + if (nodeidto < 0 || nodeidto >= nodegraph->nodes_count) + { + Con_DPrintf("%s, nodeidto is out of bounds: %d\n", __FUNCTION__, nodeidto); + return false; + } + + entryindex = GRAPH_MATRIX_ELEMENT_INDEX(nodeidfrom, nodeidto); + nodegraph->links[entryindex / 8] &= ~(1 << (entryindex % 8)); + + return true; +} + +// ============================================================================ +qboolean nodegraph_graph_does_link_exist(short graphid, short nodeidfrom, short nodeidto) +{ + nodegraph_t *nodegraph; + + int entryindex; + + if (graphid < 0 || graphid >= NODEGRAPH_GRAPHSET_SIZE_LIMIT) + { + Con_DPrintf("%s, graphid is out of bounds: %d\n", __FUNCTION__, graphid); + return false; + } + + nodegraph = &g_nodegraph_set[graphid]; + + if (nodeidfrom < 0 || nodeidfrom >= nodegraph->nodes_count) + { + Con_DPrintf("%s, nodeidfrom is out of bounds: %d\n", __FUNCTION__, nodeidfrom); + return false; + } + + if (nodeidto < 0 || nodeidto >= nodegraph->nodes_count) + { + Con_DPrintf("%s, nodeidto is out of bounds: %d\n", __FUNCTION__, nodeidto); + return false; + } + + entryindex = GRAPH_MATRIX_ELEMENT_INDEX(nodeidfrom, nodeidto); + + return ((nodegraph->links[entryindex / 8] & (1 << (entryindex % 8))) != 0); +} + +// ============================================================================ +short nodegraph_graph_find_nearest_nodeid(short graphid, const vec3_t position) +{ + nodegraph_t *nodegraph; + + short i, nodeid; + float distance, shortestdistance; + + if (graphid < 0 || graphid >= NODEGRAPH_GRAPHSET_SIZE_LIMIT) + { + Con_DPrintf("%s, graphid is out of bounds: %d\n", __FUNCTION__, graphid); + return -1; + } + + nodegraph = &g_nodegraph_set[graphid]; + + nodeid = -1; + shortestdistance = 16777216.0f; + + for (i = 0; i < nodegraph->nodes_count; i++) + { + distance = VectorDistance(&nodegraph->nodes[i * 3], position); + + if (shortestdistance > distance) + { + nodeid = i; + shortestdistance = distance; + } + } + + return nodeid; +} + +// ============================================================================ +short nodegraph_graph_query_path(short graphid, short nodeidfrom, short nodeidto) +{ + nodegraph_t *nodegraph; + nodegraph_floyd_warshall_matrix_t *floyd_matrix; + + short i, queryid; + nodegraph_query_t *query; + + if (graphid < 0 || graphid >= NODEGRAPH_GRAPHSET_SIZE_LIMIT) + { + Con_DPrintf("%s, graphid is out of bounds: %d\n", __FUNCTION__, graphid); + return -1; + } + + nodegraph = &g_nodegraph_set[graphid]; + floyd_matrix = &g_nodegraph_floyd_warshall_matrices[graphid]; + + if (nodeidfrom < 0 || nodeidfrom >= nodegraph->nodes_count) + { + Con_DPrintf("%s, nodeidfrom is out of bounds: %d\n", __FUNCTION__, nodeidfrom); + return -1; + } + + if (nodeidto < 0 || nodeidto >= nodegraph->nodes_count) + { + Con_DPrintf("%s, nodeidto is out of bounds: %d\n", __FUNCTION__, nodeidto); + return -1; + } + + queryid = -1; + + for (i = 0; i < NODEGRAPH_QUERIES_COUNT_LIMIT; i++) + { + if (!nodegraph_query_is_valid(i)) + { + queryid = i; + break; + } + } + + if (queryid != -1) + { + query = &g_nodegraph_queries[queryid]; + + query->graphid = graphid; + + if (floyd_matrix->indexes[GRAPH_MATRIX_ELEMENT_INDEX(nodeidfrom, nodeidto)] != -1) + { + query->entries[query->entries_count] = nodeidfrom; + query->entries_count++; + + while (nodeidfrom != nodeidto) + { + nodeidfrom = floyd_matrix->indexes[GRAPH_MATRIX_ELEMENT_INDEX(nodeidfrom, nodeidto)]; + + query->entries[query->entries_count] = nodeidfrom; + query->entries_count++; + + if (query->entries_count >= NODEGRAPH_QUERY_ENTRIES_LIMIT) + { + break; + } + } + } + + if (query->entries_count == 0) + { + nodegraph_query_release(queryid); + queryid = -1; + } + } + + return queryid; +} + +// ============================================================================ +short nodegraph_graph_query_nodes_linked(short graphid, short nodeid) +{ + nodegraph_t *nodegraph; + + short i, queryid; + nodegraph_query_t *query; + + if (graphid < 0 || graphid >= NODEGRAPH_GRAPHSET_SIZE_LIMIT) + { + Con_DPrintf("%s, graphid is out of bounds: %d\n", __FUNCTION__, graphid); + return -1; + } + + nodegraph = &g_nodegraph_set[graphid]; + + if (nodeid < 0 || nodeid >= nodegraph->nodes_count) + { + Con_DPrintf("%s, nodeid is out of bounds: %d\n", __FUNCTION__, nodeid); + return -1; + } + + queryid = -1; + + for (i = 0; i < NODEGRAPH_QUERIES_COUNT_LIMIT; i++) + { + if (!nodegraph_query_is_valid(i)) + { + queryid = i; + break; + } + } + + if (queryid != -1) + { + query = &g_nodegraph_queries[queryid]; + + query->graphid = graphid; + + for (i = 0; i < nodegraph->nodes_count; i++) + { + if (nodegraph_graph_does_link_exist(graphid, nodeid, i)) + { + query->entries[query->entries_count] = i; + query->entries_count++; + } + + if (query->entries_count >= NODEGRAPH_QUERY_ENTRIES_LIMIT) + { + break; + } + } + + if (query->entries_count == 0) + { + nodegraph_query_release(queryid); + queryid = -1; + } + else + { + g_nodegraph_query_sort_data.queryid = queryid; + nodegraph_graph_get_node(graphid, nodeid, g_nodegraph_query_sort_data.point); + + qsort(query->entries, query->entries_count, sizeof(short), nodegraph_query_sort_function); + } + + } + + return queryid; +} + +// ============================================================================ +short nodegraph_graph_query_nodes_in_radius(short graphid, const vec3_t position, float radius) +{ + nodegraph_t *nodegraph; + + vec3_t node; + short i, queryid; + nodegraph_query_t *query; + + if (graphid < 0 || graphid >= NODEGRAPH_GRAPHSET_SIZE_LIMIT) + { + Con_DPrintf("%s, graphid is out of bounds: %d\n", __FUNCTION__, graphid); + return -1; + } + + nodegraph = &g_nodegraph_set[graphid]; + + queryid = -1; + + for (i = 0; i < NODEGRAPH_QUERIES_COUNT_LIMIT; i++) + { + if (!nodegraph_query_is_valid(i)) + { + queryid = i; + break; + } + } + + if (queryid != -1) + { + query = &g_nodegraph_queries[queryid]; + + query->graphid = graphid; + + for (i = 0; i < nodegraph->nodes_count; i++) + { + nodegraph_graph_get_node(graphid, i, node); + + if (VectorDistance(position, node) <= radius) + { + query->entries[query->entries_count] = i; + query->entries_count++; + } + + if (query->entries_count >= NODEGRAPH_QUERY_ENTRIES_LIMIT) + { + break; + } + } + + if (query->entries_count == 0) + { + nodegraph_query_release(queryid); + queryid = -1; + } + else + { + g_nodegraph_query_sort_data.queryid = queryid; + VectorCopy(position, g_nodegraph_query_sort_data.point); + + qsort(query->entries, query->entries_count, sizeof(short), nodegraph_query_sort_function); + } + } + + return queryid; +} + +// ============================================================================ +qboolean nodegraph_query_release(short queryid) +{ + nodegraph_query_t *query; + + if (queryid < 0 || queryid >= NODEGRAPH_QUERIES_COUNT_LIMIT) + { + Con_DPrintf("%s, queryid is out of bounds: %d\n", __FUNCTION__, queryid); + return false; + } + + query = &g_nodegraph_queries[queryid]; + memset((void *)query, 0, sizeof(nodegraph_query_t)); + + return true; +} + +// ============================================================================ +short nodegraph_query_entries_count(short queryid) +{ + nodegraph_query_t *query; + + if (queryid < 0 || queryid >= NODEGRAPH_QUERIES_COUNT_LIMIT) + { + Con_DPrintf("%s, queryid is out of bounds: %d\n", __FUNCTION__, queryid); + return -1; + } + + query = &g_nodegraph_queries[queryid]; + + return query->entries_count; +} + +// ============================================================================ +qboolean nodegraph_query_is_valid(short queryid) +{ + nodegraph_query_t *query; + + if (queryid < 0 || queryid >= NODEGRAPH_QUERIES_COUNT_LIMIT) + { + return false; + } + + query = &g_nodegraph_queries[queryid]; + + return query->entries_count > 0; +} + +// ============================================================================ +short nodegraph_query_get_graphid(short queryid) +{ + nodegraph_query_t *query; + + if (queryid < 0 || queryid >= NODEGRAPH_QUERIES_COUNT_LIMIT) + { + Con_DPrintf("%s, queryid is out of bounds: %d\n", __FUNCTION__, queryid); + return -1; + } + + query = &g_nodegraph_queries[queryid]; + + return query->graphid; +} + +// ============================================================================ +short nodegraph_query_get_nodeid(short queryid, short entryid) +{ + nodegraph_query_t *query; + + if (queryid < 0 || queryid >= NODEGRAPH_QUERIES_COUNT_LIMIT) + { + Con_DPrintf("%s, queryid is out of bounds: %d\n", __FUNCTION__, queryid); + return -1; + } + + query = &g_nodegraph_queries[queryid]; + + if (entryid < 0 || entryid >= query->entries_count) + { + Con_DPrintf("%s, entryid is out of bounds: %d\n", __FUNCTION__, entryid); + return -1; + } + + if (query->graphid < 0 || query->graphid >= NODEGRAPH_GRAPHSET_SIZE_LIMIT) + { + Con_DPrintf("%s, graphid is out of bounds: %d\n", __FUNCTION__, query->graphid); + return -1; + } + + return query->entries[entryid]; +} + +// ============================================================================ +qboolean nodegraph_moveprobe_fly(const vec3_t nodefrom, const vec3_t nodeto, const vec3_t mins, const vec3_t maxs, short type) +{ + int contents = SUPERCONTENTS_SOLID | SUPERCONTENTS_MONSTERCLIP | SUPERCONTENTS_BOTCLIP; + + vec3_t from, to; + trace_t trace; + + qboolean connected; + + if (type == NODEGRAPH_MOVEPROBE_TYPE_FLY_AIR || type == NODEGRAPH_MOVEPROBE_TYPE_FLY_WATER) + { + contents |= SUPERCONTENTS_LIQUIDSMASK; + } + + VectorCopy(nodefrom, from); + from[2] -= mins[2]; + + VectorCopy(nodeto, to); + to[2] -= mins[2]; + + trace = SV_TraceBox(from, mins, maxs, to, MOVE_NOMONSTERS, NULL, contents, 0, 0, 0.0f); + + connected = trace.fraction == 1.0; + + if (type == NODEGRAPH_MOVEPROBE_TYPE_FLY_AIR) + { + connected = connected && (SV_PointSuperContents(from) & (SUPERCONTENTS_LIQUIDSMASK)) == 0; + connected = connected && (SV_PointSuperContents(to) & (SUPERCONTENTS_LIQUIDSMASK)) == 0; + } + + if (type == NODEGRAPH_MOVEPROBE_TYPE_FLY_WATER) + { + connected = connected && (SV_PointSuperContents(from) & (SUPERCONTENTS_LIQUIDSMASK)) != 0; + connected = connected && (SV_PointSuperContents(to) & (SUPERCONTENTS_LIQUIDSMASK)) != 0; + } + + return connected; +} + +// ============================================================================ +qboolean nodegraph_moveprobe_walk(const vec3_t nodefrom, const vec3_t nodeto, const vec3_t mins, const vec3_t maxs, float stepheight, float dropheight) +{ + int contents = SUPERCONTENTS_SOLID | SUPERCONTENTS_MONSTERCLIP | SUPERCONTENTS_BOTCLIP; + + float distance, walked; + float tracestep = max(1.0f, min(maxs[0] - mins[0], maxs[1] - mins[1]) / 2.0f); + + vec3_t from, to, direction, destination; + + qboolean connected = false; + + VectorSubtract(nodeto, nodefrom, direction); + distance = VectorLength(direction); + + if (distance <= 0.015625f) + { + return true; + } + + direction[2] = 0.0f; + VectorNormalize(direction); + + VectorCopy(nodefrom, from); + from[2] -= mins[2]; + + VectorCopy(nodeto, destination); + destination[2] -= mins[2]; + + walked = 0.0f; + + while (walked <= distance) + { + trace_t trace; + + VectorMA(from, tracestep, direction, from); + from[2] += stepheight; + + VectorCopy(from, to); + to[2] -= stepheight + dropheight + 0.5f; + + trace = SV_TraceBox(from, mins, maxs, to, MOVE_NOMONSTERS, NULL, contents, 0, 0, 0.0f); + + if (trace.startsolid || trace.fraction == 1.0) + { + break; + } + + if (VectorDistance(trace.endpos, destination) <= tracestep) + { + connected = true; + break; + } + + VectorCopy(trace.endpos, from); + + walked += tracestep; + } + + return connected; +} + +// ============================================================================ +short nodegraph_graph_query_nodes_in_radius_fly_reachable(short graphid, const vec3_t position, float radius, const vec3_t mins, const vec3_t maxs, short type) +{ + nodegraph_t *nodegraph; + + vec3_t node; + short i, queryid; + nodegraph_query_t *query; + + if (graphid < 0 || graphid >= NODEGRAPH_GRAPHSET_SIZE_LIMIT) + { + Con_DPrintf("%s, graphid is out of bounds: %d\n", __FUNCTION__, graphid); + return -1; + } + + nodegraph = &g_nodegraph_set[graphid]; + + queryid = -1; + + for (i = 0; i < NODEGRAPH_QUERIES_COUNT_LIMIT; i++) + { + if (!nodegraph_query_is_valid(i)) + { + queryid = i; + break; + } + } + + if (queryid != -1) + { + query = &g_nodegraph_queries[queryid]; + + query->graphid = graphid; + + for (i = 0; i < nodegraph->nodes_count; i++) + { + nodegraph_graph_get_node(graphid, i, node); + + if (VectorDistance(position, node) <= radius) + { + if (nodegraph_moveprobe_fly(position, node, mins, maxs, type)) + { + query->entries[query->entries_count] = i; + query->entries_count++; + } + } + + if (query->entries_count >= NODEGRAPH_QUERY_ENTRIES_LIMIT) + { + break; + } + } + + if (query->entries_count == 0) + { + nodegraph_query_release(queryid); + queryid = -1; + } + else + { + g_nodegraph_query_sort_data.queryid = queryid; + VectorCopy(position, g_nodegraph_query_sort_data.point); + + qsort(query->entries, query->entries_count, sizeof(short), nodegraph_query_sort_function); + } + } + + return queryid; +} + +// ============================================================================ +short nodegraph_graph_query_nodes_in_radius_walk_reachable(short graphid, const vec3_t position, float radius, const vec3_t mins, const vec3_t maxs, float stepheight, float dropheight) +{ + nodegraph_t *nodegraph; + + vec3_t node; + short i, queryid; + nodegraph_query_t *query; + + if (graphid < 0 || graphid >= NODEGRAPH_GRAPHSET_SIZE_LIMIT) + { + Con_DPrintf("%s, graphid is out of bounds: %d\n", __FUNCTION__, graphid); + return -1; + } + + nodegraph = &g_nodegraph_set[graphid]; + + queryid = -1; + + for (i = 0; i < NODEGRAPH_QUERIES_COUNT_LIMIT; i++) + { + if (!nodegraph_query_is_valid(i)) + { + queryid = i; + break; + } + } + + if (queryid != -1) + { + query = &g_nodegraph_queries[queryid]; + + query->graphid = graphid; + + for (i = 0; i < nodegraph->nodes_count; i++) + { + nodegraph_graph_get_node(graphid, i, node); + + if (VectorDistance(position, node) <= radius) + { + if (nodegraph_moveprobe_walk(position, node, mins, maxs, stepheight, dropheight)) + { + query->entries[query->entries_count] = i; + query->entries_count++; + } + } + + if (query->entries_count >= NODEGRAPH_QUERY_ENTRIES_LIMIT) + { + break; + } + } + + if (query->entries_count == 0) + { + nodegraph_query_release(queryid); + queryid = -1; + } + else + { + g_nodegraph_query_sort_data.queryid = queryid; + VectorCopy(position, g_nodegraph_query_sort_data.point); + + qsort(query->entries, query->entries_count, sizeof(short), nodegraph_query_sort_function); + } + } + + return queryid; +} diff --git a/nodegraph.h b/nodegraph.h new file mode 100644 index 000000000..818634a4e --- /dev/null +++ b/nodegraph.h @@ -0,0 +1,50 @@ +#ifndef NODEGRAPH_H +#define NODEGRAPH_H + +// ============================================================================ +#define NODEGRAPH_GRAPHSET_SIZE_LIMIT 8 + +// ============================================================================ +#define NODEGRAPH_MOVEPROBE_TYPE_FLY_WHATEVER 0 +#define NODEGRAPH_MOVEPROBE_TYPE_FLY_AIR 1 +#define NODEGRAPH_MOVEPROBE_TYPE_FLY_WATER 2 + +// ============================================================================ +qboolean nodegraph_graphset_clear(void); + +qboolean nodegraph_graphset_load(void); +qboolean nodegraph_graphset_save(void); + +qboolean nodegraph_graph_clear(short graphid); + +short nodegraph_graph_nodes_count(short graphid); + +qboolean nodegraph_graph_add_node(short graphid, const vec3_t node); +qboolean nodegraph_graph_remove_node(short graphid, short nodeid); +qboolean nodegraph_graph_is_node_valid(short graphid, short nodeid); + +qboolean nodegraph_graph_get_node(short graphid, short nodeid, vec3_t outnode); + +qboolean nodegraph_graph_add_link(short graphid, short nodeidfrom, short nodeidto); +qboolean nodegraph_graph_remove_link(short graphid, short nodeidfrom, short nodeidto); +qboolean nodegraph_graph_does_link_exist(short graphid, short nodeidfrom, short nodeidto); + +short nodegraph_graph_find_nearest_nodeid(short graphid, const vec3_t position); + +short nodegraph_graph_query_path(short graphid, short nodeidfrom, short nodeidto); +short nodegraph_graph_query_nodes_linked(short graphid, short nodeid); +short nodegraph_graph_query_nodes_in_radius(short graphid, const vec3_t position, float radius); + +qboolean nodegraph_query_release(short queryid); +short nodegraph_query_entries_count(short queryid); +qboolean nodegraph_query_is_valid(short queryid); +short nodegraph_query_get_graphid(short queryid); +short nodegraph_query_get_nodeid(short queryid, short entryid); + +qboolean nodegraph_moveprobe_fly(const vec3_t nodefrom, const vec3_t nodeto, const vec3_t mins, const vec3_t maxs, short type); +qboolean nodegraph_moveprobe_walk(const vec3_t nodefrom, const vec3_t nodeto, const vec3_t mins, const vec3_t maxs, float stepheight, float dropheight); + +short nodegraph_graph_query_nodes_in_radius_fly_reachable(short graphid, const vec3_t position, float radius, const vec3_t mins, const vec3_t maxs, short type); +short nodegraph_graph_query_nodes_in_radius_walk_reachable(short graphid, const vec3_t position, float radius, const vec3_t mins, const vec3_t maxs, float stepheight, float dropheight); + +#endif // NODEGRAPH_H diff --git a/svvm_cmds.c b/svvm_cmds.c index 2ae68c1ab..011bfcdfa 100644 --- a/svvm_cmds.c +++ b/svvm_cmds.c @@ -2,6 +2,7 @@ #include "prvm_cmds.h" #include "jpeg.h" +#include "nodegraph.h" //============================================================================ // Server @@ -228,6 +229,7 @@ const char *vm_sv_extensions = "TW_SV_STEPCONTROL " "ZQ_PAUSE " "EXT_WRATH " +"EXT_NODEGRAPH " "DP_RM_CLIPGROUP " //"EXT_CSQC " // not ready yet ; @@ -3258,7 +3260,379 @@ static void VM_SV_frameduration(prvm_prog_t *prog) PRVM_G_FLOAT(OFS_RETURN) = model->animscenes[framenum].framecount / model->animscenes[framenum].framerate; } +// #700 float() nodegraph_graphset_clear (EXT_NODEGRAPH) +static void VM_nodegraph_graphset_clear(prvm_prog_t *prog) +{ + VM_SAFEPARMCOUNT(0, VM_nodegraph_graphset_clear); + + PRVM_G_FLOAT(OFS_RETURN) = (float)nodegraph_graphset_clear(); +} + +// #701 float() nodegraph_graphset_load (EXT_NODEGRAPH) +static void VM_nodegraph_graphset_load(prvm_prog_t *prog) +{ + VM_SAFEPARMCOUNT(0, VM_nodegraph_graphset_load); + + PRVM_G_FLOAT(OFS_RETURN) = (float)nodegraph_graphset_load(); +} + +// #702 float() nodegraph_graphset_save (EXT_NODEGRAPH) +static void VM_nodegraph_graphset_save(prvm_prog_t *prog) +{ + VM_SAFEPARMCOUNT(0, VM_nodegraph_graphset_save); + + PRVM_G_FLOAT(OFS_RETURN) = (float)nodegraph_graphset_save(); +} + +// #703 float(float graphid) nodegraph_graph_clear (EXT_NODEGRAPH) +static void VM_nodegraph_graph_clear(prvm_prog_t *prog) +{ + short graphid; + + VM_SAFEPARMCOUNT(1, VM_nodegraph_graph_clear); + + graphid = (short)PRVM_G_FLOAT(OFS_PARM0); + + PRVM_G_FLOAT(OFS_RETURN) = (float)nodegraph_graph_clear(graphid); +} + +// #704 float(float graphid) nodegraph_graph_nodes_count (EXT_NODEGRAPH) +static void VM_nodegraph_graph_nodes_count(prvm_prog_t *prog) +{ + short graphid; + + VM_SAFEPARMCOUNT(1, VM_nodegraph_graph_nodes_count); + + graphid = (short)PRVM_G_FLOAT(OFS_PARM0); + + PRVM_G_FLOAT(OFS_RETURN) = (float)nodegraph_graph_nodes_count(graphid); +} + +// #705 float(float graphid, vector node) nodegraph_graph_add_node (EXT_NODEGRAPH) +static void VM_nodegraph_graph_add_node(prvm_prog_t *prog) +{ + short graphid; + vec3_t node; + + VM_SAFEPARMCOUNT(2, VM_nodegraph_graph_add_node); + + graphid = (short)PRVM_G_FLOAT(OFS_PARM0); + VectorCopy(PRVM_G_VECTOR(OFS_PARM1), node); + + PRVM_G_FLOAT(OFS_RETURN) = (float)nodegraph_graph_add_node(graphid, node); +} + +// #706 float(float graphid, float nodeid) nodegraph_graph_remove_node (EXT_NODEGRAPH) +static void VM_nodegraph_graph_remove_node(prvm_prog_t *prog) +{ + short graphid; + short nodeid; + + VM_SAFEPARMCOUNT(2, VM_nodegraph_graph_remove_node); + + graphid = (short)PRVM_G_FLOAT(OFS_PARM0); + nodeid = (short)PRVM_G_FLOAT(OFS_PARM1); + + PRVM_G_FLOAT(OFS_RETURN) = (float)nodegraph_graph_remove_node(graphid, nodeid); +} + +// #707 float(float graphid, float nodeid) nodegraph_graph_is_node_valid (EXT_NODEGRAPH) +static void VM_nodegraph_graph_is_node_valid(prvm_prog_t *prog) +{ + short graphid; + short nodeid; + + VM_SAFEPARMCOUNT(2, VM_nodegraph_graph_is_node_valid); + + graphid = (short)PRVM_G_FLOAT(OFS_PARM0); + nodeid = (short)PRVM_G_FLOAT(OFS_PARM1); + + PRVM_G_FLOAT(OFS_RETURN) = (float)nodegraph_graph_is_node_valid(graphid, nodeid); +} + +// #708 vector(float graphid, float nodeid) nodegraph_graph_get_node (EXT_NODEGRAPH) +static void VM_nodegraph_graph_get_node(prvm_prog_t *prog) +{ + short graphid; + short nodeid; + vec3_t outnode; + + VM_SAFEPARMCOUNT(2, VM_nodegraph_graph_get_node); + + graphid = (short)PRVM_G_FLOAT(OFS_PARM0); + nodeid = (short)PRVM_G_FLOAT(OFS_PARM1); + + nodegraph_graph_get_node(graphid, nodeid, outnode); + + VectorCopy(outnode, PRVM_G_VECTOR(OFS_RETURN)); +} + +// #709 float(float graphid, float nodeidfrom, float nodeidto) nodegraph_graph_add_link (EXT_NODEGRAPH) +static void VM_nodegraph_graph_add_link(prvm_prog_t *prog) +{ + short graphid; + short nodeidfrom; + short nodeidto; + + VM_SAFEPARMCOUNT(3, VM_nodegraph_graph_add_link); + + graphid = (short)PRVM_G_FLOAT(OFS_PARM0); + nodeidfrom = (short)PRVM_G_FLOAT(OFS_PARM1); + nodeidto = (short)PRVM_G_FLOAT(OFS_PARM2); + + PRVM_G_FLOAT(OFS_RETURN) = (float)nodegraph_graph_add_link(graphid, nodeidfrom, nodeidto); +} + +// #710 float(float graphid, float nodeidfrom, float nodeidto) nodegraph_graph_remove_link (EXT_NODEGRAPH) +static void VM_nodegraph_graph_remove_link(prvm_prog_t *prog) +{ + short graphid; + short nodeidfrom; + short nodeidto; + + VM_SAFEPARMCOUNT(3, VM_nodegraph_graph_remove_link); + + graphid = (short)PRVM_G_FLOAT(OFS_PARM0); + nodeidfrom = (short)PRVM_G_FLOAT(OFS_PARM1); + nodeidto = (short)PRVM_G_FLOAT(OFS_PARM2); + + PRVM_G_FLOAT(OFS_RETURN) = (float)nodegraph_graph_remove_link(graphid, nodeidfrom, nodeidto); +} +// #711 float(float graphid, float nodeidfrom, float nodeidto) nodegraph_graph_does_link_exist (EXT_NODEGRAPH) +static void VM_nodegraph_graph_does_link_exist(prvm_prog_t *prog) +{ + short graphid; + short nodeidfrom; + short nodeidto; + + VM_SAFEPARMCOUNT(3, VM_nodegraph_graph_does_link_exist); + + graphid = (short)PRVM_G_FLOAT(OFS_PARM0); + nodeidfrom = (short)PRVM_G_FLOAT(OFS_PARM1); + nodeidto = (short)PRVM_G_FLOAT(OFS_PARM2); + + PRVM_G_FLOAT(OFS_RETURN) = (float)nodegraph_graph_does_link_exist(graphid, nodeidfrom, nodeidto); +} + +// #712 float(float graphid, vector position) nodegraph_graph_find_nearest_nodeid (EXT_NODEGRAPH) +static void VM_nodegraph_graph_find_nearest_nodeid(prvm_prog_t *prog) +{ + short graphid; + vec3_t position; + + VM_SAFEPARMCOUNT(2, VM_nodegraph_graph_find_nearest_nodeid); + + graphid = (short)PRVM_G_FLOAT(OFS_PARM0); + VectorCopy(PRVM_G_VECTOR(OFS_PARM1), position); + + PRVM_G_FLOAT(OFS_RETURN) = (float)nodegraph_graph_find_nearest_nodeid(graphid, position); +} + +// #713 float(float graphid, float nodeidfrom, float nodeidto) nodegraph_graph_query_path (EXT_NODEGRAPH) +static void VM_nodegraph_graph_query_path(prvm_prog_t *prog) +{ + short graphid; + short nodeidfrom; + short nodeidto; + + VM_SAFEPARMCOUNT(3, VM_nodegraph_graph_query_path); + + graphid = (short)PRVM_G_FLOAT(OFS_PARM0); + nodeidfrom = (short)PRVM_G_FLOAT(OFS_PARM1); + nodeidto = (short)PRVM_G_FLOAT(OFS_PARM2); + + PRVM_G_FLOAT(OFS_RETURN) = (float)nodegraph_graph_query_path(graphid, nodeidfrom, nodeidto); +} + +// #714 float(float graphid, float nodeid) nodegraph_graph_query_nodes_linked (EXT_NODEGRAPH) +static void VM_nodegraph_graph_query_nodes_linked(prvm_prog_t *prog) +{ + short graphid; + short nodeid; + + VM_SAFEPARMCOUNT(2, VM_nodegraph_graph_query_nodes_linked); + + graphid = (short)PRVM_G_FLOAT(OFS_PARM0); + nodeid = (short)PRVM_G_FLOAT(OFS_PARM1); + + PRVM_G_FLOAT(OFS_RETURN) = (float)nodegraph_graph_query_nodes_linked(graphid, nodeid); +} + +// #715 float(float graphid, vector position, float radius) nodegraph_graph_query_nodes_in_radius (EXT_NODEGRAPH) +static void VM_nodegraph_graph_query_nodes_in_radius(prvm_prog_t *prog) +{ + short graphid; + vec3_t position; + float radius; + + VM_SAFEPARMCOUNT(3, VM_nodegraph_graph_query_nodes_in_radius); + + graphid = (short)PRVM_G_FLOAT(OFS_PARM0); + VectorCopy(PRVM_G_VECTOR(OFS_PARM1), position); + radius = PRVM_G_FLOAT(OFS_PARM2); + + PRVM_G_FLOAT(OFS_RETURN) = (float)nodegraph_graph_query_nodes_in_radius(graphid, position, radius); +} + +// #716 float(float queryid) nodegraph_query_release (EXT_NODEGRAPH) +static void VM_nodegraph_query_release(prvm_prog_t *prog) +{ + short queryid; + + VM_SAFEPARMCOUNT(1, VM_nodegraph_query_release); + + queryid = (short)PRVM_G_FLOAT(OFS_PARM0); + + PRVM_G_FLOAT(OFS_RETURN) = (float)nodegraph_query_release(queryid); +} + +// #717 float(float queryid) nodegraph_query_entries_count (EXT_NODEGRAPH) +static void VM_nodegraph_query_entries_count(prvm_prog_t *prog) +{ + short queryid; + + VM_SAFEPARMCOUNT(1, VM_nodegraph_query_entries_count); + + queryid = (short)PRVM_G_FLOAT(OFS_PARM0); + + PRVM_G_FLOAT(OFS_RETURN) = (float)nodegraph_query_entries_count(queryid); +} + +// #718 float(float queryid) nodegraph_query_is_valid (EXT_NODEGRAPH) +static void VM_nodegraph_query_is_valid(prvm_prog_t *prog) +{ + short queryid; + + VM_SAFEPARMCOUNT(1, VM_nodegraph_query_is_valid); + + queryid = (short)PRVM_G_FLOAT(OFS_PARM0); + + PRVM_G_FLOAT(OFS_RETURN) = (float)nodegraph_query_is_valid(queryid); +} + +// #719 float(float queryid) nodegraph_query_get_graphid (EXT_NODEGRAPH) +static void VM_nodegraph_query_get_graphid(prvm_prog_t *prog) +{ + short queryid; + + VM_SAFEPARMCOUNT(1, VM_nodegraph_query_get_graphid); + + queryid = (short)PRVM_G_FLOAT(OFS_PARM0); + + PRVM_G_FLOAT(OFS_RETURN) = (float)nodegraph_query_get_graphid(queryid); +} + +// #720 float(float queryid, float entryid) nodegraph_query_get_nodeid (EXT_NODEGRAPH) +static void VM_nodegraph_query_get_nodeid(prvm_prog_t *prog) +{ + short queryid; + short entryid; + + VM_SAFEPARMCOUNT(2, VM_nodegraph_query_get_nodeid); + + queryid = (short)PRVM_G_FLOAT(OFS_PARM0); + entryid = (short)PRVM_G_FLOAT(OFS_PARM1); + + PRVM_G_FLOAT(OFS_RETURN) = (float)nodegraph_query_get_nodeid(queryid, entryid); +} + +// #721 float(vector nodefrom, vector nodeto, vector mins, vector maxs, float type) nodegraph_moveprobe_fly (EXT_NODEGRAPH) +static void VM_nodegraph_moveprobe_fly(prvm_prog_t *prog) +{ + vec3_t nodefrom; + vec3_t nodeto; + vec3_t mins; + vec3_t maxs; + short type; + + VM_SAFEPARMCOUNT(5, VM_nodegraph_moveprobe_fly); + + VectorCopy(PRVM_G_VECTOR(OFS_PARM0), nodefrom); + VectorCopy(PRVM_G_VECTOR(OFS_PARM1), nodeto); + VectorCopy(PRVM_G_VECTOR(OFS_PARM2), mins); + VectorCopy(PRVM_G_VECTOR(OFS_PARM3), maxs); + + type = (short)PRVM_G_FLOAT(OFS_PARM4); + + PRVM_G_FLOAT(OFS_RETURN) = (float)nodegraph_moveprobe_fly(nodefrom, nodeto, mins, maxs, type); +} + +// #722 (vector nodefrom, vector nodeto, vector mins, vector maxs, float stepheight, float dropheight) nodegraph_moveprobe_walk (EXT_NODEGRAPH) +static void VM_nodegraph_moveprobe_walk(prvm_prog_t *prog) +{ + vec3_t nodefrom; + vec3_t nodeto; + vec3_t mins; + vec3_t maxs; + float stepheight; + float dropheight; + + VM_SAFEPARMCOUNT(6, VM_nodegraph_moveprobe_walk); + + VectorCopy(PRVM_G_VECTOR(OFS_PARM0), nodefrom); + VectorCopy(PRVM_G_VECTOR(OFS_PARM1), nodeto); + VectorCopy(PRVM_G_VECTOR(OFS_PARM2), mins); + VectorCopy(PRVM_G_VECTOR(OFS_PARM3), maxs); + + stepheight = PRVM_G_FLOAT(OFS_PARM4); + dropheight = PRVM_G_FLOAT(OFS_PARM5); + + PRVM_G_FLOAT(OFS_RETURN) = (float)nodegraph_moveprobe_walk(nodefrom, nodeto, mins, maxs, stepheight, dropheight); +} +// #723 float(float graphid, vector position, float radius, vector mins, vector maxs, float type) nodegraph_graph_query_nodes_in_radius_fly_reachable (EXT_NODEGRAPH) +static void VM_nodegraph_graph_query_nodes_in_radius_fly_reachable(prvm_prog_t *prog) +{ + short graphid; + vec3_t position; + float radius; + vec3_t mins; + vec3_t maxs; + short type; + + VM_SAFEPARMCOUNT(6, VM_nodegraph_graph_query_nodes_in_radius_fly_reachable); + + graphid = (short)PRVM_G_FLOAT(OFS_PARM0); + + VectorCopy(PRVM_G_VECTOR(OFS_PARM1), position); + + radius = PRVM_G_FLOAT(OFS_PARM2); + + VectorCopy(PRVM_G_VECTOR(OFS_PARM3), mins); + VectorCopy(PRVM_G_VECTOR(OFS_PARM4), maxs); + + type = (short)PRVM_G_FLOAT(OFS_PARM5); + + PRVM_G_FLOAT(OFS_RETURN) = (float)nodegraph_graph_query_nodes_in_radius_fly_reachable(graphid, position, radius, mins, maxs, type); +} + +// #724 float(float graphid, vector position, float radius, vector mins, vector maxs, float stepheight, float dropheight) nodegraph_graph_query_nodes_in_radius_walk_reachable (EXT_NODEGRAPH) +static void VM_nodegraph_graph_query_nodes_in_radius_walk_reachable(prvm_prog_t *prog) +{ + short graphid; + vec3_t position; + float radius; + vec3_t mins; + vec3_t maxs; + float stepheight; + float dropheight; + + VM_SAFEPARMCOUNT(7, VM_nodegraph_graph_query_nodes_in_radius_walk_reachable); + + graphid = (short)PRVM_G_FLOAT(OFS_PARM0); + + VectorCopy(PRVM_G_VECTOR(OFS_PARM1), position); + + radius = PRVM_G_FLOAT(OFS_PARM2); + + VectorCopy(PRVM_G_VECTOR(OFS_PARM3), mins); + VectorCopy(PRVM_G_VECTOR(OFS_PARM4), maxs); + + stepheight = PRVM_G_FLOAT(OFS_PARM5); + dropheight = PRVM_G_FLOAT(OFS_PARM6); + + PRVM_G_FLOAT(OFS_RETURN) = (float)nodegraph_graph_query_nodes_in_radius_walk_reachable(graphid, position, radius, mins, maxs, stepheight, dropheight); +} prvm_builtin_t vm_sv_builtins[] = { NULL, // #0 NULL function (not callable) (QUAKE) VM_makevectors, // #1 void(vector ang) makevectors (QUAKE) @@ -3965,31 +4339,31 @@ NULL, // #696 NULL, // #697 NULL, // #698 NULL, // #699 -NULL, // #700 -NULL, // #701 -NULL, // #702 -NULL, // #703 -NULL, // #704 -NULL, // #705 -NULL, // #706 -NULL, // #707 -NULL, // #708 -NULL, // #709 -NULL, // #710 -NULL, // #711 -NULL, // #712 -NULL, // #713 -NULL, // #714 -NULL, // #715 -NULL, // #716 -NULL, // #717 -NULL, // #718 -NULL, // #719 -NULL, // #720 -NULL, // #721 -NULL, // #722 -NULL, // #723 -NULL, // #724 +VM_nodegraph_graphset_clear, // #700 float() nodegraph_graphset_clear (EXT_NODEGRAPH) +VM_nodegraph_graphset_load, // #701 float() nodegraph_graphset_load (EXT_NODEGRAPH) +VM_nodegraph_graphset_save, // #702 float() nodegraph_graphset_save (EXT_NODEGRAPH) +VM_nodegraph_graph_clear, // #703 float(float graphid) nodegraph_graph_clear (EXT_NODEGRAPH) +VM_nodegraph_graph_nodes_count, // #704 float(float graphid) nodegraph_graph_nodes_count (EXT_NODEGRAPH) +VM_nodegraph_graph_add_node, // #705 float(float graphid, vector node) nodegraph_graph_add_node (EXT_NODEGRAPH) +VM_nodegraph_graph_remove_node, // #706 float(float graphid, float nodeid) nodegraph_graph_remove_node (EXT_NODEGRAPH) +VM_nodegraph_graph_is_node_valid, // #707 float(float graphid, float nodeid) nodegraph_graph_is_node_valid (EXT_NODEGRAPH) +VM_nodegraph_graph_get_node, // #708 vector(float graphid, float nodeid) nodegraph_graph_get_node (EXT_NODEGRAPH) +VM_nodegraph_graph_add_link, // #709 float(float graphid, float nodeidfrom, float nodeidto) nodegraph_graph_add_link (EXT_NODEGRAPH) +VM_nodegraph_graph_remove_link, // #710 float(float graphid, float nodeidfrom, float nodeidto) nodegraph_graph_remove_link (EXT_NODEGRAPH) +VM_nodegraph_graph_does_link_exist, // #711 float(float graphid, float nodeidfrom, float nodeidto) nodegraph_graph_does_link_exist (EXT_NODEGRAPH) +VM_nodegraph_graph_find_nearest_nodeid, // #712 float(float graphid, vector position) nodegraph_graph_find_nearest_nodeid (EXT_NODEGRAPH) +VM_nodegraph_graph_query_path, // #713 float(float graphid, float nodeidfrom, float nodeidto) nodegraph_graph_query_path (EXT_NODEGRAPH) +VM_nodegraph_graph_query_nodes_linked, // #714 float(float graphid, float nodeid) nodegraph_graph_query_nodes_linked (EXT_NODEGRAPH) +VM_nodegraph_graph_query_nodes_in_radius, // #715 float(float graphid, vector position, float radius) nodegraph_graph_query_nodes_in_radius (EXT_NODEGRAPH) +VM_nodegraph_query_release, // #716 float(float queryid) nodegraph_query_release (EXT_NODEGRAPH) +VM_nodegraph_query_entries_count, // #717 float(float queryid) nodegraph_query_entries_count (EXT_NODEGRAPH) +VM_nodegraph_query_is_valid, // #718 float(float queryid) nodegraph_query_is_valid (EXT_NODEGRAPH) +VM_nodegraph_query_get_graphid, // #719 float(float queryid) nodegraph_query_get_graphid (EXT_NODEGRAPH) +VM_nodegraph_query_get_nodeid, // #720 float(float queryid, float entryid) nodegraph_query_get_nodeid (EXT_NODEGRAPH) +VM_nodegraph_moveprobe_fly, // #721 float(vector nodefrom, vector nodeto, vector mins, vector maxs, float type) nodegraph_moveprobe_fly (EXT_NODEGRAPH) +VM_nodegraph_moveprobe_walk, // #722 (vector nodefrom, vector nodeto, vector mins, vector maxs, float stepheight, float dropheight) nodegraph_moveprobe_walk (EXT_NODEGRAPH) +VM_nodegraph_graph_query_nodes_in_radius_fly_reachable, // #723 float(float graphid, vector position, float radius, vector mins, vector maxs, float type) nodegraph_graph_query_nodes_in_radius_fly_reachable (EXT_NODEGRAPH) +VM_nodegraph_graph_query_nodes_in_radius_walk_reachable, // #724 float(float graphid, vector position, float radius, vector mins, vector maxs, float stepheight, float dropheight) nodegraph_graph_query_nodes_in_radius_walk_reachable (EXT_NODEGRAPH) NULL, // #725 NULL, // #726 NULL, // #727 From 7f8bd47500a38009d5ca88f96c0037ba0615e10c Mon Sep 17 00:00:00 2001 From: Cloudwalk Date: Wed, 10 Jun 2020 13:13:10 -0400 Subject: [PATCH 22/23] Implement EXT_WRATH fields --- clvm_cmds.c | 24 ++++++++++++++---------- prvm_offsets.h | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++ sv_move.c | 17 ++++++++++------- sv_phys.c | 9 ++++++--- 4 files changed, 81 insertions(+), 20 deletions(-) diff --git a/clvm_cmds.c b/clvm_cmds.c index bf9153317..19980cab2 100644 --- a/clvm_cmds.c +++ b/clvm_cmds.c @@ -592,12 +592,13 @@ static void VM_CL_checkbottom (prvm_prog_t *prog) vec3_t mins, maxs, start, stop; trace_t trace; int x, y; - float mid, bottom; + float mid, bottom, stepheight; VM_SAFEPARMCOUNT(1, VM_CL_checkbottom); ent = PRVM_G_EDICT(OFS_PARM0); PRVM_G_FLOAT(OFS_RETURN) = 0; + stepheight = sv_stepheight.value + PRVM_clientedictfloat(ent, stepheight_delta); VectorAdd (PRVM_clientedictvector(ent, origin), PRVM_clientedictvector(ent, mins), mins); VectorAdd (PRVM_clientedictvector(ent, origin), PRVM_clientedictvector(ent, maxs), maxs); @@ -628,7 +629,7 @@ static void VM_CL_checkbottom (prvm_prog_t *prog) // the midpoint must be within 16 of the bottom start[0] = stop[0] = (mins[0] + maxs[0])*0.5; start[1] = stop[1] = (mins[1] + maxs[1])*0.5; - stop[2] = start[2] - 2*sv_stepheight.value; + stop[2] = start[2] - 2*stepheight; trace = CL_TraceLine(start, stop, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value, true, true, NULL, true, false); if (trace.fraction == 1.0) @@ -647,7 +648,7 @@ static void VM_CL_checkbottom (prvm_prog_t *prog) if (trace.fraction != 1.0 && trace.endpos[2] > bottom) bottom = trace.endpos[2]; - if (trace.fraction == 1.0 || mid - trace.endpos[2] > sv_stepheight.value) + if (trace.fraction == 1.0 || mid - trace.endpos[2] > stepheight) return; } @@ -3432,8 +3433,9 @@ static qboolean CL_CheckBottom (prvm_edict_t *ent) vec3_t mins, maxs, start, stop; trace_t trace; int x, y; - float mid, bottom; + float mid, bottom, stepheight; + stepheight = sv_stepheight.value + PRVM_clientedictfloat(ent, stepheight_delta); VectorAdd (PRVM_clientedictvector(ent, origin), PRVM_clientedictvector(ent, mins), mins); VectorAdd (PRVM_clientedictvector(ent, origin), PRVM_clientedictvector(ent, maxs), maxs); @@ -3461,7 +3463,7 @@ static qboolean CL_CheckBottom (prvm_edict_t *ent) // the midpoint must be within 16 of the bottom start[0] = stop[0] = (mins[0] + maxs[0])*0.5; start[1] = stop[1] = (mins[1] + maxs[1])*0.5; - stop[2] = start[2] - 2*sv_stepheight.value; + stop[2] = start[2] - 2*stepheight; trace = CL_TraceLine(start, stop, MOVE_NOMONSTERS, ent, CL_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value, true, false, NULL, true, false); if (trace.fraction == 1.0) @@ -3479,7 +3481,7 @@ static qboolean CL_CheckBottom (prvm_edict_t *ent) if (trace.fraction != 1.0 && trace.endpos[2] > bottom) bottom = trace.endpos[2]; - if (trace.fraction == 1.0 || mid - trace.endpos[2] > sv_stepheight.value) + if (trace.fraction == 1.0 || mid - trace.endpos[2] > stepheight) return false; } @@ -3498,13 +3500,15 @@ possible, no move is done and false is returned static qboolean CL_movestep (prvm_edict_t *ent, vec3_t move, qboolean relink, qboolean noenemy, qboolean settrace) { prvm_prog_t *prog = CLVM_prog; - float dz; + float dz, stepheight; vec3_t oldorg, neworg, end, traceendpos; vec3_t mins, maxs, start; trace_t trace; int i, svent; prvm_edict_t *enemy; + stepheight = sv_stepheight.value + PRVM_clientedictfloat(ent, stepheight_delta); + // try the move VectorCopy(PRVM_clientedictvector(ent, mins), mins); VectorCopy(PRVM_clientedictvector(ent, maxs), maxs); @@ -3552,9 +3556,9 @@ static qboolean CL_movestep (prvm_edict_t *ent, vec3_t move, qboolean relink, qb } // push down from a step height above the wished position - neworg[2] += sv_stepheight.value; + neworg[2] += stepheight; VectorCopy (neworg, end); - end[2] -= sv_stepheight.value*2; + end[2] -= stepheight*2; trace = CL_TraceBox(neworg, mins, maxs, end, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value, true, true, &svent, true); if (settrace) @@ -3562,7 +3566,7 @@ static qboolean CL_movestep (prvm_edict_t *ent, vec3_t move, qboolean relink, qb if (trace.startsolid) { - neworg[2] -= sv_stepheight.value; + neworg[2] -= stepheight; trace = CL_TraceBox(neworg, mins, maxs, end, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value, true, true, &svent, true); if (settrace) CL_VM_SetTraceGlobals(prog, &trace, svent); diff --git a/prvm_offsets.h b/prvm_offsets.h index 5eada9486..cd0f0f373 100644 --- a/prvm_offsets.h +++ b/prvm_offsets.h @@ -48,6 +48,7 @@ PRVM_DECLARE_clientfieldfloat(shadertime) PRVM_DECLARE_clientfieldfloat(skeletonindex) PRVM_DECLARE_clientfieldfloat(skin) PRVM_DECLARE_clientfieldfloat(solid) +PRVM_DECLARE_clientfieldfloat(stepheight_delta) PRVM_DECLARE_clientfieldfloat(tag_index) PRVM_DECLARE_clientfieldfloat(userwavefunc_param0) PRVM_DECLARE_clientfieldfloat(userwavefunc_param1) @@ -372,6 +373,7 @@ PRVM_DECLARE_field(skin) PRVM_DECLARE_field(solid) PRVM_DECLARE_field(sounds) PRVM_DECLARE_field(spawnflags) +PRVM_DECLARE_field(stepheight_delta) PRVM_DECLARE_field(style) PRVM_DECLARE_field(tag_entity) PRVM_DECLARE_field(tag_index) @@ -504,6 +506,54 @@ PRVM_DECLARE_global(parm13) PRVM_DECLARE_global(parm14) PRVM_DECLARE_global(parm15) PRVM_DECLARE_global(parm16) +PRVM_DECLARE_global(parm17) +PRVM_DECLARE_global(parm18) +PRVM_DECLARE_global(parm19) +PRVM_DECLARE_global(parm20) +PRVM_DECLARE_global(parm21) +PRVM_DECLARE_global(parm22) +PRVM_DECLARE_global(parm23) +PRVM_DECLARE_global(parm24) +PRVM_DECLARE_global(parm25) +PRVM_DECLARE_global(parm26) +PRVM_DECLARE_global(parm27) +PRVM_DECLARE_global(parm28) +PRVM_DECLARE_global(parm29) +PRVM_DECLARE_global(parm30) +PRVM_DECLARE_global(parm31) +PRVM_DECLARE_global(parm32) +PRVM_DECLARE_global(parm33) +PRVM_DECLARE_global(parm34) +PRVM_DECLARE_global(parm35) +PRVM_DECLARE_global(parm36) +PRVM_DECLARE_global(parm37) +PRVM_DECLARE_global(parm38) +PRVM_DECLARE_global(parm39) +PRVM_DECLARE_global(parm40) +PRVM_DECLARE_global(parm41) +PRVM_DECLARE_global(parm42) +PRVM_DECLARE_global(parm43) +PRVM_DECLARE_global(parm44) +PRVM_DECLARE_global(parm45) +PRVM_DECLARE_global(parm46) +PRVM_DECLARE_global(parm47) +PRVM_DECLARE_global(parm48) +PRVM_DECLARE_global(parm49) +PRVM_DECLARE_global(parm50) +PRVM_DECLARE_global(parm51) +PRVM_DECLARE_global(parm52) +PRVM_DECLARE_global(parm53) +PRVM_DECLARE_global(parm54) +PRVM_DECLARE_global(parm55) +PRVM_DECLARE_global(parm56) +PRVM_DECLARE_global(parm57) +PRVM_DECLARE_global(parm58) +PRVM_DECLARE_global(parm59) +PRVM_DECLARE_global(parm60) +PRVM_DECLARE_global(parm61) +PRVM_DECLARE_global(parm62) +PRVM_DECLARE_global(parm63) +PRVM_DECLARE_global(parm64) PRVM_DECLARE_global(particle_airfriction) PRVM_DECLARE_global(particle_alpha) PRVM_DECLARE_global(particle_alphafade) @@ -715,6 +765,7 @@ PRVM_DECLARE_serverfieldfloat(skin) PRVM_DECLARE_serverfieldfloat(solid) PRVM_DECLARE_serverfieldfloat(sounds) PRVM_DECLARE_serverfieldfloat(spawnflags) +PRVM_DECLARE_serverfieldfloat(stepheight_delta) PRVM_DECLARE_serverfieldfloat(style) PRVM_DECLARE_serverfieldfloat(tag_index) PRVM_DECLARE_serverfieldfloat(takedamage) diff --git a/sv_move.c b/sv_move.c index ced05ac1b..8d99e92ac 100644 --- a/sv_move.c +++ b/sv_move.c @@ -39,8 +39,9 @@ qboolean SV_CheckBottom (prvm_edict_t *ent) vec3_t mins, maxs, start, stop; trace_t trace; int x, y; - float mid, bottom; + float mid, bottom, stepheight; + stepheight = sv_stepheight.value + PRVM_serveredictfloat(ent, stepheight_delta); VectorAdd (PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), mins); VectorAdd (PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, maxs), maxs); @@ -70,7 +71,7 @@ qboolean SV_CheckBottom (prvm_edict_t *ent) // the midpoint must be within 16 of the bottom start[0] = stop[0] = (mins[0] + maxs[0])*0.5; start[1] = stop[1] = (mins[1] + maxs[1])*0.5; - stop[2] = start[2] - 2*sv_stepheight.value; + stop[2] = start[2] - 2*stepheight; trace = SV_TraceLine(start, stop, MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value); if (trace.fraction == 1.0) @@ -88,7 +89,7 @@ qboolean SV_CheckBottom (prvm_edict_t *ent) if (trace.fraction != 1.0 && trace.endpos[2] > bottom) bottom = trace.endpos[2]; - if (trace.fraction == 1.0 || mid - trace.endpos[2] > sv_stepheight.value) + if (trace.fraction == 1.0 || mid - trace.endpos[2] > stepheight) return false; } @@ -109,12 +110,14 @@ possible, no move is done and false is returned qboolean SV_movestep (prvm_edict_t *ent, vec3_t move, qboolean relink, qboolean noenemy, qboolean settrace) { prvm_prog_t *prog = SVVM_prog; - float dz; + float dz, stepheight; vec3_t oldorg, neworg, end, traceendpos, entorigin, entmins, entmaxs; trace_t trace; int i; prvm_edict_t *enemy; + stepheight = sv_stepheight.value + PRVM_serveredictfloat(ent, stepheight_delta); + // try the move VectorCopy (PRVM_serveredictvector(ent, origin), oldorg); VectorAdd (PRVM_serveredictvector(ent, origin), move, neworg); @@ -168,15 +171,15 @@ qboolean SV_movestep (prvm_edict_t *ent, vec3_t move, qboolean relink, qboolean } // push down from a step height above the wished position - neworg[2] += sv_stepheight.value; + neworg[2] += stepheight; VectorCopy (neworg, end); - end[2] -= sv_stepheight.value*2; + end[2] -= stepheight*2; trace = SV_TraceBox(neworg, entmins, entmaxs, end, MOVE_NORMAL, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value); if (trace.startsolid) { - neworg[2] -= sv_stepheight.value; + neworg[2] -= stepheight; trace = SV_TraceBox(neworg, entmins, entmaxs, end, MOVE_NORMAL, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value); if (trace.startsolid) return false; diff --git a/sv_phys.c b/sv_phys.c index 3c58d62d9..ef8e707fe 100644 --- a/sv_phys.c +++ b/sv_phys.c @@ -2312,6 +2312,7 @@ static void SV_WalkMove (prvm_edict_t *ent) vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity, entmins, entmaxs; trace_t downtrace, trace; qboolean applygravity; + float stepheight; // if frametime is 0 (due to client sending the same timestamp twice), // don't move @@ -2321,6 +2322,8 @@ static void SV_WalkMove (prvm_edict_t *ent) if (sv_gameplayfix_unstickplayers.integer) SV_CheckStuck (ent); + stepheight = sv_stepheight.value + PRVM_serveredictfloat(ent, stepheight_delta); + applygravity = !SV_CheckWater (ent) && PRVM_serveredictfloat(ent, movetype) == MOVETYPE_WALK && ! ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP); SV_CheckVelocity(ent); @@ -2331,7 +2334,7 @@ static void SV_WalkMove (prvm_edict_t *ent) VectorCopy (PRVM_serveredictvector(ent, origin), start_origin); VectorCopy (PRVM_serveredictvector(ent, velocity), start_velocity); - clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, sv_gameplayfix_stepmultipletimes.integer ? sv_stepheight.value : 0); + clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, sv_gameplayfix_stepmultipletimes.integer ? stepheight : 0); if(sv_gameplayfix_downtracesupportsongroundflag.integer) if(!(clip & 1)) @@ -2405,7 +2408,7 @@ static void SV_WalkMove (prvm_edict_t *ent) // move up VectorClear (upmove); - upmove[2] = sv_stepheight.value; + upmove[2] = stepheight; if(!SV_PushEntity(&trace, ent, upmove, true)) { // we got teleported when upstepping... must abort the move @@ -2457,7 +2460,7 @@ static void SV_WalkMove (prvm_edict_t *ent) // move down VectorClear (downmove); - downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime; + downmove[2] = -stepheight + start_velocity[2]*sv.frametime; if(!SV_PushEntity (&downtrace, ent, downmove, true)) { // we got teleported when downstepping... must abort the move From e4cec230f2edea3363d142f96a392bb835874145 Mon Sep 17 00:00:00 2001 From: Cloudwalk Date: Wed, 26 Aug 2020 09:46:29 -0400 Subject: [PATCH 23/23] Fix compile warnings --- fs.h | 1 + gl_draw.c | 2 +- prvm_cmds.c | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/fs.h b/fs.h index e466d9908..8ddaa76f4 100644 --- a/fs.h +++ b/fs.h @@ -80,6 +80,7 @@ void FS_Purge (qfile_t* file); const char *FS_FileWithoutPath (const char *in); const char *FS_FileExtension (const char *in); int FS_CheckNastyPath (const char *path, qboolean isgamedir); +int FS_rmtree(const char *dir); extern const char *const fs_checkgamedir_missing; // "(missing)" const char *FS_CheckGameDir(const char *gamedir); // returns NULL if nasty, fs_checkgamedir_missing (exact pointer) if missing diff --git a/gl_draw.c b/gl_draw.c index a40527007..3a1008859 100644 --- a/gl_draw.c +++ b/gl_draw.c @@ -326,7 +326,7 @@ void Draw_FreePic(const char *picname) qboolean Draw_PicExists(const char *name) { char vabuf[1024] = { 0 }; const char *checkfmt[] = { "%s.tga", "%s.png", "%s.jpg", "%s.pcx" }; - int i; + long unsigned int i; // TODO: actually use the gfx format list for this for (i = 0; i < sizeof(checkfmt) / sizeof(checkfmt[0]); ++i) if (FS_FileExists(va(vabuf, sizeof(vabuf), checkfmt[i], name))) diff --git a/prvm_cmds.c b/prvm_cmds.c index 0a273c2fa..8655a1c38 100644 --- a/prvm_cmds.c +++ b/prvm_cmds.c @@ -2040,7 +2040,7 @@ void VM_fcopy(prvm_prog_t *prog) FS_Close(f1); FS_Close(f2); PRVM_G_FLOAT(OFS_RETURN) = -3; - VM_Warning(prog, "VM_fcopy: %s read %I64d from %s but wrote %I64d to %s\n", prog->name, rx, fname1, wx, fname2); + VM_Warning(prog, "VM_fcopy: %s read %lli from %s but wrote %lli to %s\n", prog->name, rx, fname1, wx, fname2); return; } }