From 7f81cbb0f629d1e38cf45cd69dbb3537f76a67e7 Mon Sep 17 00:00:00 2001 From: EladNLG Date: Mon, 6 May 2024 17:56:59 +0300 Subject: [PATCH 1/2] feat: ModWeaponVars --- .github/nativefuncs.json | 36 ++++ Northstar.Custom/mod.json | 4 + .../vscripts/sh_northstar_mod_weapon_vars.nut | 159 ++++++++++++++++++ 3 files changed, 199 insertions(+) create mode 100644 Northstar.Custom/mod/scripts/vscripts/sh_northstar_mod_weapon_vars.nut diff --git a/.github/nativefuncs.json b/.github/nativefuncs.json index 1148d3e58..77204a2f7 100644 --- a/.github/nativefuncs.json +++ b/.github/nativefuncs.json @@ -742,6 +742,42 @@ "helpText":"", "returnTypeString":"MasterServerAuthResult", "argTypes":"" + }, + { + "name":"ModWeaponVars_CalculateWeaponMods", + "helpText":"", + "returnTypeString":"void", + "argTypes":"entity weapon" + }, + { + "name":"ModWeaponVars_SetInt", + "helpText":"", + "returnTypeString":"void", + "argTypes":"entity weapon, int weaponVarIndex, int value" + }, + { + "name":"ModWeaponVars_SetFloat", + "helpText":"", + "returnTypeString":"void", + "argTypes":"entity weapon, int weaponVarIndex, float value" + }, + { + "name":"ModWeaponVars_SetBool", + "helpText":"", + "returnTypeString":"void", + "argTypes":"entity weapon, int weaponVarIndex, bool value" + }, + { + "name":"ModWeaponVars_SetString", + "helpText":"", + "returnTypeString":"void", + "argTypes":"entity weapon, int weaponVarIndex, string value" + }, + { + "name":"ModWeaponVars_GetType", + "helpText":"", + "returnTypeString":"int", + "argTypes":"entity weapon, int weaponVarIndex" } ] } \ No newline at end of file diff --git a/Northstar.Custom/mod.json b/Northstar.Custom/mod.json index 69432f498..1ab461355 100644 --- a/Northstar.Custom/mod.json +++ b/Northstar.Custom/mod.json @@ -466,6 +466,10 @@ "UICallback":{ "Before": "NSCustomModSettings" } + }, + { + "Path": "sh_northstar_mod_weapon_vars.gnut", + "RunOn": "CLIENT || SERVER" } ], diff --git a/Northstar.Custom/mod/scripts/vscripts/sh_northstar_mod_weapon_vars.nut b/Northstar.Custom/mod/scripts/vscripts/sh_northstar_mod_weapon_vars.nut new file mode 100644 index 000000000..22cefaf18 --- /dev/null +++ b/Northstar.Custom/mod/scripts/vscripts/sh_northstar_mod_weapon_vars.nut @@ -0,0 +1,159 @@ +untyped +global function AddCallback_ApplyModWeaponVars +global function CodeCallback_ApplyModWeaponVars +#if SERVER +global function CodeCallback_DoWeaponModsForPlayer +#endif +#if CLIENT +global function CodeCallback_PredictWeaponMods +#endif + +// doing overrides last +global const int WEAPON_VAR_PRIORITY_OVERRIDE = 0 +global const int WEAPON_VAR_PRIORITY_ADD = 100 +global const int WEAPON_VAR_PRIORITY_MULT = 200 + +// these are utility global variables for easier setting of more "generic" things +// e.g. hipfire spread, ads spread + +global const array HIP_SPREAD_VARS = [eWeaponVar.spread_stand_hip, eWeaponVar.spread_stand_hip_run, eWeaponVar.spread_stand_hip_sprint, + eWeaponVar.spread_crouch_hip, eWeaponVar.spread_air_hip, eWeaponVar.spread_kick_on_fire_air_hip, eWeaponVar.spread_kick_on_fire_stand_hip, + eWeaponVar.spread_kick_on_fire_crouch_hip, eWeaponVar.spread_max_kick_air_hip, eWeaponVar.spread_max_kick_stand_hip, + eWeaponVar.spread_max_kick_crouch_hip, eWeaponVar.spread_wallrunning, eWeaponVar.spread_wallhanging] // wallhanging is "ads" but aaplies spread on the level of hipfire? keeping it here. + +global const array ADS_SPREAD_VARS = [eWeaponVar.spread_stand_ads, eWeaponVar.spread_crouch_ads, eWeaponVar.spread_air_ads, + eWeaponVar.spread_kick_on_fire_air_ads, eWeaponVar.spread_kick_on_fire_stand_ads, eWeaponVar.spread_kick_on_fire_crouch_ads, + eWeaponVar.spread_max_kick_air_ads, eWeaponVar.spread_max_kick_stand_ads, eWeaponVar.spread_max_kick_crouch_ads] + +global const array RELOAD_TIME_VARS = [eWeaponVar.reload_time, eWeaponVar.reloadempty_time, +eWeaponVar.reload_time_late1, eWeaponVar.reload_time_late2, eWeaponVar.reload_time_late3, eWeaponVar.reload_time_late4, +eWeaponVar.reload_time_late5, eWeaponVar.reloadempty_time_late1, eWeaponVar.reloadempty_time_late2, eWeaponVar.reloadempty_time_late3, +eWeaponVar.reloadempty_time_late4, eWeaponVar.reloadempty_time_late5, eWeaponVar.reloadsegment_time_loop, +eWeaponVar.reloadsegment_time_end, eWeaponVar.reloadsegmentempty_time_end] + +global enum WeaponVarType +{ + INTEGER = 1, + FLOAT = 2, + BOOLEAN = 3, + STRING = 4, + ASSET = 5, + VECTOR = 6, + SPECIAL +} + +struct CallbackArray +{ + int priority + array callbacks +} + +struct +{ + array weaponVarCallbacks +} file + +int function SortByPriority( CallbackArray a, CallbackArray b ) +{ + if (a.priority > b.priority) + return 1 + else if (a.priority < b.priority) + return -1 + + return 0 +} + +// because order matters when applying weapon vars, +void function AddCallback_ApplyModWeaponVars( int priority, void functionref( entity ) callback ) +{ + foreach (CallbackArray arr in file.weaponVarCallbacks) + { + if (arr.priority == priority) + { + arr.callbacks.append(callback) + } + } + + CallbackArray arr + arr.priority = priority + arr.callbacks = [ callback ] + file.weaponVarCallbacks.append(arr) + file.weaponVarCallbacks.sort(SortByPriority) +} + +// called whenever a weapon's variables are calculated +// can be done thru ModWeaponVars_CalculateWeaponMods +// or by chamging the mods of a weapon +void function CodeCallback_ApplyModWeaponVars( entity weapon ) +{ + // when a weapon is equipped for the first frame + // for some reason in prediction only ammo is -1 + // this can cause visual glitches when mods + // rely on ammo to calculate values (specifically, + // spread) + if (weapon.GetWeaponPrimaryClipCount() == -1) + { + return + } + + // dont waste performance on weapons not + // being used + if (!IsValid(weapon.GetOwner())) + return + + foreach (CallbackArray arr in file.weaponVarCallbacks) + { + foreach (void functionref( entity ) callback in arr.callbacks) + { + callback( weapon ) + } + } +} + +#if CLIENT +// called in prediction for some reason(?) +// done for every weapon the local player has +void function CodeCallback_PredictWeaponMods( entity weapon ) +{ + if (!IsValid(weapon)) + return + + // avoids some visual glitches that happen if we set weaponmods + // this is done here too to save performance + if (weapon.GetWeaponPrimaryClipCount() == -1) + { + return + } + + ModWeaponVars_CalculateWeaponMods( weapon ) +} +#endif + +#if SERVER +// called every TICK (NOT script frame) for every player. +// used for calculating a player's weaponvars for +// their active and selected (weapon being switched to) +// weapons. +void function CodeCallback_DoWeaponModsForPlayer( entity player ) +{ + if (!IsValid(player)) + return + if (!IsValid(player.GetActiveWeapon())) + return + + // recalculating mods is slightly expensive - enough + // that doing it for all weapons for all players + // is a bad idea. so offloading mod recalculation + // responsibility to the modder is better imo. + // we do it for the active weapon to not cause mispredictions. + // However, client does it every frame for the all of their weapons. + // (done in native, we arent in control of it.) + ModWeaponVars_CalculateWeaponMods( player.GetActiveWeapon() ) + + // the weapon the player is about to switch to + if (IsValid(player.GetSelectedWeapon())) + { + ModWeaponVars_CalculateWeaponMods( player.GetSelectedWeapon() ) + } +} +#endif \ No newline at end of file From 054843a919e370b25942fc94399bc5a9c8c4d727 Mon Sep 17 00:00:00 2001 From: EladNLG Date: Sat, 7 Dec 2024 17:27:08 +0200 Subject: [PATCH 2/2] Update scripts to co-operate with launcher changes --- .../vscripts/sh_northstar_mod_weapon_vars.nut | 33 ++++++++----------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/Northstar.Custom/mod/scripts/vscripts/sh_northstar_mod_weapon_vars.nut b/Northstar.Custom/mod/scripts/vscripts/sh_northstar_mod_weapon_vars.nut index 22cefaf18..3cbd2dd18 100644 --- a/Northstar.Custom/mod/scripts/vscripts/sh_northstar_mod_weapon_vars.nut +++ b/Northstar.Custom/mod/scripts/vscripts/sh_northstar_mod_weapon_vars.nut @@ -130,30 +130,25 @@ void function CodeCallback_PredictWeaponMods( entity weapon ) #endif #if SERVER -// called every TICK (NOT script frame) for every player. +// called for every player. // used for calculating a player's weaponvars for -// their active and selected (weapon being switched to) -// weapons. -void function CodeCallback_DoWeaponModsForPlayer( entity player ) +// all of their weapons. +void function CodeCallback_DoWeaponModsForPlayer( entity player, int offhandSlot ) { if (!IsValid(player)) return - if (!IsValid(player.GetActiveWeapon())) + // idk this might get called by npcs or some shit + // we dont want to calculate every tick for all npcs thats stupid + if (!player.IsPlayer()) return - // recalculating mods is slightly expensive - enough - // that doing it for all weapons for all players - // is a bad idea. so offloading mod recalculation - // responsibility to the modder is better imo. - // we do it for the active weapon to not cause mispredictions. - // However, client does it every frame for the all of their weapons. - // (done in native, we arent in control of it.) - ModWeaponVars_CalculateWeaponMods( player.GetActiveWeapon() ) - - // the weapon the player is about to switch to - if (IsValid(player.GetSelectedWeapon())) - { - ModWeaponVars_CalculateWeaponMods( player.GetSelectedWeapon() ) - } + // recalculating mods may be slightly expensive? i think? + // TODO: check performance at high player counts? + // 32 players * (max 6 offhand + 3 main weapons) = 288 calls per tick* <-- a lot!!!!! + // *may get called more than once, idk + foreach (entity w in player.GetMainWeapons()) + ModWeaponVars_CalculateWeaponMods( w ) + foreach (entity w in player.GetOffhandWeapons()) + ModWeaponVars_CalculateWeaponMods( w ) } #endif \ No newline at end of file