diff --git a/game/jbmod/cfg/vscript_convar_allowlist.txt b/game/jbmod/cfg/vscript_convar_allowlist.txt index 12ebdc60c2a..e355de8c717 100644 --- a/game/jbmod/cfg/vscript_convar_allowlist.txt +++ b/game/jbmod/cfg/vscript_convar_allowlist.txt @@ -51,4 +51,6 @@ vscript_convar_allowlist // Other sv_alltalk allowed + sv_jbmod_weapon_respawn_time allowed + sv_jbmod_item_respawn_time allowed } diff --git a/game/jbmod/maps/graphs/jb_waterhole.ain b/game/jbmod/maps/graphs/jb_waterhole.ain index 0d7c8df1267..3b24240dcf1 100644 Binary files a/game/jbmod/maps/graphs/jb_waterhole.ain and b/game/jbmod/maps/graphs/jb_waterhole.ain differ diff --git a/game/jbmod/maps/jb_waterhole.bsp b/game/jbmod/maps/jb_waterhole.bsp index 485174b7238..b95ad59c588 100644 Binary files a/game/jbmod/maps/jb_waterhole.bsp and b/game/jbmod/maps/jb_waterhole.bsp differ diff --git a/game/jbmod/scripts/client_precache.txt b/game/jbmod/scripts/client_precache.txt new file mode 100644 index 00000000000..190e354723a --- /dev/null +++ b/game/jbmod/scripts/client_precache.txt @@ -0,0 +1,18 @@ +precache +{ + "model" "models/player.mdl" + "model" "models/gibs/agibs.mdl" + "model" "models/weapons/v_hands.mdl" + + "scriptsound" "HUDQuickInfo.LowAmmo" + "scriptsound" "HUDQuickInfo.LowHealth" + + "scriptsound" "FX_AntlionImpact.ShellImpact" + "scriptsound" "Missile.ShotDown" + "scriptsound" "Bullets.DefaultNearmiss" + "scriptsound" "Bullets.GunshipNearmiss" + "scriptsound" "Bullets.StriderNearmiss" + + "scriptsound" "Geiger.BeepHigh" + "scriptsound" "Geiger.BeepLow" +} diff --git a/game/jbmod/scripts/vscripts/gamemodes/base.nut b/game/jbmod/scripts/vscripts/gamemodes/base.nut new file mode 100644 index 00000000000..e0e9f16ceac --- /dev/null +++ b/game/jbmod/scripts/vscripts/gamemodes/base.nut @@ -0,0 +1,205 @@ +// Copyright 2026 The JBMod Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//============================================================================= +// Base gamemode defaults +// All gamemodes should usually IncludeScript this first. +// Override any function below to customize behavior. +//============================================================================= + +//============================================================================= +// Spawn Point Registration +//============================================================================= +RegisterEntityClass( "info_player_deathmatch", "info_player_start" ); + +//============================================================================= +// Precache +//============================================================================= +function OnPrecache() +{ +} + +//============================================================================= +// Spawn Point Selection +//============================================================================= +function GetSpawnPointClassname( player ) +{ + return "info_player_deathmatch"; +} + +//============================================================================= +// Team Selection +//============================================================================= +function OnPlayerPickTeam( player ) +{ + return Constants.ETeam.TEAM_UNASSIGNED; +} + +//============================================================================= +// Model Selection (no-op: engine picks default) +//============================================================================= +function OnPlayerSetModel( player, model ) +{ + return model; +} + +//============================================================================= +// Model Change Notification +//============================================================================= +function OnPlayerModelChanged( player, model ) +{ +} + +//============================================================================= +// Loadout +//============================================================================= +function OnPlayerSpawn( player ) +{ + player.EquipSuit(); +} + +//============================================================================= +// Impulse 101 / Give All Items (no-op by default) +//============================================================================= +function OnGiveAllItems( player ) +{ +} + +//============================================================================= +// Respawn (immediate by default) +//============================================================================= +function OnPlayerRespawn( player ) +{ + return true; +} + +//============================================================================= +// Player Killed +//============================================================================= +function OnPlayerKilled( victim, attacker ) +{ +} + +//============================================================================= +// Death Sound +//============================================================================= +function OnPlayerDeathSound( player ) +{ + return "NPC_Citizen.Die"; +} + +//============================================================================= +// Per-tick Think +//============================================================================= +function OnThink() +{ +} + +//============================================================================= +// Damage Modification +//============================================================================= +function OnPlayerTakeDamage( victim, attacker, damage ) +{ + return damage; +} + +//============================================================================= +// Fall Damage (null for default, 0 for none, or custom value) +//============================================================================= +function GetFallDamage( player, fallSpeed ) +{ + return null; +} + +//============================================================================= +// Team Changes +//============================================================================= +function OnPlayerChangeTeam( player, newTeam ) +{ + return true; +} + +//============================================================================= +// Player Relationship +//============================================================================= +function GetPlayerRelationship( player, target ) +{ + return Constants.ERelationship.GR_NOTTEAMMATE; +} + +//============================================================================= +// Weapon/Item Respawn +//============================================================================= +function GetWeaponRespawnTime( classname, limitInWorld ) +{ + if ( Convars.GetFloat( "mp_weaponstay" ) > 0 && !limitInWorld ) + return 0; + + return Convars.GetFloat( "sv_jbmod_weapon_respawn_time" ); +} + +function GetItemRespawnTime( classname ) +{ + return Convars.GetFloat( "sv_jbmod_item_respawn_time" ); +} + +function ShouldWeaponRespawn( classname ) +{ + return true; +} + +function CanPickupWeapon( player, classname, ownsWeapon ) +{ + if ( Convars.GetFloat( "mp_weaponstay" ) > 0 && ownsWeapon ) + return false; + + return true; +} + +//============================================================================= +// Connection / Disconnection +//============================================================================= +function OnPlayerConnect( player ) +{ + local name = player.GetPlayerName(); + if ( name == "" ) + name = ""; + ClientPrint( null, Constants.EHudNotify.HUD_PRINTNOTIFY, name + " has joined the game\n" ); + + // Show MOTD by default, override in your own gamemode if you don't want it + player.ShowMOTD(); +} + +function OnPlayerDisconnect( player ) +{ +} + +//============================================================================= +// Chat (return false to block) +//============================================================================= +function OnPlayerChat( player, text ) +{ + return text; +} + +//============================================================================= +// Round Lifecycle +//============================================================================= +function OnRoundStart() +{ +} + +function OnRoundEnd() +{ +} diff --git a/game/jbmod/scripts/vscripts/gamemodes/cstrike.nut b/game/jbmod/scripts/vscripts/gamemodes/cstrike.nut index 2fedb0bf53b..7507e62d1e6 100644 --- a/game/jbmod/scripts/vscripts/gamemodes/cstrike.nut +++ b/game/jbmod/scripts/vscripts/gamemodes/cstrike.nut @@ -1,4 +1,54 @@ -printl( "Loading Counter-Strike..." ); +// Copyright 2026 The JBMod Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//============================================================================= +// Counter-Strike gamemode +// Very basic version to load maps, no gameplay logic +//============================================================================= + +//============================================================================= +// Include base gamemode for default behavior +//============================================================================= +IncludeScript( "gamemodes/base.nut" ); + +//============================================================================= +// Spawn Point Registration +//============================================================================= RegisterEntityClass( "info_player_counterterrorist", "info_player_start" ); RegisterEntityClass( "info_player_terrorist", "info_player_start" ); + +//============================================================================= +// Preserve Entities +//============================================================================= +PreserveEntityClass( "info_player_counterterrorist" ); +PreserveEntityClass( "info_player_terrorist" ); + +//============================================================================= +// Team Constants +//============================================================================= +const TEAM_COUNTERTERRORIST = Constants.ETeam.TEAM_COMBINE; +const TEAM_TERRORIST = Constants.ETeam.TEAM_REBELS; + +//============================================================================= +// Spawn Point Selection +//============================================================================= +function GetSpawnPointClassname( player ) +{ + local team = player.GetTeam(); + if ( team == TEAM_COUNTERTERRORIST ) + return "info_player_counterterrorist"; + else if ( team == TEAM_TERRORIST ) + return "info_player_terrorist"; + return "info_player_deathmatch"; +} diff --git a/game/jbmod/scripts/vscripts/gamemodes/deathmatch.nut b/game/jbmod/scripts/vscripts/gamemodes/deathmatch.nut index fe5c3af8d74..65598341378 100644 --- a/game/jbmod/scripts/vscripts/gamemodes/deathmatch.nut +++ b/game/jbmod/scripts/vscripts/gamemodes/deathmatch.nut @@ -1,5 +1,353 @@ -printl( "Loading Deathmatch..." ); +// Copyright 2026 The JBMod Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. -RegisterEntityClass( "info_player_deathmatch", "info_player_start" ); +//============================================================================= +// Deathmatch gamemode +// Implements most HL2DM logic +//============================================================================= +SetGameDescription( "JBMod Deathmatch" ); + +//============================================================================= +// Include base gamemode for default behavior +//============================================================================= +IncludeScript( "gamemodes/base.nut" ); + +//============================================================================= +// Spawn Point Registration +//============================================================================= RegisterEntityClass( "info_player_combine", "info_player_start" ); RegisterEntityClass( "info_player_rebel", "info_player_start" ); + +//============================================================================= +// Preserve Entities +//============================================================================= +PreserveEntityClass( "info_player_combine" ); +PreserveEntityClass( "info_player_rebel" ); + +//============================================================================= +// In deathmatch, players can be either combine or rebels +//============================================================================= +CitizenModels <- [ + "models/humans/group03/male_01.mdl", + "models/humans/group03/male_02.mdl", + "models/humans/group03/female_01.mdl", + "models/humans/group03/male_03.mdl", + "models/humans/group03/female_02.mdl", + "models/humans/group03/male_04.mdl", + "models/humans/group03/female_03.mdl", + "models/humans/group03/male_05.mdl", + "models/humans/group03/female_04.mdl", + "models/humans/group03/male_06.mdl", + "models/humans/group03/female_06.mdl", + "models/humans/group03/male_07.mdl", + "models/humans/group03/female_07.mdl", + "models/humans/group03/male_08.mdl", + "models/humans/group03/male_09.mdl", +]; + +CombineModels <- [ + "models/combine_soldier.mdl", + "models/combine_soldier_prisonguard.mdl", + "models/combine_super_soldier.mdl", + "models/police.mdl", +]; + +LastCitizenModel <- 0; +LastCombineModel <- 0; + +//============================================================================= +// Precache models and sounds +//============================================================================= +function OnPrecache() +{ + foreach ( m in CitizenModels ) + PrecacheModel( m ); + foreach ( m in CombineModels ) + PrecacheModel( m ); + + PrecacheScriptSound( "NPC_MetroPolice.Die" ); + PrecacheScriptSound( "NPC_CombineS.Die" ); + PrecacheScriptSound( "NPC_Citizen.die" ); +} + +//============================================================================= +// Only allow models defined above +//============================================================================= +function IsValidModel( model ) +{ + foreach ( m in CitizenModels ) + if ( m == model ) return true; + foreach ( m in CombineModels ) + if ( m == model ) return true; + return false; +} + +//============================================================================= +// Model Selection +//============================================================================= +function OnPlayerSetModel( player, model ) +{ + if ( IsTeamplay() ) + { + local team = player.GetTeam(); + if ( team == Constants.ETeam.TEAM_COMBINE ) + { + model = CombineModels[LastCombineModel % CombineModels.len()]; + LastCombineModel++; + } + else if ( team == Constants.ETeam.TEAM_REBELS ) + { + model = CitizenModels[LastCitizenModel % CitizenModels.len()]; + LastCitizenModel++; + } + } + + if ( !IsValidModel( model ) ) + model = "models/combine_soldier.mdl"; + + return model; +} + +//============================================================================= +// Model Change Notification +//============================================================================= +function OnPlayerModelChanged( player, model ) +{ + if ( IsTeamplay() ) + return; + + if ( !IsValidModel( model ) ) + player.SetPlayerModel( "models/combine_soldier.mdl" ); +} + +//============================================================================= +// Spawn Point Selection +//============================================================================= +function GetSpawnPointClassname( player ) +{ + if ( !IsTeamplay() ) + return "info_player_deathmatch"; + + local team = player.GetTeam(); + if ( team == Constants.ETeam.TEAM_COMBINE ) + return "info_player_combine"; + else if ( team == Constants.ETeam.TEAM_REBELS ) + return "info_player_rebel"; + return "info_player_deathmatch"; +} + +//============================================================================= +// Team Selection +//============================================================================= +function OnPlayerPickTeam( player ) +{ + if ( IsTeamplay() ) + { + // Auto-balance: assign to the smaller team + local combineCount = GetTeamPlayerCount( Constants.ETeam.TEAM_COMBINE ); + local rebelsCount = GetTeamPlayerCount( Constants.ETeam.TEAM_REBELS ); + + if ( combineCount > rebelsCount ) + return Constants.ETeam.TEAM_REBELS; + else if ( combineCount < rebelsCount ) + return Constants.ETeam.TEAM_COMBINE; + else + return (RandomInt( 0, 1 ) == 0) ? Constants.ETeam.TEAM_COMBINE : Constants.ETeam.TEAM_REBELS; + } + return Constants.ETeam.TEAM_UNASSIGNED; +} + +//============================================================================= +// Loadout +//============================================================================= +function IsCombineModel( player ) +{ + local model = player.GetModelName(); + return model.find( "police" ) != null || model.find( "combine" ) != null; +} +function OnPlayerSpawn( player ) +{ + player.EquipSuit(); + + player.GiveAmmo( 255, "Pistol" ); + player.GiveAmmo( 45, "SMG1" ); + player.GiveAmmo( 1, "grenade" ); + player.GiveAmmo( 6, "Buckshot" ); + player.GiveAmmo( 6, "357" ); + + if ( IsCombineModel( player ) ) + player.GiveItem( "weapon_stunstick" ); + else + player.GiveItem( "weapon_crowbar" ); + + player.GiveItem( "weapon_pistol" ); + player.GiveItem( "weapon_smg1" ); + player.GiveItem( "weapon_frag" ); + player.GiveItem( "weapon_physcannon" ); + + local defaultWpn = player.GetClientConVar( "cl_defaultweapon" ); + if ( defaultWpn != "" ) + player.SwitchToWeapon( defaultWpn ); + else + player.SwitchToWeapon( "weapon_physcannon" ); +} + +//============================================================================= +// Scoring +//============================================================================= +function OnPlayerKilled( victim, attacker ) +{ + if ( attacker == null ) + return; + + if ( attacker == victim ) + attacker.AddTeamScore( -1 ); + else + attacker.AddTeamScore( 1 ); +} + +//============================================================================= +// Impulse 101 / Give All Items +//============================================================================= +function OnGiveAllItems( player ) +{ + player.EquipSuit(); + + player.GiveAmmo( 255, "Pistol" ); + player.GiveAmmo( 255, "AR2" ); + player.GiveAmmo( 5, "AR2AltFire" ); + player.GiveAmmo( 255, "SMG1" ); + player.GiveAmmo( 1, "smg1_grenade" ); + player.GiveAmmo( 255, "Buckshot" ); + player.GiveAmmo( 32, "357" ); + player.GiveAmmo( 3, "rpg_round" ); + player.GiveAmmo( 16, "XBowBolt" ); + player.GiveAmmo( 1, "grenade" ); + player.GiveAmmo( 2, "slam" ); + + player.GiveItem( "weapon_crowbar" ); + player.GiveItem( "weapon_stunstick" ); + player.GiveItem( "weapon_pistol" ); + player.GiveItem( "weapon_357" ); + player.GiveItem( "weapon_smg1" ); + player.GiveItem( "weapon_ar2" ); + player.GiveItem( "weapon_shotgun" ); + player.GiveItem( "weapon_frag" ); + player.GiveItem( "weapon_crossbow" ); + player.GiveItem( "weapon_rpg" ); + player.GiveItem( "weapon_slam" ); + player.GiveItem( "weapon_physcannon" ); +} + +//============================================================================= +// Death Sound +//============================================================================= +function GetModelSoundPrefix( player ) +{ + local model = player.GetModelName(); + if ( model.find( "police" ) != null ) + return "NPC_MetroPolice"; + else if ( model.find( "combine" ) != null ) + return "NPC_CombineS"; + return "NPC_Citizen"; +} +function OnPlayerDeathSound( player ) +{ + return GetModelSoundPrefix( player ) + ".Die"; +} + +//============================================================================= +// Win Conditions +//============================================================================= +function CheckWinConditions() +{ + if ( IsGameOver() ) + return; + + // Time limit + if ( GetMapRemainingTime() < 0 ) + { + GoToIntermission(); + return; + } + + // Frag limit + local fragLimit = Convars.GetFloat( "mp_fraglimit" ); + if ( fragLimit <= 0 ) + return; + + if ( IsTeamplay() ) + { + if ( GetTeamScore( Constants.ETeam.TEAM_COMBINE ) >= fragLimit + || GetTeamScore( Constants.ETeam.TEAM_REBELS ) >= fragLimit ) + { + GoToIntermission(); + return; + } + } + else + { + for ( local i = 1; i <= GetMaxPlayers(); i++ ) + { + local player = GetPlayerByIndex( i ); + if ( player != null && player.GetFragCount() >= fragLimit ) + { + GoToIntermission(); + return; + } + } + } +} +function OnThink() +{ + CheckWinConditions(); +} + +//============================================================================= +// Respawn (wait for death animation, then respawn) +//============================================================================= +function OnPlayerRespawn( player ) +{ + return Time() > player.GetDeathTime() + Constants.Server.DEATH_ANIMATION_TIME; +} + +//============================================================================= +// Player Relationship +//============================================================================= +function GetPlayerRelationship( player, target ) +{ + if ( IsTeamplay() && player.GetTeam() == target.GetTeam() ) + return Constants.ERelationship.GR_TEAMMATE; + return Constants.ERelationship.GR_NOTTEAMMATE; +} + +//============================================================================= +// Player Connect +//============================================================================= +function OnPlayerConnect( player ) +{ + local name = player.GetPlayerName(); + if ( name == "" ) + name = ""; + ClientPrint( null, Constants.EHudNotify.HUD_PRINTNOTIFY, name + " has joined the game\n" ); + + // Show team info in teamplay + if ( IsTeamplay() ) + { + ClientPrint( player, Constants.EHudNotify.HUD_PRINTTALK, "You are on team " + GetTeamName( player.GetTeam() ) + "\n" ); + } + + // Show MOTD + player.ShowMOTD(); +} diff --git a/game/jbmod/scripts/vscripts/gamemodes/default.nut b/game/jbmod/scripts/vscripts/gamemodes/default.nut index bb1c769b42d..5ca3172a84c 100644 --- a/game/jbmod/scripts/vscripts/gamemodes/default.nut +++ b/game/jbmod/scripts/vscripts/gamemodes/default.nut @@ -1,18 +1,28 @@ -// Default gamemode selector -// This script runs if no jbmod_logic_gamemode is found in the map. +// Copyright 2026 The JBMod Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. -// TODO: Once we have mounting place, we can also match by folder/vpk -// This is just a demo for now +//============================================================================= +// Default gamemode selector +// This script runs if no jbmod_logic_gamemode is found in the map, and tries +// to load a gamemode based on the map name prefix. +//============================================================================= local mapname = GetMapName(); local lastSlash = mapname.find( "/" ); if ( lastSlash != null ) mapname = mapname.slice( lastSlash + 1 ); -local dot = mapname.find( ".bsp" ); -if ( dot != null ) - mapname = mapname.slice( 0, dot ); - if ( mapname.slice( 0, 3 ) == "dm_" ) IncludeScript( "gamemodes/deathmatch.nut" ); else if ( mapname.slice( 0, 3 ) == "cs_" || mapname.slice( 0, 3 ) == "de_" ) diff --git a/game/jbmod/scripts/vscripts/gamemodes/dod.nut b/game/jbmod/scripts/vscripts/gamemodes/dod.nut index 23b27676428..35af3bd8ec4 100644 --- a/game/jbmod/scripts/vscripts/gamemodes/dod.nut +++ b/game/jbmod/scripts/vscripts/gamemodes/dod.nut @@ -1,4 +1,54 @@ -printl( "Loading Day of Defeat..." ); +// Copyright 2026 The JBMod Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//============================================================================= +// Day of Defeat gamemode +// Very basic version to load maps, no gameplay logic +//============================================================================= + +//============================================================================= +// Include base gamemode for default behavior +//============================================================================= +IncludeScript( "gamemodes/base.nut" ); + +//============================================================================= +// Spawn Point Registration +//============================================================================= RegisterEntityClass( "info_player_allies", "info_player_start" ); RegisterEntityClass( "info_player_axis", "info_player_start" ); + +//============================================================================= +// Preserve Entities +//============================================================================= +PreserveEntityClass( "info_player_allies" ); +PreserveEntityClass( "info_player_axis" ); + +//============================================================================= +// Team Constants +//============================================================================= +const TEAM_ALLIES = Constants.ETeam.TEAM_COMBINE; +const TEAM_AXIS = Constants.ETeam.TEAM_REBELS; + +//============================================================================= +// Spawn Point Selection +//============================================================================= +function GetSpawnPointClassname( player ) +{ + local team = player.GetTeam(); + if ( team == TEAM_ALLIES ) + return "info_player_allies"; + else if ( team == TEAM_AXIS ) + return "info_player_axis"; + return "info_player_deathmatch"; +} diff --git a/game/jbmod/scripts/vscripts/gamemodes/rollerball.nut b/game/jbmod/scripts/vscripts/gamemodes/rollerball.nut new file mode 100644 index 00000000000..9ee7ab223a4 --- /dev/null +++ b/game/jbmod/scripts/vscripts/gamemodes/rollerball.nut @@ -0,0 +1,229 @@ +// Copyright 2026 The JBMod Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//============================================================================= +// Rollerball gamemode +// Sample gamemode for testing non-standard player movement +//============================================================================= +SetGameDescription( "Rollerball" ); + +//============================================================================= +// Include base gamemode for default behavior +//============================================================================= +IncludeScript( "gamemodes/base.nut" ); + +//============================================================================= +// Internal entity mapping +//============================================================================= +PlayerBalls <- {}; +JumpCooldowns <- {}; +SprintPower <- {}; +BallCounter <- 0; + +//============================================================================= +// Physics tuning +//============================================================================= +ROLL_FORCE <- 400.0; +ROLL_FORCE_BACK <- 320.0; +JUMP_FORCE <- 15000.0; +JUMP_COOLDOWN <- 0.8; + +//============================================================================= +// Sprint tuning +//============================================================================= +SPRINT_MULTIPLIER <- 2.0; +SPRINT_MAX <- 100.0; +SPRINT_DRAIN <- 25.0; +SPRINT_RECHARGE <- 15.0; + +//============================================================================= +// Precache rollermine +//============================================================================= +function OnPrecache() +{ + PrecacheModel( "models/roller.mdl" ); +} + +//============================================================================= +// Spawn: create a ball, parent the player to it +//============================================================================= +function OnPlayerSpawn( player ) +{ + player.RemoveAllItems(); + local idx = player.entindex(); + + // Clean up old ball if it exists (respawn case) + player.AcceptInput( "ClearParent", "", null, null ); + if ( idx in PlayerBalls && PlayerBalls[idx] != null && PlayerBalls[idx].IsValid() ) + { + PlayerBalls[idx].Destroy(); + delete PlayerBalls[idx]; + } + BallCounter++; + local ballName = "rollerball_" + idx + "_" + BallCounter; + + // Spawn a rollermine at the player's position + local ball = SpawnEntityFromTable( "prop_physics_override", { + model = "models/roller.mdl", + origin = player.GetOrigin() + Vector( 0, 0, 16 ), + targetname = ballName, + health = 0 + } ); + ball.AddEFlags( 128 ); // EFL_FORCE_CHECK_TRANSMIT + + // Track it + PlayerBalls[idx] <- ball; + SprintPower[idx] <- SPRINT_MAX; + + // Make the player invisible, non-solid + player.SetDrawEnabled( false ); + player.ShowViewModel( false ); + player.SetSolid( 0 ); // SOLID_NONE + player.SetMoveType( 0, 0 ); // MOVETYPE_NONE + + // Parent the player to the ball + player.SetOrigin( ball.GetOrigin() ); + player.AcceptInput( "SetParent", ballName, ball, ball ); +} + +//============================================================================= +// Block weapon pickups +//============================================================================= +function CanPickupWeapon( player, classname, ownsWeapon ) +{ + return false; +} + +//============================================================================= +// No fall damage +//============================================================================= +function GetFallDamage( player, fallSpeed ) +{ + return 0; +} + +//============================================================================= +// Immediate respawn +//============================================================================= +function OnPlayerRespawn( player ) +{ + return true; +} + +//============================================================================= +// Per-tick: read buttons and apply forces to the ball +//============================================================================= +function OnThink() +{ + local dt = FrameTime(); + + for ( local i = 1; i <= GetMaxPlayers(); i++ ) + { + local player = GetPlayerByIndex( i ); + if ( player == null ) + continue; + + local idx = player.entindex(); + + // If player is dead, clean up their ball immediately + if ( !player.IsAlive() ) + { + if ( idx in PlayerBalls && PlayerBalls[idx] != null && PlayerBalls[idx].IsValid() ) + { + player.AcceptInput( "ClearParent", "", null, null ); + PlayerBalls[idx].Destroy(); + } + if ( idx in PlayerBalls ) + delete PlayerBalls[idx]; + continue; + } + + if ( !(idx in PlayerBalls) || PlayerBalls[idx] == null || !PlayerBalls[idx].IsValid() ) + continue; + + local ball = PlayerBalls[idx]; + local buttons = player.GetButtons(); + local fwd = player.GetEyeForward(); + + // Sprint: boost force while holding IN_SPEED, drains power + local sprinting = false; + if ( (buttons & Constants.FButtons.IN_SPEED) && SprintPower[idx] > 0 ) + { + sprinting = true; + SprintPower[idx] <- SprintPower[idx] - SPRINT_DRAIN * dt; + if ( SprintPower[idx] < 0 ) + SprintPower[idx] <- 0.0; + } + else + { + // Recharge when not sprinting + SprintPower[idx] <- SprintPower[idx] + SPRINT_RECHARGE * dt; + if ( SprintPower[idx] > SPRINT_MAX ) + SprintPower[idx] <- SPRINT_MAX; + } + local forceMul = sprinting ? SPRINT_MULTIPLIER : 1.0; + + // Forward movement + if ( buttons & Constants.FButtons.IN_FORWARD ) + { + local f = ROLL_FORCE * forceMul; + ball.ApplyForceCenter( Vector( fwd.x * f, fwd.y * f, 0 ) ); + } + + // Backward movement + if ( buttons & Constants.FButtons.IN_BACK ) + { + local f = ROLL_FORCE_BACK * forceMul; + ball.ApplyForceCenter( Vector( fwd.x * -f, fwd.y * -f, 0 ) ); + } + + // Jump (spacebar) with cooldown + if ( buttons & Constants.FButtons.IN_JUMP ) + { + local now = Time(); + if ( !(idx in JumpCooldowns) || JumpCooldowns[idx] < now ) + { + ball.ApplyForceCenter( Vector( 0, 0, JUMP_FORCE ) ); + JumpCooldowns[idx] <- now + JUMP_COOLDOWN; + } + } + } +} + +//============================================================================= +// Clean up ball on disconnect +//============================================================================= +function OnPlayerDisconnect( player ) +{ + local idx = player.entindex(); + if ( idx in PlayerBalls && PlayerBalls[idx] != null && PlayerBalls[idx].IsValid() ) + PlayerBalls[idx].Destroy(); + if ( idx in PlayerBalls ) + delete PlayerBalls[idx]; + if ( idx in SprintPower ) + delete SprintPower[idx]; +} + +//============================================================================= +// Welcome message +//============================================================================= +function OnPlayerConnect( player ) +{ + local name = player.GetPlayerName(); + if ( name == "" ) + name = ""; + ClientPrint( null, Constants.EHudNotify.HUD_PRINTNOTIFY, name + " has joined the game\n" ); + + ClientPrint( player, Constants.EHudNotify.HUD_PRINTTALK, "Welcome to Rollerball! WASD to roll, Space to jump, Shift for a speed boost.\n" ); +} diff --git a/game/jbmod/scripts/vscripts/gamemodes/sandbox.nut b/game/jbmod/scripts/vscripts/gamemodes/sandbox.nut index e570c15bb3e..d9b73bfc35c 100644 --- a/game/jbmod/scripts/vscripts/gamemodes/sandbox.nut +++ b/game/jbmod/scripts/vscripts/gamemodes/sandbox.nut @@ -12,11 +12,22 @@ // See the License for the specific language governing permissions and // limitations under the License. -printl( "Initializing Sandbox..." ); +//============================================================================= +// Sandbox gamemode +// Starts with a physgun, will eventually be where spawn menus etc are +//============================================================================= SetGameDescription( "JBMod Sandbox" ); -Convars.SetValue( "sv_infinite_aux_power", 1 ); -Convars.SetValue( "mp_falldamage", 1 ); +Convars.SetDefault( "sv_infinite_aux_power", "1" ); +Convars.SetDefault( "mp_falldamage", "1" ); +//============================================================================= +// Include base gamemode for default behavior +//============================================================================= +IncludeScript( "gamemodes/base.nut" ); + +//============================================================================= +// Spawn with suit + physgun +//============================================================================= function OnPlayerSpawn( player ) { player.EquipSuit(); diff --git a/game/jbmod/scripts/vscripts/gamemodes/tf.nut b/game/jbmod/scripts/vscripts/gamemodes/tf.nut index 714ff61640d..b315aff9dca 100644 --- a/game/jbmod/scripts/vscripts/gamemodes/tf.nut +++ b/game/jbmod/scripts/vscripts/gamemodes/tf.nut @@ -1,3 +1,41 @@ -printl( "Loading Team Fortress..." ); +// Copyright 2026 The JBMod Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//============================================================================= +// Team Fortress gamemode +// Very basic version to load maps, no gameplay logic +//============================================================================= + +//============================================================================= +// Include base gamemode for default behavior +//============================================================================= +IncludeScript( "gamemodes/base.nut" ); + +//============================================================================= +// Spawn Point Registration +//============================================================================= RegisterEntityClass( "info_player_teamspawn", "info_player_start" ); + +//============================================================================= +// Preserve Entities +//============================================================================= +PreserveEntityClass( "info_player_teamspawn" ); + +//============================================================================= +// Spawn Point Selection +//============================================================================= +function GetSpawnPointClassname( player ) +{ + return "info_player_teamspawn"; +} diff --git a/game/jbmod/scripts/vscripts/weapons/weapon_leafblower.nut b/game/jbmod/scripts/vscripts/weapons/weapon_leafblower.nut index 6ab360b1664..79095f5d599 100644 --- a/game/jbmod/scripts/vscripts/weapons/weapon_leafblower.nut +++ b/game/jbmod/scripts/vscripts/weapons/weapon_leafblower.nut @@ -1,3 +1,17 @@ +// Copyright 2026 The JBMod Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + local FORCE = 500.0 local FORCE_MULTIPLIER = 4.0 local RANGE = 512.0 diff --git a/mapsrc/jb_waterhole.vmf b/mapsrc/jb_waterhole.vmf index 55163018e12..623e0d7d3d0 100644 --- a/mapsrc/jb_waterhole.vmf +++ b/mapsrc/jb_waterhole.vmf @@ -1,8 +1,8 @@ versioninfo { "editorversion" "400" - "editorbuild" "8870" - "mapversion" "37" + "editorbuild" "10427" + "mapversion" "38" "formatversion" "100" "prefab" "0" } @@ -15,11 +15,12 @@ viewsettings "bShowGrid" "1" "bShowLogicalGrid" "0" "nGridSpacing" "8" + "bShow3DGrid" "0" } world { "id" "0" - "mapversion" "37" + "mapversion" "38" "classname" "worldspawn" "detailmaterial" "detail/detailsprites" "detailvbsp" "detail.vbsp" @@ -1019,7 +1020,6 @@ entity editor { "color" "220 30 220" - "visgroupid" "3" "visgroupshown" "1" "visgroupautoshown" "1" "logicalpos" "[0 0]" @@ -1034,8 +1034,6 @@ entity editor { "color" "0 255 0" - "visgroupid" "3" - "visgroupid" "2" "visgroupshown" "1" "visgroupautoshown" "1" "logicalpos" "[0 500]" @@ -1051,7 +1049,6 @@ entity editor { "color" "0 0 255" - "visgroupid" "3" "visgroupshown" "1" "visgroupautoshown" "1" "logicalpos" "[0 1000]" @@ -1067,7 +1064,6 @@ entity editor { "color" "220 30 220" - "visgroupid" "3" "visgroupshown" "1" "visgroupautoshown" "1" "logicalpos" "[0 1500]" @@ -1083,7 +1079,6 @@ entity editor { "color" "220 30 220" - "visgroupid" "3" "visgroupshown" "1" "visgroupautoshown" "1" "logicalpos" "[0 2000]" @@ -1091,24 +1086,73 @@ entity } entity { - "id" "1803" - "classname" "prop_vehicle_airboat" - "actionScale" "1" - "angles" "0 90 0" - "fademindist" "-1" - "fadescale" "1" - "lightmapresolutionx" "32" - "lightmapresolutiony" "32" - "model" "models/airboat.mdl" - "origin" "504 360 8" - "solid" "6" - "vehiclescript" "scripts/vehicles/airboat.txt" + "id" "1814" + "classname" "info_player_start" + "angles" "0 180 0" + "origin" "544 312 8" + editor + { + "color" "0 255 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 500]" + } +} +entity +{ + "id" "1820" + "classname" "info_player_start" + "angles" "0 180 0" + "origin" "544 232 8" + editor + { + "color" "0 255 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 500]" + } +} +entity +{ + "id" "1826" + "classname" "info_player_start" + "angles" "0 180 0" + "origin" "544 192 8" + editor + { + "color" "0 255 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 500]" + } +} +entity +{ + "id" "1838" + "classname" "info_player_start" + "angles" "0 180 0" + "origin" "544 152 8" + editor + { + "color" "0 255 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 500]" + } +} +entity +{ + "id" "1842" + "classname" "jbmod_logic_gamemode" + "gamemode" "rollerball" + "spawnflags" "0" + "origin" "0 0 0" editor { "color" "220 30 220" "visgroupshown" "1" "visgroupautoshown" "1" - "logicalpos" "[0 2500]" + "logicalpos" "[0 0]" } } cameras @@ -1116,21 +1160,13 @@ cameras "activecamera" "0" camera { - "position" "[443.354 108.04 121.019]" - "look" "[420.522 370.495 82.9345]" + "position" "[453.647 -10.2785 138.188]" + "look" "[430.815 252.176 100.104]" } } -cordons +cordon { + "mins" "(-1024 -1024 -1024)" + "maxs" "(1024 1024 1024)" "active" "0" - cordon - { - "name" "cordon" - "active" "1" - box - { - "mins" "(99999 99999 99999)" - "maxs" "(-99999 -99999 -99999)" - } - } } diff --git a/src/game/client/jbmod/c_jbmod_player.cpp b/src/game/client/jbmod/c_jbmod_player.cpp index b499ca3eec0..e73b6fa1645 100644 --- a/src/game/client/jbmod/c_jbmod_player.cpp +++ b/src/game/client/jbmod/c_jbmod_player.cpp @@ -52,7 +52,6 @@ IMPLEMENT_CLIENTCLASS_DT(C_JBMod_Player, DT_JBMod_Player, CJBMod_Player) RecvPropEHandle( RECVINFO( m_hRagdoll ) ), RecvPropInt( RECVINFO( m_iSpawnInterpCounter ) ), - RecvPropInt( RECVINFO( m_iPlayerSoundType) ), RecvPropBool( RECVINFO( m_fIsWalking ) ), END_RECV_TABLE() @@ -953,6 +952,38 @@ void C_JBMod_Player::CalcView( Vector &eyeOrigin, QAngle &eyeAngles, float &zNea return; } + C_BaseEntity *pParent = GetMoveParent(); + if ( pParent && m_lifeState == LIFE_ALIVE && !IsObserver() ) + { + BaseClass::CalcView( eyeOrigin, eyeAngles, zNear, zFar, fov ); + + Vector vOrigin = pParent->GetAbsOrigin(); + engine->GetViewAngles( eyeAngles ); + + Vector vForward; + AngleVectors( eyeAngles, &vForward ); + VectorNormalize( vForward ); + + eyeOrigin = vOrigin; + eyeOrigin.z += 40.0f; + VectorMA( eyeOrigin, -100.0f, vForward, eyeOrigin ); + + Vector WALL_MIN( -WALL_OFFSET, -WALL_OFFSET, -WALL_OFFSET ); + Vector WALL_MAX( WALL_OFFSET, WALL_OFFSET, WALL_OFFSET ); + + trace_t trace; + C_BaseEntity::PushEnableAbsRecomputations( false ); + UTIL_TraceHull( vOrigin, eyeOrigin, WALL_MIN, WALL_MAX, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &trace ); + C_BaseEntity::PopEnableAbsRecomputations(); + + if ( trace.fraction < 1.0f ) + { + eyeOrigin = trace.endpos; + } + + return; + } + BaseClass::CalcView( eyeOrigin, eyeAngles, zNear, zFar, fov ); } diff --git a/src/game/client/jbmod/c_jbmod_player.h b/src/game/client/jbmod/c_jbmod_player.h index 18dd9c19294..8294b4c674c 100644 --- a/src/game/client/jbmod/c_jbmod_player.h +++ b/src/game/client/jbmod/c_jbmod_player.h @@ -81,7 +81,6 @@ class C_JBMod_Player : public C_BaseHLPlayer virtual void CreateLightEffects( void ) {} virtual bool ShouldReceiveProjectedTextures( int flags ); virtual void PostDataUpdate( DataUpdateType_t updateType ); - virtual void PlayStepSound( Vector &vecOrigin, surfacedata_t *psurface, float fvol, bool force ); virtual void PreThink( void ); virtual void DoImpactEffect( trace_t &tr, int nDamageType ); IRagdoll* GetRepresentativeRagdoll() const; @@ -108,8 +107,7 @@ class C_JBMod_Player : public C_BaseHLPlayer void Initialize( void ); int GetIDTarget() const; void UpdateIDTarget( void ); - void PrecacheFootStepSounds( void ); - const char *GetPlayerModelSoundPrefix( void ); + JBModPlayerState State_Get() const; @@ -153,8 +151,6 @@ class C_JBMod_Player : public C_BaseHLPlayer int m_iSpawnInterpCounter; int m_iSpawnInterpCounterCache; - int m_iPlayerSoundType; - void ReleaseFlashlight( void ); Beam_t *m_pFlashlightBeam; diff --git a/src/game/server/jbmod/jbmod_client.cpp b/src/game/server/jbmod/jbmod_client.cpp index 70cb6691e23..5dd1a03e872 100644 --- a/src/game/server/jbmod/jbmod_client.cpp +++ b/src/game/server/jbmod/jbmod_client.cpp @@ -5,13 +5,6 @@ // $NoKeywords: $ // //=============================================================================// -/* - -===== tf_client.cpp ======================================================== - - HL2 client/server game specific stuff - -*/ #include "cbase.h" #include "jbmod_player.h" @@ -25,6 +18,8 @@ #include "engine/IEngineSound.h" #include "team.h" #include "viewport_panel_names.h" +#include "vscript_shared.h" +#include "filesystem.h" #include "tier0/vprof.h" @@ -33,8 +28,6 @@ void Host_Say( edict_t *pEdict, bool teamonly ); -ConVar sv_motd_unload_on_dismissal( "sv_motd_unload_on_dismissal", "0", 0, "If enabled, the MOTD contents will be unloaded when the player closes the MOTD." ); - extern CBaseEntity* FindPickerEntityClass( CBasePlayer *pPlayer, char *classname ); extern bool g_fGameOver; @@ -43,38 +36,15 @@ void FinishClientPutInServer( CJBMod_Player *pPlayer ) pPlayer->InitialSpawn(); pPlayer->Spawn(); - - char sName[128]; - Q_strncpy( sName, pPlayer->GetPlayerName(), sizeof( sName ) ); - - // First parse the name and remove any %'s - for ( char *pApersand = sName; pApersand != NULL && *pApersand != 0; pApersand++ ) + if ( g_pScriptVM ) { - // Replace it with a space - if ( *pApersand == '%' ) - *pApersand = ' '; - } - - // notify other clients of player joining the game - UTIL_ClientPrintAll( HUD_PRINTNOTIFY, "#Game_connected", sName[0] != 0 ? sName : "" ); - - if ( JBModRules()->IsTeamplay() == true ) - { - ClientPrint( pPlayer, HUD_PRINTTALK, "You are on team %s1\n", pPlayer->GetTeam()->GetName() ); + HSCRIPT hFunction = g_pScriptVM->LookupFunction( "OnPlayerConnect" ); + if ( hFunction ) + { + g_pScriptVM->Call( hFunction, NULL, true, NULL, pPlayer->GetScriptInstance() ); + g_pScriptVM->ReleaseFunction( hFunction ); + } } - - const ConVar *hostname = cvar->FindVar( "hostname" ); - const char *title = (hostname) ? hostname->GetString() : "MESSAGE OF THE DAY"; - - KeyValues *data = new KeyValues("data"); - data->SetString( "title", title ); // info panel title - data->SetString( "type", "1" ); // show userdata from stringtable entry - data->SetString( "msg", "motd" ); // use this stringtable entry - data->SetBool( "unload", sv_motd_unload_on_dismissal.GetBool() ); - - pPlayer->ShowViewPortPanel( PANEL_INFO, true, data ); - - data->deleteThis(); } /* @@ -86,7 +56,6 @@ called each time a player is spawned into the game */ void ClientPutInServer( edict_t *pEdict, const char *playername ) { - // Allocate a CBaseTFPlayer for pev, and call spawn CJBMod_Player *pPlayer = CJBMod_Player::CreatePlayer( "player", pEdict ); pPlayer->SetPlayerName( playername ); } @@ -94,7 +63,7 @@ void ClientPutInServer( edict_t *pEdict, const char *playername ) void ClientActive( edict_t *pEdict, bool bLoadGame ) { - // Can't load games in CS! + // Can't load games! Assert( !bLoadGame ); CJBMod_Player *pPlayer = ToJBModPlayer( CBaseEntity::Instance( pEdict ) ); @@ -139,21 +108,39 @@ CBaseEntity* FindEntity( edict_t *pEdict, char *classname) //----------------------------------------------------------------------------- void ClientGamePrecache( void ) { - CBaseEntity::PrecacheModel("models/player.mdl"); - CBaseEntity::PrecacheModel( "models/gibs/agibs.mdl" ); - CBaseEntity::PrecacheModel ("models/weapons/v_hands.mdl"); - - CBaseEntity::PrecacheScriptSound( "HUDQuickInfo.LowAmmo" ); - CBaseEntity::PrecacheScriptSound( "HUDQuickInfo.LowHealth" ); - - CBaseEntity::PrecacheScriptSound( "FX_AntlionImpact.ShellImpact" ); - CBaseEntity::PrecacheScriptSound( "Missile.ShotDown" ); - CBaseEntity::PrecacheScriptSound( "Bullets.DefaultNearmiss" ); - CBaseEntity::PrecacheScriptSound( "Bullets.GunshipNearmiss" ); - CBaseEntity::PrecacheScriptSound( "Bullets.StriderNearmiss" ); - - CBaseEntity::PrecacheScriptSound( "Geiger.BeepHigh" ); - CBaseEntity::PrecacheScriptSound( "Geiger.BeepLow" ); + const char *pFilename = "scripts/client_precache.txt"; + KeyValues *pValues = new KeyValues( "ClientPrecache" ); + + if ( !pValues->LoadFromFile( filesystem, pFilename, "GAME" ) ) + { + Error( "Can't open %s for client precache info.", pFilename ); + pValues->deleteThis(); + return; + } + + for ( KeyValues *pData = pValues->GetFirstSubKey(); pData != NULL; pData = pData->GetNextKey() ) + { + const char *pszType = pData->GetName(); + const char *pszFile = pData->GetString(); + + if ( Q_strlen( pszType ) > 0 && Q_strlen( pszFile ) > 0 ) + { + if ( !Q_stricmp( pData->GetName(), "model" ) ) + { + CBaseEntity::PrecacheModel( pszFile ); + } + else if ( !Q_stricmp( pData->GetName(), "scriptsound" ) ) + { + CBaseEntity::PrecacheScriptSound( pszFile ); + } + else if ( !Q_stricmp( pData->GetName(), "particle" ) ) + { + PrecacheParticleSystem( pszFile ); + } + } + } + + pValues->deleteThis(); } @@ -164,14 +151,24 @@ void respawn( CBaseEntity *pEdict, bool fCopyCorpse ) if ( pPlayer ) { - if ( gpGlobals->curtime > pPlayer->GetDeathTime() + DEATH_ANIMATION_TIME ) - { - // respawn player - pPlayer->Spawn(); - } - else + if ( g_pScriptVM ) { - pPlayer->SetNextThink( gpGlobals->curtime + 0.1f ); + HSCRIPT hFunction = g_pScriptVM->LookupFunction( "OnPlayerRespawn" ); + if ( hFunction ) + { + ScriptVariant_t result; + g_pScriptVM->Call( hFunction, NULL, true, &result, pPlayer->GetScriptInstance() ); + g_pScriptVM->ReleaseFunction( hFunction ); + + if ( result.GetType() == FIELD_BOOLEAN ) + { + if ( (bool)result ) + pPlayer->Spawn(); + else + pPlayer->SetNextThink( gpGlobals->curtime + 0.1f ); + return; + } + } } } } @@ -190,7 +187,6 @@ void GameStartFrame( void ) //========================================================= void InstallGameRules() { - // vanilla deathmatch CreateGameRulesObject( "CJBModRules" ); } diff --git a/src/game/server/jbmod/jbmod_player.cpp b/src/game/server/jbmod/jbmod_player.cpp index 542708456fc..359f38ec932 100644 --- a/src/game/server/jbmod/jbmod_player.cpp +++ b/src/game/server/jbmod/jbmod_player.cpp @@ -15,6 +15,7 @@ #include "in_buttons.h" #include "jbmod_gamerules.h" #include "KeyValues.h" +#include "viewport_panel_names.h" #include "team.h" #include "weapon_jbmodbase.h" #include "grenade_satchel.h" @@ -29,11 +30,7 @@ #include "ilagcompensationmanager.h" #include "vscript_server.h" -int g_iLastCitizenModel = 0; -int g_iLastCombineModel = 0; -CBaseEntity *g_pLastCombineSpawn = NULL; -CBaseEntity *g_pLastRebelSpawn = NULL; extern CBaseEntity *g_pLastSpawn; ConVar jbmod_spawn_frag_fallback_radius( "jbmod_spawn_frag_fallback_radius", "48", FCVAR_NONE, "If no spawns are available, kill players with this radius to allow new players to spawn." ); @@ -87,7 +84,6 @@ IMPLEMENT_SERVERCLASS_ST(CJBMod_Player, DT_JBMod_Player) SendPropEHandle( SENDINFO( m_hRagdoll ) ), SendPropInt( SENDINFO( m_iSpawnInterpCounter), 4 ), - SendPropInt( SENDINFO( m_iPlayerSoundType), 3 ), SendPropExclude( "DT_BaseAnimating", "m_flPoseParameter" ), SendPropExclude( "DT_BaseFlex", "m_viewtarget" ), @@ -103,38 +99,31 @@ BEGIN_ENT_SCRIPTDESC( CJBMod_Player, CHL2_Player, "JBMod Player" ) DEFINE_SCRIPTFUNC_NAMED( ScriptEquipSuit, "EquipSuit", "Give the player the HEV suit." ) DEFINE_SCRIPTFUNC_NAMED( ScriptGiveItem, "GiveItem", "Give the player a specific item by classname." ) DEFINE_SCRIPTFUNC_NAMED( ScriptGiveAmmo, "GiveAmmo", "Give the player a specific amount of ammo (count, ammoName)." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSwitchToWeapon, "SwitchToWeapon", "Switch to a weapon the player owns by classname." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetClientConVar, "GetClientConVar", "Read a client ConVar value by name." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptRemoveAllItems, "RemoveAllItems", "Strip all weapons and suit from the player." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetFragCount, "GetFragCount", "Get the player's frag count." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptAddFrags, "AddFrags", "Add to the player's frag count." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptAddTeamScore, "AddTeamScore", "Add to this player's team score." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetPlayerModel, "SetPlayerModel", "Set player model." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptCommitSuicide, "CommitSuicide", "Kill the player." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptForceRespawn, "ForceRespawn", "Force the player to respawn immediately." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetPlayerName, "GetPlayerName", "Get the player's name." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptIsAlive, "IsAlive", "Returns true if the player is alive." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetDeathCount, "GetDeathCount", "Get the player's death count." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptAddDeaths, "AddDeaths", "Add to the player's death count." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetArmorValue, "GetArmorValue", "Get the player's armor value." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetArmorValue, "SetArmorValue", "Set the player's armor value." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptShowMOTD, "ShowMOTD", "Show the Message of the Day panel to this player." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetDeathTime, "GetDeathTime", "Get the time this player last died." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetMaxSpeed, "SetMaxSpeed", "Set the player's maximum movement speed." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetMaxHealth, "SetMaxHealth", "Set the player's maximum health." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetButtons, "GetButtons", "Get the player's currently pressed buttons (bitmask)." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetEyeForward, "GetEyeForward", "Get the player's eye forward direction vector." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptShowViewModel, "ShowViewModel", "Show or hide the player's viewmodel." ) END_SCRIPTDESC(); -const char *g_ppszRandomCitizenModels[] = -{ - "models/humans/group03/male_01.mdl", - "models/humans/group03/male_02.mdl", - "models/humans/group03/female_01.mdl", - "models/humans/group03/male_03.mdl", - "models/humans/group03/female_02.mdl", - "models/humans/group03/male_04.mdl", - "models/humans/group03/female_03.mdl", - "models/humans/group03/male_05.mdl", - "models/humans/group03/female_04.mdl", - "models/humans/group03/male_06.mdl", - "models/humans/group03/female_06.mdl", - "models/humans/group03/male_07.mdl", - "models/humans/group03/female_07.mdl", - "models/humans/group03/male_08.mdl", - "models/humans/group03/male_09.mdl", -}; - -const char *g_ppszRandomCombineModels[] = -{ - "models/combine_soldier.mdl", - "models/combine_soldier_prisonguard.mdl", - "models/combine_super_soldier.mdl", - "models/police.mdl", -}; - - -#define MAX_COMBINE_MODELS 4 -#define MODEL_CHANGE_INTERVAL 5.0f +ConVar sv_model_change_interval( "sv_model_change_interval", "5.0", FCVAR_GAMEDLL, "Minimum seconds between player model changes." ); #define TEAM_CHANGE_INTERVAL 5.0f #define JBMODPLAYER_PHYSDAMAGE_SCALE 4.0f @@ -167,6 +156,16 @@ CJBMod_Player::~CJBMod_Player( void ) void CJBMod_Player::UpdateOnRemove( void ) { + if ( g_pScriptVM ) + { + HSCRIPT hFunction = g_pScriptVM->LookupFunction( "OnPlayerDisconnect" ); + if ( hFunction ) + { + g_pScriptVM->Call( hFunction, NULL, true, NULL, GetScriptInstance() ); + g_pScriptVM->ReleaseFunction( hFunction ); + } + } + if ( m_hRagdoll ) { UTIL_RemoveImmediate( m_hRagdoll ); @@ -180,101 +179,14 @@ void CJBMod_Player::Precache( void ) { BaseClass::Precache(); - PrecacheModel ( "sprites/glow01.vmt" ); - - //Precache Citizen models - int nHeads = ARRAYSIZE( g_ppszRandomCitizenModels ); - int i; - - for ( i = 0; i < nHeads; ++i ) - PrecacheModel( g_ppszRandomCitizenModels[i] ); - - //Precache Combine Models - nHeads = ARRAYSIZE( g_ppszRandomCombineModels ); - - for ( i = 0; i < nHeads; ++i ) - PrecacheModel( g_ppszRandomCombineModels[i] ); - - PrecacheFootStepSounds(); - - PrecacheScriptSound( "NPC_MetroPolice.Die" ); - PrecacheScriptSound( "NPC_CombineS.Die" ); - PrecacheScriptSound( "NPC_Citizen.die" ); -} - -void CJBMod_Player::GiveAllItems( void ) -{ - EquipSuit(); - - CBasePlayer::GiveAmmo( 255, "Pistol"); - CBasePlayer::GiveAmmo( 255, "AR2" ); - CBasePlayer::GiveAmmo( 5, "AR2AltFire" ); - CBasePlayer::GiveAmmo( 255, "SMG1"); - CBasePlayer::GiveAmmo( 1, "smg1_grenade"); - CBasePlayer::GiveAmmo( 255, "Buckshot"); - CBasePlayer::GiveAmmo( 32, "357" ); - CBasePlayer::GiveAmmo( 3, "rpg_round"); - CBasePlayer::GiveAmmo( 16, "XBowBolt"); - - CBasePlayer::GiveAmmo( 1, "grenade" ); - CBasePlayer::GiveAmmo( 2, "slam" ); - - GiveNamedItem( "weapon_crowbar" ); - GiveNamedItem( "weapon_stunstick" ); - GiveNamedItem( "weapon_pistol" ); - GiveNamedItem( "weapon_357" ); - - GiveNamedItem( "weapon_smg1" ); - GiveNamedItem( "weapon_ar2" ); - - GiveNamedItem( "weapon_shotgun" ); - GiveNamedItem( "weapon_frag" ); - - GiveNamedItem( "weapon_crossbow" ); - - GiveNamedItem( "weapon_rpg" ); - - GiveNamedItem( "weapon_slam" ); - - GiveNamedItem( "weapon_physcannon" ); - -} - -void CJBMod_Player::GiveDefaultItems( void ) -{ - EquipSuit(); - - CBasePlayer::GiveAmmo( 255, "Pistol"); - CBasePlayer::GiveAmmo( 45, "SMG1"); - CBasePlayer::GiveAmmo( 1, "grenade" ); - CBasePlayer::GiveAmmo( 6, "Buckshot"); - CBasePlayer::GiveAmmo( 6, "357" ); - - if ( GetPlayerModelType() == PLAYER_SOUNDS_METROPOLICE || GetPlayerModelType() == PLAYER_SOUNDS_COMBINESOLDIER ) - { - GiveNamedItem( "weapon_stunstick" ); - } - else if ( GetPlayerModelType() == PLAYER_SOUNDS_CITIZEN ) - { - GiveNamedItem( "weapon_crowbar" ); - } - - GiveNamedItem( "weapon_pistol" ); - GiveNamedItem( "weapon_smg1" ); - GiveNamedItem( "weapon_frag" ); - GiveNamedItem( "weapon_physcannon" ); - - const char *szDefaultWeaponName = engine->GetClientConVarValue( engine->IndexOfEdict( edict() ), "cl_defaultweapon" ); - - CBaseCombatWeapon *pDefaultWeapon = Weapon_OwnsThisType( szDefaultWeaponName ); - - if ( pDefaultWeapon ) - { - Weapon_Switch( pDefaultWeapon ); - } - else + if ( g_pScriptVM ) { - Weapon_Switch( Weapon_OwnsThisType( "weapon_physcannon" ) ); + HSCRIPT hFunction = g_pScriptVM->LookupFunction( "OnPrecache" ); + if ( hFunction ) + { + g_pScriptVM->Call( hFunction, NULL, true, NULL ); + g_pScriptVM->ReleaseFunction( hFunction ); + } } } @@ -282,46 +194,18 @@ void CJBMod_Player::PickDefaultSpawnTeam( void ) { if ( GetTeamNumber() == 0 ) { - if ( JBModRules()->IsTeamplay() == false ) + if ( g_pScriptVM ) { - if ( GetModelPtr() == NULL ) + HSCRIPT hFunction = g_pScriptVM->LookupFunction( "OnPlayerPickTeam" ); + if ( hFunction ) { - const char *szModelName = NULL; - szModelName = engine->GetClientConVarValue( engine->IndexOfEdict( edict() ), "cl_playermodel" ); + ScriptVariant_t result; + g_pScriptVM->Call( hFunction, NULL, true, &result, GetScriptInstance() ); + g_pScriptVM->ReleaseFunction( hFunction ); - if ( ValidatePlayerModel( szModelName ) == false ) - { - char szReturnString[512]; - - Q_snprintf( szReturnString, sizeof (szReturnString ), "cl_playermodel models/combine_soldier.mdl\n" ); - engine->ClientCommand ( edict(), szReturnString ); - } - - ChangeTeam( TEAM_UNASSIGNED ); - } - } - else - { - CTeam *pCombine = g_Teams[TEAM_COMBINE]; - CTeam *pRebels = g_Teams[TEAM_REBELS]; - - if ( pCombine == NULL || pRebels == NULL ) - { - ChangeTeam( random->RandomInt( TEAM_COMBINE, TEAM_REBELS ) ); - } - else - { - if ( pCombine->GetNumPlayers() > pRebels->GetNumPlayers() ) - { - ChangeTeam( TEAM_REBELS ); - } - else if ( pCombine->GetNumPlayers() < pRebels->GetNumPlayers() ) + if ( result.GetType() == FIELD_INTEGER ) { - ChangeTeam( TEAM_COMBINE ); - } - else - { - ChangeTeam( random->RandomInt( TEAM_COMBINE, TEAM_REBELS ) ); + ChangeTeam( (int)result ); } } } @@ -347,10 +231,7 @@ void CJBMod_Player::Spawn(void) RemoveEffects( EF_NODRAW ); - if ( CallScriptOnPlayerSpawn() == false ) - { - GiveDefaultItems(); - } + CallScriptOnPlayerSpawn(); } SetNumAnimOverlays( 3 ); @@ -382,32 +263,14 @@ void CJBMod_Player::Spawn(void) m_bReady = false; } -bool CJBMod_Player::ValidatePlayerModel( const char *pModel ) +void CJBMod_Player::PlayerUse( void ) { - int iModels = ARRAYSIZE( g_ppszRandomCitizenModels ); - int i; - - for ( i = 0; i < iModels; ++i ) - { - if ( !Q_stricmp( g_ppszRandomCitizenModels[i], pModel ) ) - { - return true; - } - } - - iModels = ARRAYSIZE( g_ppszRandomCombineModels ); - - for ( i = 0; i < iModels; ++i ) - { - if ( !Q_stricmp( g_ppszRandomCombineModels[i], pModel ) ) - { - return true; - } - } + // Block +use when player is invisible (e.g. Rollerball mode) + if ( IsEffectActive( EF_NODRAW ) ) + return; - return false; + BaseClass::PlayerUse(); } - ConVar jbmod_allow_pickup( "jbmod_allow_pickup", "0", FCVAR_GAMEDLL ); void CJBMod_Player::PickupObject( CBaseEntity* pObject, bool bLimitMassAndSize ) @@ -418,145 +281,39 @@ void CJBMod_Player::PickupObject( CBaseEntity* pObject, bool bLimitMassAndSize ) return BaseClass::PickupObject( pObject, bLimitMassAndSize ); } -void CJBMod_Player::SetPlayerTeamModel( void ) +void CJBMod_Player::SetPlayerModel( void ) { - const char *szModelName = NULL; - szModelName = engine->GetClientConVarValue( engine->IndexOfEdict( edict() ), "cl_playermodel" ); - - int modelIndex = modelinfo->GetModelIndex( szModelName ); - - if ( modelIndex == -1 || ValidatePlayerModel( szModelName ) == false ) - { - szModelName = "models/Combine_Soldier.mdl"; - m_iModelType = TEAM_COMBINE; - - char szReturnString[512]; - - Q_snprintf( szReturnString, sizeof (szReturnString ), "cl_playermodel %s\n", szModelName ); - engine->ClientCommand ( edict(), szReturnString ); - } - - if ( GetTeamNumber() == TEAM_COMBINE ) + if ( g_pScriptVM ) { - if ( Q_stristr( szModelName, "models/human") ) + HSCRIPT hFunction = g_pScriptVM->LookupFunction( "OnPlayerSetModel" ); + if ( hFunction ) { - int nHeads = ARRAYSIZE( g_ppszRandomCombineModels ); - - g_iLastCombineModel = ( g_iLastCombineModel + 1 ) % nHeads; - szModelName = g_ppszRandomCombineModels[g_iLastCombineModel]; + const char *pRequestedModel = engine->GetClientConVarValue( entindex(), "cl_playermodel" ); + ScriptVariant_t result; + g_pScriptVM->Call( hFunction, NULL, true, &result, GetScriptInstance(), pRequestedModel ); + g_pScriptVM->ReleaseFunction( hFunction ); + + const char *pModelStr = (const char *)result; + if ( result.GetType() == FIELD_CSTRING ) + ScriptSetPlayerModel( pModelStr ); } - - m_iModelType = TEAM_COMBINE; } - else if ( GetTeamNumber() == TEAM_REBELS ) - { - if ( !Q_stristr( szModelName, "models/human") ) - { - int nHeads = ARRAYSIZE( g_ppszRandomCitizenModels ); - - g_iLastCitizenModel = ( g_iLastCitizenModel + 1 ) % nHeads; - szModelName = g_ppszRandomCitizenModels[g_iLastCitizenModel]; - } - - m_iModelType = TEAM_REBELS; - } - - SetModel( szModelName ); - SetupPlayerSoundsByModel( szModelName ); - - m_flNextModelChangeTime = gpGlobals->curtime + MODEL_CHANGE_INTERVAL; } -void CJBMod_Player::SetPlayerModel( void ) +void CJBMod_Player::ScriptShowMOTD( void ) { - const char *szModelName = NULL; - const char *pszCurrentModelName = modelinfo->GetModelName( GetModel()); - - szModelName = engine->GetClientConVarValue( engine->IndexOfEdict( edict() ), "cl_playermodel" ); - - if ( ValidatePlayerModel( szModelName ) == false ) - { - char szReturnString[512]; - - if ( ValidatePlayerModel( pszCurrentModelName ) == false ) - { - pszCurrentModelName = "models/Combine_Soldier.mdl"; - } - - Q_snprintf( szReturnString, sizeof (szReturnString ), "cl_playermodel %s\n", pszCurrentModelName ); - engine->ClientCommand ( edict(), szReturnString ); - - szModelName = pszCurrentModelName; - } - - if ( GetTeamNumber() == TEAM_COMBINE ) - { - int nHeads = ARRAYSIZE( g_ppszRandomCombineModels ); - - g_iLastCombineModel = ( g_iLastCombineModel + 1 ) % nHeads; - szModelName = g_ppszRandomCombineModels[g_iLastCombineModel]; - - m_iModelType = TEAM_COMBINE; - } - else if ( GetTeamNumber() == TEAM_REBELS ) - { - int nHeads = ARRAYSIZE( g_ppszRandomCitizenModels ); - - g_iLastCitizenModel = ( g_iLastCitizenModel + 1 ) % nHeads; - szModelName = g_ppszRandomCitizenModels[g_iLastCitizenModel]; - - m_iModelType = TEAM_REBELS; - } - else - { - if ( Q_strlen( szModelName ) == 0 ) - { - szModelName = g_ppszRandomCitizenModels[0]; - } - - if ( Q_stristr( szModelName, "models/human") ) - { - m_iModelType = TEAM_REBELS; - } - else - { - m_iModelType = TEAM_COMBINE; - } - } - - int modelIndex = modelinfo->GetModelIndex( szModelName ); - - if ( modelIndex == -1 ) - { - szModelName = "models/Combine_Soldier.mdl"; - m_iModelType = TEAM_COMBINE; - - char szReturnString[512]; - - Q_snprintf( szReturnString, sizeof (szReturnString ), "cl_playermodel %s\n", szModelName ); - engine->ClientCommand ( edict(), szReturnString ); - } + const ConVar *hostname = cvar->FindVar( "hostname" ); + const char *title = (hostname) ? hostname->GetString() : "MESSAGE OF THE DAY"; - SetModel( szModelName ); - SetupPlayerSoundsByModel( szModelName ); + KeyValues *data = new KeyValues("data"); + data->SetString( "title", title ); + data->SetString( "type", "1" ); + data->SetString( "msg", "motd" ); + data->SetBool( "unload", true ); - m_flNextModelChangeTime = gpGlobals->curtime + MODEL_CHANGE_INTERVAL; -} + ShowViewPortPanel( PANEL_INFO, true, data ); -void CJBMod_Player::SetupPlayerSoundsByModel( const char *pModelName ) -{ - if ( Q_stristr( pModelName, "models/human") ) - { - m_iPlayerSoundType = (int)PLAYER_SOUNDS_CITIZEN; - } - else if ( Q_stristr(pModelName, "police" ) ) - { - m_iPlayerSoundType = (int)PLAYER_SOUNDS_METROPOLICE; - } - else if ( Q_stristr(pModelName, "combine" ) ) - { - m_iPlayerSoundType = (int)PLAYER_SOUNDS_COMBINESOLDIER; - } + data->deleteThis(); } void CJBMod_Player::ResetAnimation( void ) @@ -722,7 +479,8 @@ bool CJBMod_Player::WantsLagCompensationOnEntity( const CBasePlayer *pPlayer, co Activity CJBMod_Player::TranslateTeamActivity( Activity ActToTranslate ) { - if ( m_iModelType == TEAM_COMBINE ) + const char *pModelName = STRING( GetModelName() ); + if ( Q_stristr( pModelName, "combine" ) || Q_stristr( pModelName, "police" ) ) return ActToTranslate; if ( ActToTranslate == ACT_RUN ) @@ -961,14 +719,19 @@ bool CJBMod_Player::BumpWeapon( CBaseCombatWeapon *pWeapon ) void CJBMod_Player::ChangeTeam( int iTeam ) { -/* if ( GetNextTeamChangeTime() >= gpGlobals->curtime ) + if ( g_pScriptVM ) { - char szReturnString[128]; - Q_snprintf( szReturnString, sizeof( szReturnString ), "Please wait %d more seconds before trying to switch teams again.\n", (int)(GetNextTeamChangeTime() - gpGlobals->curtime) ); + HSCRIPT hFunction = g_pScriptVM->LookupFunction( "OnPlayerChangeTeam" ); + if ( hFunction ) + { + ScriptVariant_t result; + g_pScriptVM->Call( hFunction, NULL, true, &result, GetScriptInstance(), iTeam ); + g_pScriptVM->ReleaseFunction( hFunction ); - ClientPrint( this, HUD_PRINTTALK, szReturnString ); - return; - }*/ + if ( result.GetType() == FIELD_BOOLEAN && !(bool)result ) + return; + } + } bool bKill = false; @@ -990,14 +753,7 @@ void CJBMod_Player::ChangeTeam( int iTeam ) m_flNextTeamChangeTime = gpGlobals->curtime + TEAM_CHANGE_INTERVAL; - if ( JBModRules()->IsTeamplay() == true ) - { - SetPlayerTeamModel(); - } - else - { - SetPlayerModel(); - } + SetPlayerModel(); if ( iTeam == TEAM_SPECTATOR ) { @@ -1094,9 +850,14 @@ void CJBMod_Player::CheatImpulseCommands( int iImpulse ) { case 101: { - if( sv_cheats->GetBool() ) + if( sv_cheats->GetBool() && g_pScriptVM ) { - GiveAllItems(); + HSCRIPT hFunction = g_pScriptVM->LookupFunction( "OnGiveAllItems" ); + if ( hFunction ) + { + g_pScriptVM->Call( hFunction, NULL, true, NULL, GetScriptInstance() ); + g_pScriptVM->ReleaseFunction( hFunction ); + } } } break; @@ -1310,7 +1071,10 @@ void CJBMod_Player::Event_Killed( const CTakeDamageInfo &info ) // Note: since we're dead, it won't draw us on the client, but we don't set EF_NODRAW // because we still want to transmit to the clients in our PVS. - CreateRagdollEntity(); + if ( !IsEffectActive( EF_NODRAW ) ) + { + CreateRagdollEntity(); + } DetonateTripmines(); @@ -1326,16 +1090,16 @@ void CJBMod_Player::Event_Killed( const CTakeDamageInfo &info ) CBaseEntity *pAttacker = info.GetAttacker(); - if ( pAttacker ) + if ( g_pScriptVM ) { - int iScoreToAdd = 1; - - if ( pAttacker == this ) + HSCRIPT hFunction = g_pScriptVM->LookupFunction( "OnPlayerKilled" ); + if ( hFunction ) { - iScoreToAdd = -1; + g_pScriptVM->Call( hFunction, NULL, true, NULL, + GetScriptInstance(), + ToHScript( pAttacker ) ); + g_pScriptVM->ReleaseFunction( hFunction ); } - - GetGlobalTeam( pAttacker->GetTeamNumber() )->AddScore( iScoreToAdd ); } FlashlightTurnOff(); @@ -1352,6 +1116,34 @@ int CJBMod_Player::OnTakeDamage( const CTakeDamageInfo &inputInfo ) if ( gpGlobals->curtime < m_flSlamProtectTime && (inputInfo.GetDamageType() == DMG_BLAST ) ) return 0; + if ( g_pScriptVM ) + { + HSCRIPT hFunction = g_pScriptVM->LookupFunction( "OnPlayerTakeDamage" ); + if ( hFunction ) + { + ScriptVariant_t result; + g_pScriptVM->Call( hFunction, NULL, true, &result, + GetScriptInstance(), + ToHScript( inputInfo.GetAttacker() ), + inputInfo.GetDamage() ); + g_pScriptVM->ReleaseFunction( hFunction ); + + if ( result.GetType() == FIELD_FLOAT || result.GetType() == FIELD_INTEGER ) + { + float flNewDamage = (float)result; + if ( flNewDamage < 0 ) + return 0; + + CTakeDamageInfo modifiedInfo = inputInfo; + modifiedInfo.SetDamage( flNewDamage ); + + m_vecTotalBulletForce += modifiedInfo.GetDamageForce(); + gamestats->Event_PlayerDamage( this, modifiedInfo ); + return BaseClass::OnTakeDamage( modifiedInfo ); + } + } + } + m_vecTotalBulletForce += inputInfo.GetDamageForce(); gamestats->Event_PlayerDamage( this, inputInfo ); @@ -1364,14 +1156,29 @@ void CJBMod_Player::DeathSound( const CTakeDamageInfo &info ) if ( m_hRagdoll && m_hRagdoll->GetBaseAnimating()->IsDissolving() ) return; - char szStepSound[128]; + const char *pSoundName = NULL; - Q_snprintf( szStepSound, sizeof( szStepSound ), "%s.Die", GetPlayerModelSoundPrefix() ); + if ( g_pScriptVM ) + { + HSCRIPT hFunction = g_pScriptVM->LookupFunction( "OnPlayerDeathSound" ); + if ( hFunction ) + { + ScriptVariant_t result; + g_pScriptVM->Call( hFunction, NULL, true, &result, GetScriptInstance() ); + g_pScriptVM->ReleaseFunction( hFunction ); + + if ( result.GetType() == FIELD_CSTRING ) + pSoundName = (const char *)result; + } + } + + if ( !pSoundName || !pSoundName[0] ) + return; const char *pModelName = STRING( GetModelName() ); CSoundParameters params; - if ( GetParametersForSound( szStepSound, params, pModelName ) == false ) + if ( GetParametersForSound( pSoundName, params, pModelName ) == false ) return; Vector vecOrigin = GetAbsOrigin(); @@ -1398,23 +1205,19 @@ CBaseEntity* CJBMod_Player::EntSelectSpawnPoint( void ) edict_t *player = edict(); const char *pSpawnpointName = "info_player_deathmatch"; - if ( JBModRules()->IsTeamplay() == true ) + if ( g_pScriptVM ) { - if ( GetTeamNumber() == TEAM_COMBINE ) + HSCRIPT hFunction = g_pScriptVM->LookupFunction( "GetSpawnPointClassname" ); + if ( hFunction ) { - pSpawnpointName = "info_player_combine"; - pLastSpawnPoint = g_pLastCombineSpawn; - } - else if ( GetTeamNumber() == TEAM_REBELS ) - { - pSpawnpointName = "info_player_rebel"; - pLastSpawnPoint = g_pLastRebelSpawn; - } + ScriptVariant_t result; + g_pScriptVM->Call( hFunction, NULL, true, &result, GetScriptInstance() ); + g_pScriptVM->ReleaseFunction( hFunction ); - if ( gEntList.FindEntityByClassname( NULL, pSpawnpointName ) == NULL ) - { - pSpawnpointName = "info_player_deathmatch"; - pLastSpawnPoint = g_pLastSpawn; + if ( result.GetType() == FIELD_CSTRING && (const char *)result != NULL && ((const char *)result)[0] ) + { + pSpawnpointName = (const char *)result; + } } } @@ -1469,60 +1272,8 @@ CBaseEntity* CJBMod_Player::EntSelectSpawnPoint( void ) goto ReturnSpot; } - // Incredibly terrible garbage way of getting CSS and DOD spawnpoints to work. - // These aren't set up for teamplay so it'll just plop you at a random spawn. - // Stops the game from immediately crashing on CSS maps. - // -nocaps 29.3.26 - - // cstrike spawns - if ( !pSpot ) - { - pSpot = gEntList.FindEntityByClassname( pSpot, "info_player_counterterrorist" ); - - if ( pSpot ) - goto ReturnSpot; - } - - if ( !pSpot ) - { - pSpot = gEntList.FindEntityByClassname( pSpot, "info_player_terrorist" ); - - if ( pSpot ) - goto ReturnSpot; - } - - // day of defeat spawns - if ( !pSpot ) - { - pSpot = gEntList.FindEntityByClassname( pSpot, "info_player_allies" ); - - if ( pSpot ) - goto ReturnSpot; - } - - if ( !pSpot ) - { - pSpot = gEntList.FindEntityByClassname( pSpot, "info_player_axis" ); - - if ( pSpot ) - goto ReturnSpot; - } - - ReturnSpot: - if ( JBModRules()->IsTeamplay() == true ) - { - if ( GetTeamNumber() == TEAM_COMBINE ) - { - g_pLastCombineSpawn = pSpot; - } - else if ( GetTeamNumber() == TEAM_REBELS ) - { - g_pLastRebelSpawn = pSpot; - } - } - g_pLastSpawn = pSpot; m_flSlamProtectTime = gpGlobals->curtime + 0.5; @@ -1603,8 +1354,64 @@ bool CJBMod_Player::CallScriptOnPlayerSpawn( void ) return false; } +void CJBMod_Player::ScriptSwitchToWeapon( const char *szWeapon ) +{ + CBaseCombatWeapon *pWeapon = Weapon_OwnsThisType( szWeapon ); + if ( pWeapon ) + { + Weapon_Switch( pWeapon ); + } +} + +const char *CJBMod_Player::ScriptGetClientConVar( const char *szCvar ) +{ + if ( !szCvar || !szCvar[0] ) + return ""; + + return engine->GetClientConVarValue( engine->IndexOfEdict( edict() ), szCvar ); +} + +void CJBMod_Player::ScriptAddTeamScore( int nScore ) +{ + CTeam *pTeam = GetTeam(); + if ( pTeam ) + { + pTeam->AddScore( nScore ); + } +} + +void CJBMod_Player::ScriptSetPlayerModel( const char *szModel ) +{ + if ( !szModel || !szModel[0] || !Q_stristr( szModel, ".mdl" ) ) + return; + + SetModel( szModel ); + m_flNextModelChangeTime = gpGlobals->curtime + sv_model_change_interval.GetFloat(); +} + void CJBMod_Player::CheckChatText( char *p, int bufsize ) { + if ( g_pScriptVM ) + { + HSCRIPT hFunction = g_pScriptVM->LookupFunction( "OnPlayerChat" ); + if ( hFunction ) + { + ScriptVariant_t result; + g_pScriptVM->Call( hFunction, NULL, true, &result, GetScriptInstance(), p ); + g_pScriptVM->ReleaseFunction( hFunction ); + + if ( result.GetType() == FIELD_BOOLEAN && !(bool)result ) + { + p[0] = '\0'; + return; + } + else if ( result.GetType() == FIELD_CSTRING ) + { + Q_strncpy( p, (const char *)result, bufsize ); + } + } + } + //Look for escape sequences and replace char *buf = new char[bufsize]; @@ -1684,6 +1491,13 @@ CJBModPlayerStateInfo *CJBMod_Player::State_LookupInfo( JBModPlayerState state ) return NULL; } +const Vector &CJBMod_Player::ScriptGetEyeForward( void ) +{ + static Vector vecForward; + AngleVectors( pl.v_angle, &vecForward ); + return vecForward; +} + bool CJBMod_Player::StartObserverMode(int mode) { //we only want to go into observer mode if the player asked to, not on a death timeout diff --git a/src/game/server/jbmod/jbmod_player.h b/src/game/server/jbmod/jbmod_player.h index 0c0f7d45695..68267d4260e 100644 --- a/src/game/server/jbmod/jbmod_player.h +++ b/src/game/server/jbmod/jbmod_player.h @@ -71,8 +71,8 @@ class CJBMod_Player : public CHL2_Player virtual bool Weapon_Switch( CBaseCombatWeapon *pWeapon, int viewmodelindex = 0); virtual bool BumpWeapon( CBaseCombatWeapon *pWeapon ); virtual void ChangeTeam( int iTeam ) OVERRIDE; + virtual void PlayerUse( void ); virtual void PickupObject ( CBaseEntity *pObject, bool bLimitMassAndSize ); - virtual void PlayStepSound( Vector &vecOrigin, surfacedata_t *psurface, float fvol, bool force ); virtual void Weapon_Drop( CBaseCombatWeapon *pWeapon, const Vector *pvecTarget = NULL, const Vector *pVelocity = NULL ); virtual void UpdateOnRemove( void ); virtual void DeathSound( const CTakeDamageInfo &info ); @@ -81,8 +81,6 @@ class CJBMod_Player : public CHL2_Player int FlashlightIsOn( void ); void FlashlightTurnOn( void ); void FlashlightTurnOff( void ); - void PrecacheFootStepSounds( void ); - bool ValidatePlayerModel( const char *pModel ); QAngle GetAnimEyeAngles( void ) { return m_angEyeAngles.Get(); } @@ -90,28 +88,43 @@ class CJBMod_Player : public CHL2_Player void CheatImpulseCommands( int iImpulse ); void CreateRagdollEntity( void ); - void GiveAllItems( void ); - void GiveDefaultItems( void ); void NoteWeaponFired( void ); void ResetAnimation( void ); void SetPlayerModel( void ); - void SetPlayerTeamModel( void ); Activity TranslateTeamActivity( Activity ActToTranslate ); float GetNextModelChangeTime( void ) { return m_flNextModelChangeTime; } float GetNextTeamChangeTime( void ) { return m_flNextTeamChangeTime; } void PickDefaultSpawnTeam( void ); - void SetupPlayerSoundsByModel( const char *pModelName ); bool CallScriptOnPlayerSpawn( void ); - const char *GetPlayerModelSoundPrefix( void ); void ScriptEquipSuit( void ) { EquipSuit(); } HSCRIPT ScriptGiveItem( const char *szItem ) { return ToHScript( GiveNamedItem( szItem ) ); } void ScriptGiveAmmo( int nCount, const char *szAmmoName ) { CBasePlayer::GiveAmmo( nCount, szAmmoName ); } - - int GetPlayerModelType( void ) { return m_iPlayerSoundType; } + void ScriptSwitchToWeapon( const char *szWeapon ); + const char *ScriptGetClientConVar( const char *szCvar ); + void ScriptRemoveAllItems( void ) { RemoveAllItems( true ); } + int ScriptGetFragCount( void ) { return FragCount(); } + void ScriptAddFrags( int nCount ) { IncrementFragCount( nCount ); } + void ScriptAddTeamScore( int nScore ); + void ScriptSetPlayerModel( const char *szModel ); + void ScriptCommitSuicide( void ) { CommitSuicide(); } + void ScriptForceRespawn( void ) { Spawn(); } + const char *ScriptGetPlayerName( void ) { return GetPlayerName(); } + bool ScriptIsAlive( void ) { return IsAlive(); } + int ScriptGetDeathCount( void ) { return DeathCount(); } + void ScriptAddDeaths( int nCount ) { IncrementDeathCount( nCount ); } + int ScriptGetArmorValue( void ) { return ArmorValue(); } + void ScriptSetArmorValue( int value ) { SetArmorValue( value ); } + void ScriptShowMOTD( void ); + float ScriptGetDeathTime( void ) { return GetDeathTime(); } + void ScriptSetMaxSpeed( float speed ) { SetMaxSpeed( speed ); } + void ScriptSetMaxHealth( int health ) { SetMaxHealth( health ); if ( m_iHealth > health ) m_iHealth = health; } + int ScriptGetButtons( void ) { return m_nButtons; } + const Vector &ScriptGetEyeForward( void ); + void ScriptShowViewModel( bool bShow ) { ShowViewModel( bShow ); } int GetMaxAmmo( int iAmmoIndex ) const; @@ -155,9 +168,7 @@ class CJBMod_Player : public CHL2_Player CPlayerAnimState m_PlayerAnimState; int m_iLastWeaponFireUsercmd; - int m_iModelType; CNetworkVar( int, m_iSpawnInterpCounter ); - CNetworkVar( int, m_iPlayerSoundType ); float m_flNextModelChangeTime; float m_flNextTeamChangeTime; diff --git a/src/game/server/vscript_server.cpp b/src/game/server/vscript_server.cpp index 547b36b8a5c..16bf5e43c61 100644 --- a/src/game/server/vscript_server.cpp +++ b/src/game/server/vscript_server.cpp @@ -38,6 +38,10 @@ #include "bot/tf_bot.h" #endif +#ifdef JBMOD +#include "jbmod/jbmod_gamerules.h" +#endif + #if defined( _WIN32 ) || defined( POSIX ) #include "vscript_server_nut.h" #endif @@ -101,6 +105,7 @@ class CScriptConvarAccessor : public CAutoGameSystem ScriptVariant_t GetStr( const char *cvar ); const char *GetClientConvarValue( const char *cvar, int entindex ); void SetValue( const char *cvar, ScriptVariant_t value ); + void SetDefault( const char *cvar, const char *value ); void LevelInitPreEntity() OVERRIDE; void LevelShutdownPostEntity() OVERRIDE; @@ -232,6 +237,28 @@ void CScriptConvarAccessor::SetValue( const char *cvar, ScriptVariant_t value ) } } +void CScriptConvarAccessor::SetDefault( const char *cvar, const char *value ) +{ + if ( !cvar || !*cvar || !value ) + return; + + if ( !IsConVarOnAllowList( cvar ) ) + { + DevMsg( "Convar %s was not in " VSCRIPT_CONVAR_ALLOWLIST_NAME "\n", cvar ); + return; + } + + ConVarRef cref( cvar ); + if ( cref.IsValid() && !cref.IsFlagSet( FCVAR_SCRIPT_NONO ) ) + { + if ( Q_strcmp( cref.GetString(), cref.GetDefault() ) == 0 ) + { + cref.SetValue( value ); + GameRules()->SaveConvar( cref ); + } + } +} + void CScriptConvarAccessor::LevelInitPreEntity() { m_AllowedConVars.RemoveAll(); @@ -275,6 +302,7 @@ DEFINE_SCRIPTFUNC( GetFloat, "GetFloat(name) : returns the convar as a float. Ma DEFINE_SCRIPTFUNC( GetStr, "GetStr(name) : returns the convar as a string. May return null if no such convar." ) DEFINE_SCRIPTFUNC( GetClientConvarValue, "GetClientConvarValue(name) : returns the convar value for the entindex as a string." ) DEFINE_SCRIPTFUNC( SetValue, "SetValue(name, value) : sets the value of the convar. The convar must be in " VSCRIPT_CONVAR_ALLOWLIST_NAME " to be set. Supported types are bool, int, float, string." ) +DEFINE_SCRIPTFUNC( SetDefault, "SetDefault(name, value) : sets the value of the convar only if it has not changed from its default." ) DEFINE_SCRIPTFUNC( IsConVarOnAllowList, "IsConVarOnAllowList(name) : checks if the convar is allowed to be used and is in " VSCRIPT_CONVAR_ALLOWLIST_NAME ". Please be nice with this and use it for *compatibility* if you need check support and NOT to force server owners to allow hostname to be set... or else this will simply lie and return true in future. ;-) You have been warned!" ) END_SCRIPTDESC() @@ -2650,7 +2678,6 @@ bool VScriptServerInit() #define DECLARE_SCRIPT_CONST_NAMED( type, name, x ) g_pScriptVM->SetValue( (HSCRIPT)vConstantsTable_##type, name, x ); #define DECLARE_SCRIPT_CONST( type, x ) DECLARE_SCRIPT_CONST_NAMED( type, #x, x ) #define REGISTER_SCRIPT_CONST_TABLE( x ) g_pScriptVM->SetValue( (HSCRIPT) vConstantsTable, #x, vConstantsTable_##x ); -#ifdef TF_DLL DECLARE_SCRIPT_CONST_TABLE( EScriptRecipientFilter ) DECLARE_SCRIPT_CONST( EScriptRecipientFilter, RECIPIENT_FILTER_DEFAULT ) DECLARE_SCRIPT_CONST( EScriptRecipientFilter, RECIPIENT_FILTER_PAS_ATTENUATION ) @@ -2661,6 +2688,7 @@ DECLARE_SCRIPT_CONST( EScriptRecipientFilter, RECIPIENT_FILTER_GLOBAL ) DECLARE_SCRIPT_CONST( EScriptRecipientFilter, RECIPIENT_FILTER_TEAM ) REGISTER_SCRIPT_CONST_TABLE( EScriptRecipientFilter ) +#ifdef TF_DLL DECLARE_SCRIPT_CONST_TABLE( ETFCond ) DECLARE_SCRIPT_CONST( ETFCond, TF_COND_INVALID ) DECLARE_SCRIPT_CONST( ETFCond, TF_COND_AIMING ) @@ -2905,6 +2933,7 @@ DECLARE_SCRIPT_CONST( FNavAttributeType, NAV_MESH_FUNC_COST ) DECLARE_SCRIPT_CONST( FNavAttributeType, NAV_MESH_HAS_ELEVATOR ) DECLARE_SCRIPT_CONST( FNavAttributeType, NAV_MESH_NAV_BLOCKER ) REGISTER_SCRIPT_CONST_TABLE( FNavAttributeType ) +#endif // TF_DLL DECLARE_SCRIPT_CONST_TABLE( FButtons ) DECLARE_SCRIPT_CONST( FButtons, IN_ATTACK ) @@ -2948,15 +2977,18 @@ DECLARE_SCRIPT_CONST( FHideHUD, HIDEHUD_CROSSHAIR ) DECLARE_SCRIPT_CONST( FHideHUD, HIDEHUD_VEHICLE_CROSSHAIR ) DECLARE_SCRIPT_CONST( FHideHUD, HIDEHUD_INVEHICLE ) DECLARE_SCRIPT_CONST( FHideHUD, HIDEHUD_BONUS_PROGRESS ) +#ifdef TF_DLL DECLARE_SCRIPT_CONST( FHideHUD, HIDEHUD_BUILDING_STATUS ) DECLARE_SCRIPT_CONST( FHideHUD, HIDEHUD_CLOAK_AND_FEIGN ) DECLARE_SCRIPT_CONST( FHideHUD, HIDEHUD_PIPES_AND_CHARGE ) DECLARE_SCRIPT_CONST( FHideHUD, HIDEHUD_METAL ) DECLARE_SCRIPT_CONST( FHideHUD, HIDEHUD_TARGET_ID ) DECLARE_SCRIPT_CONST( FHideHUD, HIDEHUD_MATCH_STATUS ) +#endif DECLARE_SCRIPT_CONST( FHideHUD, HIDEHUD_BITCOUNT ) REGISTER_SCRIPT_CONST_TABLE( FHideHUD ) +#ifdef TF_DLL DECLARE_SCRIPT_CONST_TABLE( FTaunts ) DECLARE_SCRIPT_CONST( FTaunts, TAUNT_BASE_WEAPON ) DECLARE_SCRIPT_CONST( FTaunts, TAUNT_MISC_ITEM ) @@ -2964,6 +2996,7 @@ DECLARE_SCRIPT_CONST( FTaunts, TAUNT_SHOW_ITEM ) DECLARE_SCRIPT_CONST( FTaunts, TAUNT_LONG ) DECLARE_SCRIPT_CONST( FTaunts, TAUNT_SPECIAL ) REGISTER_SCRIPT_CONST_TABLE( FTaunts ) +#endif DECLARE_SCRIPT_CONST_TABLE( EHitGroup ) DECLARE_SCRIPT_CONST( EHitGroup, HITGROUP_GENERIC ) @@ -3285,6 +3318,7 @@ DECLARE_SCRIPT_CONST( EMoveCollide, MOVECOLLIDE_COUNT ) DECLARE_SCRIPT_CONST( EMoveCollide, MOVECOLLIDE_MAX_BITS ) REGISTER_SCRIPT_CONST_TABLE( EMoveCollide ) +#ifdef TF_DLL // Unnamed enum DECLARE_SCRIPT_CONST_TABLE( ETFTeam ) DECLARE_SCRIPT_CONST( ETFTeam, TEAM_ANY ) @@ -3481,6 +3515,7 @@ DECLARE_SCRIPT_CONST( FTFNavAttributeType, TF_NAV_DOOR_ALWAYS_BLOCKS ) DECLARE_SCRIPT_CONST( FTFNavAttributeType, TF_NAV_UNBLOCKABLE ) DECLARE_SCRIPT_CONST( FTFNavAttributeType, TF_NAV_PERSISTENT_ATTRIBUTES ) REGISTER_SCRIPT_CONST_TABLE( FTFNavAttributeType ) +#endif // TF_DLL DECLARE_SCRIPT_CONST_TABLE( EHudNotify ) DECLARE_SCRIPT_CONST( EHudNotify, HUD_PRINTNOTIFY ) @@ -3489,9 +3524,11 @@ DECLARE_SCRIPT_CONST( EHudNotify, HUD_PRINTTALK ) DECLARE_SCRIPT_CONST( EHudNotify, HUD_PRINTCENTER ) REGISTER_SCRIPT_CONST_TABLE( EHudNotify ) +#ifdef TF_DLL DECLARE_SCRIPT_CONST_TABLE( EBotType ) DECLARE_SCRIPT_CONST( EBotType, TF_BOT_TYPE ) REGISTER_SCRIPT_CONST_TABLE( EBotType ) +#endif DECLARE_SCRIPT_CONST_TABLE( Math ) DECLARE_SCRIPT_CONST_NAMED( Math, "Epsilon", FLT_EPSILON) @@ -3505,13 +3542,30 @@ DECLARE_SCRIPT_CONST_NAMED( Math, "Sqrt3", 1.732050808f) DECLARE_SCRIPT_CONST_NAMED( Math, "GoldenRatio", 1.618033989f) REGISTER_SCRIPT_CONST_TABLE( Math ) +DECLARE_SCRIPT_CONST_TABLE( ETeam ) +DECLARE_SCRIPT_CONST( ETeam, TEAM_UNASSIGNED ) +DECLARE_SCRIPT_CONST( ETeam, TEAM_SPECTATOR ) +#ifdef JBMOD +DECLARE_SCRIPT_CONST( ETeam, TEAM_COMBINE ) +DECLARE_SCRIPT_CONST( ETeam, TEAM_REBELS ) +#endif +REGISTER_SCRIPT_CONST_TABLE( ETeam ) + +DECLARE_SCRIPT_CONST_TABLE( ERelationship ) +DECLARE_SCRIPT_CONST( ERelationship, GR_NOTTEAMMATE ) +DECLARE_SCRIPT_CONST( ERelationship, GR_TEAMMATE ) +DECLARE_SCRIPT_CONST( ERelationship, GR_ENEMY ) +DECLARE_SCRIPT_CONST( ERelationship, GR_ALLY ) +DECLARE_SCRIPT_CONST( ERelationship, GR_NEUTRAL ) +REGISTER_SCRIPT_CONST_TABLE( ERelationship ) + DECLARE_SCRIPT_CONST_TABLE( Server ) DECLARE_SCRIPT_CONST( Server, MAX_EDICTS ) DECLARE_SCRIPT_CONST( Server, MAX_PLAYERS ) DECLARE_SCRIPT_CONST( Server, DIST_EPSILON ) +DECLARE_SCRIPT_CONST( Server, DEATH_ANIMATION_TIME ) DECLARE_SCRIPT_CONST_NAMED( Server, "ConstantNamingConvention", "Constants are named as follows: F -> flags, E -> enums, (nothing) -> random values/constants" ) REGISTER_SCRIPT_CONST_TABLE( Server ) -#endif g_pScriptVM->SetValue( "Constants", vConstantsTable ); g_pScriptVM->SetValue( "CLIENT", false ); diff --git a/src/game/shared/jbmod/jbmod_gamerules.cpp b/src/game/shared/jbmod/jbmod_gamerules.cpp index 4927509761f..4099fb6a2eb 100644 --- a/src/game/shared/jbmod/jbmod_gamerules.cpp +++ b/src/game/shared/jbmod/jbmod_gamerules.cpp @@ -41,12 +41,11 @@ extern bool FindInList( const char **pStrings, const char *pToFind ); ConVar sv_jbmod_weapon_respawn_time( "sv_jbmod_weapon_respawn_time", "20", FCVAR_GAMEDLL | FCVAR_NOTIFY ); ConVar sv_jbmod_item_respawn_time( "sv_jbmod_item_respawn_time", "30", FCVAR_GAMEDLL | FCVAR_NOTIFY ); ConVar sv_report_client_settings("sv_report_client_settings", "0", FCVAR_GAMEDLL | FCVAR_NOTIFY ); +ConVar sv_fov_min( "sv_fov_min", "75", FCVAR_GAMEDLL, "Minimum allowed FOV for players." ); +ConVar sv_fov_max( "sv_fov_max", "90", FCVAR_GAMEDLL, "Maximum allowed FOV for players." ); extern ConVar mp_chattime; -extern CBaseEntity *g_pLastCombineSpawn; -extern CBaseEntity *g_pLastRebelSpawn; - #define WEAPON_MAX_DISTANCE_FROM_SPAWN 64 #endif @@ -87,11 +86,14 @@ static JBModViewVectors g_JBModViewVectors( Vector( 16, 16, 60 ) //VEC_CROUCH_TRACE_MAX (m_vCrouchTraceMax) ); +#ifndef CLIENT_DLL + static const char *s_PreserveEnts[] = { "ai_network", "ai_hint", "jbmod_gamerules", + "jbmod_logic_gamemode", "team_manager", "player_manager", "env_soundscape", @@ -110,8 +112,6 @@ static const char *s_PreserveEnts[] = "info_target", "info_node_hint", "info_player_deathmatch", - "info_player_combine", - "info_player_rebel", "info_map_parameters", "keyframe_rope", "move_rope", @@ -130,7 +130,22 @@ static const char *s_PreserveEnts[] = "", // END Marker }; +static CUtlVector s_ExtraPreserveEnts; + +static bool IsPreservedEntity( const char *pClassname ) +{ + if ( FindInList( s_PreserveEnts, pClassname ) ) + return true; + for ( int i = 0; i < s_ExtraPreserveEnts.Count(); i++ ) + { + if ( !Q_stricmp( s_ExtraPreserveEnts[i].Get(), pClassname ) ) + return true; + } + return false; +} + +#endif // CLIENT_DLL #ifdef CLIENT_DLL void RecvProxy_JBModRules( const RecvProp *pProp, void **pOut, void *pData, int objectID ) @@ -237,9 +252,6 @@ void CJBModRules::CreateStandardEntities( void ) BaseClass::CreateStandardEntities(); - g_pLastCombineSpawn = NULL; - g_pLastRebelSpawn = NULL; - #ifdef DBGFLAG_ASSERT CBaseEntity *pEnt = #endif @@ -255,19 +267,23 @@ void CJBModRules::CreateStandardEntities( void ) float CJBModRules::FlWeaponRespawnTime( CBaseCombatWeapon *pWeapon ) { #ifndef CLIENT_DLL - if ( weaponstay.GetInt() > 0 ) + if ( g_pScriptVM ) { - // make sure it's only certain weapons - if ( !(pWeapon->GetWeaponFlags() & ITEM_FLAG_LIMITINWORLD) ) + HSCRIPT hFunction = g_pScriptVM->LookupFunction( "GetWeaponRespawnTime" ); + if ( hFunction ) { - return 0; // weapon respawns almost instantly + bool bLimitInWorld = ( pWeapon->GetWeaponFlags() & ITEM_FLAG_LIMITINWORLD ) != 0; + ScriptVariant_t result; + g_pScriptVM->Call( hFunction, NULL, true, &result, pWeapon->GetClassname(), bLimitInWorld ); + g_pScriptVM->ReleaseFunction( hFunction ); + + if ( result.GetType() == FIELD_FLOAT || result.GetType() == FIELD_INTEGER ) + return (float)result; } } - - return sv_jbmod_weapon_respawn_time.GetFloat(); #endif - return 0; // weapon respawns almost instantly + return 0; } @@ -312,41 +328,13 @@ void CJBModRules::Think( void ) return; } -// float flTimeLimit = mp_timelimit.GetFloat() * 60; - float flFragLimit = fraglimit.GetFloat(); - - if ( GetMapRemainingTime() < 0 ) - { - GoToIntermission(); - return; - } - - if ( flFragLimit ) + if ( g_pScriptVM ) { - if( IsTeamplay() == true ) + HSCRIPT hFunction = g_pScriptVM->LookupFunction( "OnThink" ); + if ( hFunction ) { - CTeam *pCombine = g_Teams[TEAM_COMBINE]; - CTeam *pRebels = g_Teams[TEAM_REBELS]; - - if ( pCombine->GetScore() >= flFragLimit || pRebels->GetScore() >= flFragLimit ) - { - GoToIntermission(); - return; - } - } - else - { - // check if any player is over the frag limit - for ( int i = 1; i <= gpGlobals->maxClients; i++ ) - { - CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); - - if ( pPlayer && pPlayer->FragCount() >= flFragLimit ) - { - GoToIntermission(); - return; - } - } + g_pScriptVM->Call( hFunction, NULL, true, NULL ); + g_pScriptVM->ReleaseFunction( hFunction ); } } @@ -382,6 +370,16 @@ void CJBModRules::GoToIntermission( void ) if ( g_fGameOver ) return; + if ( g_pScriptVM ) + { + HSCRIPT hFunction = g_pScriptVM->LookupFunction( "OnRoundEnd" ); + if ( hFunction ) + { + g_pScriptVM->Call( hFunction, NULL, true, NULL ); + g_pScriptVM->ReleaseFunction( hFunction ); + } + } + g_fGameOver = true; m_flIntermissionEndTime = gpGlobals->curtime + mp_chattime.GetInt(); @@ -596,7 +594,21 @@ QAngle CJBModRules::VecItemRespawnAngles( CItem *pItem ) //========================================================= float CJBModRules::FlItemRespawnTime( CItem *pItem ) { - return sv_jbmod_item_respawn_time.GetFloat(); + if ( g_pScriptVM ) + { + HSCRIPT hFunction = g_pScriptVM->LookupFunction( "GetItemRespawnTime" ); + if ( hFunction ) + { + ScriptVariant_t result; + g_pScriptVM->Call( hFunction, NULL, true, &result, pItem->GetClassname() ); + g_pScriptVM->ReleaseFunction( hFunction ); + + if ( result.GetType() == FIELD_FLOAT || result.GetType() == FIELD_INTEGER ) + return (float)result; + } + } + + return 0.0f; } @@ -606,15 +618,48 @@ float CJBModRules::FlItemRespawnTime( CItem *pItem ) //========================================================= bool CJBModRules::CanHavePlayerItem( CBasePlayer *pPlayer, CBaseCombatWeapon *pItem ) { - if ( weaponstay.GetInt() > 0 ) + if ( g_pScriptVM ) { - if ( pPlayer->Weapon_OwnsThisType( pItem->GetClassname(), pItem->GetSubType() ) ) - return false; + HSCRIPT hFunction = g_pScriptVM->LookupFunction( "CanPickupWeapon" ); + if ( hFunction ) + { + bool bOwnsWeapon = pPlayer->Weapon_OwnsThisType( pItem->GetClassname(), pItem->GetSubType() ) != NULL; + ScriptVariant_t result; + g_pScriptVM->Call( hFunction, NULL, true, &result, + pPlayer->GetScriptInstance(), + pItem->GetClassname(), + bOwnsWeapon ); + g_pScriptVM->ReleaseFunction( hFunction ); + + if ( result.GetType() == FIELD_BOOLEAN ) + return (bool)result; + } } return BaseClass::CanHavePlayerItem( pPlayer, pItem ); } +float CJBModRules::FlPlayerFallDamage( CBasePlayer *pPlayer ) +{ + if ( g_pScriptVM ) + { + HSCRIPT hFunction = g_pScriptVM->LookupFunction( "GetFallDamage" ); + if ( hFunction ) + { + ScriptVariant_t result; + g_pScriptVM->Call( hFunction, NULL, true, &result, + pPlayer->GetScriptInstance(), + pPlayer->m_Local.m_flFallVelocity ); + g_pScriptVM->ReleaseFunction( hFunction ); + + if ( result.GetType() == FIELD_FLOAT || result.GetType() == FIELD_INTEGER ) + return (float)result; + } + } + + return BaseClass::FlPlayerFallDamage( pPlayer ); +} + #endif //========================================================= @@ -624,6 +669,20 @@ bool CJBModRules::CanHavePlayerItem( CBasePlayer *pPlayer, CBaseCombatWeapon *pI int CJBModRules::WeaponShouldRespawn( CBaseCombatWeapon *pWeapon ) { #ifndef CLIENT_DLL + if ( g_pScriptVM ) + { + HSCRIPT hFunction = g_pScriptVM->LookupFunction( "ShouldWeaponRespawn" ); + if ( hFunction ) + { + ScriptVariant_t result; + g_pScriptVM->Call( hFunction, NULL, true, &result, pWeapon->GetClassname() ); + g_pScriptVM->ReleaseFunction( hFunction ); + + if ( result.GetType() == FIELD_BOOLEAN ) + return (bool)result ? GR_WEAPON_RESPAWN_YES : GR_WEAPON_RESPAWN_NO; + } + } + if ( pWeapon->HasSpawnFlags( SF_NORESPAWN ) ) { return GR_WEAPON_RESPAWN_NO; @@ -652,6 +711,16 @@ void CJBModRules::ClientDisconnected( edict_t *pClient ) if ( pPlayer->IsInAVehicle() ) pPlayer->LeaveVehicle(); + + if ( g_pScriptVM ) + { + HSCRIPT hFunction = g_pScriptVM->LookupFunction( "OnPlayerDisconnect" ); + if ( hFunction ) + { + g_pScriptVM->Call( hFunction, NULL, true, NULL, pPlayer->GetScriptInstance() ); + g_pScriptVM->ReleaseFunction( hFunction ); + } + } } BaseClass::ClientDisconnected( pClient ); @@ -789,26 +858,13 @@ void CJBModRules::ClientSettingsChanged( CBasePlayer *pPlayer ) return; } - if ( JBModRules()->IsTeamplay() == false ) - { - pHL2Player->SetPlayerModel(); - - const char *pszCurrentModelName = modelinfo->GetModelName( pHL2Player->GetModel() ); - - char szReturnString[128]; - Q_snprintf( szReturnString, sizeof( szReturnString ), "Your player model is: %s\n", pszCurrentModelName ); - - ClientPrint( pHL2Player, HUD_PRINTTALK, szReturnString ); - } - else + if ( g_pScriptVM ) { - if ( Q_stristr( szModelName, "models/human") ) - { - pHL2Player->ChangeTeam( TEAM_REBELS ); - } - else + HSCRIPT hFunction = g_pScriptVM->LookupFunction( "OnPlayerModelChanged" ); + if ( hFunction ) { - pHL2Player->ChangeTeam( TEAM_COMBINE ); + g_pScriptVM->Call( hFunction, NULL, true, NULL, pHL2Player->GetScriptInstance(), szModelName ); + g_pScriptVM->ReleaseFunction( hFunction ); } } } @@ -820,8 +876,10 @@ void CJBModRules::ClientSettingsChanged( CBasePlayer *pPlayer ) const char *pszFov = engine->GetClientConVarValue( pHL2Player->entindex(), "fov_desired" ); if ( pszFov ) { + static ConVarRef sv_fov_min( "sv_fov_min" ); + static ConVarRef sv_fov_max( "sv_fov_max" ); int iFov = atoi( pszFov ); - iFov = clamp( iFov, 75, 90 ); + iFov = clamp( iFov, sv_fov_min.GetInt(), sv_fov_max.GetInt() ); pHL2Player->SetDefaultFOV( iFov ); } @@ -833,14 +891,23 @@ void CJBModRules::ClientSettingsChanged( CBasePlayer *pPlayer ) int CJBModRules::PlayerRelationship( CBaseEntity *pPlayer, CBaseEntity *pTarget ) { #ifndef CLIENT_DLL - // half life multiplay has a simple concept of Player Relationships. - // you are either on another player's team, or you are not. - if ( !pPlayer || !pTarget || !pTarget->IsPlayer() || IsTeamplay() == false ) + if ( !pPlayer || !pTarget || !pTarget->IsPlayer() ) return GR_NOTTEAMMATE; - if ( (*GetTeamID(pPlayer) != '\0') && (*GetTeamID(pTarget) != '\0') && !stricmp( GetTeamID(pPlayer), GetTeamID(pTarget) ) ) + if ( g_pScriptVM ) { - return GR_TEAMMATE; + HSCRIPT hFunction = g_pScriptVM->LookupFunction( "GetPlayerRelationship" ); + if ( hFunction ) + { + ScriptVariant_t result; + g_pScriptVM->Call( hFunction, NULL, true, &result, + ToHScript( pPlayer ), + ToHScript( pTarget ) ); + g_pScriptVM->ReleaseFunction( hFunction ); + + if ( result.GetType() == FIELD_INTEGER ) + return (int)result; + } } #endif @@ -883,37 +950,6 @@ void CJBModRules::Precache( void ) CBaseEntity::PrecacheScriptSound( "AlyxEmp.Charge" ); } -#ifdef GAME_DLL -bool CJBModRules::IsOfficialMap( void ) -{ - static const char *s_OfficialMaps[] = - { - "devtest", - "dm_lockdown", - "dm_overwatch", - "dm_powerhouse", - "dm_resistance", - "dm_runoff", - "dm_steamlab", - "dm_underpass", - "halls3", - }; - - char szCurrentMap[MAX_MAP_NAME]; - Q_strncpy( szCurrentMap, STRING( gpGlobals->mapname ), sizeof( szCurrentMap ) ); - - for ( int i = 0; i < ARRAYSIZE( s_OfficialMaps ); ++i ) - { - if ( !Q_stricmp( s_OfficialMaps[i], szCurrentMap ) ) - { - return true; - } - } - - return BaseClass::IsOfficialMap(); -} -#endif - bool CJBModRules::ShouldCollide( int collisionGroup0, int collisionGroup1 ) { if ( collisionGroup0 > collisionGroup1 ) @@ -1075,6 +1111,16 @@ void CJBModRules::RestartGame() gameeventmanager->FireEvent( event ); } + + if ( g_pScriptVM ) + { + HSCRIPT hFunction = g_pScriptVM->LookupFunction( "OnRoundStart" ); + if ( hFunction ) + { + g_pScriptVM->Call( hFunction, NULL, true, NULL ); + g_pScriptVM->ReleaseFunction( hFunction ); + } + } } #ifdef GAME_DLL @@ -1103,7 +1149,7 @@ void CJBModRules::CleanUpMap() } } // remove entities that has to be restored on roundrestart (breakables etc) - else if ( !FindInList( s_PreserveEnts, pCur->GetClassname() ) ) + else if ( !IsPreservedEntity( pCur->GetClassname() ) ) { UTIL_Remove( pCur ); } @@ -1125,7 +1171,7 @@ void CJBModRules::CleanUpMap() virtual bool ShouldCreateEntity( const char *pClassname ) { // Don't recreate the preserved entities. - if ( !FindInList( s_PreserveEnts, pClassname ) ) + if ( !IsPreservedEntity( pClassname ) ) { return true; } @@ -1331,11 +1377,133 @@ static const char *ScriptGetGameMode() return ""; } +static void ScriptGoToIntermission() +{ + if ( JBModRules() ) + { + JBModRules()->GoToIntermission(); + } +} + +static bool ScriptIsGameOver() +{ + return g_fGameOver; +} + +static float ScriptGetMapRemainingTime() +{ + if ( JBModRules() ) + { + return JBModRules()->GetMapRemainingTime(); + } + return -1; +} + +static bool ScriptIsTeamplay() +{ + if ( JBModRules() ) + { + return JBModRules()->IsTeamplay(); + } + return false; +} + +static void ScriptPrecacheModel( const char *szModel ) +{ + if ( szModel && szModel[0] ) + { + CBaseEntity::PrecacheModel( szModel ); + } +} + +static void ScriptPrecacheSound( const char *szSound ) +{ + if ( szSound && szSound[0] ) + { + CBaseEntity::PrecacheScriptSound( szSound ); + } +} + +static HSCRIPT ScriptGetPlayerByIndex( int index ) +{ + CBasePlayer *pPlayer = UTIL_PlayerByIndex( index ); + if ( pPlayer ) + return pPlayer->GetScriptInstance(); + return NULL; +} + +static int ScriptGetMaxPlayers() +{ + return gpGlobals->maxClients; +} + +static int ScriptGetTeamScore( int team ) +{ + if ( team >= 0 && team < g_Teams.Count() ) + return g_Teams[team]->GetScore(); + return 0; +} + +static int ScriptGetTeamPlayerCount( int team ) +{ + if ( team >= 0 && team < g_Teams.Count() ) + return g_Teams[team]->GetNumPlayers(); + return 0; +} + +static const char *ScriptGetMapName() +{ + return STRING( gpGlobals->mapname ); +} + +static const char *ScriptGetTeamName( int team ) +{ + if ( team >= 0 && team < g_Teams.Count() ) + return g_Teams[team]->GetName(); + return ""; +} + +static void ScriptSetTeamName( int team, const char *pszName ) +{ + if ( team >= 0 && team < g_Teams.Count() && pszName ) + { + Q_strncpy( g_Teams[team]->m_szTeamname.GetForModify(), pszName, MAX_TEAM_NAME_LENGTH ); + } +} + +static void ScriptPreserveEntityClass( const char *pszClassname ) +{ + if ( !pszClassname || !pszClassname[0] ) + return; + + for ( int i = 0; i < s_ExtraPreserveEnts.Count(); i++ ) + { + if ( !Q_stricmp( s_ExtraPreserveEnts[i].Get(), pszClassname ) ) + return; + } + + s_ExtraPreserveEnts.AddToTail( CUtlString( pszClassname ) ); +} + void CJBModRules::RegisterScriptFunctions() { ScriptRegisterFunctionNamed( g_pScriptVM, ScriptSetGameDescription, "SetGameDescription", "Set the game description." ); ScriptRegisterFunctionNamed( g_pScriptVM, ScriptGetGameDescription, "GetGameDescription", "Get the current game description." ); ScriptRegisterFunctionNamed( g_pScriptVM, ScriptGetGameMode, "GetGameMode", "Get the current game mode name." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptGoToIntermission, "GoToIntermission", "End the game and go to the scoreboard." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptIsGameOver, "IsGameOver", "Returns true if the game is in intermission." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptGetMapRemainingTime, "GetMapRemainingTime", "Returns seconds remaining on the map timer, or -1 if no limit." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptIsTeamplay, "IsTeamplay", "Returns true if teamplay is enabled." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptPrecacheModel, "PrecacheModel", "Precache a model for use." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptPrecacheSound, "PrecacheSound", "Precache a sound for use." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptGetPlayerByIndex, "GetPlayerByIndex", "Get a player entity by index (1-based)." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptGetMaxPlayers, "GetMaxPlayers", "Get the maximum number of players." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptGetTeamScore, "GetTeamScore", "Get the score for a team by index." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptGetTeamPlayerCount, "GetTeamPlayerCount", "Get the number of players on a team." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptGetMapName, "GetMapName", "Get the current map name." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptGetTeamName, "GetTeamName", "Get the name of a team by index." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptSetTeamName, "SetTeamName", "Set the name of a team by index." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptPreserveEntityClass, "PreserveEntityClass", "Add an entity classname to the round-restart preserve list." ); } #endif diff --git a/src/game/shared/jbmod/jbmod_gamerules.h b/src/game/shared/jbmod/jbmod_gamerules.h index f64f189385c..e38ddbd68a1 100644 --- a/src/game/shared/jbmod/jbmod_gamerules.h +++ b/src/game/shared/jbmod/jbmod_gamerules.h @@ -129,6 +129,7 @@ class CJBModRules : public CTeamplayRules virtual float FlItemRespawnTime( CItem *pItem ); virtual bool CanHavePlayerItem( CBasePlayer *pPlayer, CBaseCombatWeapon *pItem ); virtual bool FShouldSwitchWeapon( CBasePlayer *pPlayer, CBaseCombatWeapon *pWeapon ); + virtual float FlPlayerFallDamage( CBasePlayer *pPlayer ); void AddLevelDesignerPlacedObject( CBaseEntity *pEntity ); void RemoveLevelDesignerPlacedObject( CBaseEntity *pEntity ); @@ -139,8 +140,6 @@ class CJBModRules : public CTeamplayRules virtual void RegisterScriptFunctions(); #endif - bool IsOfficialMap( void ); - virtual void ClientDisconnected( edict_t *pClient ); bool CheckGameOver( void ); diff --git a/src/game/shared/jbmod/jbmod_player_shared.cpp b/src/game/shared/jbmod/jbmod_player_shared.cpp index 11167f61152..6d246783f54 100644 --- a/src/game/shared/jbmod/jbmod_player_shared.cpp +++ b/src/game/shared/jbmod/jbmod_player_shared.cpp @@ -18,37 +18,6 @@ #include "engine/IEngineSound.h" #include "SoundEmitterSystem/isoundemittersystembase.h" -extern ConVar sv_footsteps; - -const char *g_ppszPlayerSoundPrefixNames[PLAYER_SOUNDS_MAX] = -{ - "NPC_Citizen", - "NPC_CombineS", - "NPC_MetroPolice", -}; - -const char *CJBMod_Player::GetPlayerModelSoundPrefix( void ) -{ - return g_ppszPlayerSoundPrefixNames[m_iPlayerSoundType]; -} - -void CJBMod_Player::PrecacheFootStepSounds( void ) -{ - int iFootstepSounds = ARRAYSIZE( g_ppszPlayerSoundPrefixNames ); - int i; - - for ( i = 0; i < iFootstepSounds; ++i ) - { - char szFootStepName[128]; - - Q_snprintf( szFootStepName, sizeof( szFootStepName ), "%s.RunFootstepLeft", g_ppszPlayerSoundPrefixNames[i] ); - PrecacheScriptSound( szFootStepName ); - - Q_snprintf( szFootStepName, sizeof( szFootStepName ), "%s.RunFootstepRight", g_ppszPlayerSoundPrefixNames[i] ); - PrecacheScriptSound( szFootStepName ); - } -} - //----------------------------------------------------------------------------- // Consider the weapon's built-in accuracy, this character's proficiency with // the weapon, and the status of the target. Use this information to determine @@ -62,66 +31,6 @@ Vector CJBMod_Player::GetAttackSpread( CBaseCombatWeapon *pWeapon, CBaseEntity * return VECTOR_CONE_15DEGREES; } -//----------------------------------------------------------------------------- -// Purpose: -// Input : step - -// fvol - -// force - force sound to play -//----------------------------------------------------------------------------- -void CJBMod_Player::PlayStepSound( Vector &vecOrigin, surfacedata_t *psurface, float fvol, bool force ) -{ - if ( gpGlobals->maxClients > 1 && !sv_footsteps.GetFloat() ) - return; - -#if defined( CLIENT_DLL ) - // during prediction play footstep sounds only once - if ( !prediction->IsFirstTimePredicted() ) - return; -#endif - - if ( GetFlags() & FL_DUCKING ) - return; - - m_Local.m_nStepside = !m_Local.m_nStepside; - - char szStepSound[128]; - - if ( m_Local.m_nStepside ) - { - Q_snprintf( szStepSound, sizeof( szStepSound ), "%s.RunFootstepLeft", g_ppszPlayerSoundPrefixNames[m_iPlayerSoundType] ); - } - else - { - Q_snprintf( szStepSound, sizeof( szStepSound ), "%s.RunFootstepRight", g_ppszPlayerSoundPrefixNames[m_iPlayerSoundType] ); - } - - CSoundParameters params; - if ( GetParametersForSound( szStepSound, params, NULL ) == false ) - return; - - CRecipientFilter filter; - filter.AddRecipientsByPAS( vecOrigin ); - -#ifndef CLIENT_DLL - // im MP, server removed all players in origins PVS, these players - // generate the footsteps clientside - if ( gpGlobals->maxClients > 1 ) - filter.RemoveRecipientsByPVS( vecOrigin ); -#endif - - EmitSound_t ep; - ep.m_nChannel = CHAN_BODY; - ep.m_pSoundName = params.soundname; - ep.m_flVolume = fvol; - ep.m_SoundLevel = params.soundlevel; - ep.m_nFlags = 0; - ep.m_nPitch = params.pitch; - ep.m_pOrigin = &vecOrigin; - - EmitSound( filter, entindex(), ep ); -} - - //========================== // ANIMATION CODE //========================== diff --git a/src/game/shared/jbmod/jbmod_player_shared.h b/src/game/shared/jbmod/jbmod_player_shared.h index 0d483b15b5f..026c292f4f6 100644 --- a/src/game/shared/jbmod/jbmod_player_shared.h +++ b/src/game/shared/jbmod/jbmod_player_shared.h @@ -12,15 +12,6 @@ #define JBMOD_PUSHAWAY_THINK_INTERVAL (1.0f / 20.0f) #include "studio.h" - -enum -{ - PLAYER_SOUNDS_CITIZEN = 0, - PLAYER_SOUNDS_COMBINESOLDIER, - PLAYER_SOUNDS_METROPOLICE, - PLAYER_SOUNDS_MAX, -}; - enum JBModPlayerState { // Happily running around in the game.