From b397cba7c68071d9c66b4219279890af243cf4e9 Mon Sep 17 00:00:00 2001 From: newby Date: Fri, 22 Dec 2023 23:20:05 -0800 Subject: [PATCH 01/57] new-balance: add menu option for enabling new balance tweaks --- csqc/csextradefs.qc | 9 +-------- csqc/events.qc | 1 + csqc/menu.qc | 15 +++++++++------ ssqc/client.qc | 7 +++++++ ssqc/commands.qc | 12 ++++++++++++ ssqc/csmenu.qc | 1 + ssqc/qw.qc | 2 ++ 7 files changed, 33 insertions(+), 14 deletions(-) diff --git a/csqc/csextradefs.qc b/csqc/csextradefs.qc index 6fb4449c..32613e5a 100644 --- a/csqc/csextradefs.qc +++ b/csqc/csextradefs.qc @@ -328,6 +328,7 @@ typedef struct { float clanmode; float quadmode; float duelmode; + float new_balance; float quad_rounds; float quad_round_time; float fo_login_required; @@ -345,14 +346,6 @@ typedef struct { float class_limit_pyro; float class_limit_spy; float class_limit_engineer; - float spurs_on; - float spurs_scout; - float spurs_spy; - float spurs_engineer; - float spurs_duration; - float spurs_boost; - float spurs_consume; - float spurs_flag; } SERVER_SETTINGS; SERVER_SETTINGS SERVER_ADMIN; diff --git a/csqc/events.qc b/csqc/events.qc index 4e55bcb9..fb1fc752 100644 --- a/csqc/events.qc +++ b/csqc/events.qc @@ -272,6 +272,7 @@ void() CSQC_Parse_Event = { SERVER_ADMIN.clanmode = readfloat(); SERVER_ADMIN.quadmode = readfloat(); SERVER_ADMIN.duelmode = readfloat(); + SERVER_ADMIN.new_balance = readfloat(); SERVER_ADMIN.pubmode = (((SERVER_ADMIN.clanmode & 1) || (SERVER_ADMIN.quadmode & 1) || (SERVER_ADMIN.duelmode & 1))?1:0) + (((SERVER_ADMIN.clanmode & 2) || (SERVER_ADMIN.quadmode & 2) || (SERVER_ADMIN.duelmode & 2))?2:0); SERVER_ADMIN.pubmode = 3 - SERVER_ADMIN.pubmode; //Invert diff --git a/csqc/menu.qc b/csqc/menu.qc index bf23d658..2f746daa 100644 --- a/csqc/menu.qc +++ b/csqc/menu.qc @@ -7,6 +7,7 @@ void (float force) FO_Menu_Admin_Timelimit; void (float force) FO_Menu_Admin_Fraglimit; void (float force) FO_Menu_Admin_QuadTimelimit; void (float force) FO_Menu_Admin_FoMatchRated; +void (float force) FO_Menu_Admin_NewBalance; void (float force) FO_Menu_Spy; void (float force) FO_Menu_Spy_Skin; void (float force) FO_Menu_Spy_Team; @@ -438,10 +439,9 @@ var fo_menu FO_MENU_ADMIN_MODES = { {"2","Clan Mode","","Game has a prematch",FO_MENU_STATE_NORMAL,{localcmd("cmd clanmode\n");},MENU_BORDER_WARNING}, {"3","Quad Mode","","Play for a set number of rounds, designed for attack vs defence",FO_MENU_STATE_NORMAL,{localcmd("cmd quadmode\n");},MENU_BORDER_WARNING}, {"4","Duel Mode","","Simplifies 1 on 1 action",FO_MENU_STATE_NORMAL,{localcmd("cmd duelmode\n");},MENU_BORDER_WARNING}, - {"5","Quad Rounds...","","Number of rounds in Quad mode. Usually 2",FO_MENU_STATE_NORMAL,{FO_Menu_Admin_Rounds(TRUE);},MENU_BORDER_WARNING}, - {"6","Quad Round Time...","","Round time for each quad round",FO_MENU_STATE_NORMAL,{FO_Menu_Admin_QuadTimelimit(TRUE);},MENU_BORDER_WARNING}, + {"5","Captains Mode","","Select captains who can then pick their teams",FO_MENU_STATE_NORMAL,{FO_Menu_Admin_Players(TRUE, CLIENT_MENU_CAPTAIN_1, 0);},MENU_BORDER_WARNING}, {"7","Rated/Unrated","","Will player ratings be affected?",FO_MENU_STATE_NORMAL,{FO_Menu_Admin_FoMatchRated(TRUE);},MENU_BORDER_WARNING}, - {"8","Captains Mode","","Select captains who can then pick their teams",FO_MENU_STATE_NORMAL,{FO_Menu_Admin_Players(TRUE, CLIENT_MENU_CAPTAIN_1, 0);},MENU_BORDER_WARNING}, + {"8","New Balance","","New Balance Test",FO_MENU_STATE_NORMAL,{localcmd("cmd new_balance");},MENU_BORDER_WARNING}, {"9","Force Start","","Skip prematch and start the game\nPlease use sparingly",FO_MENU_STATE_NORMAL,{localcmd("cmd forcestart\n");},MENU_BORDER_WARNING}, {"0","Close Menu","","",FO_MENU_STATE_NORMAL,{Menu_Cancel();},MENU_BUTTON}, {"+","Next - Settings","","",FO_MENU_STATE_NORMAL,{Menu_Cancel(); FO_Menu_Admin_Settings(TRUE);},MENU_BUTTON}, @@ -452,16 +452,17 @@ var fo_menu FO_MENU_ADMIN_MODES = { FO_MENU_ADMIN_MODES.options[1].value = modeStatus(SERVER_ADMIN.clanmode); FO_MENU_ADMIN_MODES.options[2].value = modeStatus(SERVER_ADMIN.quadmode); FO_MENU_ADMIN_MODES.options[3].value = modeStatus(SERVER_ADMIN.duelmode); - FO_MENU_ADMIN_MODES.options[4].value = ftos(SERVER_ADMIN.quad_rounds); - FO_MENU_ADMIN_MODES.options[5].value = ftos(SERVER_ADMIN.quad_round_time); + FO_MENU_ADMIN_MODES.options[4].value = SERVER_ADMIN.captainmode?"on":"off"; FO_MENU_ADMIN_MODES.options[6].value = ratedStatus(SERVER_ADMIN.fo_matchrated); - FO_MENU_ADMIN_MODES.options[7].value = SERVER_ADMIN.captainmode?"on":"off"; + FO_MENU_ADMIN_MODES.options[6].value = modeStatus(SERVER_ADMIN.new_balance); } }; var fo_menu FO_MENU_ADMIN_SETTINGS = { [0,0], [300,300], "Settings [3/3]", FO_MENU_FLAG_USE_MOUSE | FO_MENU_FLAG_CENTER | FO_MENU_FLAG_SHOW_SHORTCUTS | FO_MENU_FLAG_SHOW_VALUES | FO_MENU_FLAG_WARNING | FO_MENU_FLAG_ALLOW_INTERMISSION, { {"1","Timelimit","","",FO_MENU_STATE_NORMAL,{FO_Menu_Admin_Timelimit(TRUE);},MENU_BORDER_WARNING}, {"2","Fraglimit","","",FO_MENU_STATE_NORMAL,{FO_Menu_Admin_Fraglimit(TRUE);},MENU_BORDER_WARNING}, + {"3","Quad Rounds...","","Number of rounds in Quad mode. Usually 2",FO_MENU_STATE_NORMAL,{FO_Menu_Admin_Rounds(TRUE);},MENU_BORDER_WARNING}, + {"4","Quad Round Time...","","Round time for each quad round",FO_MENU_STATE_NORMAL,{FO_Menu_Admin_QuadTimelimit(TRUE);},MENU_BORDER_WARNING}, MenuSpacer, MenuSpacer, MenuSpacer, @@ -476,6 +477,8 @@ var fo_menu FO_MENU_ADMIN_SETTINGS = { updateAdminMenuInfo(); FO_MENU_ADMIN_SETTINGS.options[0].value = ftos(SERVER_ADMIN.timelimit); FO_MENU_ADMIN_SETTINGS.options[1].value = ftos(SERVER_ADMIN.fraglimit); + FO_MENU_ADMIN_SETTINGS.options[2].value = ftos(SERVER_ADMIN.quad_rounds); + FO_MENU_ADMIN_SETTINGS.options[3].value = ftos(SERVER_ADMIN.quad_round_time); } }; var void execute_admin_players(float choice, float page) { diff --git a/ssqc/client.qc b/ssqc/client.qc index 95755c2a..f8b8fc75 100644 --- a/ssqc/client.qc +++ b/ssqc/client.qc @@ -328,6 +328,9 @@ void () DecodeLevelParms = { clanbattle = CF_GetSetting("c", "clan", "off"); quadmode = CF_GetSetting("quadmode", "quadmode", "off"); duelmode = CF_GetSetting("duelmode", "duelmode", "off"); + new_balance = CF_GetSetting("new_balance", "new_balance", "off"); + // mirror current state into desired state bit + new_balance |= (new_balance << 1); disable_voting = clanbattle || quadmode; @@ -1037,6 +1040,10 @@ void () DecodeLevelParms = { p = find(p, classname, "player"); } */ + + // Overrides other settings. + if (new_balance) { + } }; entity() FindIntermission = diff --git a/ssqc/commands.qc b/ssqc/commands.qc index 481527fe..2cbcbf7d 100644 --- a/ssqc/commands.qc +++ b/ssqc/commands.qc @@ -123,6 +123,14 @@ void () DuelMode = bprint(PRINT_HIGH, "Map Restart needed to take effect!\n"); } +void new_balance_mode() { + new_balance ^= 2; + if (new_balance & 2) + localcmd("localinfo new_balance 1\n"); + else + localcmd("localinfo new_balance 0\n"); +} + void (entity pe) SetEquipmentForPlayer = { entity oldself = self; self = pe; @@ -1228,6 +1236,10 @@ float (string arg1, string arg2, string arg3) ParseCmds = { processedCmd = TRUE; DuelMode(); break; + case "new_balance": + processedCmd = TRUE; + new_balance_mode(); + break; case "forcestart": processedCmd = TRUE; ForceStartMatch(); diff --git a/ssqc/csmenu.qc b/ssqc/csmenu.qc index 2dff5fc4..7f9988bc 100644 --- a/ssqc/csmenu.qc +++ b/ssqc/csmenu.qc @@ -184,6 +184,7 @@ void Update_ServerAdminInfo(entity pl) = { WriteFloat(MSG_MULTICAST, (clanbattle?1:0) + (cm?2:0)); WriteFloat(MSG_MULTICAST, (quadmode?1:0) + (qm?2:0)); WriteFloat(MSG_MULTICAST, (duelmode?1:0) + (dm?2:0)); + WriteFloat(MSG_MULTICAST, new_balance); WriteFloat(MSG_MULTICAST, captainmode); multicast('0 0 0', MULTICAST_ONE_NOSPECS); } diff --git a/ssqc/qw.qc b/ssqc/qw.qc index 1c98aad5..47c3197b 100644 --- a/ssqc/qw.qc +++ b/ssqc/qw.qc @@ -800,3 +800,5 @@ string match_id; string backend_address; float fo_login_required; .entity filter_ent; + +float new_balance; From 921f055e028494fce1d721239072b9f9b0339442 Mon Sep 17 00:00:00 2001 From: newby Date: Fri, 22 Dec 2023 23:36:57 -0800 Subject: [PATCH 02/57] new_balance: add helpers for server region and staging status --- ssqc/helpers.qc | 19 +++++++++++++++++++ ssqc/progs.src | 3 ++- 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 ssqc/helpers.qc diff --git a/ssqc/helpers.qc b/ssqc/helpers.qc new file mode 100644 index 00000000..417d6552 --- /dev/null +++ b/ssqc/helpers.qc @@ -0,0 +1,19 @@ +enum { + kRegionUS, + kRegionOther, +}; + +const string kNorthAmericaChannel = "504171613793681408"; + +float ServerRegion() { + if (discord_channel_id == kNorthAmericaChannel) + return kRegionUS; + + return kRegionOther; +} + +float ServerIsStaging() { + string hostname = serverkey("hostname"); + + return strstrofs(hostname, "Staging") >= 0; +} diff --git a/ssqc/progs.src b/ssqc/progs.src index 33cf1839..e563dbaa 100644 --- a/ssqc/progs.src +++ b/ssqc/progs.src @@ -23,7 +23,8 @@ time.qc ../share/prediction.qc ../share/classes.qc ../share/animate.qc -../share/mcp_precache.qc +../share/mcp_precache.qc +helpers.qc events.qc roles.qc q3defs.qc From 79294f2311d19a8314e0162c19e91bcd82acecbb Mon Sep 17 00:00:00 2001 From: newby Date: Fri, 22 Dec 2023 23:42:24 -0800 Subject: [PATCH 03/57] new_balance: default on for us-staging --- ssqc/client.qc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ssqc/client.qc b/ssqc/client.qc index f8b8fc75..f9acf158 100644 --- a/ssqc/client.qc +++ b/ssqc/client.qc @@ -328,7 +328,13 @@ void () DecodeLevelParms = { clanbattle = CF_GetSetting("c", "clan", "off"); quadmode = CF_GetSetting("quadmode", "quadmode", "off"); duelmode = CF_GetSetting("duelmode", "duelmode", "off"); - new_balance = CF_GetSetting("new_balance", "new_balance", "off"); + new_balance = CF_GetSetting("new_balance", "new_balance", "4"); + + + if (new_balance == 4) { + ServerIsStaging(); + new_balance = ((ServerRegion() == kRegionUS) && ServerIsStaging()) ? 1 : 0; + } // mirror current state into desired state bit new_balance |= (new_balance << 1); From b1adba9a6011de46e9a1b8fea9d16b92f27b4995 Mon Sep 17 00:00:00 2001 From: newby Date: Fri, 22 Dec 2023 23:57:33 -0800 Subject: [PATCH 04/57] new-balance: first experiments - max 3 gren1 - 1 mirv demo, 0 mirv heavy, no detpack - no gren1 pickups from packs - no gren2 pickups spawns - 20s airblast cooldown - 50 minping t --- share/defs.h | 1 - ssqc/client.qc | 21 +++++++++++++++++++-- ssqc/demoman.qc | 4 ++++ ssqc/qw.qc | 1 + 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/share/defs.h b/share/defs.h index 01baa576..10d29f1f 100644 --- a/share/defs.h +++ b/share/defs.h @@ -1062,7 +1062,6 @@ enumflags { #define PC_PYRO_GRENADE_MAX_2 4 #define PC_PYRO_TF_ITEMS 0 #define PC_PYRO_AIRBLAST_RANGE 400 -#define PC_PYRO_AIRBLAST_COOLDOWN 5 #define PC_PYRO_AIRBLAST_CELLS 55 #define PC_PYRO_AIRBLASTJUMP_CELLS 75 #define PC_PYRO_LAVA_LIFETIME 3 diff --git a/ssqc/client.qc b/ssqc/client.qc index f9acf158..16bf4e2a 100644 --- a/ssqc/client.qc +++ b/ssqc/client.qc @@ -223,6 +223,23 @@ void InitPrematch() } } +float ActivateNewBalance() { + disable_resup_gren = 2; // No gren2 pickups + if (fo_config.min_ping_ms < 50) // 50 min-ping + localcmd("localinfo mpm 50"); + + drop_gren1 = 0; // No grenades in backpacks + + int i; + for (i = 1; i <= 9; i++) // 3 gren spawn + Role_None.gren1_limits[i] = 3; + + Role_None.gren2_limits[4] = 1; // 1 mirv for demoman + Role_None.gren2_limits[6] = 0; // 0 mirv for heavy + + PC_PYRO_AIRBLAST_COOLDOWN = 20; +} + void () DecodeLevelParms = { local float fl; local string st; @@ -1048,8 +1065,8 @@ void () DecodeLevelParms = { */ // Overrides other settings. - if (new_balance) { - } + if (new_balance) + ActivateNewBalance(); }; entity() FindIntermission = diff --git a/ssqc/demoman.qc b/ssqc/demoman.qc index 94575ceb..54d823f2 100644 --- a/ssqc/demoman.qc +++ b/ssqc/demoman.qc @@ -146,6 +146,10 @@ void (float timer) TeamFortress_SetDetpack = { self.impulse = 0; self.last_impulse = 0; + + if (new_balance & 1) // No detpacks + self.ammo_detpack = 0; + if (self.ammo_detpack <= 0) { sprint(self, PRINT_HIGH, "You don't have any detpacks left\n"); return; diff --git a/ssqc/qw.qc b/ssqc/qw.qc index 47c3197b..a96bd6ba 100644 --- a/ssqc/qw.qc +++ b/ssqc/qw.qc @@ -802,3 +802,4 @@ float fo_login_required; .entity filter_ent; float new_balance; +var float PC_PYRO_AIRBLAST_COOLDOWN = 5; From 64e4ca9bf58d8faa135ed5cbee0dfc9928f2ffc2 Mon Sep 17 00:00:00 2001 From: newby Date: Sat, 23 Dec 2023 00:01:44 -0800 Subject: [PATCH 05/57] pmove: possibly fix free flight in demos --- csqc/pmove.qc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/csqc/pmove.qc b/csqc/pmove.qc index fb975262..068c5c4b 100644 --- a/csqc/pmove.qc +++ b/csqc/pmove.qc @@ -803,6 +803,9 @@ void PM_Update(float sendflags) { float enabled = (pstate_server.predict_flags & PF_PMOVE) && game_state.is_player; + if (isdemo()) + enabled = 0; + if (enabled != was_enabled || game_state.is_player != prev_game_state.is_player) PM_SetEnabled(enabled); From 92a7b9d98267e2c10c3ea5356bde18b9c20cf9ac Mon Sep 17 00:00:00 2001 From: newby Date: Sat, 23 Dec 2023 00:08:44 -0800 Subject: [PATCH 06/57] pmove: no knockback prediction from occluded explosions --- csqc/pmove.qc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/csqc/pmove.qc b/csqc/pmove.qc index 068c5c4b..302950e0 100644 --- a/csqc/pmove.qc +++ b/csqc/pmove.qc @@ -387,6 +387,11 @@ static void PM_NudgeExplosion(PM_Nudge* nudge, entity ent) { if (vlen(ent.origin - nudge->org) > dmg + 40) return; + const float MOVE_WORLDONLY = 3; + traceline(ent.origin, nudge->org, MOVE_WORLDONLY, pm.ent); + if (trace_fraction < 1) // No knock from occluded explosions + return; + float flg = KF_BOTH_PLAYER; // Assumes everything is from a player for now if (nudge->src.owner == pengine.player_ent) flg |= KF_SELF; From 58c46694b5d422d1b22bda222b10a5e4dd5b79ef Mon Sep 17 00:00:00 2001 From: newby Date: Sat, 23 Dec 2023 00:27:15 -0800 Subject: [PATCH 07/57] new-balance: move auto-init down We depend on discord_channel_id which is not initialized until later. Just move to end of init. --- ssqc/client.qc | 20 ++++++++++---------- ssqc/helpers.qc | 4 ++++ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/ssqc/client.qc b/ssqc/client.qc index 16bf4e2a..c27ad7ba 100644 --- a/ssqc/client.qc +++ b/ssqc/client.qc @@ -345,15 +345,6 @@ void () DecodeLevelParms = { clanbattle = CF_GetSetting("c", "clan", "off"); quadmode = CF_GetSetting("quadmode", "quadmode", "off"); duelmode = CF_GetSetting("duelmode", "duelmode", "off"); - new_balance = CF_GetSetting("new_balance", "new_balance", "4"); - - - if (new_balance == 4) { - ServerIsStaging(); - new_balance = ((ServerRegion() == kRegionUS) && ServerIsStaging()) ? 1 : 0; - } - // mirror current state into desired state bit - new_balance |= (new_balance << 1); disable_voting = clanbattle || quadmode; @@ -1065,7 +1056,16 @@ void () DecodeLevelParms = { */ // Overrides other settings. - if (new_balance) + new_balance = CF_GetSetting("new_balance", "new_balance", "4"); + + if (new_balance == 4) { + printf("Staging = %d\n", ServerIsStaging()); + new_balance = ((ServerRegion() == kRegionUS) && ServerIsStaging()) ? 1 : 0; + } + + // mirror current state into desired state bit + new_balance |= (new_balance << 1); + if (ServerIsNewBalance()) ActivateNewBalance(); }; diff --git a/ssqc/helpers.qc b/ssqc/helpers.qc index 417d6552..647a7cf7 100644 --- a/ssqc/helpers.qc +++ b/ssqc/helpers.qc @@ -17,3 +17,7 @@ float ServerIsStaging() { return strstrofs(hostname, "Staging") >= 0; } + +float ServerIsNewBalance() { + return new_balance & 1; +} From 75a4e495735b0a4bb0014acb95356647f5a68d33 Mon Sep 17 00:00:00 2001 From: newby Date: Sat, 23 Dec 2023 01:01:43 -0800 Subject: [PATCH 08/57] new-balance: fix debug print spam --- ssqc/client.qc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ssqc/client.qc b/ssqc/client.qc index c27ad7ba..a9a1b18a 100644 --- a/ssqc/client.qc +++ b/ssqc/client.qc @@ -1058,10 +1058,8 @@ void () DecodeLevelParms = { // Overrides other settings. new_balance = CF_GetSetting("new_balance", "new_balance", "4"); - if (new_balance == 4) { - printf("Staging = %d\n", ServerIsStaging()); + if (new_balance == 4) new_balance = ((ServerRegion() == kRegionUS) && ServerIsStaging()) ? 1 : 0; - } // mirror current state into desired state bit new_balance |= (new_balance << 1); From a737e9b2e6595b4b24d6eb0e4d99b9754931c02e Mon Sep 17 00:00:00 2001 From: newby Date: Sat, 23 Dec 2023 21:30:09 -0800 Subject: [PATCH 09/57] new-balance: no knockback on napalm - airblast cooldown reduced from 20 -> 15 to compensate --- ssqc/client.qc | 2 +- ssqc/pyro.qc | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/ssqc/client.qc b/ssqc/client.qc index a9a1b18a..fbb8dd6d 100644 --- a/ssqc/client.qc +++ b/ssqc/client.qc @@ -237,7 +237,7 @@ float ActivateNewBalance() { Role_None.gren2_limits[4] = 1; // 1 mirv for demoman Role_None.gren2_limits[6] = 0; // 0 mirv for heavy - PC_PYRO_AIRBLAST_COOLDOWN = 20; + PC_PYRO_AIRBLAST_COOLDOWN = 15; } void () DecodeLevelParms = { diff --git a/ssqc/pyro.qc b/ssqc/pyro.qc index d6036f47..f1e144fd 100644 --- a/ssqc/pyro.qc +++ b/ssqc/pyro.qc @@ -169,8 +169,11 @@ void () NapalmGrenadeExplode2 = { if (head.takedamage) { deathmsg = DMSG_FLAME; - TF_T_Damage(head, self, self.owner, explodeDam, TF_TD_NOTTEAM, - TF_TD_FIRE); + float aflags = TF_TD_FIRE; + if (ServerIsNewBalance()) + aflags |= TF_TD_NOMOMENTUM; + + TF_T_Damage(head, self, self.owner, explodeDam, TF_TD_NOTTEAM, aflags); other = head; Napalm_touch(); if (other.classname == "player") { From 60693b24e5349a2dde5549039db97f97fc84e7c3 Mon Sep 17 00:00:00 2001 From: newby Date: Sat, 23 Dec 2023 21:31:53 -0800 Subject: [PATCH 10/57] new-balance: smaller emp blast radius --- share/defs.h | 1 - ssqc/client.qc | 1 + ssqc/qw.qc | 1 + 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/share/defs.h b/share/defs.h index 10d29f1f..2a81d90e 100644 --- a/share/defs.h +++ b/share/defs.h @@ -1125,7 +1125,6 @@ enumflags { #define PC_ENGINEER_GRENADE_MAX_1 4 #define PC_ENGINEER_GRENADE_MAX_2 4 #define PC_ENGINEER_TF_ITEMS 0 -#define PC_ENGINEER_GRENADE_TYPE_2_RANGE 240 #define PC_ENGINEER_RAILSPEED 1500 // Class Details for CIVILIAN diff --git a/ssqc/client.qc b/ssqc/client.qc index fbb8dd6d..7b289631 100644 --- a/ssqc/client.qc +++ b/ssqc/client.qc @@ -238,6 +238,7 @@ float ActivateNewBalance() { Role_None.gren2_limits[6] = 0; // 0 mirv for heavy PC_PYRO_AIRBLAST_COOLDOWN = 15; + PC_ENGINEER_GRENADE_TYPE_2_RANGE = 200; } void () DecodeLevelParms = { diff --git a/ssqc/qw.qc b/ssqc/qw.qc index a96bd6ba..01ca7c7f 100644 --- a/ssqc/qw.qc +++ b/ssqc/qw.qc @@ -803,3 +803,4 @@ float fo_login_required; float new_balance; var float PC_PYRO_AIRBLAST_COOLDOWN = 5; +var float PC_ENGINEER_GRENADE_TYPE_2_RANGE = 240; From 1fa763b557e6873a12fa05b3eaae2596ba763563 Mon Sep 17 00:00:00 2001 From: newby Date: Sun, 24 Dec 2023 13:46:35 -0800 Subject: [PATCH 11/57] new-balance: publish activation to client --- share/commondefs.qc | 2 ++ share/prediction.qc | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/share/commondefs.qc b/share/commondefs.qc index a6bc12d4..02d5a635 100644 --- a/share/commondefs.qc +++ b/share/commondefs.qc @@ -53,6 +53,8 @@ var struct { float fo_concuss; float rj; + + float new_balance; } fo_config; enumflags { diff --git a/share/prediction.qc b/share/prediction.qc index 495bc7b8..cdc735f9 100644 --- a/share/prediction.qc +++ b/share/prediction.qc @@ -479,6 +479,7 @@ void EntUpdate_Config() { COMM(Byte, gren_beta_disable); COMM(Byte, old_ng_rof); COMM(Short, fo_concuss); + COMM(Byte, new_balance); #ifdef SSQC return TRUE; @@ -724,6 +725,7 @@ void Predict_InitDefaultConfig() { fo_config.clown_flags = 0; fo_config.clown_grav = 400; fo_config.fo_concuss = 0; + fo_config.new_balance = 0; } #ifdef SSQC @@ -790,6 +792,11 @@ static void WeaponPred_CheckConfigUpdate() { update = TRUE; } + if ((new_balance & 1) != fo_config.new_balance) { + fo_config.new_balance = new_balance & 1; + update = TRUE; + } + // Not dynamically updatable. static float read_rof_once = FALSE; if (!read_rof_once) { From 269ee0e9bba54c57ef9d2f079dc9d721ab775fc8 Mon Sep 17 00:00:00 2001 From: newby Date: Wed, 27 Dec 2023 18:03:58 -0800 Subject: [PATCH 12/57] new-balance: fix quad mode grenades and ammobox pickups --- ssqc/client.qc | 18 +++++++++++++----- ssqc/items.qc | 9 ++++++++- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/ssqc/client.qc b/ssqc/client.qc index 7b289631..d845f1f3 100644 --- a/ssqc/client.qc +++ b/ssqc/client.qc @@ -223,6 +223,15 @@ void InitPrematch() } } +static void SetAllRoles(float pc, float gren, float limit) { + if (gren == 1) + Role_None.gren1_limits[pc] = Role_Attack.gren1_limits[pc] = + Role_Defence.gren1_limits[pc] = limit; + else + Role_None.gren2_limits[pc] = Role_Attack.gren2_limits[pc] = + Role_Defence.gren2_limits[pc] = limit; +} + float ActivateNewBalance() { disable_resup_gren = 2; // No gren2 pickups if (fo_config.min_ping_ms < 50) // 50 min-ping @@ -231,11 +240,11 @@ float ActivateNewBalance() { drop_gren1 = 0; // No grenades in backpacks int i; - for (i = 1; i <= 9; i++) // 3 gren spawn - Role_None.gren1_limits[i] = 3; + for (i = 1; i <= 9; i++) // 3 gren spawn + SetAllRoles(i, 1, 3); - Role_None.gren2_limits[4] = 1; // 1 mirv for demoman - Role_None.gren2_limits[6] = 0; // 0 mirv for heavy + SetAllRoles(4, 2, 1); // 1 mirv for demoman + SetAllRoles(6, 2, 0); // 0 mirv for hwguy PC_PYRO_AIRBLAST_COOLDOWN = 15; PC_ENGINEER_GRENADE_TYPE_2_RANGE = 200; @@ -262,7 +271,6 @@ void () DecodeLevelParms = { self.armortype = parm9 * 0.01; if (!(toggleflags & TFLAG_FIRSTENTRY)) { - toggleflags = parm10; flagem_checked = 0; invis_only = 0; diff --git a/ssqc/items.qc b/ssqc/items.qc index ed1875cb..c4857d0b 100644 --- a/ssqc/items.qc +++ b/ssqc/items.qc @@ -733,7 +733,14 @@ float () GetGrenadePossibility = { if (random() < 0.500) return 0; - float index = random() < 0.5 ? 0 : 1; + float index; + switch (disable_resup_gren) { + case 0: index = random() < 0.5 ? 0 : 1; break; + case 1: index = 1; break; + case 2: index = 0; break; + case 3: return 0; + } + FO_GrenInfo* gt = FO_PlayerGren(other, index); if (gt->id == GREN_NONE) return 0; From b79b913174bd7e63e1182fc15022862d95d6ac5f Mon Sep 17 00:00:00 2001 From: newby Date: Wed, 27 Dec 2023 21:08:35 -0800 Subject: [PATCH 13/57] weapons: fix grenade pickup text --- share/weapons.qc | 4 +++- ssqc/items.qc | 25 ++++++------------------- 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/share/weapons.qc b/share/weapons.qc index 1d44c5dc..6101212f 100644 --- a/share/weapons.qc +++ b/share/weapons.qc @@ -159,7 +159,7 @@ enum { struct FO_GrenInfo { int id; - string logname; + string name; string model; int skin; string icon; @@ -167,6 +167,7 @@ struct FO_GrenInfo { // Automatically initialized below this line. float modelindex; + string logname; }; FO_GrenInfo fo_grenades[] = { @@ -815,6 +816,7 @@ void FO_Weapons_Init() { FO_GrenInfo* gdesc = &fo_grenades[i]; ASSERTD_EQ(GREN_FIRST + i, gdesc->id); + gdesc->logname = gdesc->name; if (i < GREN_RED) gdesc->logname = strcat(strtolower(gdesc->logname), "grenade"); diff --git a/ssqc/items.qc b/ssqc/items.qc index c4857d0b..4d99d6c7 100644 --- a/ssqc/items.qc +++ b/ssqc/items.qc @@ -704,29 +704,16 @@ void () weapon_lightning = { StartItem(); }; -void (entity pl, float type) PrintGrenadeType = { - // TODO: this is still awful - string st = FO_GrenDesc(type)->logname; - sprint(pl, PRINT_HIGH, st); -}; - void (entity pe_player, float pf_grenade_type, float pf_grenade_count) PrintFoundGrenade = { if (!pf_grenade_count) return; - sprint(pe_player, PRINT_HIGH, "You found "); - if (pf_grenade_count > 1) - sprint(pe_player, PRINT_HIGH, ftos(pf_grenade_count), " "); - else - sprint(pe_player, PRINT_HIGH, "a "); - PrintGrenadeType(pe_player, pf_grenade_type); - if (pf_grenade_type == GREN_CALTROP) - sprint(pe_player, PRINT_HIGH, " canister"); - else - sprint(pe_player, PRINT_HIGH, " grenade"); - if (pf_grenade_count > 1) - sprint(pe_player, PRINT_HIGH, "s"); - sprint(pe_player, PRINT_HIGH, "\n"); + string buf = sprintf("You found %s%s %s%s\n", + pf_grenade_count > 1 ? " " : "a ", + FO_GrenDesc(pf_grenade_type)->name, + pf_grenade_type == GREN_CALTROP ? "cannister" : "grenade", + pf_grenade_count > 1 ? "s" : ""); + sprint(pe_player, PRINT_HIGH, buf); }; float () GetGrenadePossibility = { From b38200e31c403512569ccbbc9ad514841c3a80ad Mon Sep 17 00:00:00 2001 From: newby Date: Fri, 29 Dec 2023 03:49:39 -0800 Subject: [PATCH 14/57] clock: fix clock sync --- csqc/csextradefs.qc | 2 ++ csqc/status.qc | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/csqc/csextradefs.qc b/csqc/csextradefs.qc index 32613e5a..6aff7a82 100644 --- a/csqc/csextradefs.qc +++ b/csqc/csextradefs.qc @@ -918,3 +918,5 @@ void cvar_parse4(string s, __out veci target) { target.v[2] = stof(argv(2)); target.i = stof(argv(3)); } + +float servertime; diff --git a/csqc/status.qc b/csqc/status.qc index 16752c7e..6999f0d1 100644 --- a/csqc/status.qc +++ b/csqc/status.qc @@ -305,9 +305,9 @@ string gameClockString() { if (!end) end = timelimit * 60; - if (end) { - minutes = (end - time)/60; - seconds = (end - time)%60; + if (end && end > servertime) { + minutes = (end - servertime)/60; + seconds = (end - servertime)%60; } else { minutes = time/60; seconds = time%60; From a9d332df350d348a64c21fc8f54566a5c6bea632 Mon Sep 17 00:00:00 2001 From: newby Date: Fri, 29 Dec 2023 04:24:16 -0800 Subject: [PATCH 15/57] new-balance: long live the detpack bring back detpack but with a 30 second cooldown for defense only --- ssqc/demoman.qc | 15 ++++++++++++--- ssqc/quadmode.qc | 3 ++- ssqc/qw.qc | 2 ++ 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/ssqc/demoman.qc b/ssqc/demoman.qc index 54d823f2..3ecca191 100644 --- a/ssqc/demoman.qc +++ b/ssqc/demoman.qc @@ -147,9 +147,6 @@ void (float timer) TeamFortress_SetDetpack = { self.impulse = 0; self.last_impulse = 0; - if (new_balance & 1) // No detpacks - self.ammo_detpack = 0; - if (self.ammo_detpack <= 0) { sprint(self, PRINT_HIGH, "You don't have any detpacks left\n"); return; @@ -206,6 +203,17 @@ void (float timer) TeamFortress_SetDetpack = { return; } + float quad_limit = quadmode && round_active && + (time < round_end_time) && (rounds % 2) != (self.team_no - 1); + if (ServerIsNewBalance() && quad_limit) { + float last = max(self.detpack_last, round_start_time); + float next = last + 30; // TODO: tunable if this sticks + if (next > time) { + sprint(self, PRINT_HIGH, sprintf("%0.1f seconds until next detpack\n", next - time)); + return; + } + } + self.is_detpacking = 3; self.detpack_left = timer; self.ammo_detpack = self.ammo_detpack - 1; @@ -221,6 +229,7 @@ void (float timer) TeamFortress_SetDetpack = { " seconds...\n"); Menu_Demoman_Cancel(); + self.detpack_last = time; newmis = spawn(); newmis.owner = self; newmis.classname = "timer"; diff --git a/ssqc/quadmode.qc b/ssqc/quadmode.qc index 3f673ad0..8693e192 100644 --- a/ssqc/quadmode.qc +++ b/ssqc/quadmode.qc @@ -518,7 +518,8 @@ void () QuadRoundBegin = { if (rounds == 1) PostFOQuadFinalRoundStarted(); - round_end_time = time + round_time * 60 + 1; + round_start_time = time; + round_end_time = round_start_time + round_time * 60 + 1; UpdateClientQuadRoundBegin(te, round_time); if (!self.cnt) { diff --git a/ssqc/qw.qc b/ssqc/qw.qc index 01ca7c7f..883a8652 100644 --- a/ssqc/qw.qc +++ b/ssqc/qw.qc @@ -468,6 +468,7 @@ float rounds; float round_active; float round_over; float round_delay_time; +float round_start_time; float round_end_time; float map_restart_time; float gametime; @@ -804,3 +805,4 @@ float fo_login_required; float new_balance; var float PC_PYRO_AIRBLAST_COOLDOWN = 5; var float PC_ENGINEER_GRENADE_TYPE_2_RANGE = 240; +.float detpack_last; From df08b2baaa75de64d6b7af411c8f558a65963785 Mon Sep 17 00:00:00 2001 From: newby Date: Sat, 6 Jan 2024 00:26:08 -0800 Subject: [PATCH 16/57] rewind: remove FPF_FIXED_DYNAMIC --- share/commondefs.qc | 1 - ssqc/rewind.qc | 2 -- ssqc/tfort.qc | 6 ------ 3 files changed, 9 deletions(-) diff --git a/share/commondefs.qc b/share/commondefs.qc index 02d5a635..252a488c 100644 --- a/share/commondefs.qc +++ b/share/commondefs.qc @@ -24,7 +24,6 @@ struct ConcState { enumflags { FPF_NO_REWIND, - FPF_FIXED_DYNAMIC, }; var struct { diff --git a/ssqc/rewind.qc b/ssqc/rewind.qc index 58d4ea1b..62e16a8c 100644 --- a/ssqc/rewind.qc +++ b/ssqc/rewind.qc @@ -336,8 +336,6 @@ void Forward_Projectile(int fpp_type, entity proj, float use_ctime) { float static_dt = offset.static_ms / 1000.0; float dynamic_dt = offset.dynamic_ms / 1000.0; - if (proj.fpp.flags & FPF_FIXED_DYNAMIC) - dynamic_dt = proj.fpp.dynamic_dt; float stime = time - dynamic_dt; float no_rewind = proj.fpp.flags & FPF_NO_REWIND; diff --git a/ssqc/tfort.qc b/ssqc/tfort.qc index c9a9920c..b145e1f2 100644 --- a/ssqc/tfort.qc +++ b/ssqc/tfort.qc @@ -2398,12 +2398,6 @@ void () TeamFortress_ExplodePerson = { proj.angles = vectoangles(proj.velocity); proj.think = SUB_Null; float expires = time + 0.1; // Server generated, no client time here. - float rw_dt = FO_RewindGrenWinDt(gtype); - if (rw_dt > 0) { - proj.fpp.flags |= FPF_FIXED_DYNAMIC; - proj.fpp.dynamic_dt = rw_dt; - expires -= rw_dt; - } proj.nextthink = expires; if (self.weapon == GREN_FLARE) { From 156d1f140f7620d93cef27450847aa69ec200957 Mon Sep 17 00:00:00 2001 From: newby Date: Sat, 6 Jan 2024 00:24:21 -0800 Subject: [PATCH 17/57] rewind: improve accuracy, simplify forward knock, refactor --- ssqc/rewind.qc | 25 +++++++++++++------------ ssqc/weapons.qc | 8 +------- 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/ssqc/rewind.qc b/ssqc/rewind.qc index 62e16a8c..8208a00f 100644 --- a/ssqc/rewind.qc +++ b/ssqc/rewind.qc @@ -8,7 +8,7 @@ #define rw_printd(...) #endif -#define MAX_SNAPSHOTS 25 +#define MAX_SNAPSHOTS 50 inline int NextRewindIdx(int idx) { return (idx + 1) % MAX_SNAPSHOTS; @@ -111,7 +111,7 @@ RewindSnapshot* RewindLog(RewindState* target) { target->owner->client_lastupdate = time; RewindSnapshot* rs = &target->snapshot[target->cur]; - if (time > rs->time + 0.05) { + if (time > rs->time) { if (rs->time) { target->cur = NextRewindIdx(target->cur); rs = &target->snapshot[target->cur]; @@ -336,30 +336,31 @@ void Forward_Projectile(int fpp_type, entity proj, float use_ctime) { float static_dt = offset.static_ms / 1000.0; float dynamic_dt = offset.dynamic_ms / 1000.0; - float stime = time - dynamic_dt; - float no_rewind = proj.fpp.flags & FPF_NO_REWIND; - float rewind_hit = !no_rewind && RewindFlagEnabled(REWIND_PROJ_FIRE); + float rewind_hit = RewindFlagEnabled(REWIND_PROJ_FIRE) && + !(proj.fpp.flags & FPF_NO_REWIND) && + dynamic_dt > 0; + float phys_flags = PHYSF_CONSUME_ALL; + float stime = time; + if (rewind_hit) { + stime -= dynamic_dt; phys_flags |= PHYSF_REWIND_PLAYERS; RL_StashPositions(rewind_players); RL_RewindTo(rewind_players, proj.owner, stime); } - if (!no_rewind && RewindFlagEnabled(REWIND_FORWARD_PROJ_SELFKNOCK)) - phys_flags |= PHYSF_FORWARD_KNOCK; - // Static projection happens instantly. If rewind is active, we'll do it at // a prior point in time, but we don't advance time while stepping. - proj.s_origin = proj.origin; - proj.s_time = 0; float ft = Phys_Init(proj, stime, static_dt, PHYSF_CONSUME_ALL); // We initialize s_origin/s_time after Phys_Init, they are used when // knockback forwarding is on to determine delay. - proj.s_origin = proj.origin; - proj.s_time = time; + if (rewind_hit && RewindFlagEnabled(REWIND_FORWARD_PROJ_SELFKNOCK)) { + phys_flags |= PHYSF_FORWARD_KNOCK; + proj.s_origin = proj.origin; + } if (!proj.voided) { RewindSyncTime = ProjRewindForPhys; diff --git a/ssqc/weapons.qc b/ssqc/weapons.qc index 7f7574fb..e22c15b2 100644 --- a/ssqc/weapons.qc +++ b/ssqc/weapons.qc @@ -1043,9 +1043,7 @@ void T_Knock_Antilag() { } float AntilagKnock(entity e, float dmg) { - if (!RewindFlagEnabled(REWIND_FORWARD_PROJ_SELFKNOCK) || e.s_time == 0) - return FALSE; - + e.forward_knock = -1; // Only applies during initial antilag forward if (e.flags & FL_FORWARD_KNOCK == 0) return FALSE; @@ -1070,7 +1068,6 @@ float AntilagKnock(entity e, float dmg) { e.forward_knock = time + ttime; } else { Antilag_Knock(knock_e); - e.forward_knock = -1; } return TRUE; @@ -1095,9 +1092,6 @@ void () T_MissileTouch = { entity ignore_self = AntilagKnock(self, dmg) ? self.owner : __NULL__; T_RadiusDamage(self, self.owner, dmg, other, ignore_self); - if (!self.s_time) - self.forward_knock = -1; - self.origin = self.origin - 8 * normalize(self.velocity); WriteByte(MSG_MULTICAST, SVC_TEMPENTITY); From 3d6f12ec951694f4f7498f0a12158a2013c119a7 Mon Sep 17 00:00:00 2001 From: newby Date: Sat, 6 Jan 2024 01:28:53 -0800 Subject: [PATCH 18/57] rewind: double-check for collisions In some cases we seem to be missing qwsv generated collisions versus entities, possibly due to order of processing; add an explicit double check as part of rewind logging --- share/commondefs.qc | 5 ++++- ssqc/rewind.qc | 29 +++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/share/commondefs.qc b/share/commondefs.qc index 252a488c..001c21c1 100644 --- a/share/commondefs.qc +++ b/share/commondefs.qc @@ -62,6 +62,7 @@ enumflags { REWIND_GRENADES, REWIND_SENDEVENT, REWIND_FORWARD_PROJ_SELFKNOCK, + REWIND_DOUBLE_COL, REWIND_FORWARD_DOORS, }; @@ -71,13 +72,15 @@ string REWIND_DESC[] = { "rewind grenade throws", "use sendevent augmentation (allows race w/ death rewind)", "forward projectile self-knockback", + "additional collision checks", "open doors earlier for hpbs [requires rfd=1]", }; const float REWIND_DEFAULT_FLAGS = REWIND_PROJ_FIRE | REWIND_PROJ_TRAVEL | REWIND_FORWARD_PROJ_SELFKNOCK | - REWIND_SENDEVENT; + REWIND_SENDEVENT | + REWIND_DOUBLE_COL; float RewindFlagEnabled(float flag) { diff --git a/ssqc/rewind.qc b/ssqc/rewind.qc index 8208a00f..2407946e 100644 --- a/ssqc/rewind.qc +++ b/ssqc/rewind.qc @@ -104,6 +104,31 @@ static SeekResult RewindSeek(RewindState* rstate, float rtime) { return r; } +static void DoubleCheckCollision(entity player, + RewindSnapshot* a, + RewindSnapshot* b) { + + if (vlen(b->origin - a->origin) > 48) + return; // Teleport + + const vector PLAYER_MINS = [-16, -16, -24], PLAYER_MAXS = [16, 16, 32]; + tracebox(a->origin, PLAYER_MINS, PLAYER_MAXS, b->origin, 0, player); + if (trace_fraction == 1) + return; + + entity hit = trace_ent; + + if (!hit.voided && hit.fpp.index && hit.touch) { + setorigin(player, trace_endpos); + + entity held_self = self; + self = hit; + other = player; + self.touch(); + self = held_self; + } +} + RewindSnapshot* RewindLog(RewindState* target) { if (target->owner != self) error("Log mismatch\n"); @@ -111,6 +136,7 @@ RewindSnapshot* RewindLog(RewindState* target) { target->owner->client_lastupdate = time; RewindSnapshot* rs = &target->snapshot[target->cur]; + RewindSnapshot* last = rs; if (time > rs->time) { if (rs->time) { target->cur = NextRewindIdx(target->cur); @@ -125,6 +151,9 @@ RewindSnapshot* RewindLog(RewindState* target) { rs->origin = target->owner.origin; rs->velocity = target->owner.velocity; + if (RewindFlagEnabled(REWIND_DOUBLE_COL) && rs != last) + DoubleCheckCollision(target->owner, last, rs); + return rs; } From c34581da1b0509beaa1c4c61382e85db9415c70d Mon Sep 17 00:00:00 2001 From: newby Date: Sat, 6 Jan 2024 01:30:29 -0800 Subject: [PATCH 19/57] nit: fix warning --- ssqc/items.qc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ssqc/items.qc b/ssqc/items.qc index 4d99d6c7..e0916a35 100644 --- a/ssqc/items.qc +++ b/ssqc/items.qc @@ -725,7 +725,10 @@ float () GetGrenadePossibility = { case 0: index = random() < 0.5 ? 0 : 1; break; case 1: index = 1; break; case 2: index = 0; break; - case 3: return 0; + + case 3: + default: + return 0; } FO_GrenInfo* gt = FO_PlayerGren(other, index); From 8c533c12a9683da26fbbecc9758bc7ea0cd4dc1a Mon Sep 17 00:00:00 2001 From: newby Date: Sat, 6 Jan 2024 12:31:14 -0800 Subject: [PATCH 20/57] rewind: check deadflag in double-check --- ssqc/rewind.qc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ssqc/rewind.qc b/ssqc/rewind.qc index 2407946e..c31c2365 100644 --- a/ssqc/rewind.qc +++ b/ssqc/rewind.qc @@ -107,7 +107,8 @@ static SeekResult RewindSeek(RewindState* rstate, float rtime) { static void DoubleCheckCollision(entity player, RewindSnapshot* a, RewindSnapshot* b) { - + if (player.deadflag) + return; // Dead if (vlen(b->origin - a->origin) > 48) return; // Teleport From 2915db9750ba743a3a7d03f23bea6cd6bfe66074 Mon Sep 17 00:00:00 2001 From: newby Date: Thu, 11 Jan 2024 09:08:44 -0800 Subject: [PATCH 21/57] new-balance: expand region matching --- ssqc/client.qc | 7 +++++-- ssqc/helpers.qc | 35 +++++++++++++++++++++++++++++------ 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/ssqc/client.qc b/ssqc/client.qc index d845f1f3..13130a30 100644 --- a/ssqc/client.qc +++ b/ssqc/client.qc @@ -234,8 +234,6 @@ static void SetAllRoles(float pc, float gren, float limit) { float ActivateNewBalance() { disable_resup_gren = 2; // No gren2 pickups - if (fo_config.min_ping_ms < 50) // 50 min-ping - localcmd("localinfo mpm 50"); drop_gren1 = 0; // No grenades in backpacks @@ -248,6 +246,11 @@ float ActivateNewBalance() { PC_PYRO_AIRBLAST_COOLDOWN = 15; PC_ENGINEER_GRENADE_TYPE_2_RANGE = 200; + + if (ServerRegion() == kRegionUS || ServerRegion() == kRegionEU) { + if (fo_config.min_ping_ms < 50) // 50 min-ping + localcmd("localinfo mpm 50"); + } } void () DecodeLevelParms = { diff --git a/ssqc/helpers.qc b/ssqc/helpers.qc index 647a7cf7..cda76aa0 100644 --- a/ssqc/helpers.qc +++ b/ssqc/helpers.qc @@ -1,15 +1,38 @@ enum { - kRegionUS, - kRegionOther, + kRegionUS = 1, + kRegionEU, + kRegionOCE, + kRegionUnknown, }; -const string kNorthAmericaChannel = "504171613793681408"; +struct RegionMatch { + string channel; + float region; +}; + +static RegionMatch region_matches[] = { + { "504171613793681408", kRegionUS }, // US pug + { "513699536846323712", kRegionEU }, // EU pug + { "542237808895459338", kRegionOCE }, // OCE pug +}; float ServerRegion() { - if (discord_channel_id == kNorthAmericaChannel) - return kRegionUS; + static float region; + + if (region) + return region; + + // Can also query FO_REGION env but that does not appear to be + // reliably/consistently set at the moment. + region = kRegionUnknown; + for (int i = 0; i < region_matches.length; i++) { + if (discord_channel_id == region_matches[i].channel) { + region = region_matches[i].region; + break; + } + } - return kRegionOther; + return region; } float ServerIsStaging() { From 18c5e04192bea820cbc58e241b19be330d970584 Mon Sep 17 00:00:00 2001 From: newby Date: Thu, 11 Jan 2024 09:25:28 -0800 Subject: [PATCH 22/57] new-balance: more fuzzy matching for regions --- ssqc/client.qc | 13 +++++++++++-- ssqc/helpers.qc | 23 ++++++++++++++++++++--- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/ssqc/client.qc b/ssqc/client.qc index 13130a30..d1bac75f 100644 --- a/ssqc/client.qc +++ b/ssqc/client.qc @@ -232,7 +232,16 @@ static void SetAllRoles(float pc, float gren, float limit) { Role_Defence.gren2_limits[pc] = limit; } -float ActivateNewBalance() { +static float NB_UseMinPing() { + if (ServerIsOCE()) + return FALSE; + + if (ServerRegion() == kRegionUnknown) + return FALSE; // e.g. random server, including LAN + return TRUE; +} + +void ActivateNewBalance() { disable_resup_gren = 2; // No gren2 pickups drop_gren1 = 0; // No grenades in backpacks @@ -247,7 +256,7 @@ float ActivateNewBalance() { PC_PYRO_AIRBLAST_COOLDOWN = 15; PC_ENGINEER_GRENADE_TYPE_2_RANGE = 200; - if (ServerRegion() == kRegionUS || ServerRegion() == kRegionEU) { + if (NB_UseMinPing()) { if (fo_config.min_ping_ms < 50) // 50 min-ping localcmd("localinfo mpm 50"); } diff --git a/ssqc/helpers.qc b/ssqc/helpers.qc index cda76aa0..72c633c3 100644 --- a/ssqc/helpers.qc +++ b/ssqc/helpers.qc @@ -2,6 +2,7 @@ enum { kRegionUS = 1, kRegionEU, kRegionOCE, + kRegionLeague, kRegionUnknown, }; @@ -11,9 +12,11 @@ struct RegionMatch { }; static RegionMatch region_matches[] = { - { "504171613793681408", kRegionUS }, // US pug - { "513699536846323712", kRegionEU }, // EU pug - { "542237808895459338", kRegionOCE }, // OCE pug + { "504171613793681408", kRegionUS }, // US pug + { "513699536846323712", kRegionEU }, // EU pug + { "542237808895459338", kRegionOCE }, // OCE pug + { "1147341454851719219", kRegionLeague }, // Scrim + { "1026405619231625257", kRegionLeague }, // Tourney }; float ServerRegion() { @@ -35,6 +38,20 @@ float ServerRegion() { return region; } +float ServerIsOCE() { + if (ServerRegion() == kRegionOCE) + return TRUE; + + // Additional check beyond kRegion for picking up OCE Scrim/Tourney etc + // This is pretty awful, for at least these servers we should just fix FO_REGION + // to be consistent in the future. + string hostname = serverkey("hostname"); + + return strstrofs(hostname, "Sydney") >= 0 || + strstrofs(hostname, "Melbourne") >= 0 || + strstrofs(hostname, "New Zealand") >= 0; +} + float ServerIsStaging() { string hostname = serverkey("hostname"); From e6601ab20f1a229e9a036ed1118dc3f87e5a633c Mon Sep 17 00:00:00 2001 From: newby Date: Thu, 11 Jan 2024 10:05:24 -0800 Subject: [PATCH 23/57] core: finish removing s_time from server side, fix entity removal This fixes leaking ents with sky collisions. --- share/commondefs.qc | 1 - share/prediction.qc | 1 + ssqc/debug.qc | 16 ++++++++-------- ssqc/rewind.qc | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/share/commondefs.qc b/share/commondefs.qc index 001c21c1..4885c849 100644 --- a/share/commondefs.qc +++ b/share/commondefs.qc @@ -172,7 +172,6 @@ const float SERVER_FRAME_DT = 1/SERVER_FPS; const float SERVER_FRAME_MS = SERVER_FRAME_DT * 1000.0; .vector s_origin; -.float s_time; #define MAX_FLAGINFO_LINES 10 #define ENG_BUILDING_DISMANTLE_DISTANCE 100 diff --git a/share/prediction.qc b/share/prediction.qc index cdc735f9..6e3c43b6 100644 --- a/share/prediction.qc +++ b/share/prediction.qc @@ -451,6 +451,7 @@ void InitProjectileEnt(float sendflags); void WPP_UpdateEnable(float force); .float owner_entnum; +.float s_time; #endif #ifdef SSQC diff --git a/ssqc/debug.qc b/ssqc/debug.qc index a5d81b51..4d9ccf4c 100644 --- a/ssqc/debug.qc +++ b/ssqc/debug.qc @@ -16,17 +16,17 @@ void (entity te) dremove = remove(te); }; +.float created_at; // Forward dec void dremove_sent(entity te) { - static const float epsilon = 3*SERVER_FRAME_DT; - float stime = te.s_time; - if (!stime) - stime = time; + static const float epsilon = 2*SERVER_FRAME_DT; - if (time > stime + epsilon) { - dremove(self); + float expires = te.created_at + epsilon; // In the past for 0 created at + + if (time > expires) { + dremove(te); } else { - self.nextthink = stime + epsilon; - self.think = SUB_Remove; + te.nextthink = expires; + te.think = SUB_Remove; } } diff --git a/ssqc/rewind.qc b/ssqc/rewind.qc index c31c2365..e4f8a43c 100644 --- a/ssqc/rewind.qc +++ b/ssqc/rewind.qc @@ -385,7 +385,7 @@ void Forward_Projectile(int fpp_type, entity proj, float use_ctime) { // a prior point in time, but we don't advance time while stepping. float ft = Phys_Init(proj, stime, static_dt, PHYSF_CONSUME_ALL); - // We initialize s_origin/s_time after Phys_Init, they are used when + // We initialize s_origin after Phys_Init, they are used when // knockback forwarding is on to determine delay. if (rewind_hit && RewindFlagEnabled(REWIND_FORWARD_PROJ_SELFKNOCK)) { phys_flags |= PHYSF_FORWARD_KNOCK; From 8fb2e03cd3aee202b2deb2ed28f122e1b93d6119 Mon Sep 17 00:00:00 2001 From: newby Date: Sun, 24 Dec 2023 13:41:20 -0800 Subject: [PATCH 24/57] new-balance: new take on engineer's rail gun A new attempt at railgun for the engineer, aims to add mobility to the class and add a better mechanism for catching escaping players. It still fires a rail, but it fires backwards -- from the aimed at wall, back to the engineer. This can be used to either intercept someone escape or for the engineer to gain additional mobility (jump higher, move faster). --- csqc/weapon_predict.qc | 16 ++++++++++-- share/physics.qc | 1 + ssqc/engineer.qc | 58 ++++++++++++++++++++++++++++++++++++++++++ ssqc/qw.qc | 2 ++ ssqc/tfortmap.qc | 3 +++ ssqc/weapons.qc | 3 +++ 6 files changed, 81 insertions(+), 2 deletions(-) diff --git a/csqc/weapon_predict.qc b/csqc/weapon_predict.qc index 96c549c5..75924b8a 100644 --- a/csqc/weapon_predict.qc +++ b/csqc/weapon_predict.qc @@ -1707,6 +1707,13 @@ void FO_FireAssCanPellet(vector org, vector dir, float proj_speed, int index) { proj.fpp.ammo_index = WP_GetAmmo(AMMO_SHELLS) * 100 + index; } +float NB_RailCoolDown(FO_WeapInfo* wi) { + if (!fo_config.new_balance || wi->weapon != WEAP_RAILGUN) + return FALSE; + + return pstate_pred.client_time < pstate_pred.special_next; +} + void WP_Attack() { if (getstatf(STAT_NOFIRE)) return; @@ -1719,6 +1726,9 @@ void WP_Attack() { if (!WP_CheckAmmo(wi)) return; + if (NB_RailCoolDown(wi)) + return; + // Whether firing occurs here, or is embedded in the frame animation code // (because continuous fire). int in_anim = wi->weapon == WEAP_NAILGUN || @@ -1751,7 +1761,8 @@ void WP_Attack() { PP_CreateProjectile(FPP_TRANQ, PM_Org() + v_forward * 8 + '0 0 16'); break; case WEAP_RAILGUN: - PP_CreateProjectile(FPP_RAILGUN, PM_Org() + '0 0 16'); + if (!fo_config.new_balance) + PP_CreateProjectile(FPP_RAILGUN, PM_Org() + '0 0 16'); break; case WEAP_ASSAULT_CANNON: @@ -1974,7 +1985,8 @@ void WP_UpdateViewModel() { alph = CVARF(fo_reloadalpha); } else { alph = CVARF(r_drawviewmodel); - if (!WP_WeaponReady() && CVARF(fo_nofirealpha) != -1) + float ready = WP_WeaponReady() && !NB_RailCoolDown(wi); + if (!ready && CVARF(fo_nofirealpha) != -1) alph = CVARF(fo_nofirealpha); } diff --git a/share/physics.qc b/share/physics.qc index 35584b0a..8a3e6bb2 100644 --- a/share/physics.qc +++ b/share/physics.qc @@ -27,6 +27,7 @@ void AugmentGrenadeImpact(); static void Phys_Impact(entity e, float dt, float phys_flags) { other = trace_ent; + if (other.solid == SOLID_NOT) return; diff --git a/ssqc/engineer.qc b/ssqc/engineer.qc index 1255aa27..65797c70 100644 --- a/ssqc/engineer.qc +++ b/ssqc/engineer.qc @@ -78,7 +78,65 @@ void () LaserBolt_Touch = { dremove(self); }; +static void NBRailTouch() { + self.owner = self.real_owner; + + if (other.health) { + deathmsg = DMSG_LASERBOLT; + TF_T_Damage(other, self, self.enemy, 15, 2, TF_TD_ELECTRICITY | TF_TD_NOMOMENTUM); + + float mag = 250; + if (other.has_flag) + mag *= 2; + other.velocity += normalize(self.velocity) * mag; + } + dremove(self); +} + +void W_FireNBRail() { + vector vec, org; + + makevectors(self.v_angle); + org = (self.absmin + self.absmax) / 2; + + traceline(org, org + 1500 * v_forward, MOVE_WORLDONLY, world); + + self.special_next = time + 3; + + if (trace_fraction == 1) + return; + + if (pointcontents(trace_endpos) == CONTENT_SKY) + return; + + vector term = trace_endpos; + vector vec = normalize(term - org); + + pointparticles(particleeffectnum("fo_airblast"), term); + + entity proj = FOProj_Create(FPP_RAILGUN); + proj.owner = proj; // Want collisions with everyone + proj.real_owner = self; + proj.enemy = self; + proj.movetype = MOVETYPE_FLYMISSILE; + proj.solid = SOLID_BBOX; + + proj.velocity = -vec * 2250; + proj.angles = vectoangles(proj.velocity); + + proj.nextthink = time + 5; + proj.think = SUB_Remove; + proj.touch = NBRailTouch; + proj.classname = "railslug"; + + setorigin(proj, term); + FOProj_Finalize(proj); +} + void () W_FireRailgun = { + if (new_balance) + return W_FireNBRail(); + local vector vec, org; self.ammo_nails = self.ammo_nails - 1; diff --git a/ssqc/qw.qc b/ssqc/qw.qc index 883a8652..9a86053b 100644 --- a/ssqc/qw.qc +++ b/ssqc/qw.qc @@ -145,6 +145,8 @@ float remote_client_time(); .float menu_displaytime; .f_void_float menu_input; +.float has_flag; + float toggleflags; // toggleable flags string nextmap; diff --git a/ssqc/tfortmap.qc b/ssqc/tfortmap.qc index f682d517..04cdbc1d 100644 --- a/ssqc/tfortmap.qc +++ b/ssqc/tfortmap.qc @@ -2265,6 +2265,7 @@ void (entity Item, entity AP, entity Goal) tfgoalitem_GiveToPlayer = { DoResults(Item, AP, 1); DoItemGroupWork(Item, AP); AP.goalrunningtime = gametime; + AP.has_flag = TRUE; LogEventPickupGoal(AP); }; @@ -2337,6 +2338,7 @@ void (entity Item, entity AP, float method) tfgoalitem_RemoveFromPlayer = { //Flag if ((Item.goal_activation & 1)) { local float timecarried = gametime - AP.goalrunningtime; + AP.has_flag = FALSE; RemoveFlagFollow(AP); LogEventFumble(AP, timecarried); } @@ -2928,6 +2930,7 @@ void () DropGoalItems = { //Always allow dropping 4096 else if (self.effects & EF_DIMLIGHT || te.goal_activation & TFGI_ALLOWTHROW) { timecarried = gametime - self.goalrunningtime; + self.has_flag = FALSE; RemoveFlagFollow(self); LogEventFumble(self, timecarried); te.angles = '0 0 0'; diff --git a/ssqc/weapons.qc b/ssqc/weapons.qc index e22c15b2..359f86aa 100644 --- a/ssqc/weapons.qc +++ b/ssqc/weapons.qc @@ -1744,6 +1744,9 @@ void () W_Attack = { player_shot1(); W_FireTranq(); } else if (ws.weapon == WEAP_RAILGUN) { + if (new_balance && time < self.special_next) + return; + player_shot1(); W_FireRailgun(); } From 0d652e22bbe4800892b633be7b062d03a2f51a1c Mon Sep 17 00:00:00 2001 From: newby Date: Thu, 11 Jan 2024 14:19:25 -0800 Subject: [PATCH 25/57] new-balance: more trial limits - hwguy: 1 mirv (was 0) - engineer: 1 emp (was 2) 4 gren1 (was 3) - soldier: 4 gren1 (was 3) --- ssqc/client.qc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ssqc/client.qc b/ssqc/client.qc index d1bac75f..b53ebfd1 100644 --- a/ssqc/client.qc +++ b/ssqc/client.qc @@ -251,7 +251,10 @@ void ActivateNewBalance() { SetAllRoles(i, 1, 3); SetAllRoles(4, 2, 1); // 1 mirv for demoman - SetAllRoles(6, 2, 0); // 0 mirv for hwguy + SetAllRoles(6, 2, 1); // 1 mirv for hwguy + SetAllRoles(9, 2, 1); // 1 emp for engineer + SetAllRoles(9, 1, 4); // 4 gren1 for engineer + SetAllRoles(3, 1, 4); // 4 gren1 for soldier PC_PYRO_AIRBLAST_COOLDOWN = 15; PC_ENGINEER_GRENADE_TYPE_2_RANGE = 200; From af052df6107aa51717561bc90743f12ecd7aacaa Mon Sep 17 00:00:00 2001 From: newby Date: Thu, 11 Jan 2024 17:56:46 -0800 Subject: [PATCH 26/57] weapons: remove legacy bitfield weapon representation no longer using bitfields for these so we can just use plain indices --- share/defs.h | 4 ++-- share/weapons.qc | 41 +++++------------------------------------ ssqc/tfort.qc | 1 - 3 files changed, 7 insertions(+), 39 deletions(-) diff --git a/share/defs.h b/share/defs.h index 2a81d90e..9eba59a5 100644 --- a/share/defs.h +++ b/share/defs.h @@ -733,8 +733,8 @@ struct Slot { int id; }; /*======================================================*/ #define WEAP_NONE 0 -enumflags { - WEAP_HOOK, +enum { + WEAP_HOOK = 1, WEAP_KNIFE, WEAP_MEDIKIT, WEAP_SPANNER, diff --git a/share/weapons.qc b/share/weapons.qc index 6101212f..cde432d7 100644 --- a/share/weapons.qc +++ b/share/weapons.qc @@ -37,18 +37,6 @@ float SlotIndex(Slot slot) { return slot.id - 1; } -// Convert a weapon-bit to a linear index. -static float WEAP_to_index(float weapon) { - float index = 0; - for (; weapon > 0; weapon >>= 1, index++) { - if (weapon >= 64) { - weapon >>= 6; - index += 6; - } - } - return index; -} - enum AmmoType { AMMO_NONE, AMMO_SHELLS, @@ -83,7 +71,6 @@ struct FO_WeapInfo { FO_WeapToItem* items; }; -// REQUIRES: weapon at index i == WEAP_to_index(weap) // -ve values are placeholders signifying conditional init based on game modes. FO_WeapInfo weapon_info[] = { { WEAP_NONE, NO_PREDICT, AMMO_NONE, 0, 0, 0.5, 0 }, @@ -111,7 +98,7 @@ FO_WeapInfo weapon_info[] = { }; inline var FO_WeapInfo* FO_GetWeapInfo(float weapon) { - return &weapon_info[WEAP_to_index(weapon)]; + return &weapon_info[weapon]; } struct FO_ClassWeapons { @@ -319,8 +306,8 @@ float IsHoldGrenades() { return csqc_get_user_setting("hg", "hold_grens", "off"); } #endif -// Indexed by WEAP_to_index(). We keep them out of the main table just to keep -// things a little more wieldly/readable. +// We keep these out of the main table just to keep things a little more +// wieldly/readable. static string weapon_names[] = { "None", "Hook", "Knife", "Medikit", "Spanner", "Axe", "Sniper Rifle", "Auto Rifle", "Shotgun", "Super Shotgun", "Nailgun", "Super Nailgun", @@ -330,11 +317,7 @@ static string weapon_names[] = { }; string FO_GetWeapName(float weapon) { - // Seeing a weird compiler bug here, passing WEAP_to_index directly to the - // array dereference will randomly cause OOB with length 13 limit, which is - // much smaller than `weapon_names`. - int index = WEAP_to_index(weapon); - return weapon_names[index]; + return weapon_names[weapon]; } @@ -347,7 +330,6 @@ struct FO_WeapModels { }; -// Indexed by WEAP_to_index() static FO_WeapModels weapon_models[] = { { WEAP_NONE, ""}, { WEAP_HOOK, "progs/v_grap.mdl" }, @@ -430,16 +412,6 @@ FO_WeapInfo* FO_SlotWeapInfo(float playerclass, Slot slot) { return class_weapons[playerclass]->info[idx] ?: FO_GetWeapInfo(WEAP_NONE); } -float FO_ClassWeapItemMask(float class) { - float result = 0; - for (int i = 1; i <= TF_NUM_SLOTS; i++) { - FO_WeapInfo* wi = FO_SlotWeapInfo(class, MakeSlot(i)); - if (wi) - result |= (wi->items)->it_weapon; - } - return result; -} - #ifndef CSQC float FO_WeaponsMask(entity player) { float mask = 0; @@ -752,7 +724,6 @@ void FO_Weapons_Init() { for (i = 0; i < weapon_info.length; i++) { FO_WeapInfo* wi = &weapon_info[i]; - ASSERTD_EQ(WEAP_to_index(wi->weapon), i); if (wi->weapon == WEAP_PIPE_LAUNCHER) { FO_WeapInfo* parent = FO_GetWeapInfo(WEAP_GRENADE_LAUNCHER); @@ -767,7 +738,6 @@ void FO_Weapons_Init() { } FO_WeapModels* wm = &weapon_models[i]; - ASSERTD_EQ(WEAP_to_index(wm->weapon), i); precache_model(wm->model); #ifdef CSQC wm->modelindex = getmodelindex(wm->model); @@ -775,7 +745,6 @@ void FO_Weapons_Init() { wi->models = wm; FO_WeapToItem* wti = &weapon_to_items[i]; - ASSERTD_EQ(WEAP_to_index(wti->weapon), i); wi->items = wti; switch (wi->ammo_type) { @@ -798,7 +767,7 @@ void FO_Weapons_Init() { FO_ClassWeapons* cw = &class_weapons[i]; if (cw->slots[j]) - cw->info[j] = &weapon_info[WEAP_to_index(cw->slots[j])]; + cw->info[j] = &weapon_info[cw->slots[j]]; else cw->info[j] = &weapon_info[0]; // WEAP_NONE } diff --git a/ssqc/tfort.qc b/ssqc/tfort.qc index b145e1f2..3c0f1426 100644 --- a/ssqc/tfort.qc +++ b/ssqc/tfort.qc @@ -1218,7 +1218,6 @@ void () TeamFortress_SetEquipment = { Team_Role * role = GetTeamRole(self.team_no); kept_items = self.tf_items & (IT_KEY1 | IT_KEY2); - self.items = FO_ClassWeapItemMask(self.playerclass) | kept_items; if (!remember_weapon || self.last_playerclass != self.playerclass || (self.tfstate & TFSTATE_RANDOMPC)) { From ba3d8d5761f9a893c9d9ed5478312137dc5f47fa Mon Sep 17 00:00:00 2001 From: newby Date: Thu, 11 Jan 2024 21:32:25 -0800 Subject: [PATCH 27/57] new-balance: turn new railgun into "impeller', a new weapon on slot3 --- csqc/weapon_predict.qc | 26 +++++++++++++++++++++----- share/defs.h | 3 ++- share/weapons.qc | 38 +++++++++++++++++++++++++++++--------- ssqc/engineer.qc | 13 ++++++------- ssqc/weapons.qc | 10 +++++++--- 5 files changed, 65 insertions(+), 25 deletions(-) diff --git a/csqc/weapon_predict.qc b/csqc/weapon_predict.qc index 75924b8a..ce8d9721 100644 --- a/csqc/weapon_predict.qc +++ b/csqc/weapon_predict.qc @@ -1115,6 +1115,22 @@ float WP_ReloadSlot(Slot slot) { return TRUE; } +void WP_CheckSpecialReady() { + if (!IsEffectFrame()) + return; + + if (pstate_pred.special_next > pstate_pred.server_time || + pstate_pred.special_next < pstate_pred.server_time - input_timelength) + return; + + switch (pstate_pred.playerclass) { + case PC_ENGINEER: + if (fo_config.new_balance) + printf("Impeller ready\n"); + break; + } +} + void WP_CheckReloadFinished() { if (WP_IsReloading() && pstate_pred.reload_finished && pstate_pred.client_time >= pstate_pred.reload_finished) { pstate_pred.tfstate &= ~TFSTATE_RELOADING; @@ -1707,11 +1723,11 @@ void FO_FireAssCanPellet(vector org, vector dir, float proj_speed, int index) { proj.fpp.ammo_index = WP_GetAmmo(AMMO_SHELLS) * 100 + index; } -float NB_RailCoolDown(FO_WeapInfo* wi) { - if (!fo_config.new_balance || wi->weapon != WEAP_RAILGUN) +float NB_ImpellerCoolDown(FO_WeapInfo* wi) { + if (!fo_config.new_balance || wi->weapon != WEAP_IMPELLER) return FALSE; - return pstate_pred.client_time < pstate_pred.special_next; + return pstate_pred.server_time < pstate_pred.special_next; } void WP_Attack() { @@ -1726,7 +1742,7 @@ void WP_Attack() { if (!WP_CheckAmmo(wi)) return; - if (NB_RailCoolDown(wi)) + if (NB_ImpellerCoolDown(wi)) return; // Whether firing occurs here, or is embedded in the frame animation code @@ -1985,7 +2001,7 @@ void WP_UpdateViewModel() { alph = CVARF(fo_reloadalpha); } else { alph = CVARF(r_drawviewmodel); - float ready = WP_WeaponReady() && !NB_RailCoolDown(wi); + float ready = WP_WeaponReady() && !NB_ImpellerCoolDown(wi); if (!ready && CVARF(fo_nofirealpha) != -1) alph = CVARF(fo_nofirealpha); } diff --git a/share/defs.h b/share/defs.h index 9eba59a5..048caf84 100644 --- a/share/defs.h +++ b/share/defs.h @@ -755,7 +755,8 @@ enum { WEAP_DETPACK, WEAP_TRANQ, WEAP_RAILGUN, - WEAP_LAST = WEAP_RAILGUN, + WEAP_IMPELLER, + WEAP_LAST = WEAP_IMPELLER, }; // still room for 12 more weapons diff --git a/share/weapons.qc b/share/weapons.qc index cde432d7..729eac26 100644 --- a/share/weapons.qc +++ b/share/weapons.qc @@ -94,7 +94,8 @@ FO_WeapInfo weapon_info[] = { { WEAP_LIGHTNING, NO_PREDICT, AMMO_CELLS, 0, 1, 0.1, 0 }, { WEAP_DETPACK, NO_PREDICT, AMMO_NONE, 0, 0, 0, 0 }, { WEAP_TRANQ, PRED_PROJ, AMMO_SHELLS, 0, 1, 1.5, 0 }, - { WEAP_RAILGUN, PRED_PROJ, AMMO_NAILS, 0, 1, 0.4, 0 }, + { WEAP_RAILGUN, PRED_PROJ, AMMO_NAILS, 0, 1, 0.4, 0 }, + { WEAP_IMPELLER, PRED_PROJ, AMMO_NAILS, 0, 1, 0.6, 0 }, }; inline var FO_WeapInfo* FO_GetWeapInfo(float weapon) { @@ -118,7 +119,7 @@ FO_ClassWeapons class_weapons[] = { { PC_HVYWEAP, { WEAP_ASSAULT_CANNON, WEAP_SUPER_SHOTGUN, WEAP_SHOTGUN, WEAP_AXE } }, { PC_PYRO, { WEAP_INCENDIARY, WEAP_FLAMETHROWER, WEAP_SHOTGUN, WEAP_AXE } }, { PC_SPY, { WEAP_TRANQ, WEAP_SUPER_SHOTGUN, WEAP_NAILGUN, WEAP_KNIFE } }, - { PC_ENGINEER, { WEAP_RAILGUN, WEAP_SUPER_SHOTGUN, 0, WEAP_SPANNER } }, + { PC_ENGINEER, { WEAP_RAILGUN, WEAP_SUPER_SHOTGUN, WEAP_IMPELLER, WEAP_SPANNER } }, { PC_RANDOM, { 0, 0, 0, WEAP_AXE } }, // TODO: Probably needs attention { PC_CIVILIAN, { 0, 0, 0, WEAP_AXE } }, }; @@ -313,7 +314,7 @@ static string weapon_names[] = { "Auto Rifle", "Shotgun", "Super Shotgun", "Nailgun", "Super Nailgun", "Grenade Launcher", "Pipebomb Launcher", "Flamethrower", "Rocket Launcher", "Incendiary Launcher", "Assault Cannon", "Lightning Gun", "Detpack", - "Tranquilizer", "Railgun" + "Tranquilizer", "Railgun", "Impeller" }; string FO_GetWeapName(float weapon) { @@ -353,6 +354,7 @@ static FO_WeapModels weapon_models[] = { { WEAP_DETPACK, "" }, { WEAP_TRANQ, "progs/v_tranq.mdl" }, { WEAP_RAILGUN, "progs/v_rail.mdl" }, + { WEAP_IMPELLER, "progs/v_rail.mdl" }, }; // REQUIRES: Order must match above. @@ -397,19 +399,35 @@ FO_WeapToItem weapon_to_items[] = { { WEAP_DETPACK, 0}, { WEAP_TRANQ, IT_SHOTGUN}, { WEAP_RAILGUN, IT_SHOTGUN}, + { WEAP_IMPELLER, IT_SHOTGUN}, }; +float FO_WeapAvailable(float weapon) { + if (weapon == WEAP_IMPELLER && !fo_config.new_balance) + return FALSE; + + return TRUE; +} + FO_WeapInfo* FO_SlotWeapInfo(float playerclass, Slot slot) { if (IsSlotNull(slot)) return FO_GetWeapInfo(WEAP_NONE); + float si = SlotIndex(slot); + if (playerclass == PC_ENGINEER && si == 2 && !fo_config.new_balance) + return FO_GetWeapInfo(WEAP_NONE); + float idx = SlotIndex(slot); float cf_pyro_impulses = IsPyroSlotSwapped(); - float si = SlotIndex(slot); if (playerclass == PC_PYRO && cf_pyro_impulses && (idx == 0 || idx == 1)) idx = 1 - idx; - return class_weapons[playerclass]->info[idx] ?: FO_GetWeapInfo(WEAP_NONE); + FO_WeapInfo* wi = class_weapons[playerclass]->info[idx]; + + if (!wi || !FO_WeapAvailable(wi->weapon)) + return FO_GetWeapInfo(WEAP_NONE); + + return wi; } #ifndef CSQC @@ -651,7 +669,7 @@ ImpulseWeapon impulse_weapons[] = { { PC_SPY, { WEAP_KNIFE, WEAP_TRANQ, WEAP_SUPER_SHOTGUN, WEAP_NAILGUN } }, { PC_ENGINEER, - { WEAP_SPANNER, WEAP_RAILGUN, WEAP_SUPER_SHOTGUN } }, + { WEAP_SPANNER, WEAP_RAILGUN, WEAP_SUPER_SHOTGUN, WEAP_IMPELLER } }, { PC_RANDOM, { WEAP_AXE } }, // Never instantiated { PC_CIVILIAN, { WEAP_AXE } }, }; @@ -679,7 +697,7 @@ Slot FO_FindPrevNextWeaponSlot(float playerclass, Slot slot, float is_prev) { float direction = is_prev ? -1 : 1; if (IsUsingOldImpulses()) { - Slot* impulse_table = impulse_weapons[playerclass].slots; + ImpulseWeapon* impulse_table = &impulse_weapons[playerclass]; // For reasons that seem to rhyme with -ompilerbug, reads from // impulse_table[offset] return junk so we use a pointer cursor. Slot* cur; @@ -687,13 +705,15 @@ Slot FO_FindPrevNextWeaponSlot(float playerclass, Slot slot, float is_prev) { direction = (direction + len) % len; for (i = 0; i < len; i++) { - cur = &impulse_table[i]; + cur = &impulse_table.slots[i]; if (IsSameSlot(*cur, slot)) break; } do { i = (i + direction) % len; - cur = &impulse_table[i]; + if (!FO_WeapAvailable(impulse_table.weapons[i])) + continue; + cur = &impulse_table.slots[i]; } while (IsSlotNull(*cur)); // Technically this will flip OWI+CFPI, but this is a nonsense combo. diff --git a/ssqc/engineer.qc b/ssqc/engineer.qc index 65797c70..7436c4d9 100644 --- a/ssqc/engineer.qc +++ b/ssqc/engineer.qc @@ -78,12 +78,12 @@ void () LaserBolt_Touch = { dremove(self); }; -static void NBRailTouch() { +static void ImpellerTouch() { self.owner = self.real_owner; if (other.health) { deathmsg = DMSG_LASERBOLT; - TF_T_Damage(other, self, self.enemy, 15, 2, TF_TD_ELECTRICITY | TF_TD_NOMOMENTUM); + TF_T_Damage(other, self, self.owner, 15, 2, TF_TD_ELECTRICITY | TF_TD_NOMOMENTUM); float mag = 250; if (other.has_flag) @@ -93,7 +93,7 @@ static void NBRailTouch() { dremove(self); } -void W_FireNBRail() { +void W_FireImpeller() { vector vec, org; makevectors(self.v_angle); @@ -126,17 +126,16 @@ void W_FireNBRail() { proj.nextthink = time + 5; proj.think = SUB_Remove; - proj.touch = NBRailTouch; + proj.touch = ImpellerTouch; proj.classname = "railslug"; setorigin(proj, term); FOProj_Finalize(proj); + + Pred_Sound(SND_RAILGUN); } void () W_FireRailgun = { - if (new_balance) - return W_FireNBRail(); - local vector vec, org; self.ammo_nails = self.ammo_nails - 1; diff --git a/ssqc/weapons.qc b/ssqc/weapons.qc index 359f86aa..12946032 100644 --- a/ssqc/weapons.qc +++ b/ssqc/weapons.qc @@ -99,6 +99,7 @@ void () W_FireFlame; void W_FireIncendiaryCannon(vector org, vector angles, float use_ctime=0); void () W_FireTranq; void () W_FireRailgun; +void () W_FireImpeller; void () HallucinationTimer; void () TranquiliserTimer; @@ -1744,11 +1745,14 @@ void () W_Attack = { player_shot1(); W_FireTranq(); } else if (ws.weapon == WEAP_RAILGUN) { - if (new_balance && time < self.special_next) + player_shot1(); + W_FireRailgun(); + } else if (ws.weapon == WEAP_IMPELLER) { + if (!new_balance || time < self.special_next) return; player_shot1(); - W_FireRailgun(); + W_FireImpeller(); } if (ws.weapon != WEAP_FLAMETHROWER && // Variable @@ -1879,7 +1883,7 @@ void W_ChangeWeaponSlot(Slot slot) { FO_WeapState ws; FO_FillWeapState(self, slot, &ws); - if (ws.weapon == WEAP_NONE) + if (ws.weapon == WEAP_NONE || !FO_WeapAvailable(ws.weapon)) return; // queue next weapon if queue is not empty or has changed From 858555022843ae789c546066c33a50936ca14741 Mon Sep 17 00:00:00 2001 From: newby Date: Thu, 11 Jan 2024 22:25:13 -0800 Subject: [PATCH 28/57] new-balance: unify ssqc/csqc active check --- csqc/weapon_predict.qc | 6 +++--- share/commondefs.qc | 12 ++++++++++++ share/prediction.qc | 1 + share/weapons.qc | 4 ++-- ssqc/client.qc | 2 +- ssqc/demoman.qc | 2 +- ssqc/helpers.qc | 4 ---- ssqc/pyro.qc | 2 +- ssqc/qw.qc | 1 - 9 files changed, 21 insertions(+), 13 deletions(-) diff --git a/csqc/weapon_predict.qc b/csqc/weapon_predict.qc index ce8d9721..9d366ad4 100644 --- a/csqc/weapon_predict.qc +++ b/csqc/weapon_predict.qc @@ -1125,7 +1125,7 @@ void WP_CheckSpecialReady() { switch (pstate_pred.playerclass) { case PC_ENGINEER: - if (fo_config.new_balance) + if (NewBalanceActive()) printf("Impeller ready\n"); break; } @@ -1724,7 +1724,7 @@ void FO_FireAssCanPellet(vector org, vector dir, float proj_speed, int index) { } float NB_ImpellerCoolDown(FO_WeapInfo* wi) { - if (!fo_config.new_balance || wi->weapon != WEAP_IMPELLER) + if (!NewBalanceActive() || wi->weapon != WEAP_IMPELLER) return FALSE; return pstate_pred.server_time < pstate_pred.special_next; @@ -1777,7 +1777,7 @@ void WP_Attack() { PP_CreateProjectile(FPP_TRANQ, PM_Org() + v_forward * 8 + '0 0 16'); break; case WEAP_RAILGUN: - if (!fo_config.new_balance) + if (!NewBalanceActive()) PP_CreateProjectile(FPP_RAILGUN, PM_Org() + '0 0 16'); break; diff --git a/share/commondefs.qc b/share/commondefs.qc index 4885c849..34218402 100644 --- a/share/commondefs.qc +++ b/share/commondefs.qc @@ -292,3 +292,15 @@ enumflags { CSQC_FORCE_POS, CSQC_SNIPER_SIGHT, }; + +#ifdef SSQC +float new_balance; +#endif + +float NewBalanceActive() { +#ifdef CSQC + return fo_config.new_balance; +#else + return new_balance & 1; +#endif +} diff --git a/share/prediction.qc b/share/prediction.qc index 6e3c43b6..8e43fbb1 100644 --- a/share/prediction.qc +++ b/share/prediction.qc @@ -1161,3 +1161,4 @@ void AugmentGrenadeImpact() { self.SendFlags |= FOPP_MOVETYPE; #endif } + diff --git a/share/weapons.qc b/share/weapons.qc index 729eac26..b2f25f1d 100644 --- a/share/weapons.qc +++ b/share/weapons.qc @@ -403,7 +403,7 @@ FO_WeapToItem weapon_to_items[] = { }; float FO_WeapAvailable(float weapon) { - if (weapon == WEAP_IMPELLER && !fo_config.new_balance) + if (weapon == WEAP_IMPELLER && !NewBalanceActive()) return FALSE; return TRUE; @@ -414,7 +414,7 @@ FO_WeapInfo* FO_SlotWeapInfo(float playerclass, Slot slot) { return FO_GetWeapInfo(WEAP_NONE); float si = SlotIndex(slot); - if (playerclass == PC_ENGINEER && si == 2 && !fo_config.new_balance) + if (playerclass == PC_ENGINEER && si == 2 && !NewBalanceActive()) return FO_GetWeapInfo(WEAP_NONE); float idx = SlotIndex(slot); diff --git a/ssqc/client.qc b/ssqc/client.qc index b53ebfd1..93867709 100644 --- a/ssqc/client.qc +++ b/ssqc/client.qc @@ -1087,7 +1087,7 @@ void () DecodeLevelParms = { // mirror current state into desired state bit new_balance |= (new_balance << 1); - if (ServerIsNewBalance()) + if (NewBalanceActive()) ActivateNewBalance(); }; diff --git a/ssqc/demoman.qc b/ssqc/demoman.qc index 3ecca191..c996e18d 100644 --- a/ssqc/demoman.qc +++ b/ssqc/demoman.qc @@ -205,7 +205,7 @@ void (float timer) TeamFortress_SetDetpack = { float quad_limit = quadmode && round_active && (time < round_end_time) && (rounds % 2) != (self.team_no - 1); - if (ServerIsNewBalance() && quad_limit) { + if (NewBalanceActive() && quad_limit) { float last = max(self.detpack_last, round_start_time); float next = last + 30; // TODO: tunable if this sticks if (next > time) { diff --git a/ssqc/helpers.qc b/ssqc/helpers.qc index 72c633c3..2b85c437 100644 --- a/ssqc/helpers.qc +++ b/ssqc/helpers.qc @@ -57,7 +57,3 @@ float ServerIsStaging() { return strstrofs(hostname, "Staging") >= 0; } - -float ServerIsNewBalance() { - return new_balance & 1; -} diff --git a/ssqc/pyro.qc b/ssqc/pyro.qc index f1e144fd..e67e7b88 100644 --- a/ssqc/pyro.qc +++ b/ssqc/pyro.qc @@ -170,7 +170,7 @@ void () NapalmGrenadeExplode2 = { deathmsg = DMSG_FLAME; float aflags = TF_TD_FIRE; - if (ServerIsNewBalance()) + if (NewBalanceActive()) aflags |= TF_TD_NOMOMENTUM; TF_T_Damage(head, self, self.owner, explodeDam, TF_TD_NOTTEAM, aflags); diff --git a/ssqc/qw.qc b/ssqc/qw.qc index 9a86053b..20743614 100644 --- a/ssqc/qw.qc +++ b/ssqc/qw.qc @@ -804,7 +804,6 @@ string backend_address; float fo_login_required; .entity filter_ent; -float new_balance; var float PC_PYRO_AIRBLAST_COOLDOWN = 5; var float PC_ENGINEER_GRENADE_TYPE_2_RANGE = 240; .float detpack_last; From 018a52ed41dddb1d9bd284a290b069e20110924e Mon Sep 17 00:00:00 2001 From: newby Date: Thu, 11 Jan 2024 23:05:19 -0800 Subject: [PATCH 29/57] nb: rebalance conc grenades - initial planar velocity cap of 1050 (down from 1400+) - caps immediately on first landing/jump to 950 planar (predicted by pmove) tldr: easier to det, no speed cap lerping, some longer slide setups now open to med --- csqc/pmove.qc | 8 ++++++++ share/classes.qc | 31 +++++++++++++++++++++++++++++++ share/defs.h | 4 ++++ ssqc/client.qc | 2 ++ ssqc/scout.qc | 7 +++++++ 5 files changed, 52 insertions(+) diff --git a/csqc/pmove.qc b/csqc/pmove.qc index 302950e0..32069b2e 100644 --- a/csqc/pmove.qc +++ b/csqc/pmove.qc @@ -470,6 +470,9 @@ static void PM_NudgeBounce(PM_Nudge* nudge, entity ent) { float points = dmg - vlen(targ_org - nudge->org) / 2; ent.velocity = (targ_org - nudge->org) * points/20; + + NB_ConcCapAction(ent, pstate_pred.playerclass, &pstate_pred.tfstate, + kLaunch); } @@ -630,6 +633,11 @@ static void PM_RunMovement(PMS_Data* pmsd, float endframe) { if ((ent.pmove_flags & PMF_JUMP_HELD) && (pstate_pred.tfstate & TFSTATE_CONC)) Conc_Jump(&pstate_pred.conc_state, ent); + + if ((ent.pmove_flags & PMF_JUMP_HELD) && (pstate_pred.tfstate & TFSTATE_CONC_CAP)) { + NB_ConcCapAction(ent, pstate_pred.playerclass, &pstate_pred.tfstate, + kLand); + } } pm.seq++; diff --git a/share/classes.qc b/share/classes.qc index b690274b..d4d80390 100644 --- a/share/classes.qc +++ b/share/classes.qc @@ -166,3 +166,34 @@ float Class_ScaleMoment(float playerclass, float damage) { return damage; return damage <= 50 ? 0 : damage / 4; } + +enum { + kLaunch, + kLand +}; + +void NB_ConcCap(entity ent, float speed) { + vector planar_vel = [ent.velocity_x, ent.velocity_y, 0]; + + if (vlen(planar_vel) > speed) { + planar_vel = normalize(planar_vel) * speed; + } + + ent.velocity_x = planar_vel.x; + ent.velocity_y = planar_vel.y; +} + +void NB_ConcCapAction(entity ent, float player_class, float* tfstate, float action) { + if (!NewBalanceActive()) + return; + + if (action == kLaunch) { + if (player_class = PC_MEDIC) + *tfstate |= TFSTATE_CONC_CAP; + + NB_ConcCap(ent, NB_CONC_CAP_AIR); + } else if (action == kLand && (*tfstate & TFSTATE_CONC_CAP)) { + *tfstate &= ~TFSTATE_CONC_CAP; + NB_ConcCap(ent, NB_CONC_CAP_LAND); + } +} diff --git a/share/defs.h b/share/defs.h index 048caf84..5a4b108e 100644 --- a/share/defs.h +++ b/share/defs.h @@ -249,6 +249,7 @@ enumflags { TFSTATE_AIMING, // is using the laser sight or spinning TFSTATE_CANT_MOVE, TFSTATE_CONC, + TFSTATE_CONC_CAP, // Speed-cap next jump TFSTATE_NO_WEAPON, // Weapon is disabled and not visible (e.g. detpack) // (Note: We don't use NO_WEAPON for reloading // as it could result in stacked no-weapon states.) @@ -1664,3 +1665,6 @@ TFAlias csqc_aliases[] = { {"+dropflag", 0, "+button7"}, {"-dropflag", 0, "-button7"}, }; + +#define NB_CONC_CAP_AIR 1050 +#define NB_CONC_CAP_LAND 950 diff --git a/ssqc/client.qc b/ssqc/client.qc index 93867709..518cdb19 100644 --- a/ssqc/client.qc +++ b/ssqc/client.qc @@ -2547,6 +2547,7 @@ void () PlayerJump = { if (self.flags & FL_WATERJUMP) return; + if (self.waterlevel >= 2) { if (self.watertype == CONTENT_WATER) self.velocity_z = 100; @@ -2578,6 +2579,7 @@ void () PlayerJump = { self.flags = self.flags - (self.flags & FL_JUMPRELEASED); self.button2 = 0; + NB_ConcCapAction(self, self.playerclass, &self.tfstate, kLand); FO_Sound(self, CHAN_AUTO, "player/plyrjmp8.wav", 1, ATTN_NORM); if (!cannon_air && self.tfstate & TFSTATE_AC_FIRING) { diff --git a/ssqc/scout.qc b/ssqc/scout.qc index 36f325e3..71f27756 100644 --- a/ssqc/scout.qc +++ b/ssqc/scout.qc @@ -861,12 +861,19 @@ void (entity inflictor, entity attacker, float bounce, head.velocity = head.velocity * (points / 20); Predict_AddFilterEnt(head, inflictor); + NB_ConcCapAction(head, head.playerclass, &head.tfstate, kLaunch); + if (head.classname != "player") { if (head.flags & FL_ONGROUND) head.flags = head.flags - FL_ONGROUND; } else { if (head.playerclass == PC_MEDIC) { + if (NewBalanceActive()) { + head = head.chain; + continue; + } + if (medicnocuss && (medic_type != MEDIC_TYPE_BLAST)) { entity speedbump; From f31197a05ebbc6c8866fe7014e5b26d84a8a1eb6 Mon Sep 17 00:00:00 2001 From: newby Date: Fri, 12 Jan 2024 16:49:02 -0800 Subject: [PATCH 30/57] new-balance: add a small lockout to conc-cap free hop in first 0.5s, initial cap moved to 1100, improve prediction sync --- csqc/pmove.qc | 3 ++- share/classes.qc | 14 +++++++++++--- share/defs.h | 2 +- share/prediction.qc | 5 ++++- ssqc/client.qc | 3 ++- ssqc/scout.qc | 3 ++- 6 files changed, 22 insertions(+), 8 deletions(-) diff --git a/csqc/pmove.qc b/csqc/pmove.qc index 32069b2e..95db379e 100644 --- a/csqc/pmove.qc +++ b/csqc/pmove.qc @@ -472,7 +472,7 @@ static void PM_NudgeBounce(PM_Nudge* nudge, entity ent) { ent.velocity = (targ_org - nudge->org) * points/20; NB_ConcCapAction(ent, pstate_pred.playerclass, &pstate_pred.tfstate, - kLaunch); + pm.interp_t, &pstate_pred.conc_cap_time, kLaunch); } @@ -636,6 +636,7 @@ static void PM_RunMovement(PMS_Data* pmsd, float endframe) { if ((ent.pmove_flags & PMF_JUMP_HELD) && (pstate_pred.tfstate & TFSTATE_CONC_CAP)) { NB_ConcCapAction(ent, pstate_pred.playerclass, &pstate_pred.tfstate, + pm.interp_t, &pstate_pred.conc_cap_time, kLand); } } diff --git a/share/classes.qc b/share/classes.qc index d4d80390..7f368f35 100644 --- a/share/classes.qc +++ b/share/classes.qc @@ -183,16 +183,24 @@ void NB_ConcCap(entity ent, float speed) { ent.velocity_y = planar_vel.y; } -void NB_ConcCapAction(entity ent, float player_class, float* tfstate, float action) { +void NB_ConcCapAction(entity ent, float player_class, float* tfstate, + float gtime, float* cap_time, float action) { if (!NewBalanceActive()) return; + const float kLockout = 0.5; + if (action == kLaunch) { - if (player_class = PC_MEDIC) + if (player_class == PC_MEDIC) { *tfstate |= TFSTATE_CONC_CAP; + *cap_time = gtime + kLockout; + } NB_ConcCap(ent, NB_CONC_CAP_AIR); - } else if (action == kLand && (*tfstate & TFSTATE_CONC_CAP)) { + + } else if (action == kLand && (*tfstate & TFSTATE_CONC_CAP) && + gtime > *cap_time) { + float cap = *cap_time; *tfstate &= ~TFSTATE_CONC_CAP; NB_ConcCap(ent, NB_CONC_CAP_LAND); } diff --git a/share/defs.h b/share/defs.h index 5a4b108e..54d10de9 100644 --- a/share/defs.h +++ b/share/defs.h @@ -1666,5 +1666,5 @@ TFAlias csqc_aliases[] = { {"-dropflag", 0, "-button7"}, }; -#define NB_CONC_CAP_AIR 1050 +#define NB_CONC_CAP_AIR 1100 #define NB_CONC_CAP_LAND 950 diff --git a/share/prediction.qc b/share/prediction.qc index 8e43fbb1..b5e77eca 100644 --- a/share/prediction.qc +++ b/share/prediction.qc @@ -67,6 +67,7 @@ struct predict_tf_state { float conc_amp; float conc_finished; + float conc_cap_time; float special_next; float client_time, server_time; @@ -314,6 +315,7 @@ float FO_MaxRewindGrenWinDt() { .predict_tf_state predict_state; .entity predict_entity; +.float conc_cap_time; inline float *self_tf_state() { return &self.tfstate; } @@ -367,7 +369,7 @@ static float Prediction_ChangedMask(entity player, entity src) { M2(FOWP_TFSTATE, tfstate, csqc_maxspeed); M1(FOWP_LASTPRIME, last_prime); M2(FOWP_CLASSIC_CONC, conc_state.next, conc_state.mag); - M2(FOWP_CONCFINISH, conc_finished, conc_amp); + M3(FOWP_CONCFINISH, conc_finished, conc_amp, conc_cap_time); M1(FOWP_WF, weaponframe); M1(FOWP_AF, attack_finished); M2(FOWP_THINK, client_nextthink, client_thinkindex); @@ -566,6 +568,7 @@ void EntUpdate_WeaponPred(float isnew) { if (sendflags & FOWP_CONCFINISH) { COMM(Byte, conc_amp); COMM(Float, conc_finished); + COMM(Float, conc_cap_time); } if (sendflags & FOWP_THINK) { diff --git a/ssqc/client.qc b/ssqc/client.qc index 518cdb19..da307d1c 100644 --- a/ssqc/client.qc +++ b/ssqc/client.qc @@ -2579,7 +2579,8 @@ void () PlayerJump = { self.flags = self.flags - (self.flags & FL_JUMPRELEASED); self.button2 = 0; - NB_ConcCapAction(self, self.playerclass, &self.tfstate, kLand); + NB_ConcCapAction(self, self.playerclass, &self.tfstate, + time, &self.conc_cap_time, kLand); FO_Sound(self, CHAN_AUTO, "player/plyrjmp8.wav", 1, ATTN_NORM); if (!cannon_air && self.tfstate & TFSTATE_AC_FIRING) { diff --git a/ssqc/scout.qc b/ssqc/scout.qc index 71f27756..470f046f 100644 --- a/ssqc/scout.qc +++ b/ssqc/scout.qc @@ -861,7 +861,8 @@ void (entity inflictor, entity attacker, float bounce, head.velocity = head.velocity * (points / 20); Predict_AddFilterEnt(head, inflictor); - NB_ConcCapAction(head, head.playerclass, &head.tfstate, kLaunch); + NB_ConcCapAction(head, head.playerclass, &head.tfstate, + time, &head.conc_cap_time, kLaunch); if (head.classname != "player") { if (head.flags & FL_ONGROUND) From f6d24d623006ff40df10284441d895588eb3ca19 Mon Sep 17 00:00:00 2001 From: newby Date: Fri, 12 Jan 2024 17:00:40 -0800 Subject: [PATCH 31/57] new-balance: add command to disable conc change `localinfo nbc 0` is prior conc behavior. --- share/classes.qc | 14 +++++++++++--- share/commondefs.qc | 6 ++++++ share/prediction.qc | 2 ++ ssqc/client.qc | 7 +++++++ ssqc/scout.qc | 2 +- 5 files changed, 27 insertions(+), 4 deletions(-) diff --git a/share/classes.qc b/share/classes.qc index 7f368f35..55d0037b 100644 --- a/share/classes.qc +++ b/share/classes.qc @@ -172,6 +172,15 @@ enum { kLand }; +float NB_UseNewConc() { + return fo_config.new_balance_flags & NBF_OLD_CONC == 0; +} + +float NB_NoCap() { + return fo_config.new_balance_flags & NBF_NO_CAP; +} + + void NB_ConcCap(entity ent, float speed) { vector planar_vel = [ent.velocity_x, ent.velocity_y, 0]; @@ -185,19 +194,18 @@ void NB_ConcCap(entity ent, float speed) { void NB_ConcCapAction(entity ent, float player_class, float* tfstate, float gtime, float* cap_time, float action) { - if (!NewBalanceActive()) + if (!NewBalanceActive() || !NB_UseNewConc()) return; const float kLockout = 0.5; if (action == kLaunch) { - if (player_class == PC_MEDIC) { + if (player_class == PC_MEDIC && !NB_NoCap()) { *tfstate |= TFSTATE_CONC_CAP; *cap_time = gtime + kLockout; } NB_ConcCap(ent, NB_CONC_CAP_AIR); - } else if (action == kLand && (*tfstate & TFSTATE_CONC_CAP) && gtime > *cap_time) { float cap = *cap_time; diff --git a/share/commondefs.qc b/share/commondefs.qc index 34218402..3c232ca6 100644 --- a/share/commondefs.qc +++ b/share/commondefs.qc @@ -54,6 +54,7 @@ var struct { float rj; float new_balance; + float new_balance_flags; } fo_config; enumflags { @@ -304,3 +305,8 @@ float NewBalanceActive() { return new_balance & 1; #endif } + +enumflags { + NBF_OLD_CONC, + NBF_NO_CAP, +}; diff --git a/share/prediction.qc b/share/prediction.qc index b5e77eca..7cf0bee3 100644 --- a/share/prediction.qc +++ b/share/prediction.qc @@ -483,6 +483,7 @@ void EntUpdate_Config() { COMM(Byte, old_ng_rof); COMM(Short, fo_concuss); COMM(Byte, new_balance); + COMM(Byte, new_balance_flags); #ifdef SSQC return TRUE; @@ -730,6 +731,7 @@ void Predict_InitDefaultConfig() { fo_config.clown_grav = 400; fo_config.fo_concuss = 0; fo_config.new_balance = 0; + fo_config.new_balance_flags = 0; } #ifdef SSQC diff --git a/ssqc/client.qc b/ssqc/client.qc index da307d1c..9d5d1b88 100644 --- a/ssqc/client.qc +++ b/ssqc/client.qc @@ -259,6 +259,13 @@ void ActivateNewBalance() { PC_PYRO_AIRBLAST_COOLDOWN = 15; PC_ENGINEER_GRENADE_TYPE_2_RANGE = 200; + float use_new_conc = CF_GetSetting("nbc", "new_balance_concs", "1"); + if (!use_new_conc) + fo_config.new_balance_flags |= NBF_OLD_CONC; + float no_cap = CF_GetSetting("nbnc", "new_balance_no_cap", "0"); + if (no_cap) + fo_config.new_balance_flags |= NBF_NO_CAP; + if (NB_UseMinPing()) { if (fo_config.min_ping_ms < 50) // 50 min-ping localcmd("localinfo mpm 50"); diff --git a/ssqc/scout.qc b/ssqc/scout.qc index 470f046f..94b7c5a1 100644 --- a/ssqc/scout.qc +++ b/ssqc/scout.qc @@ -870,7 +870,7 @@ void (entity inflictor, entity attacker, float bounce, } else { if (head.playerclass == PC_MEDIC) { - if (NewBalanceActive()) { + if ((NewBalanceActive() && NB_UseNewConc()) || NB_NoCap()) { head = head.chain; continue; } From 7efef098de096a540e90fadf92bb570948dfb5bb Mon Sep 17 00:00:00 2001 From: newby Date: Fri, 12 Jan 2024 17:08:52 -0800 Subject: [PATCH 32/57] new-balance: add impeller ready/not ready text to hud --- csqc/hud.qc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/csqc/hud.qc b/csqc/hud.qc index 0ab8b15f..ef32b274 100644 --- a/csqc/hud.qc +++ b/csqc/hud.qc @@ -1001,6 +1001,15 @@ void Hud_DrawClassInfoPanel(float playerclass) } break; case PC_ENGINEER: + if (NewBalanceActive()) { + basepos = pos; + msg = pstate_pred.server_time >= pstate_pred.special_next ? + "Impeller charged" : "Impeller recharging"; + HRC_drawOffsetString(panel->Orientation, msg, size, fontSize, + pos, basepos, FALSE, colour); + + pos = [pos_x, pos_y + size_y + 4]; + } if (SBAR.HasSentry) { icon = ICON_ENGINEER_SG; //HudIcons[playerclass+6].icon; From deed7418ef07349decc7ab9051bd5475bee937df Mon Sep 17 00:00:00 2001 From: newby Date: Wed, 17 Jan 2024 15:53:18 -0800 Subject: [PATCH 33/57] new-balance: change airblast cooldown to 10s --- ssqc/client.qc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ssqc/client.qc b/ssqc/client.qc index 9d5d1b88..7322302a 100644 --- a/ssqc/client.qc +++ b/ssqc/client.qc @@ -256,7 +256,7 @@ void ActivateNewBalance() { SetAllRoles(9, 1, 4); // 4 gren1 for engineer SetAllRoles(3, 1, 4); // 4 gren1 for soldier - PC_PYRO_AIRBLAST_COOLDOWN = 15; + PC_PYRO_AIRBLAST_COOLDOWN = 10; PC_ENGINEER_GRENADE_TYPE_2_RANGE = 200; float use_new_conc = CF_GetSetting("nbc", "new_balance_concs", "1"); From 4fc9952e13a88c71bec1494d3d1679a447799313 Mon Sep 17 00:00:00 2001 From: newby Date: Wed, 17 Jan 2024 15:55:27 -0800 Subject: [PATCH 34/57] new-balance: reduce conc time on medic (not scout) grens to 2s --- ssqc/scout.qc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ssqc/scout.qc b/ssqc/scout.qc index 94b7c5a1..aa2957b5 100644 --- a/ssqc/scout.qc +++ b/ssqc/scout.qc @@ -830,6 +830,9 @@ void (entity inflictor, entity attacker, float bounce, local entity te; local vector org; + if (NewBalanceActive() && inflictor.owner.playerclass == PC_MEDIC) + actual_cuss_time = 2; + head = findradius(inflictor.origin, bounce + 40); while (head) { if (head != ignore) { From 03069f99e56fde125fd8abc761d873d0bd0117d1 Mon Sep 17 00:00:00 2001 From: newby Date: Wed, 17 Jan 2024 16:13:15 -0800 Subject: [PATCH 35/57] new-balance: turn on by default for OCE --- ssqc/client.qc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ssqc/client.qc b/ssqc/client.qc index 7322302a..6010e93c 100644 --- a/ssqc/client.qc +++ b/ssqc/client.qc @@ -1089,8 +1089,10 @@ void () DecodeLevelParms = { // Overrides other settings. new_balance = CF_GetSetting("new_balance", "new_balance", "4"); + float new_balance_region = (ServerRegion() == kRegionUS || + ServerRegion() == kRegionOCE); if (new_balance == 4) - new_balance = ((ServerRegion() == kRegionUS) && ServerIsStaging()) ? 1 : 0; + new_balance = (new_balance_region && ServerIsStaging()) ? 1 : 0; // mirror current state into desired state bit new_balance |= (new_balance << 1); From 0b04574904e616ed12adf77e2ef054c7cd12ca33 Mon Sep 17 00:00:00 2001 From: newby Date: Wed, 17 Jan 2024 17:07:18 -0800 Subject: [PATCH 36/57] hud: make ident expire --- csqc/hud.qc | 3 +++ ssqc/qw.qc | 1 - ssqc/status.qc | 4 ---- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/csqc/hud.qc b/csqc/hud.qc index ef32b274..94d154d3 100644 --- a/csqc/hud.qc +++ b/csqc/hud.qc @@ -1058,6 +1058,9 @@ void Hud_DrawIdentifyPanel(string identify) { // click event } + if (time > last_id_time + 3) + return; + vector fontSize = FO_Hud_Icon_Font_Size * panel->Scale; float count = tokenizebyseparator(identify, "\n"); diff --git a/ssqc/qw.qc b/ssqc/qw.qc index 20743614..089e4ba8 100644 --- a/ssqc/qw.qc +++ b/ssqc/qw.qc @@ -117,7 +117,6 @@ float remote_client_time(); // Identify variables .string ident_string; // Status bar string for identify -.string last_ident_string; // Last Status bar string for identify .float ident_time; // The time when last identify found a player .float autoid_type; // 0 = ignore noone, 1 = ignore teammates, 2 = ignore enemies .float autoid_time; // Time when autoid settings were last checked diff --git a/ssqc/status.qc b/ssqc/status.qc index 85b9efae..1be22004 100644 --- a/ssqc/status.qc +++ b/ssqc/status.qc @@ -800,10 +800,6 @@ void UpdateClientGrenadeThrown(entity pl) = { void UpdateClientIDString(entity pl) { string ident = time < pl.ident_time ? pl.ident_string : ""; - if (ident == pl.last_ident_string) // only send updates - return; - pl.last_ident_string = ident; - if (ident == "") // No need to send null, we'll expire clientside. return; From b760afd0ba8a2e3ab626464ae5e689a7f010c2cf Mon Sep 17 00:00:00 2001 From: newby Date: Wed, 17 Jan 2024 18:00:03 -0800 Subject: [PATCH 37/57] core: split tfstate into pstate size limits(?) on bitops dont play well with large enumflags values. Split tfstate into a second word, moving some of our less commonly used fields for now. This fixes issues like infinite tranq --- share/defs.h | 14 +++++++++----- ssqc/client.qc | 8 ++++---- ssqc/qw.qc | 1 + ssqc/tfortmap.qc | 16 ++++++++-------- ssqc/weapons.qc | 6 +++--- 5 files changed, 25 insertions(+), 20 deletions(-) diff --git a/share/defs.h b/share/defs.h index 54d10de9..7b8b8af1 100644 --- a/share/defs.h +++ b/share/defs.h @@ -259,21 +259,25 @@ enumflags { TFSTATE_AC_SPINDOWN, TFSTATE_LOCK, // assault cannon locked TFSTATE_INFECTED, // set when player is infected by the bioweapon - TFSTATE_INVINCIBLE, // Player has permanent Invincibility (Usually by GoalItem) - TFSTATE_INVISIBLE, // Player has permanent Invisibility (Usually by GoalItem) - TFSTATE_QUAD, // Player has permanent Quad Damage (Usually by GoalItem) - TFSTATE_RADSUIT, // Player has permanent Radsuit (Usually by GoalItem) TFSTATE_BURNING, // Is on fire TFSTATE_FEIGNED, // Is feigned TFSTATE_AIMING, // is using the laser sight or spinning cannon TFSTATE_RESPAWN_READY, // is waiting for respawn, and has pressed fire, // as sentry gun,indicate it needs to die TFSTATE_HALLUCINATING, // set when player is hallucinating - TFSTATE_TRANQUILISED, // set when player is tranquilised + TFSTATE_FLAMES_MAX, // Peak burnination. + TFSTATE_TRANQUILISED, // set when player is tranquilised TFSTATE_RANDOMPC, + // QC/Compiler limitation: Bits past-24 unsafe with bitops }; +enumflags { + PSTATE_INVINCIBLE, // Player has permanent Invincibility (Usually by GoalItem) + PSTATE_INVISIBLE, // Player has permanent Invisibility (Usually by GoalItem) + PSTATE_QUAD, // Player has permanent Quad Damage (Usually by GoalItem) + PSTATE_RADSUIT, // Player has permanent Radsuit (Usually by GoalItem) +}; #define TFSTATE_GREN_MASK_PRIMED (TFSTATE_GREN1_PRIMED|TFSTATE_GREN2_PRIMED) #define TFSTATE_GREN_MASK_ALL (TFSTATE_GREN_MASK_PRIMED|TFSTATE_GRENTHROWING) diff --git a/ssqc/client.qc b/ssqc/client.qc index 6010e93c..b35d297d 100644 --- a/ssqc/client.qc +++ b/ssqc/client.qc @@ -2844,7 +2844,7 @@ void () CheckPowerups = { self.modelindex = modelindex_eyes; } else { if (self.invisible_finished) { - if (self.tfstate & TFSTATE_INVISIBLE) { + if (self.pstate & PSTATE_INVISIBLE) { if (self.invisible_finished < (time + 10)) { self.invisible_finished = time + 666; } @@ -2879,7 +2879,7 @@ void () CheckPowerups = { } } if (self.invincible_finished) { - if (self.tfstate & TFSTATE_INVINCIBLE) { + if (self.pstate & PSTATE_INVINCIBLE) { if (self.invincible_finished < (time + 10)) { self.invincible_finished = time + 666; } @@ -2923,7 +2923,7 @@ void () CheckPowerups = { } } if (self.super_damage_finished) { - if (self.tfstate & TFSTATE_QUAD) { + if (self.pstate & PSTATE_QUAD) { if (self.super_damage_finished == (time + 10)) { self.super_damage_finished = time + 666; } @@ -2965,7 +2965,7 @@ void () CheckPowerups = { } if (self.radsuit_finished) { self.air_finished = time + 12; - if (self.tfstate & TFSTATE_RADSUIT) { + if (self.pstate & PSTATE_RADSUIT) { if (self.radsuit_finished == (time + 10)) { self.radsuit_finished = time + 666; } diff --git a/ssqc/qw.qc b/ssqc/qw.qc index 089e4ba8..3eac851a 100644 --- a/ssqc/qw.qc +++ b/ssqc/qw.qc @@ -108,6 +108,7 @@ float remote_client_time(); .entity nopickup; // Don't pick up this backpack/ammobox because it doesn't contain any of your ammo types +.float pstate; // Player state flags .float tfstate; // State flags for TeamFortress .float last_tfstate; // Internal cache of tfstate for detecting updates .entity linked_list; // Used just like chain. Has to be separate so diff --git a/ssqc/tfortmap.qc b/ssqc/tfortmap.qc index 04cdbc1d..4a44d286 100644 --- a/ssqc/tfortmap.qc +++ b/ssqc/tfortmap.qc @@ -908,7 +908,7 @@ void (entity Goal, entity Player, entity AP, float addb) Apply_Results = { Player.invincible_finished = time + Goal.invincible_finished; if (Goal.classname == "item_tfgoal") { - Player.tfstate = Player.tfstate | TFSTATE_INVINCIBLE; + Player.pstate |= PSTATE_INVINCIBLE; Player.invincible_finished = time + 666; } } @@ -917,7 +917,7 @@ void (entity Goal, entity Player, entity AP, float addb) Apply_Results = { Player.invisible_time = 1; Player.invisible_finished = time + Goal.invisible_finished; if (Goal.classname == "item_tfgoal") { - Player.tfstate = Player.tfstate | TFSTATE_INVISIBLE; + Player.pstate |= PSTATE_INVISIBLE; Player.invisible_finished = time + 666; } } @@ -927,7 +927,7 @@ void (entity Goal, entity Player, entity AP, float addb) Apply_Results = { Player.super_damage_finished = time + Goal.super_damage_finished; if (Goal.classname == "item_tfgoal") { - Player.tfstate = Player.tfstate | TFSTATE_QUAD; + Player.pstate |= PSTATE_QUAD; Player.super_damage_finished = time + 666; } } @@ -936,7 +936,7 @@ void (entity Goal, entity Player, entity AP, float addb) Apply_Results = { Player.rad_time = 1; Player.radsuit_finished = time + Goal.radsuit_finished; if (Goal.classname == "item_tfgoal") { - Player.tfstate = Player.tfstate | TFSTATE_RADSUIT; + Player.pstate |= PSTATE_RADSUIT; Player.radsuit_finished = time + 666; } } @@ -1179,25 +1179,25 @@ void (entity Goal, entity Player) RemoveResults = { te = find(te, classname, "item_tfgoal"); } if ((Goal.invincible_finished > 0) && !puinvin) { - Player.tfstate &= ~TFSTATE_INVINCIBLE; + Player.pstate &= ~PSTATE_INVINCIBLE; Player.items = Player.items | IT_INVULNERABILITY; Player.invincible_time = 1; Player.invincible_finished = time + Goal.invincible_finished; } if ((Goal.invisible_finished > 0) && !puinvis) { - Player.tfstate &= ~TFSTATE_INVISIBLE; + Player.pstate &= ~PSTATE_INVISIBLE; Player.items = Player.items | IT_INVISIBILITY; Player.invisible_time = 1; Player.invisible_finished = time + Goal.invisible_finished; } if ((Goal.super_damage_finished > 0) && !puquad) { - Player.tfstate &= ~TFSTATE_QUAD; + Player.pstate &= ~PSTATE_QUAD; Player.items = Player.items | IT_QUAD; Player.super_time = 1; Player.super_damage_finished = time + Goal.super_damage_finished; } if ((Goal.radsuit_finished > 0) && !purad) { - Player.tfstate &= ~TFSTATE_RADSUIT; + Player.pstate &= ~PSTATE_RADSUIT; Player.items = Player.items | IT_SUIT; Player.rad_time = 1; Player.radsuit_finished = time + Goal.radsuit_finished; diff --git a/ssqc/weapons.qc b/ssqc/weapons.qc index 12946032..7c32756d 100644 --- a/ssqc/weapons.qc +++ b/ssqc/weapons.qc @@ -2676,16 +2676,16 @@ void () SuperDamageSound = { }; void () ToggleInvincibility = { - if(self.tfstate & TFSTATE_INVINCIBLE) { + if(self.pstate & PSTATE_INVINCIBLE) { self.items &= ~IT_INVULNERABILITY; self.invincible_time = 0; self.invincible_finished = 0; - self.tfstate &= ~TFSTATE_INVINCIBLE; + self.tfstate &= ~PSTATE_INVINCIBLE; self.effects &= ~(EF_RED | EF_DIMLIGHT | EF_BRIGHTLIGHT); } else { self.items |= IT_INVULNERABILITY; self.invincible_time = 1; - self.tfstate |= TFSTATE_INVINCIBLE; + self.pstate |= PSTATE_INVINCIBLE; self.invincible_finished = time + 666; } }; From 7017b4d6c106fc754aef51866ebc40e3c69a53a8 Mon Sep 17 00:00:00 2001 From: newby Date: Thu, 18 Jan 2024 00:24:46 -0800 Subject: [PATCH 38/57] new-balance: rework impeller - no more projectile - no cooldown during prematch, and target anyone -- spam away - different model to railgun - now only hits enemy team when carrying flag, but has some aim assist; knockback is reduced for non-direct hits - still gives mini-rj for eng - damage reduced --- csqc/weapon_predict.qc | 3 +- share/weapons.qc | 2 +- ssqc/engineer.qc | 145 +++++++++++++++++++++++++++++++++-------- 3 files changed, 119 insertions(+), 31 deletions(-) diff --git a/csqc/weapon_predict.qc b/csqc/weapon_predict.qc index 9d366ad4..68b94e73 100644 --- a/csqc/weapon_predict.qc +++ b/csqc/weapon_predict.qc @@ -1777,8 +1777,7 @@ void WP_Attack() { PP_CreateProjectile(FPP_TRANQ, PM_Org() + v_forward * 8 + '0 0 16'); break; case WEAP_RAILGUN: - if (!NewBalanceActive()) - PP_CreateProjectile(FPP_RAILGUN, PM_Org() + '0 0 16'); + PP_CreateProjectile(FPP_RAILGUN, PM_Org() + '0 0 16'); break; case WEAP_ASSAULT_CANNON: diff --git a/share/weapons.qc b/share/weapons.qc index b2f25f1d..a8919e08 100644 --- a/share/weapons.qc +++ b/share/weapons.qc @@ -354,7 +354,7 @@ static FO_WeapModels weapon_models[] = { { WEAP_DETPACK, "" }, { WEAP_TRANQ, "progs/v_tranq.mdl" }, { WEAP_RAILGUN, "progs/v_rail.mdl" }, - { WEAP_IMPELLER, "progs/v_rail.mdl" }, + { WEAP_IMPELLER, "progs/v_light.mdl" }, }; // REQUIRES: Order must match above. diff --git a/ssqc/engineer.qc b/ssqc/engineer.qc index 7436c4d9..224edb0f 100644 --- a/ssqc/engineer.qc +++ b/ssqc/engineer.qc @@ -93,46 +93,135 @@ static void ImpellerTouch() { dremove(self); } -void W_FireImpeller() { - vector vec, org; - makevectors(self.v_angle); - org = (self.absmin + self.absmax) / 2; +static float ValidImpellerTarget(entity t) { + if (t.classname != "player") + return FALSE; - traceline(org, org + 1500 * v_forward, MOVE_WORLDONLY, world); + if (cb_prematch) // Shoot anyone in prematch + return TRUE; - self.special_next = time + 3; + return (t.team_no != self.team_no && t.has_flag); +} - if (trace_fraction == 1) - return; +static entity FindImpellerTarget(vector org, float min_a, float* direct) { - if (pointcontents(trace_endpos) == CONTENT_SKY) - return; + traceline(org, org + 1500 * v_forward, MOVE_NORMAL, world); + if (trace_fraction != 1 && ValidImpellerTarget(trace_ent)) + return trace_ent; - vector term = trace_endpos; - vector vec = normalize(term - org); + int count; + entity* players; + float best_a = min_a; + entity best_p = world; - pointparticles(particleeffectnum("fo_airblast"), term); + players = find_list(classname, "player", EV_STRING, count); + for (int i = 0; i < count; i++) { + entity p = players[i]; - entity proj = FOProj_Create(FPP_RAILGUN); - proj.owner = proj; // Want collisions with everyone - proj.real_owner = self; - proj.enemy = self; - proj.movetype = MOVETYPE_FLYMISSILE; - proj.solid = SOLID_BBOX; + if (!ValidImpellerTarget(p)) + continue; - proj.velocity = -vec * 2250; - proj.angles = vectoangles(proj.velocity); + vector dir = normalize(p.origin - org); + float a = v_forward * dir; - proj.nextthink = time + 5; - proj.think = SUB_Remove; - proj.touch = ImpellerTouch; - proj.classname = "railslug"; + traceline(org, p.origin, MOVE_NORMAL, world); + if (trace_fraction < 1) { + if (trace_ent != p) { + continue; // Something in the way + } else { + *direct = TRUE; + return p; + } + } - setorigin(proj, term); - FOProj_Finalize(proj); + if (a < best_a) + continue; // Not in AA cone + + best_a = a; + best_p = p; + } + + return best_p; +} + +static vector find_closest(vector a, vector b, vector p) { + vector v = b - a, u = a - p; + float t = -(v * u) / (v * v); + + if (t >= 0 && t <= 1) + return (1 - t) * a + t * b; + return b; +} + +static void draw_beam(vector a, vector b, int effect) { + WriteByte(MSG_MULTICAST, SVC_TEMPENTITY); + WriteByte(MSG_MULTICAST, effect); + WriteEntity(MSG_MULTICAST, world); + WriteCoord(MSG_MULTICAST, a_x); + WriteCoord(MSG_MULTICAST, a_y); + WriteCoord(MSG_MULTICAST, a_z); + WriteCoord(MSG_MULTICAST, b_x); + WriteCoord(MSG_MULTICAST, b_y); + WriteCoord(MSG_MULTICAST, b_z); + multicast(a, MULTICAST_PHS); +} + +void W_FireImpeller() { + if (!cb_prematch) { + self.special_next = time + 3; + } + + FO_Sound(self, CHAN_WEAPON, "weapons/lhit.wav", 1, ATTN_NORM); + makevectors(self.v_angle); + + vector org = self.origin + '0 0 16'; + float direct = FALSE, jump = FALSE; + entity t = FindImpellerTarget(org, 0.985, &direct); + vector end = t.origin; + vector imp; + + float mag = 250; + entity hit; + + if (t == world) { + traceline(org, org + 350 * v_forward, MOVE_WORLDONLY, world); + if (trace_fraction == 1) + return; + + end = trace_endpos; + hit = self; + draw_beam(org, trace_endpos, TE_LIGHTNING2); + } else { + hit = t; + + if (direct) { + end = t.origin; + draw_beam(org, t.origin, TE_LIGHTNING2); + } else { + vector a = org, b = org + 1000 * v_forward; + traceline(a, b, MOVE_WORLDONLY, world); + if (trace_fraction == 1) + return; + end = trace_endpos; + vector x = find_closest(a, end, t.origin); + + draw_beam(a, x, TE_LIGHTNING2); + draw_beam(x, t.origin, TE_LIGHTNING2); + + // Discount knockback by help given + mag -= max(10, vlen(t.origin - x)); + } + + hit = t; + end = t.origin; + } - Pred_Sound(SND_RAILGUN); + TF_T_Damage(hit, self, self, 5, 2, TF_TD_ELECTRICITY | TF_TD_NOMOMENTUM); + hit.flags &= ~FL_ONGROUND; + hit.velocity -= mag * normalize(end - org); + if (other.has_flag) + mag *= 2; } void () W_FireRailgun = { From ad2f844c3668352f62692b3a90da0632e027e541 Mon Sep 17 00:00:00 2001 From: newby Date: Thu, 18 Jan 2024 00:26:38 -0800 Subject: [PATCH 39/57] new-balance: old concs outside of OCE --- ssqc/client.qc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ssqc/client.qc b/ssqc/client.qc index b35d297d..47fab406 100644 --- a/ssqc/client.qc +++ b/ssqc/client.qc @@ -259,7 +259,12 @@ void ActivateNewBalance() { PC_PYRO_AIRBLAST_COOLDOWN = 10; PC_ENGINEER_GRENADE_TYPE_2_RANGE = 200; - float use_new_conc = CF_GetSetting("nbc", "new_balance_concs", "1"); + float use_new_conc = CF_GetSetting("nbc", "new_balance_concs", "4"); + if (use_new_conc == 4 && ServerRegion() == kRegionOCE) + use_new_conc = 1; + else + use_new_conc = 0; + if (!use_new_conc) fo_config.new_balance_flags |= NBF_OLD_CONC; float no_cap = CF_GetSetting("nbnc", "new_balance_no_cap", "0"); From 744c3ada0a43cf1dd237244ed6747d54621571d3 Mon Sep 17 00:00:00 2001 From: newby Date: Fri, 19 Jan 2024 10:25:32 -0800 Subject: [PATCH 40/57] new-balance: add new-balance flags to wpp_status --- csqc/weapon_predict.qc | 4 ++++ share/classes.qc | 2 +- share/commondefs.qc | 7 ++++++- ssqc/client.qc | 16 ++++++++-------- 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/csqc/weapon_predict.qc b/csqc/weapon_predict.qc index 68b94e73..b39a8c31 100644 --- a/csqc/weapon_predict.qc +++ b/csqc/weapon_predict.qc @@ -275,6 +275,7 @@ void WPP_Status() { PRINT_CONFIG(max_rewind_fast_projectile_ms); PRINT_CONFIG(max_rewind_grenade_ms); PRINT_CONFIG(rewind_fast_projectile_thresh); + PRINT_CONFIG(new_balance); printf("\n"); PrintFlagField("Rewind Settings [rewind_flags=%d]\n", @@ -286,6 +287,9 @@ void WPP_Status() { PrintFlagField("Clown mode [clown_flags=%d]\n", fo_config.clown_flags, CLOWN_DESC.length, CLOWN_DESC); + PrintFlagField("NewBalance Flags [newbalance_flags=%d]:\n", + fo_config.new_balance_flags, NBF_DESC.length, NBF_DESC); + PrintFlagField("Concs [fo_concuss=%d]\n", fo_config.fo_concuss, FO_CONC.length, FO_CONC); } diff --git a/share/classes.qc b/share/classes.qc index 55d0037b..49441d68 100644 --- a/share/classes.qc +++ b/share/classes.qc @@ -173,7 +173,7 @@ enum { }; float NB_UseNewConc() { - return fo_config.new_balance_flags & NBF_OLD_CONC == 0; + return fo_config.new_balance_flags & NBF_CONC_NEW_CAP; } float NB_NoCap() { diff --git a/share/commondefs.qc b/share/commondefs.qc index 3c232ca6..ea918842 100644 --- a/share/commondefs.qc +++ b/share/commondefs.qc @@ -307,6 +307,11 @@ float NewBalanceActive() { } enumflags { - NBF_OLD_CONC, + NBF_CONC_NEW_CAP, NBF_NO_CAP, }; + +string NBF_DESC[] = { + "Reworked conc speed cap", + "No conc speed cap", +}; diff --git a/ssqc/client.qc b/ssqc/client.qc index 47fab406..309c04c7 100644 --- a/ssqc/client.qc +++ b/ssqc/client.qc @@ -259,16 +259,16 @@ void ActivateNewBalance() { PC_PYRO_AIRBLAST_COOLDOWN = 10; PC_ENGINEER_GRENADE_TYPE_2_RANGE = 200; - float use_new_conc = CF_GetSetting("nbc", "new_balance_concs", "4"); - if (use_new_conc == 4 && ServerRegion() == kRegionOCE) - use_new_conc = 1; + float use_new_cap = CF_GetSetting("nbcc", "new_balance_conc_cap", "4"); + if (use_new_cap == 4 && ServerRegion() == kRegionOCE) + use_new_cap = 1; else - use_new_conc = 0; + use_new_cap = 0; - if (!use_new_conc) - fo_config.new_balance_flags |= NBF_OLD_CONC; - float no_cap = CF_GetSetting("nbnc", "new_balance_no_cap", "0"); - if (no_cap) + if (use_new_cap) + fo_config.new_balance_flags |= NBF_CONC_NEW_CAP; + + if (CF_GetSetting("nbnc", "new_balance_no_cap", "0")) fo_config.new_balance_flags |= NBF_NO_CAP; if (NB_UseMinPing()) { From e5d53f8b87d2b7b70baaf7a1356d8844cc9b869c Mon Sep 17 00:00:00 2001 From: newby Date: Fri, 19 Jan 2024 10:43:55 -0800 Subject: [PATCH 41/57] new-balance: add death messages for impeller, slightly expand range --- share/defs.h | 1 + ssqc/client.qc | 5 +++++ ssqc/engineer.qc | 25 ++++++++----------------- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/share/defs.h b/share/defs.h index 7b8b8af1..414687d7 100644 --- a/share/defs.h +++ b/share/defs.h @@ -1312,6 +1312,7 @@ enum { #define DMSG_GREN_SHOCK 42 #define DMSG_GREN_BURST 43 #define DMSG_KNIFE 44 +#define DMSG_IMPELLER 45 /*======================================================*/ /* Menus */ diff --git a/ssqc/client.qc b/ssqc/client.qc index 309c04c7..2bba965a 100644 --- a/ssqc/client.qc +++ b/ssqc/client.qc @@ -3391,6 +3391,8 @@ string (entity pe_target, entity pe_attacker, float pf_deathmsg) GetDeathMessage s_deathstring = " gets too friendly with his sentry gun\n"; else if (pf_deathmsg == DMSG_DISPENSER_EXPLODE) s_deathstring = " dispenses with himself\n"; + else if (pf_deathmsg == DMSG_IMPELLER) + s_deathstring = " couldn't resist their inner impelsions\n"; return strcat(pe_target.netname, s_deathstring); @@ -3658,6 +3660,9 @@ string (entity pe_target, entity pe_attacker, float pf_deathmsg) GetDeathMessage } else if (pf_deathmsg == DMSG_LASERBOLT) { s_deathstring = " gets a hole in his heart from "; s_deathstring2 = "'s railgun\n"; + } else if (pf_deathmsg == DMSG_IMPELLER) { + s_deathstring = " gets pushed around by "; + s_deathstring2 = "\n"; } else if (pf_deathmsg == DMSG_INCENDIARY) { s_deathstring = " gets well done by "; s_deathstring2 = "'s incendiary rocket\n"; diff --git a/ssqc/engineer.qc b/ssqc/engineer.qc index 224edb0f..c4fff665 100644 --- a/ssqc/engineer.qc +++ b/ssqc/engineer.qc @@ -78,32 +78,22 @@ void () LaserBolt_Touch = { dremove(self); }; -static void ImpellerTouch() { - self.owner = self.real_owner; - - if (other.health) { - deathmsg = DMSG_LASERBOLT; - TF_T_Damage(other, self, self.owner, 15, 2, TF_TD_ELECTRICITY | TF_TD_NOMOMENTUM); - - float mag = 250; - if (other.has_flag) - mag *= 2; - other.velocity += normalize(self.velocity) * mag; - } - dremove(self); -} - +static const float kImpellerTargetRange = 1500; static float ValidImpellerTarget(entity t) { if (t.classname != "player") return FALSE; + if (vlen(t.origin - self.origin) > kImpellerTargetRange) + return FALSE; + if (cb_prematch) // Shoot anyone in prematch return TRUE; return (t.team_no != self.team_no && t.has_flag); } + static entity FindImpellerTarget(vector org, float min_a, float* direct) { traceline(org, org + 1500 * v_forward, MOVE_NORMAL, world); @@ -199,7 +189,7 @@ void W_FireImpeller() { end = t.origin; draw_beam(org, t.origin, TE_LIGHTNING2); } else { - vector a = org, b = org + 1000 * v_forward; + vector a = org, b = org + kImpellerTargetRange * v_forward; traceline(a, b, MOVE_WORLDONLY, world); if (trace_fraction == 1) return; @@ -217,7 +207,8 @@ void W_FireImpeller() { end = t.origin; } - TF_T_Damage(hit, self, self, 5, 2, TF_TD_ELECTRICITY | TF_TD_NOMOMENTUM); + deathmsg = DMSG_IMPELLER; + TF_T_Damage(hit, self, self, 5, TF_TD_NOTTEAM, TF_TD_ELECTRICITY | TF_TD_NOMOMENTUM); hit.flags &= ~FL_ONGROUND; hit.velocity -= mag * normalize(end - org); if (other.has_flag) From dbf414308fa8d5d584c814499ad5abcff35f3e1b Mon Sep 17 00:00:00 2001 From: newby Date: Fri, 19 Jan 2024 10:52:20 -0800 Subject: [PATCH 42/57] weapons: fix firing after dead when gibbed Being gibbed does not reset some of the animation state and so weapons which fired in animation were able to continue firing. Prevent this. --- share/animate.qc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/share/animate.qc b/share/animate.qc index 466ba598..7345379e 100644 --- a/share/animate.qc +++ b/share/animate.qc @@ -46,6 +46,9 @@ void FO_SetClientThink(void() func, float offset, float override = TRUE) { } void client_anim_frames(void() parent_thunk, void() extra, anim_t* anim) { + if (self.deadflag) + return; + float tidx = *thinkindex()++ - 1; int wfi = tidx % anim->num_wf; From 4ce67ff40b45bd225709c9a3442a2b0d23b8d590 Mon Sep 17 00:00:00 2001 From: newby Date: Fri, 19 Jan 2024 11:55:46 -0800 Subject: [PATCH 43/57] misc: fix compile nits --- ssqc/engineer.qc | 1 - ssqc/items.qc | 4 +--- ssqc/qw.qc | 1 - 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/ssqc/engineer.qc b/ssqc/engineer.qc index c4fff665..c1b0eb3d 100644 --- a/ssqc/engineer.qc +++ b/ssqc/engineer.qc @@ -169,7 +169,6 @@ void W_FireImpeller() { float direct = FALSE, jump = FALSE; entity t = FindImpellerTarget(org, 0.985, &direct); vector end = t.origin; - vector imp; float mag = 250; entity hit; diff --git a/ssqc/items.qc b/ssqc/items.qc index e0916a35..75505f9b 100644 --- a/ssqc/items.qc +++ b/ssqc/items.qc @@ -720,13 +720,11 @@ float () GetGrenadePossibility = { if (random() < 0.500) return 0; - float index; + float index = -1; switch (disable_resup_gren) { case 0: index = random() < 0.5 ? 0 : 1; break; case 1: index = 1; break; case 2: index = 0; break; - - case 3: default: return 0; } diff --git a/ssqc/qw.qc b/ssqc/qw.qc index 3eac851a..b1253a51 100644 --- a/ssqc/qw.qc +++ b/ssqc/qw.qc @@ -624,7 +624,6 @@ float walls_block_emp; float new_emp; float fo_sentry_targeting; // new improved targeting for sentry guns float cb_keepteams; -float fo_matchrated; float fo_flashtime; float fo_repair_ratio; float solid_nailgren; From b0226a9b004ba62ed45b1fbf4c77b0b0910e9fc2 Mon Sep 17 00:00:00 2001 From: newby Date: Fri, 19 Jan 2024 11:48:04 -0800 Subject: [PATCH 44/57] wp: add some more shared ssqc/csqc helpers for getting to clips/class/cslot --- csqc/weapon_predict.qc | 7 +++---- share/prediction.qc | 8 ++++++++ share/weapons.qc | 26 +++++++++++++++----------- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/csqc/weapon_predict.qc b/csqc/weapon_predict.qc index b39a8c31..0af9b842 100644 --- a/csqc/weapon_predict.qc +++ b/csqc/weapon_predict.qc @@ -1031,11 +1031,10 @@ void WP_Impulse() { } static float* WP_ClipFired(Slot slot) { - float index = SlotIndex(slot); - if (pstate_pred.playerclass == PC_DEMOMAN && index == 1) - index = 0; // Special case: Pipebomb shares with Grenadel launcher. + if (self_class() == PC_DEMOMAN && SlotIndex(slot) == 1) + slot = MakeSlot(1); // Special case: pipebomb shares with grenade launcher. - return &pstate_pred.clip_fired[index]; + return self_clip_fired(slot); } float WP_CurrentClipFired() { diff --git a/share/prediction.qc b/share/prediction.qc index 7cf0bee3..0f1c0651 100644 --- a/share/prediction.qc +++ b/share/prediction.qc @@ -318,12 +318,20 @@ float FO_MaxRewindGrenWinDt() { .float conc_cap_time; inline float *self_tf_state() { return &self.tfstate; } +float self_class() { return self.playerclass; } +Slot self_current_slot() { return self.current_slot; } +Slot self_queue_slot() { return self.queue_slot; } +float* self_clip_fired(Slot slot) { return &self.clip_fired[SlotIndex(slot)]; } #else predict_tf_state pstate_pred, pstate_server; inline float *self_tf_state() { return &pstate_pred.tfstate; } +float self_class() { return pstate_pred.playerclass; } +Slot self_current_slot() { return pstate_pred.current_slot; } +Slot self_queue_slot() { return pstate_pred.queue_slot; } +float* self_clip_fired(Slot slot) { return &pstate_pred.clip_fired[SlotIndex(slot)]; } #define MASK_PRED_ENT 256 #define MASK_PRED_PROJECTILE 512 diff --git a/share/weapons.qc b/share/weapons.qc index a8919e08..f0f7f092 100644 --- a/share/weapons.qc +++ b/share/weapons.qc @@ -466,6 +466,21 @@ float FO_CanReloadMsg(FO_WeapInfo* wi, int ammo_remaining, int clip_fired, return FALSE; // msg filled in above. } +float self_class(); +Slot self_current_slot(); +float* self_clip_fired(Slot slot); + +float FO_PlayerCurrentWeapon(entity player) { + FO_WeapInfo* wi = FO_SlotWeapInfo(self_class(), self_current_slot()); + if (!wi) + return WEAP_NONE; + return wi->weapon; +} + +inline float FO_CurrentWeapon() { + return FO_PlayerCurrentWeapon(self); +} + #ifdef SSQC struct FO_WeapState { float weapon; @@ -502,17 +517,6 @@ void FO_FillWeapState(entity player, Slot slot, FO_WeapState* result) { } -float FO_PlayerCurrentWeapon(entity player) { - FO_WeapInfo* wi = FO_SlotWeapInfo(player.playerclass, player.current_slot); - if (!wi) - return WEAP_NONE; - return wi->weapon; -} - -inline float FO_CurrentWeapon() { - return FO_PlayerCurrentWeapon(self); -} - inline void FO_FillCurrentWeapState(FO_WeapState* result) { FO_FillWeapState(self, self.current_slot, result); } From e316399ebd1d081aa6b620231574d6163a69ab55 Mon Sep 17 00:00:00 2001 From: newby Date: Fri, 19 Jan 2024 12:28:52 -0800 Subject: [PATCH 45/57] misc: fix prematch invincibility toggle --- ssqc/weapons.qc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ssqc/weapons.qc b/ssqc/weapons.qc index 7c32756d..587470ce 100644 --- a/ssqc/weapons.qc +++ b/ssqc/weapons.qc @@ -2680,7 +2680,7 @@ void () ToggleInvincibility = { self.items &= ~IT_INVULNERABILITY; self.invincible_time = 0; self.invincible_finished = 0; - self.tfstate &= ~PSTATE_INVINCIBLE; + self.pstate &= ~PSTATE_INVINCIBLE; self.effects &= ~(EF_RED | EF_DIMLIGHT | EF_BRIGHTLIGHT); } else { self.items |= IT_INVULNERABILITY; From cd6a42b2886e92c9befdda099946f3d52754e56c Mon Sep 17 00:00:00 2001 From: newby Date: Fri, 19 Jan 2024 12:58:52 -0800 Subject: [PATCH 46/57] wp: unify fire_in_anim into weapon_info rather than it being a special case --- csqc/weapon_predict.qc | 12 +++--------- share/weapons.qc | 11 ++++++----- ssqc/weapons.qc | 13 +++++-------- 3 files changed, 14 insertions(+), 22 deletions(-) diff --git a/csqc/weapon_predict.qc b/csqc/weapon_predict.qc index 0af9b842..ac0eb1c3 100644 --- a/csqc/weapon_predict.qc +++ b/csqc/weapon_predict.qc @@ -1754,7 +1754,7 @@ void WP_Attack() { wi->weapon == WEAP_SUPER_NAILGUN || wi->weapon == WEAP_ASSAULT_CANNON; - if (!in_anim && !WP_ConsumeAmmo(CurrentSlot())) + if (!wi->fire_in_anim && !WP_ConsumeAmmo(CurrentSlot())) return; // OK. We're ready to pew. @@ -1762,7 +1762,7 @@ void WP_Attack() { // Must be set prior to animation code, which might internally modify. pstate_pred.client_thinkindex = 1; - if (IsEffectFrame() && !in_anim && (time > filter_pproj_time)) { + if (IsEffectFrame() && !wi->fire_in_anim && (time > filter_pproj_time)) { float send_event = FALSE; switch (wi->weapon) { case WEAP_ROCKET_LAUNCHER: @@ -1773,9 +1773,6 @@ void WP_Attack() { PP_CreateProjectile(FPP_INCENDIARY, PM_Org() + v_forward * 8 + '0 0 16'); send_event = TRUE; break; - case WEAP_FLAMETHROWER: - PP_CreateProjectile(FPP_FLAMETHROWER, PM_Org() + v_forward * 16 + '0 0 16'); - break; case WEAP_TRANQ: PP_CreateProjectile(FPP_TRANQ, PM_Org() + v_forward * 8 + '0 0 16'); break; @@ -1783,9 +1780,6 @@ void WP_Attack() { PP_CreateProjectile(FPP_RAILGUN, PM_Org() + '0 0 16'); break; - case WEAP_ASSAULT_CANNON: - break; - case WEAP_GRENADE_LAUNCHER: send_event = TRUE; case WEAP_PIPE_LAUNCHER: @@ -1819,7 +1813,7 @@ void WP_Attack() { } } - if (!in_anim) + if (!wi->fire_in_anim) Attack_Finished(wi->attack_time); // Start the AC state machine when necessary. diff --git a/share/weapons.qc b/share/weapons.qc index f0f7f092..1769f2e9 100644 --- a/share/weapons.qc +++ b/share/weapons.qc @@ -63,6 +63,7 @@ struct FO_WeapInfo { float ammo_per_shot; float attack_time; float full_reload_time; + float fire_in_anim; // firing triggered from animation (e.g. continuous fire) // Fields below this are automatically initialized by Init. // ** Do not include in table. ** @@ -80,17 +81,17 @@ FO_WeapInfo weapon_info[] = { { WEAP_SPANNER, PRED_MODEL, AMMO_CELLS, 0, 0, 0.5, 0 }, { WEAP_AXE, PRED_MODEL, AMMO_NONE, 0, 0, 0.5, 0 }, { WEAP_SNIPER_RIFLE, PRED_MODEL, AMMO_SHELLS, -9, 1, 1.5, 4 }, - { WEAP_AUTO_RIFLE, PRED_MODEL, AMMO_SHELLS, 0, 1, 0.1, 0 }, + { WEAP_AUTO_RIFLE, PRED_MODEL, AMMO_SHELLS, 0, 1, 0.1, 0, 1 }, { WEAP_SHOTGUN, PRED_MODEL, AMMO_SHELLS, 8, 1, 0.5, 2 }, { WEAP_SUPER_SHOTGUN, PRED_MODEL, AMMO_SHELLS, 16, 2, 0.7, 3 }, - { WEAP_NAILGUN, PRED_PROJ, AMMO_NAILS, 0, 1, 0.2, 0 }, - { WEAP_SUPER_NAILGUN, PRED_PROJ, AMMO_NAILS, 0, 2, 0.2, 0 }, + { WEAP_NAILGUN, PRED_PROJ, AMMO_NAILS, 0, 1, 0.2, 0, 1 }, + { WEAP_SUPER_NAILGUN, PRED_PROJ, AMMO_NAILS, 0, 2, 0.2, 0, 1 }, { WEAP_GRENADE_LAUNCHER , PRED_PROJ, AMMO_ROCKETS, 6, 1, 0.6, 4 }, { WEAP_PIPE_LAUNCHER, PRED_PROJ, AMMO_ROCKETS, 6, 1, 0.6, 4 }, // Overlaps GL - { WEAP_FLAMETHROWER, PRED_PROJ, AMMO_CELLS, 0, 1, 0.15, 0 }, + { WEAP_FLAMETHROWER, PRED_PROJ, AMMO_CELLS, 0, 1, 0.15, 0, 1 }, { WEAP_ROCKET_LAUNCHER, PRED_PROJ, AMMO_ROCKETS, 4, 1, 0.8, 5 }, { WEAP_INCENDIARY, PRED_PROJ, AMMO_ROCKETS, 0, 3, 0.9, 0 }, - { WEAP_ASSAULT_CANNON, PRED_PROJ, AMMO_SHELLS, 100, 1, 0.2, 4 }, + { WEAP_ASSAULT_CANNON, PRED_PROJ, AMMO_SHELLS, 100, 1, 0.2, 4, 1 }, { WEAP_LIGHTNING, NO_PREDICT, AMMO_CELLS, 0, 1, 0.1, 0 }, { WEAP_DETPACK, NO_PREDICT, AMMO_NONE, 0, 0, 0, 0 }, { WEAP_TRANQ, PRED_PROJ, AMMO_SHELLS, 0, 1, 1.5, 0 }, diff --git a/ssqc/weapons.qc b/ssqc/weapons.qc index 587470ce..fa8f1f3a 100644 --- a/ssqc/weapons.qc +++ b/ssqc/weapons.qc @@ -1755,19 +1755,16 @@ void () W_Attack = { W_FireImpeller(); } - if (ws.weapon != WEAP_FLAMETHROWER && // Variable - ws.weapon != WEAP_ASSAULT_CANNON && // In animation - ws.weapon != WEAP_NAILGUN && // In animation - ws.weapon != WEAP_SUPER_NAILGUN) // In animation + if (!wi->fire_in_anim) { Attack_Finished(wi->attack_time); - - if (wi->needs_reload) { - if (ws.weapon != WEAP_ASSAULT_CANNON) // In animation + if (wi->needs_reload) { *ws->clip_fired += wi->ammo_per_shot; - FO_CheckForReload(); + FO_CheckForReload(); + } } //These weapons have to log each projectile launched/bullet fired + // TODO: probably equivalent to fire_in_anim... if (ws.weapon != WEAP_ASSAULT_CANNON || ws.weapon != WEAP_NAILGUN || ws.weapon != WEAP_SUPER_NAILGUN) LogEventAttack(self); From 9bc4cbaa1bda5cfad6103d7f7cba7017c3c4ae21 Mon Sep 17 00:00:00 2001 From: newby Date: Fri, 19 Jan 2024 11:50:41 -0800 Subject: [PATCH 47/57] weapons: example of adding reload to in-animation weapon --- share/animate.qc | 6 ++++++ share/weapons.qc | 2 +- ssqc/weapons.qc | 3 +++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/share/animate.qc b/share/animate.qc index 7345379e..f8f44fd5 100644 --- a/share/animate.qc +++ b/share/animate.qc @@ -219,6 +219,12 @@ void nail_extra_csqc_ssqc() { return; } + FO_WeapInfo* wi = FO_GetWeapInfo(FO_CurrentWeapon()); + if (wi->needs_reload && FO_CheckForReload()) { + player_run(); + return; + } + float tidx = *thinkindex() % 2; // 1 based, and incremented. if (tidx == 0 || !OldNgRof()) { SuperDamageSound(); diff --git a/share/weapons.qc b/share/weapons.qc index 1769f2e9..d5f24dc1 100644 --- a/share/weapons.qc +++ b/share/weapons.qc @@ -85,7 +85,7 @@ FO_WeapInfo weapon_info[] = { { WEAP_SHOTGUN, PRED_MODEL, AMMO_SHELLS, 8, 1, 0.5, 2 }, { WEAP_SUPER_SHOTGUN, PRED_MODEL, AMMO_SHELLS, 16, 2, 0.7, 3 }, { WEAP_NAILGUN, PRED_PROJ, AMMO_NAILS, 0, 1, 0.2, 0, 1 }, - { WEAP_SUPER_NAILGUN, PRED_PROJ, AMMO_NAILS, 0, 2, 0.2, 0, 1 }, + { WEAP_SUPER_NAILGUN, PRED_PROJ, AMMO_NAILS, 50, 2, 0.2, 1, 1 }, { WEAP_GRENADE_LAUNCHER , PRED_PROJ, AMMO_ROCKETS, 6, 1, 0.6, 4 }, { WEAP_PIPE_LAUNCHER, PRED_PROJ, AMMO_ROCKETS, 6, 1, 0.6, 4 }, // Overlaps GL { WEAP_FLAMETHROWER, PRED_PROJ, AMMO_CELLS, 0, 1, 0.15, 0, 1 }, diff --git a/ssqc/weapons.qc b/ssqc/weapons.qc index fa8f1f3a..12726a34 100644 --- a/ssqc/weapons.qc +++ b/ssqc/weapons.qc @@ -1407,6 +1407,9 @@ void (float ox) W_FireSpikes = { } *ws->ammo_remaining -= wi->ammo_per_shot; + if (wi->needs_reload) + *ws->clip_fired += wi->ammo_per_shot; + if (wi->weapon == WEAP_NAILGUN) W_FireNail(FPP_NAIL, v_right * ox); else From 247a65d5977b2541b647533364110d07432a8de1 Mon Sep 17 00:00:00 2001 From: newby Date: Fri, 19 Jan 2024 13:10:21 -0800 Subject: [PATCH 48/57] wp: revert sng clip from previous example --- share/weapons.qc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/weapons.qc b/share/weapons.qc index d5f24dc1..1769f2e9 100644 --- a/share/weapons.qc +++ b/share/weapons.qc @@ -85,7 +85,7 @@ FO_WeapInfo weapon_info[] = { { WEAP_SHOTGUN, PRED_MODEL, AMMO_SHELLS, 8, 1, 0.5, 2 }, { WEAP_SUPER_SHOTGUN, PRED_MODEL, AMMO_SHELLS, 16, 2, 0.7, 3 }, { WEAP_NAILGUN, PRED_PROJ, AMMO_NAILS, 0, 1, 0.2, 0, 1 }, - { WEAP_SUPER_NAILGUN, PRED_PROJ, AMMO_NAILS, 50, 2, 0.2, 1, 1 }, + { WEAP_SUPER_NAILGUN, PRED_PROJ, AMMO_NAILS, 0, 2, 0.2, 0, 1 }, { WEAP_GRENADE_LAUNCHER , PRED_PROJ, AMMO_ROCKETS, 6, 1, 0.6, 4 }, { WEAP_PIPE_LAUNCHER, PRED_PROJ, AMMO_ROCKETS, 6, 1, 0.6, 4 }, // Overlaps GL { WEAP_FLAMETHROWER, PRED_PROJ, AMMO_CELLS, 0, 1, 0.15, 0, 1 }, From 9172e0dd767db3913bcb901b8e4aba09f5d030bf Mon Sep 17 00:00:00 2001 From: newby Date: Fri, 19 Jan 2024 13:13:07 -0800 Subject: [PATCH 49/57] weapons: allow changing from nailguns when firing allow weapon changes while firing the nailgun. this has been annoying since time began, it's also easy to config-script around; just fix it. --- share/animate.qc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/animate.qc b/share/animate.qc index f8f44fd5..6a4e63f1 100644 --- a/share/animate.qc +++ b/share/animate.qc @@ -214,7 +214,7 @@ static inline float FO_CheckForReload() { return WP_ReloadCurrentIfNeeded(); } void W_FireSpikes(float ox); // Shared by both client and server side. void nail_extra_csqc_ssqc() { - if (!is_attacking() || is_intermission()) { + if (!is_attacking() || is_intermission() || !IsSlotNull(self_queue_slot())) { player_run(); return; } From cab70113c57a22c8a8d031975788adc786d3384a Mon Sep 17 00:00:00 2001 From: newby Date: Fri, 19 Jan 2024 13:44:38 -0800 Subject: [PATCH 50/57] anim: fix respawn while dead --- share/animate.qc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/share/animate.qc b/share/animate.qc index 6a4e63f1..428c0169 100644 --- a/share/animate.qc +++ b/share/animate.qc @@ -46,8 +46,10 @@ void FO_SetClientThink(void() func, float offset, float override = TRUE) { } void client_anim_frames(void() parent_thunk, void() extra, anim_t* anim) { - if (self.deadflag) + if (self.deadflag) { + player_run(); return; + } float tidx = *thinkindex()++ - 1; From db7bb2d3abf0a20110a85c8c8eeadb419950c036 Mon Sep 17 00:00:00 2001 From: newby Date: Fri, 19 Jan 2024 13:49:00 -0800 Subject: [PATCH 51/57] weapons: fix playercurrentweapon mistakenly made generic --- share/weapons.qc | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/share/weapons.qc b/share/weapons.qc index 1769f2e9..b93fa43c 100644 --- a/share/weapons.qc +++ b/share/weapons.qc @@ -471,18 +471,21 @@ float self_class(); Slot self_current_slot(); float* self_clip_fired(Slot slot); -float FO_PlayerCurrentWeapon(entity player) { +float FO_CurrentWeapon() { FO_WeapInfo* wi = FO_SlotWeapInfo(self_class(), self_current_slot()); if (!wi) return WEAP_NONE; return wi->weapon; } -inline float FO_CurrentWeapon() { - return FO_PlayerCurrentWeapon(self); +#ifdef SSQC +float FO_PlayerCurrentWeapon(entity player) { + FO_WeapInfo* wi = FO_SlotWeapInfo(player.playerclass, player.current_slot); + if (!wi) + return WEAP_NONE; + return wi->weapon; } -#ifdef SSQC struct FO_WeapState { float weapon; Slot slot; From dc639a96c6db94f7dfc913aa13deea7ebd4d8f62 Mon Sep 17 00:00:00 2001 From: newby Date: Fri, 19 Jan 2024 13:59:14 -0800 Subject: [PATCH 52/57] wp: remove fire_in_anim tag from autorifle, it just does many individual shots --- share/weapons.qc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/weapons.qc b/share/weapons.qc index b93fa43c..acc8ee22 100644 --- a/share/weapons.qc +++ b/share/weapons.qc @@ -81,7 +81,7 @@ FO_WeapInfo weapon_info[] = { { WEAP_SPANNER, PRED_MODEL, AMMO_CELLS, 0, 0, 0.5, 0 }, { WEAP_AXE, PRED_MODEL, AMMO_NONE, 0, 0, 0.5, 0 }, { WEAP_SNIPER_RIFLE, PRED_MODEL, AMMO_SHELLS, -9, 1, 1.5, 4 }, - { WEAP_AUTO_RIFLE, PRED_MODEL, AMMO_SHELLS, 0, 1, 0.1, 0, 1 }, + { WEAP_AUTO_RIFLE, PRED_MODEL, AMMO_SHELLS, 0, 1, 0.1, 0 }, { WEAP_SHOTGUN, PRED_MODEL, AMMO_SHELLS, 8, 1, 0.5, 2 }, { WEAP_SUPER_SHOTGUN, PRED_MODEL, AMMO_SHELLS, 16, 2, 0.7, 3 }, { WEAP_NAILGUN, PRED_PROJ, AMMO_NAILS, 0, 1, 0.2, 0, 1 }, From 79f5d518f7f832896f99fd69f5d6d62a8cca746a Mon Sep 17 00:00:00 2001 From: newby Date: Fri, 19 Jan 2024 15:21:30 -0800 Subject: [PATCH 53/57] revert dead-player animation interpose, interactions with respawn still to fix --- share/animate.qc | 5 ----- 1 file changed, 5 deletions(-) diff --git a/share/animate.qc b/share/animate.qc index 428c0169..3dbd8482 100644 --- a/share/animate.qc +++ b/share/animate.qc @@ -46,11 +46,6 @@ void FO_SetClientThink(void() func, float offset, float override = TRUE) { } void client_anim_frames(void() parent_thunk, void() extra, anim_t* anim) { - if (self.deadflag) { - player_run(); - return; - } - float tidx = *thinkindex()++ - 1; int wfi = tidx % anim->num_wf; From bc4de74f5668178df0e3fc9e75b780b97821154f Mon Sep 17 00:00:00 2001 From: Sheldon Johnson Date: Sun, 21 Jan 2024 22:35:13 +1100 Subject: [PATCH 54/57] special2 should dismantle gun when in range --- ssqc/engineer.qc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ssqc/engineer.qc b/ssqc/engineer.qc index 1255aa27..fd52233c 100644 --- a/ssqc/engineer.qc +++ b/ssqc/engineer.qc @@ -340,7 +340,7 @@ void () FO_Engineer_ToggleDispenser = { void () FO_Engineer_ToggleSentry = { if (self.has_sentry) { - DestroyBuilding(self, "building_sentrygun"); + Menu_Engineer_Input(3); } else { if (self.health <= 0) { sprint(self, PRINT_HIGH, "Can't build while dead.\n"); From 9ce06a36f1c7bfbfc619cbbf928fc5e23e56dee7 Mon Sep 17 00:00:00 2001 From: newby Date: Sun, 21 Jan 2024 21:23:04 -0800 Subject: [PATCH 55/57] rewind: remove localinfo to disable detpipe rewind --- ssqc/client.qc | 4 ---- ssqc/qw.qc | 5 ----- ssqc/rewind.qc | 3 --- 3 files changed, 12 deletions(-) diff --git a/ssqc/client.qc b/ssqc/client.qc index 2bba965a..a0bca49c 100644 --- a/ssqc/client.qc +++ b/ssqc/client.qc @@ -640,10 +640,6 @@ void () DecodeLevelParms = { asscanrange = CF_GetSetting("acr", "asscanrange", "0"); asscanrangedie = CF_GetSetting("acrd", "asscanrangedie", "0"); - // remote_client_time max delta - antilag_settings.rewind_detpipe = - CF_GetSetting("alrd", "al_rewind_detpipe", "on"); - // CSQC projectiles fo_projectiles = CF_GetSetting("focp", "fo_csqc_projectiles", "on"); diff --git a/ssqc/qw.qc b/ssqc/qw.qc index b1253a51..a7f5b256 100644 --- a/ssqc/qw.qc +++ b/ssqc/qw.qc @@ -9,11 +9,6 @@ enum { CT_FAST_PROJECTILE, // Fast proj => less judder (e.g. heavy bullet) }; -struct antilag_settings_t { - float rewind_detpipe; -} antilag_settings; - -.vector antilag_origin; .float last_death_ctime; .float last_attack_ctime; diff --git a/ssqc/rewind.qc b/ssqc/rewind.qc index e4f8a43c..0e5d6efa 100644 --- a/ssqc/rewind.qc +++ b/ssqc/rewind.qc @@ -322,9 +322,6 @@ void RW_RestoreAll() { float (string ps_short, string ps_setting, string ps_default) CF_GetSetting; float RewindPlayersExceptSelf(float farthest_rewind_point) { - if (!antilag_settings.rewind_detpipe) - return FALSE; - float rewind_max_offset = (MAX_SNAPSHOTS - 1) * SERVER_FRAME_DT; farthest_rewind_point = max(farthest_rewind_point, time - rewind_max_offset); From 5234f4863080c9bf9a0a7c7bd7d8494fa7220e73 Mon Sep 17 00:00:00 2001 From: newby Date: Sun, 21 Jan 2024 21:27:03 -0800 Subject: [PATCH 56/57] nb: add antilag to impeller --- ssqc/engineer.qc | 45 +++++++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/ssqc/engineer.qc b/ssqc/engineer.qc index c1b0eb3d..bdb4e848 100644 --- a/ssqc/engineer.qc +++ b/ssqc/engineer.qc @@ -158,60 +158,57 @@ static void draw_beam(vector a, vector b, int effect) { } void W_FireImpeller() { - if (!cb_prematch) { - self.special_next = time + 3; - } - FO_Sound(self, CHAN_WEAPON, "weapons/lhit.wav", 1, ATTN_NORM); - makevectors(self.v_angle); + + float rewound = RewindPlayersExceptSelf(0); + + if (!cb_prematch) + self.special_next = time + 3; vector org = self.origin + '0 0 16'; - float direct = FALSE, jump = FALSE; - entity t = FindImpellerTarget(org, 0.985, &direct); - vector end = t.origin; + float direct = FALSE; + entity hit = FindImpellerTarget(org, 0.985, &direct); + + if (rewound) + FOPlayer::RestoreAll(); float mag = 250; - entity hit; + vector end; - if (t == world) { + if (hit == world) { traceline(org, org + 350 * v_forward, MOVE_WORLDONLY, world); if (trace_fraction == 1) return; end = trace_endpos; hit = self; - draw_beam(org, trace_endpos, TE_LIGHTNING2); + draw_beam(org, end, TE_LIGHTNING2); } else { - hit = t; - if (direct) { - end = t.origin; - draw_beam(org, t.origin, TE_LIGHTNING2); + end = hit.origin; + draw_beam(org, end, TE_LIGHTNING2); } else { vector a = org, b = org + kImpellerTargetRange * v_forward; traceline(a, b, MOVE_WORLDONLY, world); - if (trace_fraction == 1) - return; end = trace_endpos; - vector x = find_closest(a, end, t.origin); + vector x = find_closest(a, end, hit.origin); draw_beam(a, x, TE_LIGHTNING2); - draw_beam(x, t.origin, TE_LIGHTNING2); + draw_beam(x, hit.origin, TE_LIGHTNING2); // Discount knockback by help given - mag -= max(10, vlen(t.origin - x)); + mag -= max(10, vlen(hit.origin - x)); } - hit = t; - end = t.origin; + end = hit.origin; } deathmsg = DMSG_IMPELLER; TF_T_Damage(hit, self, self, 5, TF_TD_NOTTEAM, TF_TD_ELECTRICITY | TF_TD_NOMOMENTUM); hit.flags &= ~FL_ONGROUND; - hit.velocity -= mag * normalize(end - org); - if (other.has_flag) + if (hit.has_flag) mag *= 2; + hit.velocity -= mag * normalize(end - org); } void () W_FireRailgun = { From b66492f48d936b78bd43d1fb74f7499e5ea76263 Mon Sep 17 00:00:00 2001 From: newby Date: Mon, 22 Jan 2024 01:32:42 -0800 Subject: [PATCH 57/57] core: fix being able to fire after being gibbed in some cases --- ssqc/player.qc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ssqc/player.qc b/ssqc/player.qc index 1757e6bb..a934ed92 100644 --- a/ssqc/player.qc +++ b/ssqc/player.qc @@ -411,7 +411,7 @@ void () GibPlayer = { ThrowGib("progs/gib1.mdl", self.health); ThrowGib("progs/gib2.mdl", self.health); ThrowGib("progs/gib3.mdl", self.health); - if (deathmsg == 36) { + if (deathmsg == DMSG_TRIGGER) { newmis = spawn(); newmis.owner = self; newmis.think = KillPlayer; @@ -424,8 +424,7 @@ void () GibPlayer = { FO_Sound(self, CHAN_VOICE, "player/teledth1.wav", 1, 0); self.respawn_time = (self.respawn_time + 2) + (random() * 2); return; - } - if (damage_attacker.classname == "teledeath2") { + } else if (damage_attacker.classname == "teledeath2") { FO_Sound(self, CHAN_VOICE, "player/teledth1.wav", 1, 0); self.respawn_time = (self.respawn_time + 2) + (random() * 2); return; @@ -435,6 +434,8 @@ void () GibPlayer = { } else { FO_Sound(self, CHAN_VOICE, "player/udeath.wav", 1, 0); } + + FO_SetClientThink(think_nop, 0); }; void () PlayerDie = {