From 4ad18eaf919d3b62b5818f29bc75a8e8fcf998e0 Mon Sep 17 00:00:00 2001 From: Mario Date: Fri, 25 Sep 2020 20:02:50 +1000 Subject: [PATCH] Further cleanup miscfunctions, document the need to use intrusive lists on entities that use ECS components --- qcsrc/client/miscfunctions.qc | 14 - qcsrc/client/miscfunctions.qh | 4 - qcsrc/client/shownames.qc | 2 +- .../gamemode/domination/sv_domination.qc | 1 + .../gamemode/onslaught/sv_onslaught.qc | 1 + qcsrc/common/monsters/monster/spider.qc | 2 - qcsrc/common/notifications/all.qc | 1 + qcsrc/common/playerstats.qc | 2 +- qcsrc/common/state.qc | 3 +- qcsrc/common/turrets/sv_turrets.qc | 1 + qcsrc/common/util.qc | 130 ++++++ qcsrc/common/util.qh | 18 + qcsrc/ecs/README.md | 1 + qcsrc/lib/warpzone/server.qc | 1 + qcsrc/server/chat.qc | 8 +- qcsrc/server/cheats.qc | 8 +- qcsrc/server/client.qc | 4 +- qcsrc/server/command/cmd.qc | 5 +- qcsrc/server/command/common.qc | 2 +- qcsrc/server/command/getreplies.qc | 135 ++++++ qcsrc/server/command/getreplies.qh | 6 + qcsrc/server/command/sv_cmd.qc | 18 +- qcsrc/server/command/vote.qc | 2 +- qcsrc/server/items/items.qc | 1 + qcsrc/server/miscfunctions.qc | 415 ------------------ qcsrc/server/miscfunctions.qh | 24 - qcsrc/server/player.qc | 75 ++++ qcsrc/server/player.qh | 2 + qcsrc/server/race.qc | 12 + qcsrc/server/race.qh | 2 + qcsrc/server/scores.qc | 4 +- qcsrc/server/weapons/common.qc | 61 +++ qcsrc/server/weapons/common.qh | 4 + qcsrc/server/weapons/hitplot.qc | 2 +- qcsrc/server/world.qc | 20 +- qcsrc/server/world.qh | 2 + 36 files changed, 501 insertions(+), 492 deletions(-) diff --git a/qcsrc/client/miscfunctions.qc b/qcsrc/client/miscfunctions.qc index a653f4bf1..34d87b772 100644 --- a/qcsrc/client/miscfunctions.qc +++ b/qcsrc/client/miscfunctions.qc @@ -159,20 +159,6 @@ float PreviewExists(string name) return false; } -// decolorizes and team colors the player name when needed -string playername(string thename, float teamid) -{ - TC(int, teamid); - string t; - if (teamplay) - { - t = Team_ColorCode(teamid); - return strcat(t, strdecolorize(thename)); - } - else - return strdecolorize(thename); -} - float cvar_or(string cv, float v) { string s; diff --git a/qcsrc/client/miscfunctions.qh b/qcsrc/client/miscfunctions.qh index 5641215ae..f92633a41 100644 --- a/qcsrc/client/miscfunctions.qh +++ b/qcsrc/client/miscfunctions.qh @@ -36,10 +36,6 @@ vector Rotate(vector v, float a); #define IS_DEAD(s) (((s).classname == "csqcmodel") ? (s).csqcmodel_isdead : (GetResource((s), RES_HEALTH) <= 0)) - -// decolorizes and team colors the player name when needed -string playername(string thename, float teamid); - float cvar_or(string cv, float v); vector project_3d_to_2d(vector vec); diff --git a/qcsrc/client/shownames.qc b/qcsrc/client/shownames.qc index 7c126f10f..ad05c4a2a 100644 --- a/qcsrc/client/shownames.qc +++ b/qcsrc/client/shownames.qc @@ -177,7 +177,7 @@ void Draw_ShowNames(entity this) } string s = entcs_GetName(this.sv_entnum - 1); if ((autocvar_hud_shownames_decolorize == 1 && teamplay) || autocvar_hud_shownames_decolorize == 2) - s = playername(s, entcs_GetTeam(this.sv_entnum - 1)); + s = playername(s, entcs_GetTeam(this.sv_entnum - 1), true); drawfontscale = '1 1 0' * resize; s = textShortenToWidth(s, namewidth, '1 1 0' * autocvar_hud_shownames_fontsize, stringwidth_colors); float width = stringwidth(s, true, '1 1 0' * autocvar_hud_shownames_fontsize); diff --git a/qcsrc/common/gamemodes/gamemode/domination/sv_domination.qc b/qcsrc/common/gamemodes/gamemode/domination/sv_domination.qc index 0a6ee791a..d8e2bb362 100644 --- a/qcsrc/common/gamemodes/gamemode/domination/sv_domination.qc +++ b/qcsrc/common/gamemodes/gamemode/domination/sv_domination.qc @@ -6,6 +6,7 @@ #include #include #include +#include #include #include diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qc b/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qc index 1c87e9af6..f9011c7d9 100644 --- a/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qc +++ b/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qc @@ -6,6 +6,7 @@ #include #include #include +#include #include #include diff --git a/qcsrc/common/monsters/monster/spider.qc b/qcsrc/common/monsters/monster/spider.qc index 08d282187..d96544882 100644 --- a/qcsrc/common/monsters/monster/spider.qc +++ b/qcsrc/common/monsters/monster/spider.qc @@ -125,8 +125,6 @@ void M_Spider_Attack_Web_Touch(entity this, entity toucher) M_Spider_Attack_Web_Explode(this); } -void adaptor_think2use_hittype_splash(entity this); - void M_Spider_Attack_Web(entity this) { sound(this, CH_SHOTS, SND_ELECTRO_FIRE2, VOL_BASE, ATTEN_NORM); diff --git a/qcsrc/common/notifications/all.qc b/qcsrc/common/notifications/all.qc index e7eaa7cb2..e2c1e185a 100644 --- a/qcsrc/common/notifications/all.qc +++ b/qcsrc/common/notifications/all.qc @@ -7,6 +7,7 @@ #include #include #include + #include #include #include #endif diff --git a/qcsrc/common/playerstats.qc b/qcsrc/common/playerstats.qc index 6760ea043..cbf7f9eef 100644 --- a/qcsrc/common/playerstats.qc +++ b/qcsrc/common/playerstats.qc @@ -136,7 +136,7 @@ void PlayerStats_GameReport_FinalizePlayer(entity p) db_put(PS_GR_OUT_DB, sprintf("%s:_playerid", p.playerstats_id), ftos(p.playerid)); if(CS(p).cvar_cl_allow_uid2name == 1 || IS_BOT_CLIENT(p)) - db_put(PS_GR_OUT_DB, sprintf("%s:_netname", p.playerstats_id), playername(p, false)); + db_put(PS_GR_OUT_DB, sprintf("%s:_netname", p.playerstats_id), playername(p.netname, p.team, false)); if(teamplay) db_put(PS_GR_OUT_DB, sprintf("%s:_team", p.playerstats_id), ftos(p.team)); diff --git a/qcsrc/common/state.qc b/qcsrc/common/state.qc index 14b22e991..f117340ed 100644 --- a/qcsrc/common/state.qc +++ b/qcsrc/common/state.qc @@ -1,5 +1,7 @@ #include "state.qh" +#include + void Inventory_new(PlayerState this); void Inventory_delete(entity this); @@ -27,7 +29,6 @@ void PlayerState_detach(entity this) delete(ps); } -void GetCvars(entity this, entity store, int); void DecodeLevelParms(entity this); void PlayerScore_Attach(entity this); void ClientData_Attach(entity this); diff --git a/qcsrc/common/turrets/sv_turrets.qc b/qcsrc/common/turrets/sv_turrets.qc index e2a9ab2e4..98490610a 100644 --- a/qcsrc/common/turrets/sv_turrets.qc +++ b/qcsrc/common/turrets/sv_turrets.qc @@ -5,6 +5,7 @@ #include #include #include +#include #include // Generic aiming diff --git a/qcsrc/common/util.qc b/qcsrc/common/util.qc index ae0c2ae77..4ad1d105d 100644 --- a/qcsrc/common/util.qc +++ b/qcsrc/common/util.qc @@ -1894,3 +1894,133 @@ int Mod_Q1BSP_NativeContentsFromSuperContents(int supercontents) return CONTENT_EMPTY; } #endif + +#ifdef SVQC +void attach_sameorigin(entity e, entity to, string tag) +{ + vector org, t_forward, t_left, t_up, e_forward, e_up; + float tagscale; + + org = e.origin - gettaginfo(to, gettagindex(to, tag)); + tagscale = (vlen(v_forward) ** -2); // undo a scale on the tag + t_forward = v_forward * tagscale; + t_left = v_right * -tagscale; + t_up = v_up * tagscale; + + e.origin_x = org * t_forward; + e.origin_y = org * t_left; + e.origin_z = org * t_up; + + // current forward and up directions + if (substring(e.model, 0, 1) == "*") // bmodels have their own rules + e.angles = AnglesTransform_FromVAngles(e.angles); + else + e.angles = AnglesTransform_FromAngles(e.angles); + fixedmakevectors(e.angles); + + // untransform forward, up! + e_forward.x = v_forward * t_forward; + e_forward.y = v_forward * t_left; + e_forward.z = v_forward * t_up; + e_up.x = v_up * t_forward; + e_up.y = v_up * t_left; + e_up.z = v_up * t_up; + + e.angles = fixedvectoangles2(e_forward, e_up); + if (substring(e.model, 0, 1) == "*") // bmodels have their own rules + e.angles = AnglesTransform_ToVAngles(e.angles); + else + e.angles = AnglesTransform_ToAngles(e.angles); + + setattachment(e, to, tag); + setorigin(e, e.origin); +} + +void detach_sameorigin(entity e) +{ + vector org; + org = gettaginfo(e, 0); + e.angles = fixedvectoangles2(v_forward, v_up); + if (substring(e.model, 0, 1) == "*") // bmodels have their own rules + e.angles = AnglesTransform_ToVAngles(e.angles); + else + e.angles = AnglesTransform_ToAngles(e.angles); + setorigin(e, org); + setattachment(e, NULL, ""); + setorigin(e, e.origin); +} + +void follow_sameorigin(entity e, entity to) +{ + set_movetype(e, MOVETYPE_FOLLOW); // make the hole follow + e.aiment = to; // make the hole follow bmodel + e.punchangle = to.angles; // the original angles of bmodel + e.view_ofs = e.origin - to.origin; // relative origin + e.v_angle = e.angles - to.angles; // relative angles +} + +#if 0 +// TODO: unused, likely for a reason, possibly needs extensions (allow setting the new movetype as a parameter?) +void unfollow_sameorigin(entity e) +{ + set_movetype(e, MOVETYPE_NONE); +} +#endif + +.string aiment_classname; +.float aiment_deadflag; +void SetMovetypeFollow(entity ent, entity e) +{ + // FIXME this may not be warpzone aware + set_movetype(ent, MOVETYPE_FOLLOW); // make the hole follow + ent.solid = SOLID_NOT; // MOVETYPE_FOLLOW is always non-solid - this means this cannot be teleported by warpzones any more! Instead, we must notice when our owner gets teleported. + ent.aiment = e; // make the hole follow bmodel + ent.punchangle = e.angles; // the original angles of bmodel + ent.view_ofs = ent.origin - e.origin; // relative origin + ent.v_angle = ent.angles - e.angles; // relative angles + ent.aiment_classname = strzone(e.classname); + ent.aiment_deadflag = e.deadflag; +} +void UnsetMovetypeFollow(entity ent) +{ + set_movetype(ent, MOVETYPE_FLY); + PROJECTILE_MAKETRIGGER(ent); + ent.aiment = NULL; +} +float LostMovetypeFollow(entity ent) +{ +/* + if(ent.move_movetype != MOVETYPE_FOLLOW) + if(ent.aiment) + error("???"); +*/ + if(ent.aiment) + { + if(ent.aiment.classname != ent.aiment_classname) + return 1; + if(ent.aiment.deadflag != ent.aiment_deadflag) + return 1; + } + return 0; +} +#endif + +#ifdef GAMEQC +// decolorizes and team colors the player name when needed +string playername(string thename, int teamid, bool team_colorize) +{ + TC(int, teamid); + bool do_colorize = (teamplay && team_colorize); +#ifdef SVQC + if(do_colorize && !intermission_running) +#else + if(do_colorize) +#endif + { + string t = Team_ColorCode(teamid); + return strcat(t, strdecolorize(thename)); + } + else + return thename; +} +#endif diff --git a/qcsrc/common/util.qh b/qcsrc/common/util.qh index 756e02cf5..82a0f01c6 100644 --- a/qcsrc/common/util.qh +++ b/qcsrc/common/util.qh @@ -227,3 +227,21 @@ int Mod_Q1BSP_NativeContentsFromSuperContents(int supercontents); // Returns the correct difference between two always increasing numbers #define COMPARE_INCREASING(to,from) (to < from ? from + to + 2 : to - from) + +#ifdef SVQC +void attach_sameorigin(entity e, entity to, string tag); + +void detach_sameorigin(entity e); + +void follow_sameorigin(entity e, entity to); + +void SetMovetypeFollow(entity ent, entity e); + +void UnsetMovetypeFollow(entity ent); + +float LostMovetypeFollow(entity ent); +#endif + +#ifdef GAMEQC +string playername(string thename, int teamid, bool team_colorize); +#endif diff --git a/qcsrc/ecs/README.md b/qcsrc/ecs/README.md index d49094f7b..7273ed70d 100644 --- a/qcsrc/ecs/README.md +++ b/qcsrc/ecs/README.md @@ -21,6 +21,7 @@ ## entities entity e = new(foo); + IL_PUSH(g_components, e); e.com_$component = true; e.com_$component_$property = 42; diff --git a/qcsrc/lib/warpzone/server.qc b/qcsrc/lib/warpzone/server.qc index 1036ab0c8..1bb1aeb6a 100644 --- a/qcsrc/lib/warpzone/server.qc +++ b/qcsrc/lib/warpzone/server.qc @@ -12,6 +12,7 @@ #include #include #include + #include #endif #ifdef SVQC diff --git a/qcsrc/server/chat.qc b/qcsrc/server/chat.qc index 2ed7fa1ed..3362ab539 100644 --- a/qcsrc/server/chat.qc +++ b/qcsrc/server/chat.qc @@ -60,7 +60,7 @@ int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodc string namestr = ""; if (source) - namestr = playername(source, autocvar_g_chat_teamcolors); + namestr = playername(source.netname, source.team, (autocvar_g_chat_teamcolors && IS_PLAYER(source))); string colorprefix = (strdecolorize(namestr) == namestr) ? "^3" : "^7"; @@ -93,7 +93,7 @@ int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodc privatemsgprefixlen = strlen(msgstr); msgstr = strcat(msgstr, msgin); cmsgstr = strcat(colorstr, colorprefix, namestr, "^3 tells you:\n^7", msgin); - privatemsgprefix = strcat("\{1}\{13}* ^3You tell ", playername(privatesay, autocvar_g_chat_teamcolors), ": ^7"); + privatemsgprefix = strcat("\{1}\{13}* ^3You tell ", playername(privatesay.netname, privatesay.team, (autocvar_g_chat_teamcolors && IS_PLAYER(privatesay))), ": ^7"); } else if(teamsay) { @@ -229,7 +229,7 @@ int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodc } if(flood) - LOG_INFO("NOTE: ", playername(source, true), "^7 is flooding."); + LOG_INFO("NOTE: ", playername(source.netname, source.team, IS_PLAYER(source)), "^7 is flooding."); // build sourcemsgstr by cutting off a prefix and replacing it by the other one if(privatesay) @@ -323,7 +323,7 @@ int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodc if (source) { sprint(source, sourcemsgstr); dedicated_print(msgstr); // send to server console too - MX_Say(strcat(playername(source, true), "^7: ", msgin)); + MX_Say(strcat(playername(source.netname, source.team, IS_PLAYER(source)), "^7: ", msgin)); } FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), { sprint(it, msgstr); diff --git a/qcsrc/server/cheats.qc b/qcsrc/server/cheats.qc index eb0b95e07..3ce3ae0b4 100644 --- a/qcsrc/server/cheats.qc +++ b/qcsrc/server/cheats.qc @@ -84,13 +84,13 @@ float CheatsAllowed(entity this, float i, int argc, float fr) // the cheat gets // if we get here, player is not allowed to cheat. Log it. if(i) - bprintf("Player %s^7 tried to use cheat 'impulse %d'\n", playername(this, false), i); + bprintf("Player %s^7 tried to use cheat 'impulse %d'\n", playername(this.netname, this.team, false), i); else if(argc) - bprintf("Player %s^7 tried to use cheat '%s'\n", playername(this, false), argv(0)); + bprintf("Player %s^7 tried to use cheat '%s'\n", playername(this.netname, this.team, false), argv(0)); else if(fr) - bprintf("Player %s^7 tried to use cheat frame %d\n", playername(this, false), fr); + bprintf("Player %s^7 tried to use cheat frame %d\n", playername(this.netname, this.team, false), fr); else - bprintf("Player %s^7 tried to use an unknown cheat\n", playername(this, false)); + bprintf("Player %s^7 tried to use an unknown cheat\n", playername(this.netname, this.team, false)); return 0; } diff --git a/qcsrc/server/client.qc b/qcsrc/server/client.qc index 04fba8093..f2c261410 100644 --- a/qcsrc/server/client.qc +++ b/qcsrc/server/client.qc @@ -1123,7 +1123,7 @@ void ClientConnect(entity this) CS(this).allowed_timeouts = autocvar_sv_timeout_number; if (autocvar_sv_eventlog) - GameLogEcho(strcat(":join:", ftos(this.playerid), ":", ftos(etof(this)), ":", ((IS_REAL_CLIENT(this)) ? GameLog_ProcessIP(this.netaddress) : "bot"), ":", playername(this, false))); + GameLogEcho(strcat(":join:", ftos(this.playerid), ":", ftos(etof(this)), ":", ((IS_REAL_CLIENT(this)) ? GameLog_ProcessIP(this.netaddress) : "bot"), ":", playername(this.netname, this.team, false))); CS(this).just_joined = true; // stop spamming the eventlog with additional lines when the client connects @@ -2476,7 +2476,7 @@ void PlayerPreThink (entity this) // stuffcmd(this, strcat("name ", this.netname, "\n")); // maybe? } if (!assume_unchanged && autocvar_sv_eventlog) - GameLogEcho(strcat(":name:", ftos(this.playerid), ":", playername(this, false))); + GameLogEcho(strcat(":name:", ftos(this.playerid), ":", playername(this.netname, this.team, false))); strcpy(CS(this).netname_previous, this.netname); } diff --git a/qcsrc/server/command/cmd.qc b/qcsrc/server/command/cmd.qc index c2c7c0992..8ffc52b24 100644 --- a/qcsrc/server/command/cmd.qc +++ b/qcsrc/server/command/cmd.qc @@ -7,6 +7,7 @@ #include #include "common.qh" +#include "getreplies.qh" #include "vote.qh" #include "../bot/api.qh" @@ -395,13 +396,13 @@ void ClientCommand_ready(entity caller, int request) // todo: anti-spam for tog { caller.ready = false; if(IS_PLAYER(caller) || caller.caplayer == 1) - bprint(playername(caller, false), "^2 is ^1NOT^2 ready\n"); + bprint(playername(caller.netname, caller.team, false), "^2 is ^1NOT^2 ready\n"); } else { caller.ready = true; if(IS_PLAYER(caller) || caller.caplayer == 1) - bprint(playername(caller, false), "^2 is ready\n"); + bprint(playername(caller.netname, caller.team, false), "^2 is ready\n"); } // cannot reset the game while a timeout is active! diff --git a/qcsrc/server/command/common.qc b/qcsrc/server/command/common.qc index c5622ead3..d67927282 100644 --- a/qcsrc/server/command/common.qc +++ b/qcsrc/server/command/common.qc @@ -32,7 +32,7 @@ string GetCommandPrefix(entity caller) // if client return player nickname, or if server return admin nickname string GetCallerName(entity caller) { - if (caller) return playername(caller, false); + if (caller) return playername(caller.netname, caller.team, false); else return ((autocvar_sv_adminnick != "") ? autocvar_sv_adminnick : "SERVER ADMIN"); // autocvar_hostname } diff --git a/qcsrc/server/command/getreplies.qc b/qcsrc/server/command/getreplies.qc index 1cf124ad3..95ee35a36 100644 --- a/qcsrc/server/command/getreplies.qc +++ b/qcsrc/server/command/getreplies.qc @@ -1,6 +1,7 @@ #include "getreplies.qh" #include +#include #include #include #include @@ -300,3 +301,137 @@ string getmonsterlist() return sprintf("^7Monsters available: %s\n", monsterlist); } + +/* +============= +GetCvars +============= +Called with: + 0: sends the request + >0: receives a cvar from name=argv(f) value=argv(f+1) +*/ +void GetCvars_handleString(entity this, entity store, string thisname, float f, .string field, string name) +{ + if (f < 0) + { + strfree(store.(field)); + } + else if (f > 0) + { + if (thisname == name) + { + strcpy(store.(field), argv(f + 1)); + } + } + else + stuffcmd(this, strcat("cl_cmd sendcvar ", name, "\n")); +} +void GetCvars_handleString_Fixup(entity this, entity store, string thisname, float f, .string field, string name, string(entity, string) func) +{ + GetCvars_handleString(this, store, thisname, f, field, name); + if (f >= 0) // also initialize to the fitting value for "" when sending cvars out + if (thisname == name) + { + string s = func(this, strcat1(store.(field))); + if (s != store.(field)) + { + strcpy(store.(field), s); + } + } +} +void GetCvars_handleFloat(entity this, entity store, string thisname, float f, .float field, string name) +{ + if (f < 0) + { + } + else if (f > 0) + { + if (thisname == name) + store.(field) = stof(argv(f + 1)); + } + else + stuffcmd(this, strcat("cl_cmd sendcvar ", name, "\n")); +} +void GetCvars_handleFloatOnce(entity this, entity store, string thisname, float f, .float field, string name) +{ + if (f < 0) + { + } + else if (f > 0) + { + if (thisname == name) + { + if (!store.(field)) + { + store.(field) = stof(argv(f + 1)); + if (!store.(field)) + store.(field) = -1; + } + } + } + else + { + if (!store.(field)) + stuffcmd(this, strcat("cl_cmd sendcvar ", name, "\n")); + } +} +string W_FixWeaponOrder_ForceComplete_AndBuildImpulseList(entity this, string wo) +{ + string o = W_FixWeaponOrder_ForceComplete(wo); + strcpy(CS(this).weaponorder_byimpulse, W_FixWeaponOrder_BuildImpulseList(o)); + return o; +} + +/** + * @param f -1: cleanup, 0: request, 1: receive + */ +void GetCvars(entity this, entity store, int f) +{ + string s = string_null; + + if (f == 0) + LOG_INFO("Warning: requesting cvar values is deprecated. Client should send them automatically using REPLICATE.\n"); + + if (f > 0) + s = strcat1(argv(f)); + + get_cvars_f = f; + get_cvars_s = s; + MUTATOR_CALLHOOK(GetCvars); + + Notification_GetCvars(this); + + ReplicateVars(this, store, s, f); + + GetCvars_handleString_Fixup(this, store, s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete_AndBuildImpulseList); + GetCvars_handleString_Fixup(this, store, s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete); + GetCvars_handleString_Fixup(this, store, s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete); + GetCvars_handleString_Fixup(this, store, s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete); + GetCvars_handleString_Fixup(this, store, s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete); + GetCvars_handleString_Fixup(this, store, s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete); + GetCvars_handleString_Fixup(this, store, s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete); + GetCvars_handleString_Fixup(this, store, s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete); + GetCvars_handleString_Fixup(this, store, s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete); + GetCvars_handleString_Fixup(this, store, s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete); + GetCvars_handleString_Fixup(this, store, s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete); + + GetCvars_handleFloat(this, store, s, f, cvar_cl_allow_uidtracking, "cl_allow_uidtracking"); + + // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early) + if (f > 0) + { + if (s == "cl_weaponpriority") + { + for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + { + .entity weaponentity = weaponentities[slot]; + if (this.(weaponentity) && (this.(weaponentity).m_weapon != WEP_Null || slot == 0)) + this.(weaponentity).m_switchweapon = w_getbestweapon(this, weaponentity); + } + } + if (s == "cl_allow_uidtracking") + PlayerStats_GameReport_AddPlayer(this); + //if (s == "cl_gunalign") + //W_ResetGunAlign(this, store.cvar_cl_gunalign); + } +} diff --git a/qcsrc/server/command/getreplies.qh b/qcsrc/server/command/getreplies.qh index 3ababeec5..f45bbe016 100644 --- a/qcsrc/server/command/getreplies.qh +++ b/qcsrc/server/command/getreplies.qh @@ -20,3 +20,9 @@ string getladder(); string getmaplist(); string getlsmaps(); string getmonsterlist(); + +void GetCvars_handleFloat(entity this, entity store, string thisname, float f, .float field, string name); + +void GetCvars_handleString(entity this, entity store, string thisname, float f, .string field, string name); + +void GetCvars(entity this, entity store, int f); diff --git a/qcsrc/server/command/sv_cmd.qc b/qcsrc/server/command/sv_cmd.qc index 281297b04..86755c7e3 100644 --- a/qcsrc/server/command/sv_cmd.qc +++ b/qcsrc/server/command/sv_cmd.qc @@ -134,8 +134,8 @@ void GameCommand_adminmsg(int request, int argc) sprint(client, strcat("\{1}\{13}^3", GetCallerName(NULL), "^7: ", admin_message, "\n")); } - successful = strcat(successful, (successful ? ", " : ""), playername(client, false)); - LOG_TRACE("Message sent to ", playername(client, false)); + successful = strcat(successful, (successful ? ", " : ""), playername(client.netname, client.team, false)); + LOG_TRACE("Message sent to ", playername(client.netname, client.team, false)); continue; } @@ -532,7 +532,7 @@ void GameCommand_defer_clear(int request, int argc) if (accepted > 0) { stuffcmd(client, "defer clear\n"); - LOG_INFO("defer clear stuffed to ", playername(client, false)); + LOG_INFO("defer clear stuffed to ", playername(client.netname, client.team, false)); } else { LOG_INFO("defer_clear: ", GetClientErrorString(accepted, argv(1)), "."); } @@ -1011,11 +1011,11 @@ void GameCommand_moveplayer(int request, int argc) if (client.caplayer) client.caplayer = 0; PutObserverInServer(client); - successful = strcat(successful, (successful ? ", " : ""), playername(client, false)); + successful = strcat(successful, (successful ? ", " : ""), playername(client.netname, client.team, false)); } else { - LOG_INFO("Player ", ftos(GetFilteredNumber(t)), " (", playername(client, false), ") is already spectating."); + LOG_INFO("Player ", ftos(GetFilteredNumber(t)), " (", playername(client.netname, client.team, false), ") is already spectating."); } continue; } @@ -1035,7 +1035,7 @@ void GameCommand_moveplayer(int request, int argc) if (team_num == client.team) // already on the destination team { // keep the forcing undone - LOG_INFO("Player ", ftos(GetFilteredNumber(t)), " (", playername(client, false), ") is already on the ", Team_ColoredFullName(client.team), (targets ? "^7, skipping to next player.\n" : "^7.")); + LOG_INFO("Player ", ftos(GetFilteredNumber(t)), " (", playername(client.netname, client.team, false), ") is already on the ", Team_ColoredFullName(client.team), (targets ? "^7, skipping to next player.\n" : "^7.")); continue; } else if (team_num == 0) // auto team @@ -1069,12 +1069,12 @@ void GameCommand_moveplayer(int request, int argc) Player_SetForcedTeamIndex(client, TEAM_FORCE_DEFAULT); if (MoveToTeam(client, team_id, 6)) { - successful = strcat(successful, (successful ? ", " : ""), playername(client, false)); - LOG_INFO("Player ", ftos(GetFilteredNumber(t)), " (", playername(client, false), ") has been moved to the ", Team_ColoredFullName(team_id), "^7."); + successful = strcat(successful, (successful ? ", " : ""), playername(client.netname, client.team, false)); + LOG_INFO("Player ", ftos(GetFilteredNumber(t)), " (", playername(client.netname, client.team, false), ") has been moved to the ", Team_ColoredFullName(team_id), "^7."); } else { - LOG_INFO("Unable to move player ", ftos(GetFilteredNumber(t)), " (", playername(client, false), ")"); + LOG_INFO("Unable to move player ", ftos(GetFilteredNumber(t)), " (", playername(client.netname, client.team, false), ")"); } continue; } diff --git a/qcsrc/server/command/vote.qc b/qcsrc/server/command/vote.qc index 3d0cb2974..0f85507fd 100644 --- a/qcsrc/server/command/vote.qc +++ b/qcsrc/server/command/vote.qc @@ -120,7 +120,7 @@ void Nagger_ReadyCounted() // If the vote_caller is still here, return their name, otherwise vote_caller_name string OriginalCallerName() { - if (IS_REAL_CLIENT(vote_caller)) return playername(vote_caller, false); + if (IS_REAL_CLIENT(vote_caller)) return playername(vote_caller.netname, vote_caller.team, false); return vote_caller_name; } diff --git a/qcsrc/server/items/items.qc b/qcsrc/server/items/items.qc index 861b21eed..41c7eca4e 100644 --- a/qcsrc/server/items/items.qc +++ b/qcsrc/server/items/items.qc @@ -11,6 +11,7 @@ #include #include +#include #include #include diff --git a/qcsrc/server/miscfunctions.qc b/qcsrc/server/miscfunctions.qc index 9a0c4ae65..78ffd9a8b 100644 --- a/qcsrc/server/miscfunctions.qc +++ b/qcsrc/server/miscfunctions.qc @@ -90,153 +90,6 @@ void WarpZone_crosshair_trace(entity pl) WarpZone_traceline_antilag(pl, CS(pl).cursor_trace_start, CS(pl).cursor_trace_start + normalize(CS(pl).cursor_trace_endpos - CS(pl).cursor_trace_start) * max_shot_distance, MOVE_NORMAL, pl, ANTILAG_LATENCY(pl)); } -/* -============= -GetCvars -============= -Called with: - 0: sends the request - >0: receives a cvar from name=argv(f) value=argv(f+1) -*/ -void GetCvars_handleString(entity this, entity store, string thisname, float f, .string field, string name) -{ - if (f < 0) - { - strfree(store.(field)); - } - else if (f > 0) - { - if (thisname == name) - { - strcpy(store.(field), argv(f + 1)); - } - } - else - stuffcmd(this, strcat("cl_cmd sendcvar ", name, "\n")); -} -void GetCvars_handleString_Fixup(entity this, entity store, string thisname, float f, .string field, string name, string(entity, string) func) -{ - GetCvars_handleString(this, store, thisname, f, field, name); - if (f >= 0) // also initialize to the fitting value for "" when sending cvars out - if (thisname == name) - { - string s = func(this, strcat1(store.(field))); - if (s != store.(field)) - { - strcpy(store.(field), s); - } - } -} -void GetCvars_handleFloat(entity this, entity store, string thisname, float f, .float field, string name) -{ - if (f < 0) - { - } - else if (f > 0) - { - if (thisname == name) - store.(field) = stof(argv(f + 1)); - } - else - stuffcmd(this, strcat("cl_cmd sendcvar ", name, "\n")); -} -void GetCvars_handleFloatOnce(entity this, entity store, string thisname, float f, .float field, string name) -{ - if (f < 0) - { - } - else if (f > 0) - { - if (thisname == name) - { - if (!store.(field)) - { - store.(field) = stof(argv(f + 1)); - if (!store.(field)) - store.(field) = -1; - } - } - } - else - { - if (!store.(field)) - stuffcmd(this, strcat("cl_cmd sendcvar ", name, "\n")); - } -} -string W_FixWeaponOrder_ForceComplete_AndBuildImpulseList(entity this, string wo) -{ - string o = W_FixWeaponOrder_ForceComplete(wo); - strcpy(CS(this).weaponorder_byimpulse, W_FixWeaponOrder_BuildImpulseList(o)); - return o; -} - -/** - * @param f -1: cleanup, 0: request, 1: receive - */ -void GetCvars(entity this, entity store, int f) -{ - string s = string_null; - - if (f == 0) - LOG_INFO("Warning: requesting cvar values is deprecated. Client should send them automatically using REPLICATE.\n"); - - if (f > 0) - s = strcat1(argv(f)); - - get_cvars_f = f; - get_cvars_s = s; - MUTATOR_CALLHOOK(GetCvars); - - Notification_GetCvars(this); - - ReplicateVars(this, store, s, f); - - GetCvars_handleString_Fixup(this, store, s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete_AndBuildImpulseList); - GetCvars_handleString_Fixup(this, store, s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete); - GetCvars_handleString_Fixup(this, store, s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete); - GetCvars_handleString_Fixup(this, store, s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete); - GetCvars_handleString_Fixup(this, store, s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete); - GetCvars_handleString_Fixup(this, store, s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete); - GetCvars_handleString_Fixup(this, store, s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete); - GetCvars_handleString_Fixup(this, store, s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete); - GetCvars_handleString_Fixup(this, store, s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete); - GetCvars_handleString_Fixup(this, store, s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete); - GetCvars_handleString_Fixup(this, store, s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete); - - GetCvars_handleFloat(this, store, s, f, cvar_cl_allow_uidtracking, "cl_allow_uidtracking"); - - // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early) - if (f > 0) - { - if (s == "cl_weaponpriority") - { - for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) - { - .entity weaponentity = weaponentities[slot]; - if (this.(weaponentity) && (this.(weaponentity).m_weapon != WEP_Null || slot == 0)) - this.(weaponentity).m_switchweapon = w_getbestweapon(this, weaponentity); - } - } - if (s == "cl_allow_uidtracking") - PlayerStats_GameReport_AddPlayer(this); - //if (s == "cl_gunalign") - //W_ResetGunAlign(this, store.cvar_cl_gunalign); - } -} - -// decolorizes and team colors the player name when needed -string playername(entity p, bool team_colorize) -{ - string t; - if (team_colorize && teamplay && !intermission_running && IS_PLAYER(p)) - { - t = Team_ColorCode(p.team); - return strcat(t, strdecolorize(p.netname)); - } - else - return p.netname; -} - float want_weapon(entity weaponinfo, float allguns) { int d = 0; @@ -580,81 +433,6 @@ void readplayerstartcvars() warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel); } -void precache_playermodel(string m) -{ - int globhandle, i, n; - string f; - - // remove : suffix - int j = strstrofs(m, ":", 0); - if(j >= 0) - m = substring(m, 0, j); - - if(substring(m, -9, 5) == "_lod1") - return; - if(substring(m, -9, 5) == "_lod2") - return; - precache_model(m); - f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1)); - if(fexists(f)) - precache_model(f); - f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1)); - if(fexists(f)) - precache_model(f); - - globhandle = search_begin(strcat(m, "_*.sounds"), true, false); - if (globhandle < 0) - return; - n = search_getsize(globhandle); - for (i = 0; i < n; ++i) - { - //print(search_getfilename(globhandle, i), "\n"); - f = search_getfilename(globhandle, i); - PrecachePlayerSounds(f); - } - search_end(globhandle); -} -void precache_all_playermodels(string pattern) -{ - int globhandle = search_begin(pattern, true, false); - if (globhandle < 0) return; - int n = search_getsize(globhandle); - for (int i = 0; i < n; ++i) - { - string s = search_getfilename(globhandle, i); - precache_playermodel(s); - } - search_end(globhandle); -} - -void precache_playermodels(string s) -{ - FOREACH_WORD(s, true, { precache_playermodel(it); }); -} - -PRECACHE(PlayerModels) -{ - // Precache all player models if desired - if (autocvar_sv_precacheplayermodels) - { - PrecachePlayerSounds("sound/player/default.sounds"); - precache_all_playermodels("models/player/*.zym"); - precache_all_playermodels("models/player/*.dpm"); - precache_all_playermodels("models/player/*.md3"); - precache_all_playermodels("models/player/*.psk"); - precache_all_playermodels("models/player/*.iqm"); - } - - if (autocvar_sv_defaultcharacter) - { - precache_playermodels(autocvar_sv_defaultplayermodel_red); - precache_playermodels(autocvar_sv_defaultplayermodel_blue); - precache_playermodels(autocvar_sv_defaultplayermodel_yellow); - precache_playermodels(autocvar_sv_defaultplayermodel_pink); - precache_playermodels(autocvar_sv_defaultplayermodel); - } -} - void InitializeEntity(entity e, void(entity this) func, int order) { entity prev, cur; @@ -726,27 +504,6 @@ void InitializeEntitiesRun() delete_fn = remove_unsafely; } -void adaptor_think2use_hittype_splash(entity this) // for timed projectile detonation -{ - if(!(IS_ONGROUND(this))) // if onground, we ARE touching something, but HITTYPE_SPLASH is to be networked if the damage causing projectile is not touching ANYTHING - this.projectiledeathtype |= HITTYPE_SPLASH; - adaptor_think2use(this); -} - -// deferred dropping -void DropToFloor_Handler(entity this) -{ - WITHSELF(this, builtin_droptofloor()); - this.dropped_origin = this.origin; -} - -void droptofloor(entity this) -{ - InitializeEntity(this, DropToFloor_Handler, INITPRIO_DROPTOFLOOR); -} - - - float trace_hits_box_a0, trace_hits_box_a1; float trace_hits_box_1d(float end, float thmi, float thma) @@ -796,59 +553,6 @@ float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector t return trace_hits_box(start, end, thmi - ma, thma - mi); } -bool SUB_NoImpactCheck(entity this, entity toucher) -{ - // zero hitcontents = this is not the real impact, but either the - // mirror-impact of something hitting the projectile instead of the - // projectile hitting the something, or a touchareagrid one. Neither of - // these stop the projectile from moving, so... - if(trace_dphitcontents == 0) - { - LOG_TRACEF("A hit from a projectile happened with no hit contents! DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct. (edict: %i, classname: %s, origin: %v)", this, this.classname, this.origin); - checkclient(this); // TODO: .health is checked in the engine with this, possibly replace with a QC function? - } - if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT) - return true; - if (toucher == NULL && this.size != '0 0 0') - { - vector tic; - tic = this.velocity * sys_frametime; - tic = tic + normalize(tic) * vlen(this.maxs - this.mins); - traceline(this.origin - tic, this.origin + tic, MOVE_NORMAL, this); - if (trace_fraction >= 1) - { - LOG_TRACE("Odd... did not hit...?"); - } - else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT) - { - LOG_TRACE("Detected and prevented the sky-grapple bug."); - return true; - } - } - - return false; -} - -bool WarpZone_Projectile_Touch_ImpactFilter_Callback(entity this, entity toucher) -{ - // owner check - if(toucher && toucher == this.owner) - return true; - if(SUB_NoImpactCheck(this, toucher)) - { - if(this.classname == "nade") - return false; // no checks here - else if(this.classname == "grapplinghook") - RemoveHook(this); - else - delete(this); - return true; - } - if(trace_ent && trace_ent.solid > SOLID_TRIGGER) - UpdateCSQCProjectile(this); - return false; -} - /** engine callback */ void URI_Get_Callback(float id, float status, string data) { @@ -1027,122 +731,3 @@ float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, f { return MoveToRandomLocationWithinBounds(e, world.mins, world.maxs, goodcontents, badcontents, badsurfaceflags, attempts, maxaboveground, minviewdistance); } - -void write_recordmarker(entity pl, float tstart, float dt) -{ - GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt))); - - // also write a marker into demo files for demotc-race-record-extractor to find - stuffcmd(pl, - strcat( - strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))), - " ", ftos(tstart), " ", ftos(dt), "\n")); -} - -void attach_sameorigin(entity e, entity to, string tag) -{ - vector org, t_forward, t_left, t_up, e_forward, e_up; - float tagscale; - - org = e.origin - gettaginfo(to, gettagindex(to, tag)); - tagscale = (vlen(v_forward) ** -2); // undo a scale on the tag - t_forward = v_forward * tagscale; - t_left = v_right * -tagscale; - t_up = v_up * tagscale; - - e.origin_x = org * t_forward; - e.origin_y = org * t_left; - e.origin_z = org * t_up; - - // current forward and up directions - if (substring(e.model, 0, 1) == "*") // bmodels have their own rules - e.angles = AnglesTransform_FromVAngles(e.angles); - else - e.angles = AnglesTransform_FromAngles(e.angles); - fixedmakevectors(e.angles); - - // untransform forward, up! - e_forward.x = v_forward * t_forward; - e_forward.y = v_forward * t_left; - e_forward.z = v_forward * t_up; - e_up.x = v_up * t_forward; - e_up.y = v_up * t_left; - e_up.z = v_up * t_up; - - e.angles = fixedvectoangles2(e_forward, e_up); - if (substring(e.model, 0, 1) == "*") // bmodels have their own rules - e.angles = AnglesTransform_ToVAngles(e.angles); - else - e.angles = AnglesTransform_ToAngles(e.angles); - - setattachment(e, to, tag); - setorigin(e, e.origin); -} - -void detach_sameorigin(entity e) -{ - vector org; - org = gettaginfo(e, 0); - e.angles = fixedvectoangles2(v_forward, v_up); - if (substring(e.model, 0, 1) == "*") // bmodels have their own rules - e.angles = AnglesTransform_ToVAngles(e.angles); - else - e.angles = AnglesTransform_ToAngles(e.angles); - setorigin(e, org); - setattachment(e, NULL, ""); - setorigin(e, e.origin); -} - -void follow_sameorigin(entity e, entity to) -{ - set_movetype(e, MOVETYPE_FOLLOW); // make the hole follow - e.aiment = to; // make the hole follow bmodel - e.punchangle = to.angles; // the original angles of bmodel - e.view_ofs = e.origin - to.origin; // relative origin - e.v_angle = e.angles - to.angles; // relative angles -} - -#if 0 -// TODO: unused, likely for a reason, possibly needs extensions (allow setting the new movetype as a parameter?) -void unfollow_sameorigin(entity e) -{ - set_movetype(e, MOVETYPE_NONE); -} -#endif - -.string aiment_classname; -.float aiment_deadflag; -void SetMovetypeFollow(entity ent, entity e) -{ - // FIXME this may not be warpzone aware - set_movetype(ent, MOVETYPE_FOLLOW); // make the hole follow - ent.solid = SOLID_NOT; // MOVETYPE_FOLLOW is always non-solid - this means this cannot be teleported by warpzones any more! Instead, we must notice when our owner gets teleported. - ent.aiment = e; // make the hole follow bmodel - ent.punchangle = e.angles; // the original angles of bmodel - ent.view_ofs = ent.origin - e.origin; // relative origin - ent.v_angle = ent.angles - e.angles; // relative angles - ent.aiment_classname = strzone(e.classname); - ent.aiment_deadflag = e.deadflag; -} -void UnsetMovetypeFollow(entity ent) -{ - set_movetype(ent, MOVETYPE_FLY); - PROJECTILE_MAKETRIGGER(ent); - ent.aiment = NULL; -} -float LostMovetypeFollow(entity ent) -{ -/* - if(ent.move_movetype != MOVETYPE_FOLLOW) - if(ent.aiment) - error("???"); -*/ - if(ent.aiment) - { - if(ent.aiment.classname != ent.aiment_classname) - return 1; - if(ent.aiment.deadflag != ent.aiment_deadflag) - return 1; - } - return 0; -} diff --git a/qcsrc/server/miscfunctions.qh b/qcsrc/server/miscfunctions.qh index 87b02725b..b5a4d0679 100644 --- a/qcsrc/server/miscfunctions.qh +++ b/qcsrc/server/miscfunctions.qh @@ -16,50 +16,26 @@ .vector dropped_origin; -void write_recordmarker(entity pl, float tstart, float dt); - -void GetCvars_handleFloat(entity this, entity store, string thisname, float f, .float field, string name); - -void GetCvars_handleString(entity this, entity store, string thisname, float f, .string field, string name); - -void precache_all_playermodels(string pattern); - void InitializeEntitiesRun(); -void droptofloor(entity this); - float trace_hits_box_1d(float end, float thmi, float thma); float trace_hits_box(vector start, vector end, vector thmi, vector thma); float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma); -void attach_sameorigin(entity e, entity to, string tag); - void crosshair_trace(entity pl); void crosshair_trace_plusvisibletriggers(entity pl); void WarpZone_crosshair_trace_plusvisibletriggers(entity pl); void crosshair_trace_plusvisibletriggers__is_wz(entity pl, bool is_wz); -void detach_sameorigin(entity e); - -void follow_sameorigin(entity e, entity to); - -void GetCvars(entity this, entity store, int f); - -float LostMovetypeFollow(entity ent); - string uid2name(string myuid); bool MoveToRandomLocationWithinBounds(entity e, vector boundmin, vector boundmax, float goodcontents, float badcontents, float badsurfaceflags, int attempts, float maxaboveground, float minviewdistance); float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance); -string playername(entity p, bool team_colorize); - -void SetMovetypeFollow(entity ent, entity e); - float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma); void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag); diff --git a/qcsrc/server/player.qc b/qcsrc/server/player.qc index ef6045403..bd73d3494 100644 --- a/qcsrc/server/player.qc +++ b/qcsrc/server/player.qc @@ -609,3 +609,78 @@ bool PlayerHeal(entity targ, entity inflictor, float amount, float limit) GiveResourceWithLimit(targ, RES_HEALTH, amount, limit); return true; } + +void precache_playermodel(string m) +{ + int globhandle, i, n; + string f; + + // remove : suffix + int j = strstrofs(m, ":", 0); + if(j >= 0) + m = substring(m, 0, j); + + if(substring(m, -9, 5) == "_lod1") + return; + if(substring(m, -9, 5) == "_lod2") + return; + precache_model(m); + f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1)); + if(fexists(f)) + precache_model(f); + f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1)); + if(fexists(f)) + precache_model(f); + + globhandle = search_begin(strcat(m, "_*.sounds"), true, false); + if (globhandle < 0) + return; + n = search_getsize(globhandle); + for (i = 0; i < n; ++i) + { + //print(search_getfilename(globhandle, i), "\n"); + f = search_getfilename(globhandle, i); + PrecachePlayerSounds(f); + } + search_end(globhandle); +} +void precache_all_playermodels(string pattern) +{ + int globhandle = search_begin(pattern, true, false); + if (globhandle < 0) return; + int n = search_getsize(globhandle); + for (int i = 0; i < n; ++i) + { + string s = search_getfilename(globhandle, i); + precache_playermodel(s); + } + search_end(globhandle); +} + +void precache_playermodels(string s) +{ + FOREACH_WORD(s, true, { precache_playermodel(it); }); +} + +PRECACHE(PlayerModels) +{ + // Precache all player models if desired + if (autocvar_sv_precacheplayermodels) + { + PrecachePlayerSounds("sound/player/default.sounds"); + precache_all_playermodels("models/player/*.zym"); + precache_all_playermodels("models/player/*.dpm"); + precache_all_playermodels("models/player/*.md3"); + precache_all_playermodels("models/player/*.psk"); + precache_all_playermodels("models/player/*.iqm"); + } + + if (autocvar_sv_defaultcharacter) + { + precache_playermodels(autocvar_sv_defaultplayermodel_red); + precache_playermodels(autocvar_sv_defaultplayermodel_blue); + precache_playermodels(autocvar_sv_defaultplayermodel_yellow); + precache_playermodels(autocvar_sv_defaultplayermodel_pink); + precache_playermodels(autocvar_sv_defaultplayermodel); + } +} diff --git a/qcsrc/server/player.qh b/qcsrc/server/player.qh index 86433d456..523a573a9 100644 --- a/qcsrc/server/player.qh +++ b/qcsrc/server/player.qh @@ -21,5 +21,7 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, bool PlayerHeal(entity targ, entity inflictor, float amount, float limit); +void precache_all_playermodels(string pattern); + IntrusiveList g_clones; STATIC_INIT(g_clones) { g_clones = IL_NEW(); } diff --git a/qcsrc/server/race.qc b/qcsrc/server/race.qc index 5014e02ae..7c04ea1da 100644 --- a/qcsrc/server/race.qc +++ b/qcsrc/server/race.qc @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -28,6 +29,17 @@ #include #include "../common/mutators/mutator/waypoints/waypointsprites.qh" +void write_recordmarker(entity pl, float tstart, float dt) +{ + GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt))); + + // also write a marker into demo files for demotc-race-record-extractor to find + stuffcmd(pl, + strcat( + strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))), + " ", ftos(tstart), " ", ftos(dt), "\n")); +} + IntrusiveList g_race_targets; IntrusiveList g_racecheckpoints; STATIC_INIT(g_race) diff --git a/qcsrc/server/race.qh b/qcsrc/server/race.qh index 129e99da1..496fd3874 100644 --- a/qcsrc/server/race.qh +++ b/qcsrc/server/race.qh @@ -34,6 +34,8 @@ float race_completing; .entity race_respawn_spotref; // try THIS spawn in case you respawn // definitions for functions used outside race.qc +void write_recordmarker(entity pl, float tstart, float dt); + float race_PreviousCheckpoint(float f); float race_NextCheckpoint(float f); void race_AbandonRaceCheck(entity p); diff --git a/qcsrc/server/scores.qc b/qcsrc/server/scores.qc index 7bd776989..de298e220 100644 --- a/qcsrc/server/scores.qc +++ b/qcsrc/server/scores.qc @@ -863,7 +863,7 @@ void Score_NicePrint_Player(entity to, entity p, float w) sk = CS(p).scorekeeper; - s = strcat(s, playername(p, false)); + s = strcat(s, playername(p.netname, p.team, false)); for (;;) { i = strlennocol(s) - NAMEWIDTH; @@ -895,7 +895,7 @@ void Score_NicePrint_Spectators(entity to) void Score_NicePrint_Spectator(entity to, entity p) { - print_to(to, strcat(" ", playername(p, false))); + print_to(to, strcat(" ", playername(p.netname, p.team, false))); } .float score_dummyfield; diff --git a/qcsrc/server/weapons/common.qc b/qcsrc/server/weapons/common.qc index 7cd56faa7..a1a5ad964 100644 --- a/qcsrc/server/weapons/common.qc +++ b/qcsrc/server/weapons/common.qc @@ -1,5 +1,6 @@ #include "common.qh" +#include #include #include #include @@ -113,3 +114,63 @@ void W_PrepareExplosionByDamage(entity this, entity attacker, void(entity this) this.nextthink = time; setthink(this, explode); } + +void adaptor_think2use_hittype_splash(entity this) // for timed projectile detonation +{ + if(!(IS_ONGROUND(this))) // if onground, we ARE touching something, but HITTYPE_SPLASH is to be networked if the damage causing projectile is not touching ANYTHING + this.projectiledeathtype |= HITTYPE_SPLASH; + adaptor_think2use(this); +} + +bool SUB_NoImpactCheck(entity this, entity toucher) +{ + // zero hitcontents = this is not the real impact, but either the + // mirror-impact of something hitting the projectile instead of the + // projectile hitting the something, or a touchareagrid one. Neither of + // these stop the projectile from moving, so... + if(trace_dphitcontents == 0) + { + LOG_TRACEF("A hit from a projectile happened with no hit contents! DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct. (edict: %i, classname: %s, origin: %v)", this, this.classname, this.origin); + checkclient(this); // TODO: .health is checked in the engine with this, possibly replace with a QC function? + } + if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT) + return true; + if (toucher == NULL && this.size != '0 0 0') + { + vector tic; + tic = this.velocity * sys_frametime; + tic = tic + normalize(tic) * vlen(this.maxs - this.mins); + traceline(this.origin - tic, this.origin + tic, MOVE_NORMAL, this); + if (trace_fraction >= 1) + { + LOG_TRACE("Odd... did not hit...?"); + } + else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT) + { + LOG_TRACE("Detected and prevented the sky-grapple bug."); + return true; + } + } + + return false; +} + +bool WarpZone_Projectile_Touch_ImpactFilter_Callback(entity this, entity toucher) +{ + // owner check + if(toucher && toucher == this.owner) + return true; + if(SUB_NoImpactCheck(this, toucher)) + { + if(this.classname == "nade") + return false; // no checks here + else if(this.classname == "grapplinghook") + RemoveHook(this); + else + delete(this); + return true; + } + if(trace_ent && trace_ent.solid > SOLID_TRIGGER) + UpdateCSQCProjectile(this); + return false; +} diff --git a/qcsrc/server/weapons/common.qh b/qcsrc/server/weapons/common.qh index 83df91eaa..c8e3eb533 100644 --- a/qcsrc/server/weapons/common.qh +++ b/qcsrc/server/weapons/common.qh @@ -8,6 +8,10 @@ void W_PlayStrengthSound(entity player); float W_CheckProjectileDamage(entity inflictor, entity projowner, int deathtype, float exception); void W_PrepareExplosionByDamage(entity this, entity attacker, void(entity this) explode); +void adaptor_think2use_hittype_splash(entity this); + +bool WarpZone_Projectile_Touch_ImpactFilter_Callback(entity this, entity toucher); + .float misc_bulletcounter; .int projectiledeathtype; diff --git a/qcsrc/server/weapons/hitplot.qc b/qcsrc/server/weapons/hitplot.qc index 0407ad799..2d56b783f 100644 --- a/qcsrc/server/weapons/hitplot.qc +++ b/qcsrc/server/weapons/hitplot.qc @@ -85,7 +85,7 @@ void W_HitPlotOpen(entity player) if(autocvar_g_hitplots || strhasword(autocvar_g_hitplots_individuals, player.netaddress)) { CS(player).hitplotfh = fopen(strcat("hits-", matchid, "-", player.netaddress, "-", ftos(player.playerid), ".plot"), FILE_WRITE); - fputs(CS(player).hitplotfh, strcat("#name ", playername(player, false), "\n")); + fputs(CS(player).hitplotfh, strcat("#name ", playername(player.netname, player.team, false), "\n")); } else { CS(player).hitplotfh = -1; } } diff --git a/qcsrc/server/world.qc b/qcsrc/server/world.qc index 329aab087..83bd283f7 100644 --- a/qcsrc/server/world.qc +++ b/qcsrc/server/world.qc @@ -1088,11 +1088,11 @@ void DumpStats(float final) s = strcat(s, "spectator:"); if(to_console) - LOG_INFO(s, playername(it, false)); + LOG_INFO(s, playername(it.netname, it.team, false)); if(to_eventlog) - GameLogEcho(strcat(s, ftos(it.playerid), ":", playername(it, false))); + GameLogEcho(strcat(s, ftos(it.playerid), ":", playername(it.netname, it.team, false))); if(to_file) - fputs(file, strcat(s, playername(it, false), "\n")); + fputs(file, strcat(s, playername(it.netname, it.team, false), "\n")); }); if(teamplay) @@ -1171,7 +1171,7 @@ void NextLevel() FOREACH_CLIENT(IS_PLAYER(it), { FixIntermissionClient(it); if(it.winning) - bprint(playername(it, false), " ^7wins.\n"); + bprint(playername(it.netname, it.team, false), " ^7wins.\n"); }); target_music_kill(); @@ -1568,6 +1568,18 @@ void CheckRules_World() } } +// deferred dropping +void DropToFloor_Handler(entity this) +{ + WITHSELF(this, builtin_droptofloor()); + this.dropped_origin = this.origin; +} + +void droptofloor(entity this) +{ + InitializeEntity(this, DropToFloor_Handler, INITPRIO_DROPTOFLOOR); +} + bool autocvar_sv_gameplayfix_multiplethinksperframe = true; void RunThink(entity this) { diff --git a/qcsrc/server/world.qh b/qcsrc/server/world.qh index 3d8ca9973..cf1c150fd 100644 --- a/qcsrc/server/world.qh +++ b/qcsrc/server/world.qh @@ -48,5 +48,7 @@ void DumpStats(float final); void CheckRules_World(); float RedirectionThink(); +void droptofloor(entity this); + IntrusiveList g_moveables; STATIC_INIT(g_moveables) { g_moveables = IL_NEW(); } -- 2.39.2