diff --git a/engine/client/cl_ignore.c b/engine/client/cl_ignore.c index a39b6fb2..46166c80 100644 --- a/engine/client/cl_ignore.c +++ b/engine/client/cl_ignore.c @@ -89,12 +89,12 @@ char *Player_MyName (void) -cvar_t ignore_spec = CVAR("ignore_spec", "0"); +cvar_t ignore_spec = CVARD("ignore_spec", "0", "0: Never ignore spectators.\n1: Ignore spectators only when playing.\n2: Always ignore spectators even when spectating."); cvar_t ignore_qizmo_spec = CVAR("ignore_qizmo_spec", "0"); cvar_t ignore_mode = CVAR("ignore_mode", "0"); cvar_t ignore_flood_duration = CVARD("ignore_flood_duration", "4", "Time limit for inbound messages to be considered duplicates."); cvar_t ignore_flood = CVARD("ignore_flood", "0", "Provides a way to reduce inbound spam from flooding out your chat (dupe messages are ignored).\n0: No inbound flood protection.\n1: Duplicate non-team messages will be filtered.\n2: ALL duplicate messages will be filtered"); -cvar_t ignore_opponents = CVAR("ignore_opponents", "0"); +cvar_t ignore_opponents = CVARD("ignore_opponents", "0", "0: Don't ignore chat from enemies.\n1: Always ignore chat from opponents (note: can also ignore f_ruleset checks).\n2: Ignore chat from opponents only during a match (requires servers that actually reports match state).\n"); char ignoreteamlist[MAX_TEAMIGNORELIST][16 + 1]; @@ -597,7 +597,7 @@ qboolean Ignore_Message(const char *sendername, const char *s, int flags) if (ignore_opponents.ival && ( (int) ignore_opponents.ival == 1 || - (cls.state >= ca_connected && /*!cl.standby &&*/ !cls.demoplayback && !pv->spectator) // match? + (cls.state >= ca_connected && cl.matchstate == MATCH_INPROGRESS && !cls.demoplayback && !pv->spectator) // match? ) && flags == 1 && !pv->spectator && slot != pv->playernum && (!cl.teamplay || strcmp(cl.players[slot].team, cl.players[pv->playernum].team)) diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 1c27d964..e2d59d42 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -5770,6 +5770,9 @@ static char *CL_ParseChat(char *text, player_info_t **player, int *msgflags) if (!cls.demoplayback) Sys_ServerActivity(); //chat always flashes the screen.. + if (Ignore_Message((*player)->name, s, flags)) + return NULL; + //check f_ stuff if (*player && (!strncmp(s, "f_", 2)|| !strncmp(s, "q_", 2))) { diff --git a/engine/client/cl_pred.c b/engine/client/cl_pred.c index b7485a98..328a1565 100644 --- a/engine/client/cl_pred.c +++ b/engine/client/cl_pred.c @@ -21,10 +21,12 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "winquake.h" cvar_t cl_predict_extrapolate = CVARD("cl_predict_extrapolate", "", "If 1, enables prediction based upon partial input frames which can change over time resulting in a swimmy feel but does not need to interpolate. If 0, prediction will stay in the past and thus use only completed frames. Interpolation will then be used to smooth movement.\nThis cvar only applies when video and input frames are independant (ie: cl_netfps is set)."); -cvar_t cl_predict_timenudge = CVARD("cl_predict_timenudge", "0", "A debug feature. You should normally leave this as 0. Nudges local player prediction into the future if positive (resulting in extrapolation), or into the past if negative (resulting in laggy interpolation). Value is in seconds, so small decimals are required. This cvar applies even if input frames are tied to video frames."); -cvar_t cl_predict_smooth = CVARD("cl_lerp_smooth", "2", "If 2, will act as 1 when playing demos/singleplayer and otherwise act as if set to 0 (ie: deathmatch).\nIf 1, interpolation will run in the past, resulting in really smooth movement at the cost of latency (even on bunchy german ISDNs).\nIf 0, interpolation will be based upon packet arrival times and may judder due to packet loss."); +static cvar_t cl_predict_timenudge = CVARD("cl_predict_timenudge", "0", "A debug feature. You should normally leave this as 0. Nudges local player prediction into the future if positive (resulting in extrapolation), or into the past if negative (resulting in laggy interpolation). Value is in seconds, so small decimals are required. This cvar applies even if input frames are tied to video frames."); +cvar_t cl_lerp_smooth = CVARD("cl_lerp_smooth", "2", "If 2, will act as 1 when playing demos/singleplayer and otherwise act as if set to 0 (ie: deathmatch).\nIf 1, interpolation will run in the past, resulting in really smooth movement at the cost of latency (even on bunchy german ISDNs).\nIf 0, interpolation will be based upon packet arrival times and may judder due to packet loss."); +static cvar_t cl_lerp_driftbias = CVARD("cl_lerp_driftbias", "0", "Additional bias, can be set to a negative value to hold interpolation in the past."); +static cvar_t cl_lerp_driftfrac = CVARD("cl_lerp_driftfrac", "0", "Proportion of the latest time vs the older time to favour drifting towards."); cvar_t cl_nopred = CVARD("cl_nopred","0", "Disables clientside movement prediction."); -cvar_t cl_pushlatency = CVAR("pushlatency","-999"); +static cvar_t cl_pushlatency = CVAR("pushlatency","-999"); extern float pm_airaccelerate; @@ -632,7 +634,7 @@ void CL_CalcClientTime(void) //q3 always drifts. //nq+qw code can drift //default is to drift in demos+SP but not live (oh noes! added latency!) - if (cls.protocol == CP_QUAKE2 || (cls.protocol != CP_QUAKE3 && (!cl_predict_smooth.ival || (cl_predict_smooth.ival == 2 && !(cls.demoplayback || cl.allocated_client_slots == 1 || cl.playerview[0].spectator))) && cls.demoplayback != DPB_MVD)) + if (cls.protocol == CP_QUAKE2 || (cls.protocol != CP_QUAKE3 && (!cl_lerp_smooth.ival || (cl_lerp_smooth.ival == 2 && !(cls.demoplayback || cl.allocated_client_slots == 1 || cl.playerview[0].spectator))) && cls.demoplayback != DPB_MVD)) { //no drift logic float f; f = cl.gametime - cl.oldgametime; @@ -650,6 +652,8 @@ void CL_CalcClientTime(void) max = cl.gametime; min = cl.oldgametime; + FloatInterpolate(min, cl_lerp_driftfrac.value, max, min); + min += cl_lerp_driftbias.value; if (max < min) max = min; @@ -664,7 +668,7 @@ void CL_CalcClientTime(void) else cl.servertime = 0; - if (cl.servertime > max) + if (cl.servertime > min) { if (cl.servertime > max) { @@ -1445,5 +1449,8 @@ void CL_InitPrediction (void) Cvar_Register (&cl_nopred, cl_predictiongroup); Cvar_Register (&cl_predict_extrapolate, cl_predictiongroup); Cvar_Register (&cl_predict_timenudge, cl_predictiongroup); - Cvar_Register (&cl_predict_smooth, cl_predictiongroup); + Cvar_Register (&cl_lerp_smooth, cl_predictiongroup); + + Cvar_Register (&cl_lerp_driftbias, cl_predictiongroup); + Cvar_Register (&cl_lerp_driftfrac, cl_predictiongroup); } diff --git a/engine/client/m_multi.c b/engine/client/m_multi.c index 08ea4d18..65a6ff3b 100644 --- a/engine/client/m_multi.c +++ b/engine/client/m_multi.c @@ -1010,7 +1010,7 @@ void M_Menu_Network_f (void) }; static const char *smoothingvalues[] = {"0", "1", "2", NULL}; extern cvar_t cl_download_csprogs, cl_download_redirection, requiredownloads, cl_solid_players; - extern cvar_t cl_predict_players, cl_predict_smooth, cl_predict_extrapolate; + extern cvar_t cl_predict_players, cl_lerp_smooth, cl_predict_extrapolate; static menuresel_t resel; int y; menubulk_t bulk[] = @@ -1025,7 +1025,7 @@ void M_Menu_Network_f (void) MB_CHECKBOXCVARTIP("Redirect Download", cl_download_redirection, 0, "Whether the client will ignore download redirection from servers"), MB_CHECKBOXCVARTIP("Download CSQC", cl_download_csprogs, 0, "Whether to allow the client to download CSQC (client-side QuakeC) progs from servers"), MB_SPACING(4), - MB_COMBOCVAR("Network Smoothing", cl_predict_smooth, smoothingopts, smoothingvalues, "Smoother gameplay comes at the cost of higher latency. Which do you favour?"), + MB_COMBOCVAR("Network Smoothing", cl_lerp_smooth, smoothingopts, smoothingvalues, "Smoother gameplay comes at the cost of higher latency. Which do you favour?"), MB_CHECKBOXCVARTIP("Extrapolate Prediction", cl_predict_extrapolate, 0, "Extrapolate local player movement beyond the frames already sent to the server"), MB_CHECKBOXCVARTIP("Predict Other Players", cl_predict_players, 0, "Toggle player prediction"), MB_CHECKBOXCVARTIP("Solid Players", cl_solid_players, 0, "When running/clipping into other players, ON make it appear they are solid, OFF will make it appear like running into a marshmellon."), diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index 87b207e3..4fe59e30 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -2768,7 +2768,7 @@ static void QCBUILTIN PF_cs_pointcontents(pubprogfuncs_t *prinst, struct globalv v = G_VECTOR(OFS_PARM0); - cont = cl.worldmodel?World_PointContents(w, v):FTECONTENTS_EMPTY; + cont = cl.worldmodel?World_PointContentsWorldOnly(w, v):FTECONTENTS_EMPTY; if (cont & FTECONTENTS_SOLID) G_FLOAT(OFS_RETURN) = Q1CONTENTS_SOLID; else if (cont & FTECONTENTS_SKY) @@ -2786,13 +2786,14 @@ static void QCBUILTIN PF_cs_pointcontents(pubprogfuncs_t *prinst, struct globalv static int CS_FindModel(const char *name, int *free) { int i; + const char *fixedname; *free = 0; if (!name || !*name) return 0; - name = Mod_FixName(name, csqc_world.worldmodel->name); + fixedname = Mod_FixName(name, csqc_world.worldmodel->publicname); for (i = 1; i < MAX_CSMODELS; i++) { @@ -2801,7 +2802,7 @@ static int CS_FindModel(const char *name, int *free) *free = -i; break; } - if (!strcmp(cl.model_csqcname[i], name)) + if (!strcmp(cl.model_csqcname[i], fixedname)) return -i; } for (i = 1; i < MAX_PRECACHE_MODELS; i++) @@ -2908,16 +2909,14 @@ static int PF_cs_PrecacheModel_Internal(pubprogfuncs_t *prinst, const char *mode if (!*modelname) return 0; - fixedname = Mod_FixName(modelname, csqc_world.worldmodel->publicname); - for (i = 1; i < MAX_PRECACHE_MODELS; i++) //Make sure that the server specified model is loaded.. { if (!*cl.model_name[i]) break; - if (!strcmp(cl.model_name[i], fixedname)) + if (!strcmp(cl.model_name[i], modelname)) { if (!cl.model_precache[i]) - cl.model_precache[i] = Mod_ForName(cl.model_name[i], MLV_WARN); + cl.model_precache[i] = Mod_ForName(Mod_FixName(modelname, csqc_world.worldmodel->publicname), MLV_WARN); break; } } @@ -6481,7 +6480,9 @@ static struct { {"stringwidth", PF_CL_stringwidth, 327}, // #327 EXT_CSQC_'DARKPLACES' {"drawsubpic", PF_CL_drawsubpic, 328}, // #328 EXT_CSQC_'DARKPLACES' {"drawrotsubpic", PF_CL_drawrotsubpic, 0}, -// {"?", PF_Fixme, 329}, // #329 EXT_CSQC_'DARKPLACES' +#ifdef HAVE_LEGACY + {"drawrotpic_dp", PF_CL_drawrotpic_dp, 329}, +#endif //330 {"getstati", PF_cs_getstati, 330}, // #330 float(float stnum) getstati (EXT_CSQC) diff --git a/engine/client/pr_menu.c b/engine/client/pr_menu.c index 99c529fe..aa51f78a 100644 --- a/engine/client/pr_menu.c +++ b/engine/client/pr_menu.c @@ -689,6 +689,57 @@ void QCBUILTIN PF_CL_drawrotsubpic (pubprogfuncs_t *prinst, struct globalvars_s G_FLOAT(OFS_RETURN) = 1; } +#ifdef HAVE_LEGACY +/*fuck sake, why does no one give a shit about existing extension?!? seriously this stuff is pissing me off*/ +void QCBUILTIN PF_CL_drawrotpic_dp (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + float *pivot = G_VECTOR(OFS_PARM0); + const char *picname = PR_GetStringOfs(prinst, OFS_PARM1); + float *size = G_VECTOR(OFS_PARM2); + float *mins = G_VECTOR(OFS_PARM3); + float angle = (G_FLOAT(OFS_PARM4) * M_PI)/180; + float *rgb = G_VECTOR(OFS_PARM5); + float alpha = G_FLOAT(OFS_PARM6); + int flag = prinst->callargc >= 8?(int) G_FLOAT(OFS_PARM7):0; + + vec3_t maxs; + + vec2_t points[4]; + vec2_t tcoords[4]; + vec2_t saxis; + vec2_t taxis; + + mpic_t *p; + + VectorSubtract(size, mins, maxs); + + p = R2D_SafeCachePic(picname); + if (!p) + p = R2D_SafePicFromWad(picname); + + saxis[0] = cos(angle); + saxis[1] = sin(angle); + taxis[0] = -sin(angle); + taxis[1] = cos(angle); + + Vector2MA(pivot, mins[0], saxis, points[0]); Vector2MA(points[0], mins[1], taxis, points[0]); + Vector2MA(pivot, maxs[0], saxis, points[1]); Vector2MA(points[1], mins[1], taxis, points[1]); + Vector2MA(pivot, maxs[0], saxis, points[2]); Vector2MA(points[2], maxs[1], taxis, points[2]); + Vector2MA(pivot, mins[0], saxis, points[3]); Vector2MA(points[3], maxs[1], taxis, points[3]); + + Vector2Set(tcoords[0], 0, 0); + Vector2Set(tcoords[1], 1, 0); + Vector2Set(tcoords[2], 1, 1); + Vector2Set(tcoords[3], 0, 1); + + r2d_be_flags = PF_SelectDPDrawFlag(prinst, flag); + R2D_ImageColours(rgb[0], rgb[1], rgb[2], alpha); + R2D_Image2dQuad((const vec2_t*)points, (const vec2_t*)tcoords, NULL, p); + r2d_be_flags = 0; + + G_FLOAT(OFS_RETURN) = 1; +} +#endif void QCBUILTIN PF_CL_is_cached_pic (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) @@ -1417,7 +1468,11 @@ void QCBUILTIN PF_clientstate (pubprogfuncs_t *prinst, struct globalvars_s *pr_g if (isDedicated) G_FLOAT(OFS_RETURN) = 0; else - G_FLOAT(OFS_RETURN) = cls.state >= ca_connected ? 2 : 1; //fit in with netquake (we never run a menu.dat dedicated) + { + //fit in with netquake (we never run a menu.dat dedicated) + //we return 2 for trying to connect, to avoid bugs with certain menuqc mods + G_FLOAT(OFS_RETURN) = (cls.state >= ca_connected||CL_TryingToConnect()) ? 2 : 1; + } } //too specific to the prinst's builtins. @@ -2312,7 +2367,10 @@ static struct { {"drawsubpic", PF_CL_drawsubpic, 469}, {"drawrotsubpic", PF_CL_drawrotsubpic, 0}, {"drawtextfield", PF_CL_DrawTextField, 0}, - //470 + +#ifdef HAVE_LEGACY + {"drawrotpic_dp", PF_CL_drawrotpic_dp, 470}, +#endif //MERGES WITH CLIENT+SERVER BUILTIN MAPPINGS BELOW {"asin", PF_asin, 471}, {"acos", PF_acos, 472}, @@ -2612,6 +2670,7 @@ void MP_Shutdown (void) if (!menu_world.progs) return; + menuqc.release = NULL; //don't notify Menu_Unlink(&menuqc); /* { @@ -2798,6 +2857,7 @@ qboolean MP_Init (void) menuqc.keyevent = MP_KeyEvent; menuqc.joyaxis = MP_JoystickAxis; menuqc.release = MP_TryRelease; + menuqc.persist = true; //don't bother trying to kill it... menutime = Sys_DoubleTime(); if (!menu_world.progs) diff --git a/engine/client/r_2d.c b/engine/client/r_2d.c index 9d18173a..9817abb6 100644 --- a/engine/client/r_2d.c +++ b/engine/client/r_2d.c @@ -55,7 +55,7 @@ struct extern cvar_t scr_conalpha; extern cvar_t gl_conback; -extern cvar_t gl_font; +extern cvar_t gl_font, con_textfont; extern cvar_t gl_screenangle; extern cvar_t vid_conautoscale; extern cvar_t vid_conheight; @@ -135,6 +135,7 @@ qbyte GetPaletteIndexNoFB(int red, int green, int blue) void R2D_Shutdown(void) { + Cvar_Unhook(&con_textfont); Cvar_Unhook(&gl_font); Cvar_Unhook(&vid_conautoscale); Cvar_Unhook(&gl_screenangle); @@ -393,6 +394,7 @@ void R2D_Init(void) ); + Cvar_Hook(&con_textfont, R2D_Font_Callback); Cvar_Hook(&gl_font, R2D_Font_Callback); Cvar_Hook(&vid_conautoscale, R2D_Conautoscale_Callback); Cvar_Hook(&gl_screenangle, R2D_ScreenAngle_Callback); @@ -1057,14 +1059,17 @@ int R2D_Font_ListSystemFonts(const char *fname, qofs_t fsize, time_t modtime, vo void R2D_Font_Changed(void) { float tsize; + const char *con_font_name = con_textfont.string; if (!con_textsize.modified) return; + if (!*con_font_name) + con_font_name = gl_font.string; con_textsize.modified = false; if (con_textsize.value < 0) - tsize = (-con_textsize.value * vid.height) / vid.pixelheight; + tsize = (-con_textsize.value * vid.height) / vid.pixelheight; //size defined in physical pixels else - tsize = con_textsize.value; + tsize = con_textsize.value; //size defined in virtual pixels. if (!tsize) tsize = 8; @@ -1151,9 +1156,9 @@ void R2D_Font_Changed(void) if (!font_default && *gl_font.string) font_default = Font_LoadFont("", 8); - if (tsize != 8) + if (tsize != 8 || strcmp(gl_font.string, con_font_name)) { - font_console = Font_LoadFont(gl_font.string, tsize); + font_console = Font_LoadFont(con_font_name, tsize); if (!font_console) font_console = Font_LoadFont("", tsize); } diff --git a/engine/client/renderer.c b/engine/client/renderer.c index 3ff6936b..44f20777 100644 --- a/engine/client/renderer.c +++ b/engine/client/renderer.c @@ -367,7 +367,12 @@ cvar_t gl_conback = CVARFCD ("gl_conback", "", //cvar_t gl_detail = CVARF ("gl_detail", "0", // CVAR_ARCHIVE); //cvar_t gl_detailscale = CVAR ("gl_detailscale", "5"); -cvar_t gl_font = CVARAFD ("gl_font", "", "gl_consolefont", +cvar_t gl_font = CVARFD ("gl_font", "", + CVAR_RENDERERCALLBACK|CVAR_ARCHIVE, ("Specifies the font file to use. a value such as FONT:ALTFONT specifies an alternative font to be used when ^^a is used.\n" + "When using TTF fonts, you will likely need to scale text to at least 150% - vid_conautoscale 1.5 will do this.\n" + "TTF fonts may be loaded from your windows directory. \'gl_font cour?col=1,1,1:couri?col=0,1,0\' loads eg: c:\\windows\\fonts\\cour.ttf, and uses the italic version of courier for alternative text, with specific colour tints." + )); +cvar_t con_textfont = CVARAFD ("con_textfont", "", "gl_consolefont", CVAR_RENDERERCALLBACK|CVAR_ARCHIVE, ("Specifies the font file to use. a value such as FONT:ALTFONT specifies an alternative font to be used when ^^a is used.\n" "When using TTF fonts, you will likely need to scale text to at least 150% - vid_conautoscale 1.5 will do this.\n" "TTF fonts may be loaded from your windows directory. \'gl_font cour?col=1,1,1:couri?col=0,1,0\' loads eg: c:\\windows\\fonts\\cour.ttf, and uses the italic version of courier for alternative text, with specific colour tints." @@ -925,6 +930,7 @@ void Renderer_Init(void) //screen + Cvar_Register (&con_textfont, GRAPHICALNICETIES); Cvar_Register (&gl_font, GRAPHICALNICETIES); Cvar_Register (&scr_conspeed, SCREENOPTIONS); Cvar_Register (&scr_conalpha, SCREENOPTIONS); diff --git a/engine/client/zqtp.c b/engine/client/zqtp.c index 0388c27c..85df32a4 100644 --- a/engine/client/zqtp.c +++ b/engine/client/zqtp.c @@ -66,11 +66,25 @@ qboolean suppress; #define Q_rint(f) ((int)((f)+0.5)) // callbacks used for TP cvars + +#ifdef QWSKINS static void QDECL TP_SkinCvar_Callback(struct cvar_s *var, char *oldvalue); +static void QDECL TP_TeamColor_CB (struct cvar_s *var, char *oldvalue); +static void QDECL TP_EnemyColor_CB (struct cvar_s *var, char *oldvalue); +#define TP_SKIN_CVARS \ + TP_CVARAC(cl_teamskin, "", teamskin, TP_SkinCvar_Callback); \ + TP_CVARAC(cl_enemyskin, "", enemyskin, TP_SkinCvar_Callback); \ + TP_CVARC(enemycolor, "off", TP_EnemyColor_CB); \ + TP_CVARC(teamcolor, "off", TP_TeamColor_CB); +#else +#define TP_SKIN_CVARS +#endif + //a list of all the cvars //this is down to the fact that I keep defining them but forgetting to register. :/ #define TP_CVARS \ + TP_SKIN_CVARS \ TP_CVAR(cl_fakename, ""); \ TP_CVAR(cl_parseSay, "1"); \ TP_CVAR(cl_parseFunChars, "1"); \ @@ -78,8 +92,6 @@ static void QDECL TP_SkinCvar_Callback(struct cvar_s *var, char *oldvalue); TP_CVAR(tp_autostatus, ""); /* things which will not always change, but are useful */ \ TP_CVAR(tp_forceTriggers, "0"); \ TP_CVAR(tp_loadlocs, "1"); \ - TP_CVARAC(cl_teamskin, "", teamskin, TP_SkinCvar_Callback); \ - TP_CVARAC(cl_enemyskin, "", enemyskin, TP_SkinCvar_Callback); \ TP_CVAR(tp_soundtrigger, "~"); \ \ TP_CVAR(tp_name_none, ""); \ @@ -2050,88 +2062,54 @@ static void TP_Colourise_f (void) } } -static void TP_TeamColor_f (void) +static void TP_TeamColor_CB (struct cvar_s *var, char *oldvalue) { unsigned int top, bottom; int i; - - if (Cmd_Argc() == 1) - { - - Com_Printf ("\"teamcolor\" is \""); - if (cl_teamtopcolor == ~0) - Com_Printf ("off"); - else - Com_Printf (cl_teamtopcolor>=16?"0x%06x":"%i", cl_teamtopcolor&0x00ffffff); - if (cl_teamtopcolor != cl_teambottomcolor) - { - if (cl_teambottomcolor == ~0) - Com_Printf (" off"); - else - Com_Printf (cl_teambottomcolor>=16?" 0x%06x":" %i", cl_teambottomcolor&0x00ffffff); - } - Com_Printf ("\"\n"); - return; - } - - top = TP_ForceColour(Cmd_Argv(1)); - if (Cmd_Argc() == 2) + char *n = COM_Parse(var->string); + top = TP_ForceColour(com_token); + COM_Parse(n); + if (!*com_token) bottom = top; else { - bottom = TP_ForceColour(Cmd_Argv(2)); + bottom = TP_ForceColour(com_token); } -// if (top != cl_teamtopcolor || bottom != cl_teambottomcolor) - { - cl_teamtopcolor = top; - cl_teambottomcolor = bottom; + cl_teamtopcolor = top; + cl_teambottomcolor = bottom; - if (qrenderer != QR_NONE) //make sure we have the renderer initialised... - for (i = 0; i < cl.allocated_client_slots; i++) - CL_NewTranslation(i); - } + if (qrenderer != QR_NONE) //make sure we have the renderer initialised... + for (i = 0; i < cl.allocated_client_slots; i++) + CL_NewTranslation(i); } -static void TP_EnemyColor_f (void) +static void TP_EnemyColor_CB (struct cvar_s *var, char *oldvalue) { unsigned int top, bottom; int i; - - if (Cmd_Argc() == 1) - { - Com_Printf ("\"enemycolor\" is \""); - if (cl_enemytopcolor == ~0) - Com_Printf ("off"); - else - Com_Printf (cl_enemytopcolor>=16?"0x%06x":"%i", cl_enemytopcolor&0x00ffffff); - if (cl_enemytopcolor != cl_enemybottomcolor) - { - if (cl_enemybottomcolor == ~0) - Com_Printf (" off"); - else - Com_Printf (cl_enemybottomcolor>=16?" 0x%06x":" %i", cl_enemybottomcolor&0x00ffffff); - } - Com_Printf ("\"\n"); - return; - } - - top = TP_ForceColour(Cmd_Argv(1)); - if (Cmd_Argc() == 2) + char *n = COM_Parse(var->string); + top = TP_ForceColour(com_token); + COM_Parse(n); + if (!*com_token) bottom = top; else { - bottom = TP_ForceColour(Cmd_Argv(2)); + bottom = TP_ForceColour(com_token); } -// if (top != cl_enemytopcolor || bottom != cl_enemybottomcolor) - { - cl_enemytopcolor = top; - cl_enemybottomcolor = bottom; + cl_enemytopcolor = top; + cl_enemybottomcolor = bottom; - if (qrenderer != QR_NONE) //make sure we have the renderer initialised... - for (i = 0; i < cl.allocated_client_slots; i++) - CL_NewTranslation(i); - } + if (qrenderer != QR_NONE) //make sure we have the renderer initialised... + for (i = 0; i < cl.allocated_client_slots; i++) + CL_NewTranslation(i); } + + +static void QDECL TP_SkinCvar_Callback(struct cvar_s *var, char *oldvalue) +{ + Skin_FlushPlayers(); +} + #endif //=================================================================== @@ -3681,11 +3659,6 @@ static void TP_MsgFilter_f (void) } } -static void QDECL TP_SkinCvar_Callback(struct cvar_s *var, char *oldvalue) -{ - Skin_FlushPlayers(); -} - void TP_Init (void) { #define TEAMPLAYVARS "Teamplay Variables" @@ -3703,8 +3676,6 @@ void TP_Init (void) Cmd_AddCommand ("filter", TP_MsgFilter_f); Cmd_AddCommand ("msg_trigger", TP_MsgTrigger_f); #ifdef QWSKINS - Cmd_AddCommand ("teamcolor", TP_TeamColor_f); - Cmd_AddCommand ("enemycolor", TP_EnemyColor_f); Cmd_AddCommand ("colourise", TP_Colourise_f); //uk Cmd_AddCommand ("colorize", TP_Colourise_f); //us //Cmd_AddCommand ("colorise", TP_Colourise_f); //piss off both. diff --git a/engine/common/com_mesh.c b/engine/common/com_mesh.c index 64d6c266..09c55cba 100644 --- a/engine/common/com_mesh.c +++ b/engine/common/com_mesh.c @@ -9287,7 +9287,7 @@ static qboolean QDECL Mod_LoadObjModel(model_t *mod, void *buffer, size_t fsize) size_t numverts = 0; size_t maxverts = 0; - struct objvert *vert = NULL, defaultvert={{-1,-1,-1}}; + struct objvert *vert = NULL, defaultvert={{-1,-1,-2}}; size_t numelems = 0; size_t maxelems = 0; @@ -9351,11 +9351,10 @@ static qboolean QDECL Mod_LoadObjModel(model_t *mod, void *buffer, size_t fsize) if(!isspace(c[1])) continue; while(isalpha(*c)) c++; while(isspace(*c)) c++; - int key = strtol(c, &c, 10); //make sure that these verts are not merged, ensuring that they get smoothed. //different texture coords will still have discontinuities though. - defaultvert.attrib[2] = -2-key; + defaultvert.attrib[2] = -1-strtol(c, &c, 10); break; } case 'f': @@ -9425,7 +9424,7 @@ static qboolean QDECL Mod_LoadObjModel(model_t *mod, void *buffer, size_t fsize) { if (vert[cur].attrib[0] == vkey.attrib[0] && vert[cur].attrib[1] == vkey.attrib[1] && - vert[cur].attrib[2] == vkey.attrib[2]) + vert[cur].attrib[2] == vkey.attrib[2] && vkey.attrib[2]!=-1) break; } if (cur == numverts) diff --git a/engine/common/fs.c b/engine/common/fs.c index 43c85c62..8c2c6b97 100644 --- a/engine/common/fs.c +++ b/engine/common/fs.c @@ -556,6 +556,10 @@ static qboolean FS_Manifest_ParseTokens(ftemanifest_t *man) Z_Free(man->defaultexec); man->defaultexec = Z_StrDup(Cmd_Argv(1)); } + else if (!Q_strcasecmp(cmd, "-bind") || !Q_strcasecmp(cmd, "-set") || !Q_strcasecmp(cmd, "-seta") || !Q_strcasecmp(cmd, "-alias")) + { + Z_StrCat(&man->defaultexec, va("%s %s\n", Cmd_Argv(0)+1, Cmd_Args())); + } else if (!Q_strcasecmp(cmd, "bind") || !Q_strcasecmp(cmd, "set") || !Q_strcasecmp(cmd, "seta") || !Q_strcasecmp(cmd, "alias")) { Z_StrCat(&man->defaultoverrides, va("%s %s\n", Cmd_Argv(0), Cmd_Args())); @@ -810,7 +814,7 @@ static int QDECL COM_Dir_List(const char *name, qofs_t size, time_t mtime, void } else if (!Q_strcasecmp(link, "bsp") || !Q_strcasecmp(link, "spr") || !Q_strcasecmp(link, "mdl") || !Q_strcasecmp(link, "md3") || !Q_strcasecmp(link, "iqm") || !Q_strcasecmp(link, "vvm") || !Q_strcasecmp(link, "psk") || !Q_strcasecmp(link, "dpm") || !Q_strcasecmp(link, "zym") || !Q_strcasecmp(link, "md5mesh") || - !Q_strcasecmp(link, "mdx") || !Q_strcasecmp(link, "md2") || + !Q_strcasecmp(link, "mdx") || !Q_strcasecmp(link, "md2") || !Q_strcasecmp(link, "obj") || !Q_strcasecmp(link, "md5anim") || !Q_strcasecmp(link, "gltf") || !Q_strcasecmp(link, "glb") || !Q_strcasecmp(link, "ase") || !Q_strcasecmp(link, "lwo")) Q_snprintfz(link, sizeof(link), "\\tip\\Open in Model Viewer\\modelviewer\\%s", name); else if (!Q_strcasecmp(link, "qc") || !Q_strcasecmp(link, "src") || !Q_strcasecmp(link, "qh") || !Q_strcasecmp(link, "h") || !Q_strcasecmp(link, "c") @@ -6405,12 +6409,18 @@ static void COM_InitHomedir(ftemanifest_t *man) { wchar_t wfolder[MAX_PATH]; char folder[MAX_OSPATH]; - // 0x5 == CSIDL_PERSONAL - if (dSHGetFolderPathW(NULL, 0x5, NULL, 0, wfolder) == S_OK) + if (dSHGetFolderPathW(NULL, 0x5/*CSIDL_PERSONAL*/, NULL, 0, wfolder) == S_OK) { narrowen(folder, sizeof(folder), wfolder); Q_snprintfz(com_homepath, sizeof(com_homepath), "%s/My Games/%s/", folder, HOMESUBDIR); } + //at some point, microsoft (in their infinitesimal wisdom) decided that 'CSIDL_PERSONAL' should mean 'CSIDL_GIVEITALLTOMICROSOFT' + //so use the old/CSIDL_NOTACTUALLYPERSONAL path by default for compat, but if there's nothing there then switch to CSIDL_LOCAL_APPDATA instead + if ((!*com_homepath||!GetFileAttributesU(com_homepath)) && dSHGetFolderPathW(NULL, 0x1c/*CSIDL_LOCAL_APPDATA*/, NULL, 0, wfolder) == S_OK) + { + narrowen(folder, sizeof(folder), wfolder); + Q_snprintfz(com_homepath, sizeof(com_homepath), "%s/%s/", folder, HOMESUBDIR); + } } // if (shfolder) // FreeLibrary(shfolder); diff --git a/engine/common/pr_bgcmd.c b/engine/common/pr_bgcmd.c index 65aa9a93..aed5bc50 100644 --- a/engine/common/pr_bgcmd.c +++ b/engine/common/pr_bgcmd.c @@ -2904,6 +2904,8 @@ void QCBUILTIN PF_WasFreed (pubprogfuncs_t *prinst, struct globalvars_s *pr_glob { wedict_t *ent; ent = G_WEDICT(prinst, OFS_PARM0); + if (!ent) + PR_BIError(prinst, "PF_WasFreed: invalid entity"); G_FLOAT(OFS_RETURN) = ED_ISFREE(ent); } @@ -2920,7 +2922,12 @@ void QCBUILTIN PF_edict_for_num(pubprogfuncs_t *prinst, struct globalvars_s *pr_ unsigned int num = G_FLOAT(OFS_PARM0); if (num >= w->num_edicts) RETURN_EDICT(prinst, w->edicts); - G_INT(OFS_RETURN) = num; //just directly store it. if its not spawned yet we'll need to catch that elsewhere anyway. + else + { + G_INT(OFS_RETURN) = num; //just directly store it. if its not spawned yet we'll need to catch that elsewhere anyway. + if (G_WEDICT(prinst, OFS_RETURN)) + RETURN_EDICT(prinst, w->edicts); //hoi! it wasn't valid! + } } /* diff --git a/engine/common/pr_common.h b/engine/common/pr_common.h index 57b77213..cdae5651 100644 --- a/engine/common/pr_common.h +++ b/engine/common/pr_common.h @@ -404,6 +404,9 @@ void QCBUILTIN PF_CL_stringwidth (pubprogfuncs_t *prinst, struct globalvars_s *p void QCBUILTIN PF_CL_drawsubpic (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_CL_drawrotpic (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_CL_drawrotsubpic (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); +#ifdef HAVE_LEGACY +void QCBUILTIN PF_CL_drawrotpic_dp (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); +#endif void QCBUILTIN PF_CL_findfont (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_CL_loadfont (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); #if defined(CSQC_DAT) && !defined(SERVERONLY) diff --git a/engine/common/world.h b/engine/common/world.h index 955e2277..fa2b8fc1 100644 --- a/engine/common/world.h +++ b/engine/common/world.h @@ -331,7 +331,8 @@ void World_TouchLinks (world_t *w, wedict_t *ent, areanode_t *node); #define World_TouchAllLinks(w,e) World_TouchLinks(w,e,(w)->areanodes) #endif -int World_PointContents (world_t *w, vec3_t p); +int World_PointContentsWorldOnly (world_t *w, vec3_t p); +int World_PointContentsAllBSPs (world_t *w, vec3_t p); // returns the CONTENTS_* value from the world at the given point. // does not check any entities at all diff --git a/engine/gl/gl_bloom.c b/engine/gl/gl_bloom.c index a205384c..6b644dd3 100644 --- a/engine/gl/gl_bloom.c +++ b/engine/gl/gl_bloom.c @@ -311,14 +311,14 @@ void R_BloomBlend (texid_t source, int x, int y, int w, int h) return; /*update textures if we need to resize them*/ - R_SetupBloomTextures(w, h); + R_SetupBloomTextures(r_refdef.pxrect.width, r_refdef.pxrect.height); /*filter the screen into a downscaled image*/ if (!TEXVALID(pingtex[0][0])) { sprintf(name, "***bloom*%c*%i***", 'a'+0, 0); TEXASSIGN(pingtex[0][0], Image_CreateTexture(name, NULL, IF_CLAMP|IF_NOMIPMAP|IF_NOPICMIP|IF_LINEAR)); - Image_Upload(pingtex[0][0], TF_RGBA32, NULL, NULL, texwidth[0], texheight[0], IF_CLAMP|IF_NOMIPMAP|IF_NOPICMIP|IF_LINEAR); + Image_Upload(pingtex[0][0], PTI_RGBA8, NULL, NULL, texwidth[0], texheight[0], IF_CLAMP|IF_NOMIPMAP|IF_NOPICMIP|IF_LINEAR|IF_NOSRGB); } if (R2D_Flush) @@ -341,13 +341,13 @@ void R_BloomBlend (texid_t source, int x, int y, int w, int h) { sprintf(name, "***bloom*%c*%i***", 'a'+0, i); TEXASSIGN(pingtex[0][i], Image_CreateTexture(name, NULL, IF_CLAMP|IF_NOMIPMAP|IF_NOPICMIP|IF_LINEAR)); - Image_Upload(pingtex[0][i], TF_RGBA32, NULL, NULL, texwidth[i], texheight[i], IF_CLAMP|IF_NOMIPMAP|IF_NOPICMIP|IF_LINEAR); + Image_Upload(pingtex[0][i], PTI_RGBA8, NULL, NULL, texwidth[i], texheight[i], IF_CLAMP|IF_NOMIPMAP|IF_NOPICMIP|IF_LINEAR|IF_NOSRGB); } if (!TEXVALID(pingtex[1][i])) { sprintf(name, "***bloom*%c*%i***", 'a'+1, i); TEXASSIGN(pingtex[1][i], Image_CreateTexture(name, NULL, IF_CLAMP|IF_NOMIPMAP|IF_NOPICMIP|IF_LINEAR)); - Image_Upload(pingtex[1][i], TF_RGBA32, NULL, NULL, texwidth[i], texheight[i], IF_CLAMP|IF_NOMIPMAP|IF_NOPICMIP|IF_LINEAR); + Image_Upload(pingtex[1][i], PTI_RGBA8, NULL, NULL, texwidth[i], texheight[i], IF_CLAMP|IF_NOMIPMAP|IF_NOPICMIP|IF_LINEAR|IF_NOSRGB); } if (R2D_Flush) diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c index 1ef843a0..fe3aada5 100644 --- a/engine/gl/gl_shader.c +++ b/engine/gl/gl_shader.c @@ -4863,7 +4863,12 @@ void Shader_Programify (parsestate_t *ps) { pass = &s->passes[i]; if (pass->rgbgen == RGB_GEN_LIGHTING_DIFFUSE || pass->rgbgen == RGB_GEN_ENTITY_LIGHTING_DIFFUSE) - modellighting = pass; + { + if (s->usageflags & SUF_LIGHTMAP) + lightmap = pass; + else + modellighting = pass; + } else if (pass->rgbgen == RGB_GEN_ENTITY) modellighting = pass; else if (pass->rgbgen == RGB_GEN_VERTEX_LIGHTING || pass->rgbgen == RGB_GEN_VERTEX_EXACT) diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index df8e0f36..8e3eae3a 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -4828,7 +4828,7 @@ static void QCBUILTIN PF_pointcontents (pubprogfuncs_t *prinst, struct globalvar v = G_VECTOR(OFS_PARM0); - cont = World_PointContents(w, v); + cont = World_PointContentsWorldOnly(w, v); if (cont & FTECONTENTS_SOLID) G_FLOAT(OFS_RETURN) = Q1CONTENTS_SOLID; else if (cont & FTECONTENTS_SKY) diff --git a/engine/server/pr_q1qvm.c b/engine/server/pr_q1qvm.c index 99dd6c01..71b3829f 100755 --- a/engine/server/pr_q1qvm.c +++ b/engine/server/pr_q1qvm.c @@ -1176,7 +1176,7 @@ static qintptr_t QVM_WriteEntity (void *offset, quintptr_t mask, const qintptr_t } static qintptr_t QVM_FlushSignon (void *offset, quintptr_t mask, const qintptr_t *arg) { - SV_FlushSignon (); + SV_FlushSignon (false); return 0; } static qintptr_t QVM_memset (void *offset, quintptr_t mask, const qintptr_t *arg) diff --git a/engine/server/server.h b/engine/server/server.h index 4a656800..74736c17 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -21,8 +21,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define QW_SERVER -#define MAX_SIGNON_BUFFERS 16 - typedef enum { ss_dead, // no map loaded ss_clustermode, @@ -229,9 +227,8 @@ typedef struct // multiple signon messages are kept // fte only stores writebyted stuff in here. everything else is regenerated based upon the client's extensions. sizebuf_t signon; - int num_signon_buffers; - int signon_buffer_size[MAX_SIGNON_BUFFERS]; - qbyte signon_buffers[MAX_SIGNON_BUFFERS][MAX_DATAGRAM]; + int used_signon_space; + qbyte signon_buffer[MAX_OVERALLMSGLEN]; //flushed after every 512 bytes (two leading bytes says the size of the buffer). qboolean gamedirchanged; @@ -1261,7 +1258,7 @@ void MSV_OpenUserDatabase(void); // void SV_SpawnServer (const char *server, const char *startspot, qboolean noents, qboolean usecinematic); void SV_UnspawnServer (void); -void SV_FlushSignon (void); +void SV_FlushSignon (qboolean force); void SV_UpdateMaxPlayers(int newmax); void SV_FilterImpulseInit(void); diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index eccbb666..d6a43052 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -118,19 +118,26 @@ int SV_SafeModelIndex (char *name) SV_FlushSignon Moves to the next signon buffer if needed +This stops any chunk from getting too large, hopefully, but if the worst happens then hopefully network fragmentation will work. ================ */ -void SV_FlushSignon (void) -{ - if (sv.signon.cursize < sv.signon.maxsize - 512) - return; +void SV_FlushSignon (qboolean force) +{ //flush only when it gets too big. + if (sv.signon.cursize < MAX_DATAGRAM - 512) + { + if (!force || !sv.signon.cursize) + return; + } - if (sv.num_signon_buffers == MAX_SIGNON_BUFFERS-1) - SV_Error ("sv.num_signon_buffers == MAX_SIGNON_BUFFERS-1"); + if (sv.signon.cursize) + { + sv.signon.data[-2] = (sv.signon.cursize>>0)&0xff; + sv.signon.data[-1] = (sv.signon.cursize>>8)&0xff; + sv.used_signon_space += 2+sv.signon.cursize; + } - sv.signon_buffer_size[sv.num_signon_buffers-1] = sv.signon.cursize; - sv.signon.data = sv.signon_buffers[sv.num_signon_buffers]; - sv.num_signon_buffers++; + sv.signon.data = sv.signon_buffer + sv.used_signon_space+2; + sv.signon.maxsize = sizeof(sv.signon_buffer) - (sv.used_signon_space+2); sv.signon.cursize = 0; sv.signon.prim = svs.netprim; } @@ -784,10 +791,10 @@ void SV_SetupNetworkBuffers(qboolean bigcoords) sv.master.data = sv.master_buf; sv.master.prim = msg_nullnetprim; - sv.signon.maxsize = sizeof(sv.signon_buffers[0]); - sv.signon.data = sv.signon_buffers[0]; + sv.signon.data = sv.signon_buffer+2; + sv.used_signon_space = 0; sv.signon.prim = svs.netprim; - sv.num_signon_buffers = 1; + sv.signon.maxsize = sizeof(sv.signon_buffer)-sv.used_signon_space; } void SV_WipeServerState(void) @@ -1612,7 +1619,8 @@ MSV_OpenUserDatabase(); #ifdef Q2SERVER SVQ2_BuildBaselines(); #endif - sv.signon_buffer_size[sv.num_signon_buffers-1] = sv.signon.cursize; + + SV_FlushSignon(true); // all spawning is completed, any further precache statements // or prog writes to the signon message are errors @@ -1679,7 +1687,7 @@ MSV_OpenUserDatabase(); #endif ) { - if (sv.num_signon_buffers > 1 || sv.signon.cursize) + if (sv.used_signon_space || sv.signon.cursize) Con_Printf("Cannot auto-enable extended coords as the init buffer was used\n"); else { diff --git a/engine/server/sv_move.c b/engine/server/sv_move.c index 860704b3..298227b9 100644 --- a/engine/server/sv_move.c +++ b/engine/server/sv_move.c @@ -88,7 +88,7 @@ qboolean World_CheckBottom (world_t *world, wedict_t *ent, vec3_t up) { start[a0] = x ? maxs[a0] : mins[a0]; start[a1] = y ? maxs[a1] : mins[a1]; - if (!(World_PointContents (world, start) & FTECONTENTS_SOLID)) + if (!(World_PointContentsWorldOnly (world, start) & FTECONTENTS_SOLID)) goto realcheck; } @@ -195,7 +195,7 @@ qboolean World_movestep (world_t *world, wedict_t *ent, vec3_t move, vec3_t axis if (trace.fraction == 1) { - if ( (eflags & FL_SWIM) && !(World_PointContents(world, trace.endpos) & FTECONTENTS_FLUID)) + if ( (eflags & FL_SWIM) && !(World_PointContentsWorldOnly(world, trace.endpos) & FTECONTENTS_FLUID)) continue; // swim monster left water VectorCopy (trace.endpos, ent->v->origin); diff --git a/engine/server/sv_phys.c b/engine/server/sv_phys.c index c7b8b0be..125aefa6 100644 --- a/engine/server/sv_phys.c +++ b/engine/server/sv_phys.c @@ -1261,7 +1261,7 @@ static void WPhys_CheckWaterTransition (world_t *w, wedict_t *ent) { int cont; - cont = World_PointContents (w, ent->v->origin); + cont = World_PointContentsWorldOnly (w, ent->v->origin); //needs to be q1 progs compatible if (cont & FTECONTENTS_LAVA) @@ -1602,11 +1602,38 @@ static void WPhys_CheckStuck (world_t *w, wedict_t *ent) ============= SV_CheckWater ============= + +for players */ static qboolean WPhys_CheckWater (world_t *w, wedict_t *ent) { vec3_t point; int cont; + int hc; + trace_t tr; + + //check if we're on a ladder, and if so fire a trace forwards to ensure its a valid ladder instead of a random volume + hc = ent->xv->hitcontentsmaski; //lame + ent->xv->hitcontentsmaski = ~0; + tr = World_Move(w, ent->v->origin, ent->v->mins, ent->v->maxs, ent->v->origin, 0, ent); + ent->xv->hitcontentsmaski = hc; + if (tr.contents & FTECONTENTS_LADDER) + { + vec3_t flatforward; + flatforward[0] = cos((M_PI/180)*ent->v->angles[1]); + flatforward[1] = sin((M_PI/180)*ent->v->angles[1]); + flatforward[2] = 0; + VectorMA (ent->v->origin, 24, flatforward, point); + + tr = World_Move(w, ent->v->origin, ent->v->mins, ent->v->maxs, point, 0, ent); + if (tr.fraction < 1) + ent->xv->pmove_flags = (int)ent->xv->pmove_flags|PMF_LADDER; + else if ((int)ent->xv->pmove_flags & PMF_LADDER) + ent->xv->pmove_flags -= PMF_LADDER; + } + else if ((int)ent->xv->pmove_flags & PMF_LADDER) + ent->xv->pmove_flags -= PMF_LADDER; + point[0] = ent->v->origin[0]; point[1] = ent->v->origin[1]; @@ -1614,7 +1641,7 @@ static qboolean WPhys_CheckWater (world_t *w, wedict_t *ent) ent->v->waterlevel = 0; ent->v->watertype = Q1CONTENTS_EMPTY; - cont = World_PointContents (w, point); + cont = World_PointContentsAllBSPs (w, point); if (cont & FTECONTENTS_FLUID) { if (cont & FTECONTENTS_LAVA) @@ -1627,12 +1654,12 @@ static qboolean WPhys_CheckWater (world_t *w, wedict_t *ent) ent->v->watertype = Q1CONTENTS_SKY; ent->v->waterlevel = 1; point[2] = ent->v->origin[2] + (ent->v->mins[2] + ent->v->maxs[2])*0.5; - cont = World_PointContents (w, point); + cont = World_PointContentsAllBSPs (w, point); if (cont & FTECONTENTS_FLUID) { ent->v->waterlevel = 2; point[2] = ent->v->origin[2] + ent->v->view_ofs[2]; - cont = World_PointContents (w, point); + cont = World_PointContentsAllBSPs (w, point); if (cont & FTECONTENTS_FLUID) ent->v->waterlevel = 3; } @@ -2242,7 +2269,8 @@ void WPhys_RunEntity (world_t *w, wedict_t *ent) gravitydir = w->g.defaultgravitydir; if (!WPhys_CheckWater (w, ent) && ! ((int)ent->v->flags & FL_WATERJUMP) ) //Vanilla Bug: the QC checks waterlevel inside PlayerPreThink, with waterlevel from a different position from the origin. - WPhys_AddGravity (w, ent, gravitydir); + if (!((int)ent->xv->pmove_flags & PMF_LADDER)) + WPhys_AddGravity (w, ent, gravitydir); WPhys_CheckStuck (w, ent); WPhys_WalkMove (w, ent, gravitydir); diff --git a/engine/server/sv_send.c b/engine/server/sv_send.c index 1f9e4faf..cac18301 100644 --- a/engine/server/sv_send.c +++ b/engine/server/sv_send.c @@ -541,7 +541,7 @@ void SV_MulticastProtExt(vec3_t origin, multicast_t to, int dimension_mask, int if (to == MULTICAST_INIT) { //we only have one signon buffer. make sure you don't put non-identical protocols in the buffer - SV_FlushSignon(); + SV_FlushSignon(false); SZ_Write (&sv.signon, sv.multicast.data, sv.multicast.cursize); //and send to players that are already on diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index d6ace095..1e63d418 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -1461,24 +1461,32 @@ void SV_SendClientPrespawnInfo(client_t *client) if (client->prespawn_stage == PRESPAWN_SIGNON_BUF) { + int nextsize; while (client->netchan.message.cursize < maxsize) { - if (client->prespawn_idx >= sv.num_signon_buffers) + if (client->prespawn_idx >= sv.used_signon_space) { client->prespawn_stage++; client->prespawn_idx = 0; break; } - if (client->netchan.message.cursize+sv.signon_buffer_size[client->prespawn_idx]+30 < client->netchan.message.maxsize) + nextsize = sv.signon_buffer[client->prespawn_idx] | (sv.signon_buffer[client->prespawn_idx+1]<<8); + if (client->netchan.message.cursize+nextsize+30 <= client->netchan.message.maxsize) { - SZ_Write (&client->netchan.message, - sv.signon_buffers[client->prespawn_idx], - sv.signon_buffer_size[client->prespawn_idx]); + SZ_Write (&client->netchan.message, sv.signon_buffer+client->prespawn_idx+2, nextsize); + client->prespawn_idx+=2+nextsize; + } + else if (!client->netchan.message.cursize && nextsize+30 > client->netchan.message.maxsize) + { //signon data is meant to be split up into smallish chunks to avoid network fragmentation. + //but sometimes a single blob is too large (eg: gamecode not using MSG_MULTICAST and just writing 16k in one splurge) + //fteqw and nq protocols can cope, vanilla qw cannot, so we do need to warn. the alternative is to kick. + SV_PrintToClient(client, PRINT_HIGH, va("\x01" "Dropping %i bytes of signon data\n", nextsize)); + client->prespawn_idx+=2+nextsize; + break; } else break; - client->prespawn_idx++; } } @@ -8996,6 +9004,80 @@ static void SV_WaterMove (void) velocity[i] += accelspeed * wishvel[i]; } + +static void SV_LadderMove (void) +{ + int i; + vec3_t wishvel; + float speed, newspeed, wishspeed, addspeed, accelspeed; + float scale; + float maxspeed; + +// +// user intentions +// + AngleVectors (sv_player->v->v_angle, forward, right, up); + + for (i=0 ; i<3 ; i++) + wishvel[i] = forward[i]*cmd.forwardmove + right[i]*cmd.sidemove; + wishvel[2] += cmd.upmove; + + wishspeed = Length(wishvel); +// val = GetEdictFieldValue(sv_player, "scale", &scalecache); +// if (!val || !val->_float) + scale = 1; +// else +// scale = val->_float; + +// val = GetEdictFieldValue(sv_player, "maxspeed", &maxspeedcache); +// if (val && val->_float) +// maxspeed = sv_maxspeed.value*val->_float; +// else + maxspeed = host_client->maxspeed; + if (wishspeed > maxspeed*scale) + { + VectorScale (wishvel, maxspeed/wishspeed, wishvel); + wishspeed = maxspeed*scale; + } + wishspeed *= 0.7; + +// +// water friction +// + speed = Length (velocity); + if (speed) + { +// val = GetEdictFieldValue(sv_player, "friction", &frictioncache); +// if (val&&val->_float) +// newspeed = speed - host_frametime * speed * sv_friction.value*val->_float; +// else + newspeed = speed - host_frametime * speed * sv_friction.value; + if (newspeed < 0) + newspeed = 0; + VectorScale (velocity, newspeed/speed, velocity); + } + else + newspeed = 0; + +// +// water acceleration +// + if (!wishspeed) + return; + + addspeed = wishspeed - newspeed; + if (addspeed <= 0) + return; + + VectorNormalize (wishvel); + accelspeed = sv_accelerate.value * wishspeed * host_frametime; + if (accelspeed > addspeed) + accelspeed = addspeed; + + for (i=0 ; i<3 ; i++) + velocity[i] += accelspeed * wishvel[i]; +} + static void SV_WaterJump (void) { if (sv.time > sv_player->v->teleport_time @@ -9087,14 +9169,12 @@ void SV_ClientThink (void) // // walk // - if ( (sv_player->v->waterlevel >= 2) - && (sv_player->v->movetype != MOVETYPE_NOCLIP) ) - { + if ( (sv_player->v->waterlevel >= 2) && (sv_player->v->movetype != MOVETYPE_NOCLIP) ) SV_WaterMove (); - return; - } - - SV_AirMove (); + else if (((int)sv_player->xv->pmove_flags&PMF_LADDER) && (sv_player->v->movetype != MOVETYPE_NOCLIP) ) + SV_LadderMove(); + else + SV_AirMove (); } #endif diff --git a/engine/server/world.c b/engine/server/world.c index 82662e40..8df50f0d 100644 --- a/engine/server/world.c +++ b/engine/server/world.c @@ -53,6 +53,7 @@ typedef struct qboolean capsule; } moveclip_t; +static unsigned int World_ContentsOfAllLinks (world_t *w, vec3_t pos); /* =============================================================================== @@ -123,7 +124,7 @@ hull_t *World_HullForBox (vec3_t mins, vec3_t maxs) return &box_hull; } -model_t mod_capsule; +static model_t mod_capsule; qboolean World_BoxTrace(struct model_s *model, int hulloverride, int frame, vec3_t axis[3], vec3_t p1, vec3_t p2, vec3_t mins, vec3_t maxs, unsigned int against, struct trace_s *trace) { hull_t *hull = &box_hull; @@ -1151,11 +1152,18 @@ SV_PointContents ================== */ -int World_PointContents (world_t *w, vec3_t p) +int World_PointContentsWorldOnly (world_t *w, vec3_t p) { return w->worldmodel->funcs.PointContents(w->worldmodel, NULL, p); } +int World_PointContentsAllBSPs (world_t *w, vec3_t p) +{ + int c = w->worldmodel->funcs.PointContents(w->worldmodel, NULL, p); + c |= World_ContentsOfAllLinks(w, p); + return c; +} + //=========================================================================== /* @@ -1326,7 +1334,7 @@ static trace_t World_ClipMoveToEntity (world_t *w, wedict_t *ent, vec3_t eorg, v { memset (&trace, 0, sizeof(trace_t)); trace.fraction = 1; - trace.allsolid = true; + trace.allsolid = false; trace.startsolid = false; trace.inopen = true; //probably wrong... VectorCopy (end, trace.endpos); @@ -1460,13 +1468,13 @@ int World_AreaEdicts (world_t *w, vec3_t mins, vec3_t maxs, wedict_t **list, int } #else -float *area_mins, *area_maxs; -wedict_t **area_list; +static float *area_mins, *area_maxs; +static wedict_t **area_list; #ifdef Q2SERVER -q2edict_t **area_q2list; +static q2edict_t **area_q2list; #endif -int area_count, area_maxcount; -int area_type; +static int area_count, area_maxcount; +static int area_type; static void World_AreaEdicts_r (areanode_t *node) { link_t *l, *next, *start; @@ -2088,6 +2096,95 @@ static void World_ClipToAllLinks (world_t *w, moveclip_t *clip) World_ClipToLinks(w, &w->gridareas[g[0] + g[1]*w->gridsize[0]], clip); } } + +static unsigned int World_ContentsOfLinks (world_t *w, areagridlink_t *node, vec3_t pos) +{ + link_t *l, *next; + wedict_t *touch; + + model_t *model; + int mdlidx; + vec3_t pos_l, axis[3]; + unsigned int ret = 0, c; + +// touch linked edicts + for (l = node->l.next ; l != &node->l ; l = next) + { + next = l->next; + touch = ((areagridlink_t*)l)->ed; + + if (touch->gridareasequence == areagridsequence) + continue; + touch->gridareasequence = areagridsequence; + + if (touch->v->solid != SOLID_BSP) + continue; + + if ( pos[0] > touch->v->absmax[0] + || pos[1] > touch->v->absmax[1] + || pos[2] > touch->v->absmax[2] + || pos[0] < touch->v->absmin[0] + || pos[1] < touch->v->absmin[1] + || pos[2] < touch->v->absmin[2] ) + continue; + +// if (touch->v->solid == SOLID_PORTAL) +// //FIXME: recurse! + + mdlidx = touch->v->modelindex; + if (!mdlidx) + continue; + model = w->Get_CModel(w, mdlidx); + if (!model || (model->type != mod_brush && model->type != mod_heightmap) || model->loadstate != MLS_LOADED) + continue; + + VectorSubtract (pos, touch->v->origin, pos_l); + if (touch->v->angles[0] || touch->v->angles[1] || touch->v->angles[2]) + { + AngleVectors (touch->v->angles, axis[0], axis[1], axis[2]); + VectorNegate(axis[1], axis[1]); + c = model->funcs.PointContents(model, axis, pos_l); + } + else + c = model->funcs.PointContents(model, NULL, pos_l); + + if (c && touch->v->skin < 0) + { //if forcedcontents is set, then ALL brushes in this model are forced to the specified contents value. + //we achive this by tracing against ALL then forcing it after. + unsigned int forcedcontents; + switch((int)touch->v->skin) + { + case Q1CONTENTS_EMPTY: forcedcontents = FTECONTENTS_EMPTY; break; + case Q1CONTENTS_SOLID: forcedcontents = FTECONTENTS_SOLID; break; + case Q1CONTENTS_WATER: forcedcontents = FTECONTENTS_WATER; break; + case Q1CONTENTS_SLIME: forcedcontents = FTECONTENTS_SLIME; break; + case Q1CONTENTS_LAVA: forcedcontents = FTECONTENTS_LAVA; break; + case Q1CONTENTS_SKY: forcedcontents = FTECONTENTS_SKY; break; + case Q1CONTENTS_LADDER: forcedcontents = FTECONTENTS_LADDER;break; + default: forcedcontents = 0; break; + } + c = forcedcontents; + } + ret |= c; + } + return ret; +} +static unsigned int World_ContentsOfAllLinks (world_t *w, vec3_t pos) +{ + int ming[2], maxg[2], g[2]; + unsigned int ret; + areagridsequence++; + ret = World_ContentsOfLinks(w, &w->jumboarea, pos); + + CALCAREAGRIDBOUNDS(w, pos, pos); + + for ( g[0] = ming[0]; g[0] < maxg[0]; g[0]++) + for (g[1] = ming[1]; g[1] < maxg[1]; g[1]++) + { + ret |= World_ContentsOfLinks(w, &w->gridareas[g[0] + g[1]*w->gridsize[0]], pos); + } + return ret; +} #else /* ==================== @@ -2227,6 +2324,62 @@ static void World_ClipToLinks (world_t *w, areanode_t *node, moveclip_t *clip) if ( clip->boxmins[node->axis] < node->dist ) World_ClipToLinks (w, node->children[1], clip ); } + + + +static unsigned int World_ContentsOfLinks (world_t *w, areanode_t *node, vec3_t pos) +{ + unsigned int c = 0; + link_t *l, *next; + wedict_t *touch; + trace_t trace; + +// touch linked edicts + for (l = node->edicts.next ; l != &node->edicts ; l = next) + { + next = l->next; + touch = EDICT_FROM_AREA(l); + if (touch->v->solid == SOLID_NOT) + continue; + + /*if its a trigger, we only clip against it if the flags are aligned*/ + if (touch->v->solid == SOLID_TRIGGER) + continue; + + if (pos[0] > touch->v->absmax[0] + || pos[1] > touch->v->absmax[1] + || pos[2] > touch->v->absmax[2] + || pos[0] < touch->v->absmin[0] + || pos[1] < touch->v->absmin[1] + || pos[2] < touch->v->absmin[2] ) + continue; + + /*if (touch->v->solid == SOLID_PORTAL) + { + //make sure we don't hit the world if we're inside the portal + World_PortalCSG(touch, vec3_origin, vec3_origin, pos, pos, &clip->trace); + }*/ + + trace = World_ClipMoveToEntity (w, touch, touch->v->origin, touch->v->angles, pos, vec3_origin, vec3_origin, pos, 0, false, false, ~0u); + if (trace.startsolid) + c |= trace.contents; + } + +// recurse down both sides + if (node->axis == -1) + return c; + + if ( pos[node->axis] > node->dist ) + c |= World_ContentsOfLinks (w, node->children[0], pos ); + if ( pos[node->axis] < node->dist ) + c |= World_ContentsOfLinks (w, node->children[1], pos ); + + return c; +} +static unsigned int World_ContentsOfAllLinks (world_t *w, vec3_t pos) +{ + return World_ContentsOfLinks(w, w->areanodes, pos); +} #endif #if defined(HAVE_CLIENT) && defined(CSQC_DAT) @@ -2329,6 +2482,9 @@ static void World_ClipToNetwork (world_t *w, moveclip_t *clip) }*/ } + if (model && touchcontents != ~0) + trace.contents = touchcontents; + if (trace.fraction < clip->trace.fraction) { //trace traveled less, but don't forget if we started in a solid. diff --git a/plugins/models/exportiqm.c b/plugins/models/exportiqm.c index 9ec470fd..447e77b8 100644 --- a/plugins/models/exportiqm.c +++ b/plugins/models/exportiqm.c @@ -541,8 +541,10 @@ void Mod_ExportIQM(char *fname, int flags, galiasinfo_t *mesh) if (m->ofs_skel_xyz) ivert = m->ofs_skel_xyz; +#ifdef NONSKELETALMODELS else if (m->numanimations && m->ofsanimations->numposes) ivert = m->ofsanimations->poseofs->ofsverts; +#endif else ivert = NULL; if (ivert) @@ -588,12 +590,14 @@ void Mod_ExportIQM(char *fname, int flags, galiasinfo_t *mesh) isdir = m->ofs_skel_svect; itdir = m->ofs_skel_tvect; } +#ifdef NONSKELETALMODELS else if (m->numanimations && m->ofsanimations->numposes) { inorm = m->ofsanimations->poseofs->ofsnormals; isdir = m->ofsanimations->poseofs->ofssvector; itdir = m->ofsanimations->poseofs->ofstvector; } +#endif else { inorm = NULL; diff --git a/plugins/terrorgen/terragen.c b/plugins/terrorgen/terragen.c index 2a969107..bff2e72c 100644 --- a/plugins/terrorgen/terragen.c +++ b/plugins/terrorgen/terragen.c @@ -11,6 +11,7 @@ FIXME: no way to speciffy which gen plugin to use for a particular map #include "com_mesh.h" #include "gl_terrain.h" +#ifdef TERRAIN #define GENHIGHTSCALE 1024.0 static plugterrainfuncs_t *terr; @@ -276,4 +277,10 @@ qboolean Plug_Init(void) terr->AutogenerateSection = TerrorGen_GenerateBlock; return true; -} \ No newline at end of file +} +#else +qboolean Plug_Init(void) +{ + return false; +} +#endif \ No newline at end of file