├── .addon ├── README.md ├── .gitattributes ├── ui ├── hit.png ├── smls.grain.png ├── radial_gradient.png └── weapons │ ├── dm_smg.png │ ├── dm_pistol.png │ ├── dm_shotgun.png │ └── dm_crossbow.png ├── models ├── smls.player.vmdl_c ├── citizen_2021-05-20.fbx └── smls.player.vmdl ├── sounds ├── dm.ui_tap.sound_c ├── dm.ui_select.sound_c ├── dm.item_respawn.sound_c ├── dm.pickup_ammo.sound_c ├── dm.ui_attacker.sound_c ├── smls.sniper.charge.wav ├── dm.pickup_weapon.sound_c ├── smls.sniper.charge.sound_c ├── smls.sniper.charge.vsnd_c ├── smls.sniper.charge.sound ├── dm.item_respawn.sound ├── dm.pickup_ammo.sound ├── dm.pickup_weapon.sound ├── dm.ui_attacker.sound ├── dm.ui_tap.sound └── dm.ui_select.sound ├── materials ├── smls.player.vmat_c ├── default │ ├── default_ao_tga_559f1ac6.vtex_c │ ├── default_mask_tga_344101f8.vtex_c │ └── default_normal_tga_7be61377.vtex_c ├── smls.player_vmat_g_tcolor_353ec76e.vtex_c ├── smls.player_vmat_g_tcolor_a00ef19e.vtex_c ├── smls.player_vmat_g_tcolor_d2177761.vtex_c ├── smls.player_vmat_g_tnormal_53b47c48.vtex_c ├── smls.player_vmat_g_tselfillummask_ff155607.vtex_c └── smls.player.vmat ├── CREDITS.md ├── .gitignore ├── code ├── code │ └── Properties │ │ └── launchSettings.json ├── ui │ ├── Vitals.cs │ ├── KillFeedEntry.cs │ ├── DamageIndicator.scss │ ├── KillFeed.cs │ ├── Ammo.cs │ ├── InventoryIcon.cs │ ├── HitIndicator.scss │ ├── Crosshair.cs │ ├── HitIndicator.cs │ ├── PickupFeed.scss │ ├── Scoreboard.cs │ ├── PickupFeed.cs │ ├── InventoryColumn.cs │ ├── SMLSHud.cs │ ├── SMLSHud.scss │ ├── DamageIndicator.cs │ ├── KillFeed.scss │ ├── Scoreboard.scss │ ├── InventoryBar.scss │ ├── StartScreen.cs │ ├── StartScreen.scss │ ├── Crosshair.scss │ └── InventoryBar.cs ├── TPoseAnimator.cs ├── Settings.cs ├── Camera.cs ├── ViewModel.cs ├── weapons │ ├── Pistol.cs │ ├── SMG.cs │ ├── Crossbow.cs │ ├── CrossbowBolt.cs │ ├── Shotgun.cs │ ├── SniperRifle.cs │ └── DmWeapon.cs ├── util │ └── PanelExtension.cs ├── Inventory.cs ├── Etc.cs ├── Player.Ragdoll.cs ├── Player.Ammo.cs ├── ItemRespawn.cs ├── Game.cs ├── Player.cs └── Controller.cs ├── LICENSE └── config └── smls.fgd /.addon: -------------------------------------------------------------------------------- 1 | { 2 | "sharedassets": "*.*", 3 | "ident": "tesa.smls" 4 | } 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SMLS 2 | Fork of DM98 3 | 4 | For credits see [CREDITS.md](CREDITS.md) -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /ui/hit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rndtrash/shooter-more-like-shitter/master/ui/hit.png -------------------------------------------------------------------------------- /ui/smls.grain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rndtrash/shooter-more-like-shitter/master/ui/smls.grain.png -------------------------------------------------------------------------------- /ui/radial_gradient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rndtrash/shooter-more-like-shitter/master/ui/radial_gradient.png -------------------------------------------------------------------------------- /ui/weapons/dm_smg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rndtrash/shooter-more-like-shitter/master/ui/weapons/dm_smg.png -------------------------------------------------------------------------------- /models/smls.player.vmdl_c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rndtrash/shooter-more-like-shitter/master/models/smls.player.vmdl_c -------------------------------------------------------------------------------- /sounds/dm.ui_tap.sound_c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rndtrash/shooter-more-like-shitter/master/sounds/dm.ui_tap.sound_c -------------------------------------------------------------------------------- /ui/weapons/dm_pistol.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rndtrash/shooter-more-like-shitter/master/ui/weapons/dm_pistol.png -------------------------------------------------------------------------------- /ui/weapons/dm_shotgun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rndtrash/shooter-more-like-shitter/master/ui/weapons/dm_shotgun.png -------------------------------------------------------------------------------- /materials/smls.player.vmat_c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rndtrash/shooter-more-like-shitter/master/materials/smls.player.vmat_c -------------------------------------------------------------------------------- /sounds/dm.ui_select.sound_c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rndtrash/shooter-more-like-shitter/master/sounds/dm.ui_select.sound_c -------------------------------------------------------------------------------- /ui/weapons/dm_crossbow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rndtrash/shooter-more-like-shitter/master/ui/weapons/dm_crossbow.png -------------------------------------------------------------------------------- /models/citizen_2021-05-20.fbx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rndtrash/shooter-more-like-shitter/master/models/citizen_2021-05-20.fbx -------------------------------------------------------------------------------- /sounds/dm.item_respawn.sound_c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rndtrash/shooter-more-like-shitter/master/sounds/dm.item_respawn.sound_c -------------------------------------------------------------------------------- /sounds/dm.pickup_ammo.sound_c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rndtrash/shooter-more-like-shitter/master/sounds/dm.pickup_ammo.sound_c -------------------------------------------------------------------------------- /sounds/dm.ui_attacker.sound_c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rndtrash/shooter-more-like-shitter/master/sounds/dm.ui_attacker.sound_c -------------------------------------------------------------------------------- /sounds/smls.sniper.charge.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rndtrash/shooter-more-like-shitter/master/sounds/smls.sniper.charge.wav -------------------------------------------------------------------------------- /sounds/dm.pickup_weapon.sound_c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rndtrash/shooter-more-like-shitter/master/sounds/dm.pickup_weapon.sound_c -------------------------------------------------------------------------------- /sounds/smls.sniper.charge.sound_c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rndtrash/shooter-more-like-shitter/master/sounds/smls.sniper.charge.sound_c -------------------------------------------------------------------------------- /sounds/smls.sniper.charge.vsnd_c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rndtrash/shooter-more-like-shitter/master/sounds/smls.sniper.charge.vsnd_c -------------------------------------------------------------------------------- /CREDITS.md: -------------------------------------------------------------------------------- 1 | # Credits 2 | 3 | Based on https://github.com/Facepunch/dm98 4 | 5 | ## sounds 6 | `smls.sniper.charge.wav` - Windows XP Pinball -------------------------------------------------------------------------------- /materials/default/default_ao_tga_559f1ac6.vtex_c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rndtrash/shooter-more-like-shitter/master/materials/default/default_ao_tga_559f1ac6.vtex_c -------------------------------------------------------------------------------- /materials/default/default_mask_tga_344101f8.vtex_c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rndtrash/shooter-more-like-shitter/master/materials/default/default_mask_tga_344101f8.vtex_c -------------------------------------------------------------------------------- /materials/default/default_normal_tga_7be61377.vtex_c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rndtrash/shooter-more-like-shitter/master/materials/default/default_normal_tga_7be61377.vtex_c -------------------------------------------------------------------------------- /materials/smls.player_vmat_g_tcolor_353ec76e.vtex_c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rndtrash/shooter-more-like-shitter/master/materials/smls.player_vmat_g_tcolor_353ec76e.vtex_c -------------------------------------------------------------------------------- /materials/smls.player_vmat_g_tcolor_a00ef19e.vtex_c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rndtrash/shooter-more-like-shitter/master/materials/smls.player_vmat_g_tcolor_a00ef19e.vtex_c -------------------------------------------------------------------------------- /materials/smls.player_vmat_g_tcolor_d2177761.vtex_c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rndtrash/shooter-more-like-shitter/master/materials/smls.player_vmat_g_tcolor_d2177761.vtex_c -------------------------------------------------------------------------------- /materials/smls.player_vmat_g_tnormal_53b47c48.vtex_c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rndtrash/shooter-more-like-shitter/master/materials/smls.player_vmat_g_tnormal_53b47c48.vtex_c -------------------------------------------------------------------------------- /materials/smls.player_vmat_g_tselfillummask_ff155607.vtex_c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rndtrash/shooter-more-like-shitter/master/materials/smls.player_vmat_g_tselfillummask_ff155607.vtex_c -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .codegen/ 3 | obj/ 4 | code/accesslist.txt 5 | code/*.csproj 6 | code/code/ 7 | .intermediate 8 | tools_asset_info.bin 9 | code/Properties/launchSettings.json 10 | Properties/launchSettings.json 11 | -------------------------------------------------------------------------------- /materials/smls.player.vmat: -------------------------------------------------------------------------------- 1 | // THIS FILE IS AUTO-GENERATED 2 | 3 | Layer0 4 | { 5 | shader "solidcolor.vfx" 6 | 7 | //---- Animation ---- 8 | F_MORPH_SUPPORTED 1 9 | 10 | //---- Color ---- 11 | g_vColorTint "[1.000000 1.000000 1.000000 1.000000]" 12 | } -------------------------------------------------------------------------------- /sounds/smls.sniper.charge.sound: -------------------------------------------------------------------------------- 1 | 2 | { 3 | data = 4 | { 5 | sounds = 6 | [ 7 | "sounds/smls.sniper.charge.vsnd", 8 | ] 9 | } 10 | } -------------------------------------------------------------------------------- /code/code/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "smls": { 4 | "commandName": "Executable", 5 | "executablePath": "C:\\Program Files (x86)\\Steam\\steamapps\\common\\sbox\\sbox.exe", 6 | "commandLineArgs": "\u002Bdeveloper \u002Bsw -noassert -tools" 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /sounds/dm.item_respawn.sound: -------------------------------------------------------------------------------- 1 | 2 | { 3 | data = 4 | { 5 | volume = 2.0 6 | sounds = 7 | [ 8 | "sounds/electrical/powerup.vsnd", 9 | ] 10 | VolumeRandom = 0.0 11 | Pitch = 0.9 12 | PitchRandom = 0.0 13 | DistanceMax = 1000.0 14 | } 15 | } -------------------------------------------------------------------------------- /sounds/dm.pickup_ammo.sound: -------------------------------------------------------------------------------- 1 | 2 | { 3 | data = 4 | { 5 | volume = 1.0 6 | sounds = 7 | [ 8 | "sounds/pickup/ui-pickup-gun-3.vsnd", 9 | ] 10 | VolumeRandom = 0.0 11 | Pitch = 1.0 12 | PitchRandom = 0.0 13 | DistanceMax = 500.0 14 | } 15 | } -------------------------------------------------------------------------------- /sounds/dm.pickup_weapon.sound: -------------------------------------------------------------------------------- 1 | 2 | { 3 | data = 4 | { 5 | volume = 1.0 6 | sounds = 7 | [ 8 | "sounds/pickup/ui-pickup-gun-1.vsnd", 9 | ] 10 | VolumeRandom = 0.0 11 | Pitch = 1.0 12 | PitchRandom = 0.0 13 | DistanceMax = 1000.0 14 | } 15 | } -------------------------------------------------------------------------------- /sounds/dm.ui_attacker.sound: -------------------------------------------------------------------------------- 1 | 2 | { 3 | data = 4 | { 5 | volume = 1.0 6 | sounds = 7 | [ 8 | "sounds/impact/flesh-3.vsnd", 9 | ] 10 | VolumeRandom = 0.0 11 | Pitch = 1.0 12 | PitchRandom = 0.0 13 | DistanceMax = 1000.0 14 | UI = true 15 | } 16 | } -------------------------------------------------------------------------------- /sounds/dm.ui_tap.sound: -------------------------------------------------------------------------------- 1 | 2 | { 3 | data = 4 | { 5 | volume = 0.1 6 | sounds = 7 | [ 8 | "sounds/electrical/computer_off.vsnd", 9 | ] 10 | VolumeRandom = 0.0 11 | Pitch = 2.0 12 | PitchRandom = 0.0 13 | DistanceMax = 1000.0 14 | UI = true 15 | } 16 | } -------------------------------------------------------------------------------- /sounds/dm.ui_select.sound: -------------------------------------------------------------------------------- 1 | 2 | { 3 | data = 4 | { 5 | volume = 0.3 6 | sounds = 7 | [ 8 | "sounds/electrical/computer_off.vsnd", 9 | ] 10 | VolumeRandom = 0.0 11 | Pitch = 1.7 12 | PitchRandom = 0.0 13 | DistanceMax = 1000.0 14 | UI = true 15 | } 16 | } -------------------------------------------------------------------------------- /code/ui/Vitals.cs: -------------------------------------------------------------------------------- 1 | 2 | using Sandbox; 3 | using Sandbox.UI; 4 | using Sandbox.UI.Construct; 5 | 6 | public class Vitals : Panel 7 | { 8 | public Label Health; 9 | 10 | public Vitals() 11 | { 12 | Health = Add.Label( "100", "health" ); 13 | } 14 | 15 | public override void Tick() 16 | { 17 | var player = Local.Pawn; 18 | if ( player == null ) return; 19 | 20 | Health.Text = $"{player.Health.CeilToInt()}"; 21 | Health.SetClass( "danger", player.Health < 40.0f ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /code/TPoseAnimator.cs: -------------------------------------------------------------------------------- 1 | using Sandbox; 2 | 3 | class TPoseAnimator : PawnAnimator 4 | { 5 | public override void Simulate() 6 | { 7 | Rotation = Rotation.LookAt( Input.Rotation.Forward.WithZ( 0 ), Vector3.Up ); 8 | 9 | // 10 | // Look in the direction what the player's input is facing 11 | // 12 | 13 | SetLookAt( "aim_eyes", Pawn.EyePos + Input.Rotation.Forward * 200 ); 14 | 15 | if ( Pawn.ActiveChild is BaseCarriable carry ) 16 | { 17 | carry.SimulateAnimator( this ); 18 | } 19 | } 20 | 21 | public override void OnEvent( string name ) 22 | { 23 | base.OnEvent( name ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /code/ui/KillFeedEntry.cs: -------------------------------------------------------------------------------- 1 | 2 | using Sandbox; 3 | using Sandbox.UI; 4 | using Sandbox.UI.Construct; 5 | using System.Threading.Tasks; 6 | 7 | public partial class KillFeedEntry : Panel 8 | { 9 | public Label Left { get; internal set; } 10 | public Label Right { get; internal set; } 11 | public Panel Icon { get; internal set; } 12 | 13 | public KillFeedEntry() 14 | { 15 | Left = Add.Label( "", "left" ); 16 | Icon = Add.Panel( "icon" ); 17 | Right = Add.Label( "", "right" ); 18 | 19 | _ = RunAsync(); 20 | } 21 | 22 | async Task RunAsync() 23 | { 24 | await Task.Delay( 4000 ); 25 | Delete(); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /code/ui/DamageIndicator.scss: -------------------------------------------------------------------------------- 1 | damageindicator { 2 | mix-blend-mode: lighten; 3 | position: absolute; 4 | left: 0; 5 | right: 0; 6 | top: 0; 7 | bottom: 0; 8 | position: absolute; 9 | 10 | hitpoint { 11 | position: absolute; 12 | left: 50%; 13 | top: 50%; 14 | width: 600px; 15 | height: 600px; 16 | border-radius: 1000px; 17 | border: 20px solid transparent; 18 | border-bottom: 20px solid #f00; 19 | transition: opacity 0.5s ease-out; 20 | opacity: 0.8; 21 | transform-origin: 0% 0%; 22 | 23 | label { 24 | text-shadow: 0px 0px 10px #fa0; 25 | } 26 | 27 | &.dying { 28 | opacity: 0; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /code/ui/KillFeed.cs: -------------------------------------------------------------------------------- 1 | 2 | using Sandbox; 3 | using Sandbox.UI; 4 | 5 | public partial class KillFeed : Sandbox.UI.KillFeed 6 | { 7 | public KillFeed() 8 | { 9 | StyleSheet.Load( "/ui/KillFeed.scss" ); 10 | } 11 | 12 | public override Panel AddEntry( ulong lsteamid, string left, ulong rsteamid, string right, string method ) 13 | { 14 | Log.Info( $"{left} killed {right} using {method}" ); 15 | 16 | var e = Current.AddChild(); 17 | 18 | e.AddClass( method ); 19 | 20 | e.Left.Text = left; 21 | e.Left.SetClass( "me", lsteamid == (Local.SteamId) ); 22 | 23 | e.Right.Text = right; 24 | e.Right.SetClass( "me", rsteamid == (Local.SteamId) ); 25 | 26 | return e; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /code/ui/Ammo.cs: -------------------------------------------------------------------------------- 1 | 2 | using Sandbox; 3 | using Sandbox.UI; 4 | using Sandbox.UI.Construct; 5 | 6 | public class Ammo : Panel 7 | { 8 | public Label Weapon; 9 | public Label Inventory; 10 | 11 | public Ammo() 12 | { 13 | Weapon = Add.Label( "100", "weapon" ); 14 | Inventory = Add.Label( "100", "inventory" ); 15 | } 16 | 17 | public override void Tick() 18 | { 19 | var player = Local.Pawn as FPSPlayer; 20 | if ( player == null ) return; 21 | 22 | var weapon = player.ActiveChild as BaseDmWeapon; 23 | SetClass( "active", weapon != null ); 24 | 25 | if ( weapon == null ) return; 26 | 27 | Weapon.Text = $"{weapon.AvailableAmmo()}"; 28 | 29 | Inventory.Text = $" / {player.AmmoLimits[weapon.AmmoType]}"; 30 | Inventory.SetClass( "active", weapon.IsUsable() ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /code/ui/InventoryIcon.cs: -------------------------------------------------------------------------------- 1 | 2 | using Sandbox; 3 | using Sandbox.UI; 4 | using Sandbox.UI.Construct; 5 | using System; 6 | 7 | class InventoryIcon : Panel 8 | { 9 | public BaseDmWeapon Weapon; 10 | public Panel Icon; 11 | 12 | public InventoryIcon( BaseDmWeapon weapon ) 13 | { 14 | Weapon = weapon; 15 | Icon = Add.Panel( "icon" ); 16 | AddClass( weapon.ClassInfo.Name ); 17 | } 18 | 19 | internal void TickSelection( BaseDmWeapon selectedWeapon ) 20 | { 21 | SetClass( "active", selectedWeapon == Weapon ); 22 | SetClass( "empty", !Weapon?.IsUsable() ?? true ); 23 | } 24 | 25 | public override void Tick() 26 | { 27 | base.Tick(); 28 | 29 | if ( !Weapon.IsValid() || Weapon.Owner != Local.Pawn ) 30 | Delete(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /code/ui/HitIndicator.scss: -------------------------------------------------------------------------------- 1 | hitindicator { 2 | mix-blend-mode: lighten; 3 | position: absolute; 4 | left: 0; 5 | right: 0; 6 | top: 0; 7 | bottom: 0; 8 | position: absolute; 9 | align-items: center; 10 | justify-content: center; 11 | 12 | 13 | hitpoint { 14 | width: 60px; 15 | height: 60px; 16 | background-color: red; 17 | opacity: 0.7; 18 | position: absolute; 19 | transition: all 0.1s ease-out; 20 | background-image: url( /ui/hit.png ); 21 | background-image-tint: #fc0; 22 | background-size: 100%; 23 | 24 | label { 25 | } 26 | 27 | &:intro { 28 | opacity: 0; 29 | } 30 | 31 | &:outro { 32 | transition: all 0.1s ease-in; 33 | opacity: 0; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /code/Settings.cs: -------------------------------------------------------------------------------- 1 | using Sandbox; 2 | 3 | public class Settings 4 | { 5 | public static Settings Instance 6 | { 7 | get 8 | { 9 | Host.AssertClient(); 10 | if ( instance == null ) 11 | instance = new Settings(); 12 | return instance; 13 | } 14 | } 15 | 16 | public float AllyColor = 240.0f; 17 | public float EnemyColor = 0.0f; 18 | 19 | private static Settings instance; 20 | 21 | public Color GetPlayerColor( SMLSBasePlayer player ) 22 | { 23 | if ( player.Team == SMLSBasePlayer.PlayerTeam.Spectator ) 24 | return Color.White; 25 | var p = Local.Pawn as SMLSBasePlayer; 26 | 27 | if ( player.Team != SMLSBasePlayer.PlayerTeam.FFA ) 28 | return new Etc.HSV( player.Team == p.Team ? AllyColor : EnemyColor, 1.0f, 1.0f ).ToColor(); 29 | else 30 | return new Etc.HSV( player == p ? AllyColor : EnemyColor, 1.0f, 1.0f ).ToColor(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /code/Camera.cs: -------------------------------------------------------------------------------- 1 | using Sandbox; 2 | 3 | public class InGameCamera : Camera 4 | { 5 | Vector3 lastPos; 6 | 7 | public override void Activated() 8 | { 9 | var pawn = Local.Pawn; 10 | if ( pawn == null ) return; 11 | 12 | Pos = pawn.EyePos; 13 | Rot = pawn.EyeRot; 14 | 15 | lastPos = Pos; 16 | } 17 | 18 | public override void Update() 19 | { 20 | var pawn = Local.Pawn; 21 | if ( pawn == null ) return; 22 | 23 | var eyePos = pawn.EyePos; 24 | if ( eyePos.Distance( lastPos ) < 300 ) // TODO: Tweak this, or add a way to invalidate lastpos when teleporting 25 | { 26 | Pos = Vector3.Lerp( eyePos.WithZ( lastPos.z ), eyePos, 20.0f * Time.Delta ); 27 | } 28 | else 29 | { 30 | Pos = eyePos; 31 | } 32 | 33 | Rot = pawn.EyeRot; 34 | 35 | Viewer = pawn; 36 | lastPos = Pos; 37 | FieldOfView = 120; // FIXME: screen goes black when FOV is set from variable... the fuck? 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /code/ui/Crosshair.cs: -------------------------------------------------------------------------------- 1 | 2 | using Sandbox; 3 | using Sandbox.UI; 4 | using Sandbox.UI.Construct; 5 | using System; 6 | 7 | public class Crosshair : Panel 8 | { 9 | int fireCounter; 10 | 11 | public Crosshair() 12 | { 13 | StyleSheet.Load( "/ui/Crosshair.scss" ); 14 | 15 | for ( int i = 0; i < 5; i++ ) 16 | { 17 | var p = Add.Panel( "element" ); 18 | p.AddClass( $"el{i}" ); 19 | } 20 | 21 | Style.Left = Length.Fraction( .50f ); 22 | Style.Top = Length.Fraction( .50f ); 23 | Style.Dirty(); 24 | } 25 | 26 | public override void Tick() 27 | { 28 | base.Tick(); 29 | this.PositionAtCrosshair(); 30 | 31 | SetClass( "fire", fireCounter > 0 ); 32 | 33 | if ( fireCounter > 0 ) 34 | fireCounter--; 35 | } 36 | 37 | [PanelEvent] 38 | public void FireEvent() 39 | { 40 | fireCounter += 2; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /code/ui/HitIndicator.cs: -------------------------------------------------------------------------------- 1 | using Sandbox; 2 | using Sandbox.UI; 3 | using Sandbox.UI.Construct; 4 | using System; 5 | using System.Threading.Tasks; 6 | 7 | public partial class HitIndicator : Panel 8 | { 9 | public static HitIndicator Current; 10 | 11 | public HitIndicator() 12 | { 13 | Current = this; 14 | StyleSheet.Load( "/ui/HitIndicator.scss" ); 15 | } 16 | 17 | public override void Tick() 18 | { 19 | base.Tick(); 20 | } 21 | 22 | public void OnHit( Vector3 pos, float amount ) 23 | { 24 | new HitPoint( amount, pos, this ); 25 | } 26 | 27 | public class HitPoint : Panel 28 | { 29 | public HitPoint( float amount, Vector3 pos, Panel parent ) 30 | { 31 | Parent = parent; 32 | _ = Lifetime(); 33 | } 34 | 35 | async Task Lifetime() 36 | { 37 | await Task.Delay( 200 ); 38 | Delete(); 39 | } 40 | } 41 | } 42 | 43 | 44 | -------------------------------------------------------------------------------- /code/ui/PickupFeed.scss: -------------------------------------------------------------------------------- 1 | 2 | pickupfeed { 3 | position: absolute; 4 | right: 100px; 5 | bottom: 250px; 6 | font-size: 40px; 7 | flex-direction: column; 8 | text-align: right; 9 | 10 | label { 11 | color: #ff0; 12 | mix-blend-mode: lighten; 13 | text-shadow: 0px 0px 20px #f70d; 14 | position: relative; 15 | left: 0; 16 | transition: all 0.2s ease-out; 17 | height: 50px; 18 | opacity: 1; 19 | // 20 | // Pseudo class set for the first time of its life 21 | // 22 | &:intro { 23 | height: 0px; 24 | opacity: 0; 25 | } 26 | // 27 | // Pseudo class set for the last frame of its life 28 | // (and keeps the panel alive until transition ends) 29 | // 30 | &:outro { 31 | color: #fa00; 32 | text-shadow: 0px 0px 20px #f703; 33 | transition: all 7.0s ease-in; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /code/ui/Scoreboard.cs: -------------------------------------------------------------------------------- 1 | 2 | using Sandbox; 3 | using Sandbox.UI; 4 | using Sandbox.UI.Construct; 5 | 6 | public class Scoreboard : Sandbox.UI.Scoreboard 7 | { 8 | 9 | public Scoreboard() 10 | { 11 | StyleSheet.Load( "/ui/Scoreboard.scss" ); 12 | } 13 | 14 | protected override void AddHeader() 15 | { 16 | Header = Add.Panel( "header" ); 17 | Header.Add.Label( "player", "name" ); 18 | Header.Add.Label( "kills", "kills" ); 19 | Header.Add.Label( "deaths", "deaths" ); 20 | Header.Add.Label( "ping", "ping" ); 21 | Header.Add.Label( "fps", "fps" ); 22 | } 23 | } 24 | 25 | public class ScoreboardEntry : Sandbox.UI.ScoreboardEntry 26 | { 27 | public Label Fps; 28 | 29 | public ScoreboardEntry() 30 | { 31 | Fps = Add.Label( "", "fps" ); 32 | } 33 | 34 | public override void UpdateFrom( PlayerScore.Entry entry ) 35 | { 36 | base.UpdateFrom( entry ); 37 | 38 | Fps.Text = entry.Get( "fps", 0 ).ToString(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /code/ui/PickupFeed.cs: -------------------------------------------------------------------------------- 1 | 2 | using Sandbox; 3 | using Sandbox.UI; 4 | using Sandbox.UI.Construct; 5 | using System; 6 | using System.Threading.Tasks; 7 | 8 | public partial class PickupFeed : Panel 9 | { 10 | public static PickupFeed Current; 11 | 12 | public PickupFeed() 13 | { 14 | Current = this; 15 | StyleSheet.Load( "/ui/PickupFeed.scss" ); 16 | } 17 | 18 | /// 19 | /// An RPC which can be called from the server 20 | /// 21 | [ClientRpc] 22 | public static void OnPickup( string text ) 23 | { 24 | // TODO - icons for weapons? 25 | // TOPO - icons for ammo? 26 | 27 | Current?.AddEntry( $"\n{text}" ); 28 | } 29 | 30 | /// 31 | /// Spawns a label, waits for half a second and then deletes it 32 | /// The :outro style in the scss keeps it alive and fades it out 33 | /// 34 | private async Task AddEntry( string text ) 35 | { 36 | var panel = Current.Add.Label( text ); 37 | await Task.Delay( 500 ); 38 | panel.Delete(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /code/ViewModel.cs: -------------------------------------------------------------------------------- 1 | using Sandbox; 2 | using System; 3 | using System.Linq; 4 | 5 | partial class DmViewModel : BaseViewModel 6 | { 7 | float walkBob = 0; 8 | 9 | public override void PostCameraSetup( ref CameraSetup camSetup ) 10 | { 11 | base.PostCameraSetup( ref camSetup ); 12 | 13 | // camSetup.ViewModelFieldOfView = camSetup.FieldOfView + (FieldOfView - 80); 14 | 15 | AddCameraEffects( ref camSetup ); 16 | } 17 | 18 | private void AddCameraEffects( ref CameraSetup camSetup ) 19 | { 20 | Rotation = Local.Pawn.EyeRot; 21 | 22 | // 23 | // Bob up and down based on our walk movement 24 | // 25 | var speed = Owner.Velocity.Length.LerpInverse( 0, 320 ); 26 | var left = camSetup.Rotation.Left; 27 | var up = camSetup.Rotation.Up; 28 | 29 | if ( Owner.GroundEntity != null ) 30 | { 31 | walkBob += Time.Delta * 25.0f * speed; 32 | } 33 | 34 | Position += up * MathF.Sin( walkBob ) * speed * -1; 35 | Position += left * MathF.Sin( walkBob * 0.6f ) * speed * -0.5f; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Ivan Kuzmenko 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /code/weapons/Pistol.cs: -------------------------------------------------------------------------------- 1 | using Sandbox; 2 | 3 | 4 | [Library( "dm_pistol", Title = "Pistol" )] 5 | [Hammer.EditorModel( "weapons/rust_pistol/rust_pistol.vmdl" )] 6 | partial class Pistol : BaseDmWeapon 7 | { 8 | public override string ViewModelPath => "weapons/rust_pistol/v_rust_pistol.vmdl"; 9 | 10 | public override float PrimaryRate => 15.0f; 11 | public override float SecondaryRate => 1.0f; 12 | 13 | public override int Bucket => 1; 14 | 15 | public override void Spawn() 16 | { 17 | base.Spawn(); 18 | 19 | SetModel( "weapons/rust_pistol/rust_pistol.vmdl" ); 20 | } 21 | 22 | public override bool CanPrimaryAttack() 23 | { 24 | return base.CanPrimaryAttack() && Input.Pressed( InputButton.Attack1 ); 25 | } 26 | 27 | public override void AttackPrimary() 28 | { 29 | TimeSincePrimaryAttack = 0; 30 | TimeSinceSecondaryAttack = 0; 31 | 32 | if ( !TakeAmmo( 1 ) ) 33 | { 34 | DryFire(); 35 | return; 36 | } 37 | 38 | 39 | // 40 | // Tell the clients to play the shoot effects 41 | // 42 | ShootEffects(); 43 | PlaySound( "rust_pistol.shoot" ); 44 | 45 | // 46 | // Shoot the bullets 47 | // 48 | ShootBullet( 0.05f, 1.5f, 9.0f, 3.0f ); 49 | 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /code/ui/InventoryColumn.cs: -------------------------------------------------------------------------------- 1 | 2 | using Sandbox; 3 | using Sandbox.UI; 4 | using Sandbox.UI.Construct; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | 9 | public class InventoryColumn : Panel 10 | { 11 | public int Column; 12 | public bool IsSelected; 13 | public Label Header; 14 | public int SelectedIndex; 15 | 16 | internal List Icons = new(); 17 | 18 | public InventoryColumn( int i, Panel parent ) 19 | { 20 | Parent = parent; 21 | Column = i; 22 | Header = Add.Label( $"{i + 1}", "slot-number" ); 23 | } 24 | 25 | internal void UpdateWeapon( BaseDmWeapon weapon ) 26 | { 27 | var icon = ChildrenOfType().FirstOrDefault( x => x.Weapon == weapon ); 28 | if ( icon == null ) 29 | { 30 | icon = new InventoryIcon( weapon ); 31 | icon.Parent = this; 32 | Icons.Add( icon ); 33 | } 34 | } 35 | 36 | internal void TickSelection( BaseDmWeapon selectedWeapon ) 37 | { 38 | SetClass( "active", selectedWeapon?.Bucket == Column ); 39 | 40 | for ( int i = 0; i < Icons.Count; i++ ) 41 | { 42 | Icons[i].TickSelection( selectedWeapon ); 43 | } 44 | 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /code/util/PanelExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace Sandbox.UI 5 | { 6 | public static class PanelExtension 7 | { 8 | public static void PositionAtCrosshair( this Panel panel ) 9 | { 10 | panel.PositionAtCrosshair( Local.Pawn ); 11 | } 12 | 13 | public static void PositionAtCrosshair( this Panel panel, Entity player ) 14 | { 15 | if ( !player.IsValid() ) return; 16 | 17 | var eyePos = player.EyePos; 18 | var eyeRot = player.EyeRot; 19 | 20 | var tr = Trace.Ray( eyePos, eyePos + eyeRot.Forward * 2000 ) 21 | .Size( 1.0f ) 22 | .Ignore( player ) 23 | .UseHitboxes() 24 | .Run(); 25 | 26 | panel.PositionAtWorld( tr.EndPos ); 27 | 28 | } 29 | 30 | public static void PositionAtWorld( this Panel panel, Vector3 pos ) 31 | { 32 | var screenpos = pos.ToScreen(); 33 | 34 | if ( screenpos.z < 0 ) 35 | return; 36 | 37 | panel.Style.Left = Length.Fraction( screenpos.x ); 38 | panel.Style.Top = Length.Fraction( screenpos.y ); 39 | panel.Style.Dirty(); 40 | } 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /code/Inventory.cs: -------------------------------------------------------------------------------- 1 | using Sandbox; 2 | using System; 3 | using System.Linq; 4 | 5 | partial class DmInventory : BaseInventory 6 | { 7 | 8 | 9 | public DmInventory( Player player ) : base( player ) 10 | { 11 | 12 | } 13 | 14 | public override bool Add( Entity ent, bool makeActive = false ) 15 | { 16 | var player = Owner as FPSPlayer; 17 | var weapon = ent as BaseDmWeapon; 18 | var notices = !player.SupressPickupNotices; 19 | // TODO: player should drop only ammo, not weapons 20 | if ( weapon != null && IsCarryingType( ent.GetType() ) ) 21 | { 22 | /*var ammo = weapon.Ammo; 23 | var ammoType = weapon.AmmoType; 24 | 25 | if ( ammo > 0 ) 26 | { 27 | player.GiveAmmo( ammoType, ammo ); 28 | 29 | if ( notices ) 30 | { 31 | Sound.FromWorld( "dm.pickup_ammo", ent.Position ); 32 | PickupFeed.OnPickup( To.Single( player ), $"+{ammo} {ammoType}" ); 33 | } 34 | } 35 | 36 | ItemRespawn.Taken( ent ); 37 | 38 | // Despawn it 39 | ent.Delete();*/ 40 | return false; 41 | } 42 | 43 | if ( weapon != null && notices ) 44 | { 45 | Sound.FromWorld( "dm.pickup_weapon", ent.Position ); 46 | PickupFeed.OnPickup( To.Single( player ), $"{ent.ClassInfo.Title}" ); 47 | } 48 | 49 | ItemRespawn.Taken( ent ); 50 | return base.Add( ent, makeActive ); 51 | } 52 | 53 | public bool IsCarryingType( Type t ) 54 | { 55 | return List.Any( x => x.GetType() == t ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /code/ui/SMLSHud.cs: -------------------------------------------------------------------------------- 1 | 2 | using Sandbox; 3 | using Sandbox.UI; 4 | using Sandbox.UI.Construct; 5 | using System; 6 | using System.Threading.Tasks; 7 | 8 | [Library] 9 | public partial class SMLSHud : HudEntity 10 | { 11 | public static SMLSHud Instance; 12 | 13 | public SMLSHud() 14 | { 15 | if ( !IsClient ) 16 | return; 17 | 18 | Instance = this; 19 | 20 | RootPanel.StyleSheet.Load( "/ui/SMLSHud.scss" ); 21 | 22 | RootPanel.AddChild(); 23 | RootPanel.AddChild(); 24 | 25 | RootPanel.AddChild(); 26 | RootPanel.AddChild(); 27 | RootPanel.AddChild(); 28 | 29 | RootPanel.AddChild(); 30 | RootPanel.AddChild(); 31 | 32 | RootPanel.AddChild(); 33 | RootPanel.AddChild(); 34 | RootPanel.AddChild(); 35 | RootPanel.AddChild(); 36 | 37 | RootPanel.AddChild(); 38 | } 39 | 40 | private void SwitchPanelsToState( SMLSGame.State gameState ) 41 | { 42 | // 43 | } 44 | 45 | [ClientRpc] 46 | public void OnPlayerDied( string victim, string attacker = null ) 47 | { 48 | Host.AssertClient(); 49 | } 50 | 51 | [ClientRpc] 52 | public void ShowDeathScreen( string attackerName ) 53 | { 54 | Host.AssertClient(); 55 | } 56 | 57 | 58 | [ClientRpc] 59 | public static void OnGameStateChange( SMLSGame.State gameState ) 60 | { 61 | Instance.SwitchPanelsToState( gameState ); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /code/ui/SMLSHud.scss: -------------------------------------------------------------------------------- 1 | 2 | rootpanel { 3 | background-color: rgba( #333, 0 ); 4 | transition: background-color 0.2s ease-in; 5 | transform-origin: 50% 50%; 6 | 7 | &.spawnmenuopen { 8 | transition: background-color 0.3s ease-out; 9 | background-color: rgba( #333, 0.9 ); 10 | } 11 | 12 | &.devcamera { 13 | display: none; 14 | } 15 | } 16 | 17 | vitals { 18 | position: absolute; 19 | bottom: 64px; 20 | left: 100px; 21 | font-size: 64px; 22 | color: #ff0; 23 | mix-blend-mode: lighten; 24 | font-family: Roboto; 25 | 26 | label { 27 | text-shadow: 0px 0px 20px #f70a; 28 | 29 | &.danger { 30 | color: #f00; 31 | text-shadow: 0px 0px 10px #f005; 32 | } 33 | } 34 | } 35 | 36 | 37 | ammo { 38 | position: absolute; 39 | bottom: 64px; 40 | right: 100px; 41 | font-size: 64px; 42 | color: #ff0; 43 | mix-blend-mode: lighten; 44 | font-family: Roboto; 45 | 46 | label { 47 | text-shadow: 0px 0px 20px #f70a; 48 | 49 | &.danger { 50 | color: #f00; 51 | } 52 | } 53 | } 54 | 55 | 56 | .dm_pistol .icon { 57 | background-image: url( /ui/weapons/dm_pistol.png ); 58 | } 59 | 60 | .dm_shotgun .icon { 61 | background-image: url( /ui/weapons/dm_shotgun.png ); 62 | } 63 | 64 | .dm_smg .icon { 65 | background-image: url( /ui/weapons/dm_smg.png ); 66 | } 67 | 68 | .dm_crossbow .icon, .crossbow_bolt .icon { 69 | background-image: url( /ui/weapons/dm_crossbow.png ); 70 | } 71 | -------------------------------------------------------------------------------- /code/Etc.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | public static class Etc 4 | { 5 | public readonly struct HSV 6 | { 7 | /// 8 | /// Hue in degrees (0 - 360) 9 | /// 10 | public readonly float H; 11 | public readonly float S; 12 | public readonly float V; 13 | 14 | public HSV( float h, float s, float v ) 15 | { 16 | while ( h < 0 ) { h += 360.0f; } 17 | while ( h > 360 ) { h -= 360.0f; } 18 | H = h; 19 | S = s; 20 | V = v; 21 | } 22 | 23 | public static HSV FromRadians( float h, float s, float v ) 24 | { 25 | return new HSV( h * 180 / (float)Math.PI, s, v ); 26 | } 27 | 28 | public Color ToColor() 29 | { 30 | int hi = Convert.ToInt32( Math.Floor( H / 60 ) ) % 6; 31 | var f = H / 60 - (float)Math.Floor( H / 60 ); 32 | 33 | var v = V; 34 | var p = v * (1 - S); 35 | var q = v * (1 - f * S); 36 | var t = v * (1 - (1 - f) * S); 37 | 38 | if ( hi == 0 ) 39 | return new Color( v, t, p ); 40 | else if ( hi == 1 ) 41 | return new Color( q, v, p ); 42 | else if ( hi == 2 ) 43 | return new Color( p, v, t ); 44 | else if ( hi == 3 ) 45 | return new Color( p, q, v ); 46 | else if ( hi == 4 ) 47 | return new Color( t, p, v ); 48 | else 49 | return new Color( v, p, q ); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /code/ui/DamageIndicator.cs: -------------------------------------------------------------------------------- 1 | using Sandbox; 2 | using Sandbox.UI; 3 | using Sandbox.UI.Construct; 4 | using System; 5 | using System.Threading.Tasks; 6 | 7 | public partial class DamageIndicator : Panel 8 | { 9 | public static DamageIndicator Current; 10 | 11 | public DamageIndicator() 12 | { 13 | Current = this; 14 | StyleSheet.Load( "/ui/DamageIndicator.scss" ); 15 | } 16 | 17 | public void OnHit( Vector3 pos ) 18 | { 19 | var p = new HitPoint( pos ); 20 | p.Parent = this; 21 | } 22 | 23 | public class HitPoint : Panel 24 | { 25 | public Vector3 Position; 26 | 27 | public HitPoint( Vector3 pos ) 28 | { 29 | Position = pos; 30 | 31 | _ = Lifetime(); 32 | } 33 | 34 | public override void Tick() 35 | { 36 | base.Tick(); 37 | 38 | var wpos = CurrentView.Rotation.Inverse * (Position.WithZ( 0 ) - CurrentView.Position.WithZ( 0 )).Normal; 39 | wpos = wpos.WithZ( 0 ).Normal; 40 | 41 | var angle = MathF.Atan2( wpos.y, -1.0f * wpos.x ); 42 | 43 | var pt = new PanelTransform(); 44 | 45 | pt.AddTranslateX( Length.Percent( -50.0f ) ); 46 | pt.AddTranslateY( Length.Percent( -50.0f ) ); 47 | pt.AddRotation( 0, 0, angle.RadianToDegree() ); 48 | 49 | Style.Transform = pt; 50 | Style.Dirty(); 51 | 52 | } 53 | 54 | async Task Lifetime() 55 | { 56 | await Task.Delay( 200 ); 57 | AddClass( "dying" ); 58 | await Task.Delay( 500 ); 59 | Delete(); 60 | } 61 | 62 | 63 | } 64 | } 65 | 66 | 67 | -------------------------------------------------------------------------------- /code/ui/KillFeed.scss: -------------------------------------------------------------------------------- 1 | killfeed { 2 | position: absolute; 3 | top: 100px; 4 | right: 100px; 5 | z-index: 100; 6 | flex-direction: column-reverse; 7 | width: 500px; 8 | font-family: Poppins; 9 | font-size: 15px; 10 | font-weight: 500; 11 | align-items: flex-end; 12 | } 13 | 14 | KillFeedEntry { 15 | //background-color: rgba( black, 0.8 ); 16 | margin-bottom: 5px; 17 | color: white; 18 | transition: all 0.2s ease-out; 19 | opacity: 1; 20 | flex-shrink: 0; 21 | flex-grow: 0; 22 | padding: 12px; 23 | height: 45px; 24 | position: relative; 25 | align-items: center; 26 | mix-blend-mode: lighten; 27 | 28 | &:intro { 29 | transform: scale( 2 ); 30 | padding-top: 0; 31 | padding-bottom: 0; 32 | margin-bottom: 0; 33 | height: 0; 34 | opacity: 0; 35 | } 36 | 37 | &:outro { 38 | top: -20px; 39 | opacity: 0; 40 | transform: scale( 1.1 ); 41 | transition: all 0.2s ease-in; 42 | } 43 | 44 | .icon { 45 | width: 100px; 46 | height: 100px; 47 | background-size: 100%; 48 | background-image-tint: red; 49 | } 50 | 51 | .left, .right { 52 | padding: 0 12px; 53 | color: #fc0; 54 | 55 | &.me { 56 | font-weight: bold; 57 | } 58 | } 59 | 60 | .method { 61 | opacity: 0.3; 62 | } 63 | 64 | .message { 65 | color: #ccc; 66 | font-size: 14px; 67 | // background-color: red; 68 | flex-grow: 1; 69 | } 70 | 71 | &.noname .name { 72 | display: none; 73 | } 74 | 75 | &.noavatar image { 76 | display: none; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /code/ui/Scoreboard.scss: -------------------------------------------------------------------------------- 1 | .scoreboard { 2 | position: absolute; 3 | z-index: 100; 4 | flex-direction: column; 5 | font-family: Roboto; 6 | font-size: 23px; 7 | font-weight: 500; 8 | color: white; 9 | width: 100%; 10 | height: 100%; 11 | align-items: center; 12 | justify-content: center; 13 | opacity: 0; 14 | transition: all 0.1s ease-out; 15 | transform: scale( 1.05 ); 16 | transform-origin: 50% 50%; 17 | background-size: 100% 100%; 18 | background-position: center center; 19 | background-color: rgba( 10, 10, 10, 0.8 ); 20 | 21 | &.open { 22 | opacity: 1; 23 | transform: scale( 1 ); 24 | } 25 | 26 | .name { 27 | flex-grow: 1; 28 | } 29 | 30 | .deaths, .kills, .ping, .fps { 31 | width: 90px; 32 | text-align: right; 33 | } 34 | 35 | .ping, .fps { 36 | opacity: 0.3; 37 | width: 50px; 38 | } 39 | 40 | .deaths { 41 | margin-right: 50px; 42 | } 43 | 44 | .header { 45 | font-weight: 500; 46 | border-bottom: 3px solid #fc0; 47 | min-width: 30%; 48 | margin-bottom: 10px; 49 | padding-bottom: 5px; 50 | color: #fc0; 51 | mix-blend-mode: lighten; 52 | } 53 | 54 | .canvas { 55 | flex-direction: column; 56 | min-height: 50%; 57 | min-width: 30%; 58 | mix-blend-mode: lighten; 59 | 60 | .entry { 61 | mix-blend-mode: lighten; 62 | margin-bottom: 2px; 63 | padding: 4px 10px; 64 | color: #fff; 65 | 66 | label { 67 | //text-shadow: 0px 0px 10px #f70a; 68 | } 69 | 70 | &.me { 71 | background-color: #00aa; 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /code/weapons/SMG.cs: -------------------------------------------------------------------------------- 1 | using Sandbox; 2 | using System; 3 | 4 | [Library( "dm_smg", Title = "SMG" )] 5 | [Hammer.EditorModel( "weapons/rust_smg/rust_smg.vmdl" )] 6 | partial class SMG : BaseDmWeapon 7 | { 8 | public override string ViewModelPath => "weapons/rust_smg/v_rust_smg.vmdl"; 9 | 10 | public override float PrimaryRate => 15.0f; 11 | public override float SecondaryRate => 1.0f; 12 | public override int Bucket => 2; 13 | 14 | public override void Spawn() 15 | { 16 | base.Spawn(); 17 | 18 | SetModel( "weapons/rust_smg/rust_smg.vmdl" ); 19 | } 20 | 21 | public override void AttackPrimary() 22 | { 23 | TimeSincePrimaryAttack = 0; 24 | TimeSinceSecondaryAttack = 0; 25 | 26 | if ( !TakeAmmo( 1 ) ) 27 | { 28 | DryFire(); 29 | return; 30 | } 31 | 32 | (Owner as AnimEntity).SetAnimBool( "b_attack", true ); 33 | 34 | // 35 | // Tell the clients to play the shoot effects 36 | // 37 | ShootEffects(); 38 | PlaySound( "rust_smg.shoot" ); 39 | 40 | // 41 | // Shoot the bullets 42 | // 43 | ShootBullet( 0.1f, 1.5f, 5.0f, 3.0f ); 44 | 45 | } 46 | 47 | public override void AttackSecondary() 48 | { 49 | // Grenade lob 50 | } 51 | 52 | [ClientRpc] 53 | protected override void ShootEffects() 54 | { 55 | Host.AssertClient(); 56 | 57 | Particles.Create( "particles/pistol_muzzleflash.vpcf", EffectEntity, "muzzle" ); 58 | Particles.Create( "particles/pistol_ejectbrass.vpcf", EffectEntity, "ejection_point" ); 59 | 60 | if ( Owner == Local.Pawn ) 61 | { 62 | new Sandbox.ScreenShake.Perlin( 0.5f, 4.0f, 1.0f, 0.5f ); 63 | } 64 | 65 | ViewModelEntity?.SetAnimBool( "fire", true ); 66 | CrosshairPanel?.CreateEvent( "fire" ); 67 | } 68 | 69 | public override void SimulateAnimator( PawnAnimator anim ) 70 | { 71 | anim.SetParam( "holdtype", 2 ); // TODO this is shit 72 | anim.SetParam( "aimat_weight", 1.0f ); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /code/Player.Ragdoll.cs: -------------------------------------------------------------------------------- 1 | using Sandbox; 2 | using System; 3 | using System.Linq; 4 | 5 | public partial class FPSPlayer 6 | { 7 | // TODO - make ragdolls one per entity 8 | // TODO - make ragdolls dissapear after a load of seconds 9 | static EntityLimit RagdollLimit = new EntityLimit { MaxTotal = 20 }; 10 | 11 | [ClientRpc] 12 | void BecomeRagdollOnClient( Vector3 force, int forceBone ) 13 | { 14 | // TODO - lets not make everyone write this shit out all the time 15 | // maybe a CreateRagdoll() on ModelEntity? 16 | var ent = new ModelEntity(); 17 | ent.Position = Position; 18 | ent.Rotation = Rotation; 19 | ent.MoveType = MoveType.Physics; 20 | ent.UsePhysicsCollision = true; 21 | ent.SetInteractsAs( CollisionLayer.Debris ); 22 | ent.SetInteractsWith( CollisionLayer.WORLD_GEOMETRY ); 23 | ent.SetInteractsExclude( CollisionLayer.Player | CollisionLayer.Debris ); 24 | 25 | ent.SetModel( GetModelName() ); 26 | ent.CopyBonesFrom( this ); 27 | ent.TakeDecalsFrom( this ); 28 | ent.SetRagdollVelocityFrom( this ); 29 | ent.DeleteAsync( 20.0f ); 30 | 31 | // Copy the clothes over 32 | foreach ( var child in Children ) 33 | { 34 | if ( child is ModelEntity e ) 35 | { 36 | var model = e.GetModelName(); 37 | if ( model != null && !model.Contains( "clothes" ) ) // Uck we 're better than this, entity tags, entity type or something? 38 | continue; 39 | 40 | var clothing = new ModelEntity(); 41 | clothing.SetModel( model ); 42 | clothing.SetParent( ent, true ); 43 | } 44 | } 45 | 46 | ent.PhysicsGroup.AddVelocity( force ); 47 | 48 | if ( forceBone >= 0 ) 49 | { 50 | var body = ent.GetBonePhysicsBody( forceBone ); 51 | if ( body != null ) 52 | { 53 | body.ApplyForce( force * 1000 ); 54 | } 55 | else 56 | { 57 | ent.PhysicsGroup.AddVelocity( force ); 58 | } 59 | } 60 | 61 | 62 | Corpse = ent; 63 | 64 | RagdollLimit.Watch( ent ); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /code/Player.Ammo.cs: -------------------------------------------------------------------------------- 1 | using Sandbox; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | public partial class FPSPlayer 7 | { 8 | [Net] 9 | public List Ammo { get; set; } = new(); // todo - networkable dictionaries 10 | 11 | public Dictionary AmmoLimits = new() 12 | { 13 | { AmmoType.None, -1 }, 14 | { AmmoType.Buckshot, 2 * 50 }, 15 | { AmmoType.Crossbow, 20 }, 16 | { AmmoType.Pistol, 200 } 17 | }; 18 | 19 | public void ClearAmmo() 20 | { 21 | Ammo.Clear(); 22 | } 23 | 24 | public int AmmoCount( AmmoType type ) 25 | { 26 | var iType = (int)type; 27 | if ( Ammo == null ) return 0; 28 | if ( Ammo.Count <= iType ) return 0; 29 | 30 | return Ammo[(int)type]; 31 | } 32 | 33 | public bool SetAmmo( AmmoType type, int amount ) 34 | { 35 | var iType = (int)type; 36 | if ( !Host.IsServer ) return false; 37 | if ( Ammo == null ) return false; 38 | 39 | while ( Ammo.Count <= iType ) 40 | { 41 | Ammo.Add( 0 ); 42 | } 43 | 44 | if ( amount > AmmoLimits[type] ) 45 | amount = AmmoLimits[type]; 46 | Ammo[(int)type] = amount; 47 | return true; 48 | } 49 | 50 | public bool GiveAmmo( AmmoType type ) 51 | { 52 | return GiveAmmo( type, AmmoLimits[type] ); 53 | } 54 | 55 | public bool GiveAmmo( AmmoType type, int amount ) 56 | { 57 | if ( !Host.IsServer ) return false; 58 | if ( Ammo == null ) return false; 59 | 60 | var newAmount = AmmoCount( type ) + amount; 61 | if ( newAmount > AmmoLimits[type] ) 62 | newAmount = AmmoLimits[type]; 63 | SetAmmo( type, newAmount ); 64 | return true; 65 | } 66 | 67 | public int TakeAmmo( AmmoType type, int amount ) 68 | { 69 | if ( Ammo == null ) return 0; 70 | 71 | var available = AmmoCount( type ); 72 | amount = Math.Min( available, amount ); 73 | 74 | SetAmmo( type, available - amount ); 75 | return amount; 76 | } 77 | } 78 | 79 | public enum AmmoType 80 | { 81 | None, 82 | Pistol, 83 | Buckshot, 84 | Crossbow 85 | } 86 | -------------------------------------------------------------------------------- /code/weapons/Crossbow.cs: -------------------------------------------------------------------------------- 1 | using Sandbox; 2 | 3 | [Library( "dm_crossbow", Title = "Crossbow" )] 4 | [Hammer.EditorModel( "weapons/rust_crossbow/rust_crossbow.vmdl" )] 5 | partial class Crossbow : BaseDmWeapon 6 | { 7 | public override string ViewModelPath => "weapons/rust_crossbow/v_rust_crossbow.vmdl"; 8 | 9 | public override float PrimaryRate => 1; 10 | public override int Bucket => 3; 11 | public override AmmoType AmmoType => AmmoType.Crossbow; 12 | 13 | [Net] 14 | public bool Zoomed { get; set; } 15 | 16 | public override void Spawn() 17 | { 18 | base.Spawn(); 19 | 20 | SetModel( "weapons/rust_crossbow/rust_crossbow.vmdl" ); 21 | } 22 | 23 | public override void AttackPrimary() 24 | { 25 | if ( !TakeAmmo( 1 ) ) 26 | { 27 | DryFire(); 28 | return; 29 | } 30 | 31 | ShootEffects(); 32 | 33 | if ( IsServer ) 34 | using ( Prediction.Off() ) 35 | { 36 | var bolt = new CrossbowBolt(); 37 | bolt.Position = Owner.EyePos; 38 | bolt.Rotation = Owner.EyeRot; 39 | bolt.Owner = Owner; 40 | bolt.Velocity = Owner.EyeRot.Forward * 100; 41 | } 42 | } 43 | 44 | public override void Simulate( Client cl ) 45 | { 46 | base.Simulate( cl ); 47 | 48 | Zoomed = Input.Down( InputButton.Attack2 ); 49 | } 50 | 51 | public override void PostCameraSetup( ref CameraSetup camSetup ) 52 | { 53 | base.PostCameraSetup( ref camSetup ); 54 | 55 | if ( Zoomed ) 56 | { 57 | camSetup.FieldOfView = 20; 58 | } 59 | } 60 | 61 | public override void BuildInput( InputBuilder owner ) 62 | { 63 | if ( Zoomed ) 64 | { 65 | owner.ViewAngles = Angles.Lerp( owner.OriginalViewAngles, owner.ViewAngles, 0.2f ); 66 | } 67 | } 68 | 69 | [ClientRpc] 70 | protected override void ShootEffects() 71 | { 72 | Host.AssertClient(); 73 | 74 | if ( Owner == Local.Pawn ) 75 | { 76 | new Sandbox.ScreenShake.Perlin( 0.5f, 4.0f, 1.0f, 0.5f ); 77 | } 78 | 79 | ViewModelEntity?.SetAnimBool( "fire", true ); 80 | CrosshairPanel?.CreateEvent( "fire" ); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /models/smls.player.vmdl: -------------------------------------------------------------------------------- 1 | 2 | { 3 | rootNode = 4 | { 5 | _class = "RootNode" 6 | children = 7 | [ 8 | { 9 | _class = "MaterialGroupList" 10 | children = 11 | [ 12 | { 13 | _class = "DefaultMaterialGroup" 14 | remaps = 15 | [ 16 | { 17 | from = "citizen_eyeao.vmat" 18 | to = "materials/smls.player.vmat" 19 | }, 20 | { 21 | from = "citizen_eyes.vmat" 22 | to = "materials/smls.player.vmat" 23 | }, 24 | { 25 | from = "citizen_skin.vmat" 26 | to = "materials/smls.player.vmat" 27 | }, 28 | ] 29 | use_global_default = false 30 | global_default_material = "" 31 | }, 32 | ] 33 | }, 34 | { 35 | _class = "RenderMeshList" 36 | children = 37 | [ 38 | { 39 | _class = "RenderMeshFile" 40 | filename = "models/citizen_2021-05-20.fbx" 41 | import_translation = [ 0.0, 0.0, 0.0 ] 42 | import_rotation = [ 0.0, 0.0, 0.0 ] 43 | import_scale = 1.0 44 | align_origin_x_type = "None" 45 | align_origin_y_type = "None" 46 | align_origin_z_type = "None" 47 | parent_bone = "" 48 | import_filter = 49 | { 50 | exclude_by_default = false 51 | exception_list = [ ] 52 | } 53 | }, 54 | ] 55 | }, 56 | { 57 | _class = "PhysicsShapeList" 58 | children = 59 | [ 60 | { 61 | _class = "PhysicsMeshFile" 62 | parent_bone = "" 63 | surface_prop = "default" 64 | collision_prop = "default" 65 | recenter_on_parent_bone = false 66 | offset_origin = [ 0.0, 0.0, 0.0 ] 67 | offset_angles = [ 0.0, 0.0, 0.0 ] 68 | filename = "models/citizen_2021-05-20.fbx" 69 | import_scale = 1.0 70 | maxMeshVertices = 0 71 | qemError = 0.0 72 | import_filter = 73 | { 74 | exclude_by_default = false 75 | exception_list = [ ] 76 | } 77 | }, 78 | ] 79 | }, 80 | ] 81 | model_archetype = "" 82 | primary_associated_entity = "" 83 | anim_graph_name = "" 84 | } 85 | } -------------------------------------------------------------------------------- /code/weapons/CrossbowBolt.cs: -------------------------------------------------------------------------------- 1 | using Sandbox; 2 | 3 | 4 | [Library( "crossbow_bolt" )] 5 | [Hammer.Skip] 6 | partial class CrossbowBolt : ModelEntity 7 | { 8 | bool Stuck; 9 | 10 | public override void Spawn() 11 | { 12 | base.Spawn(); 13 | 14 | SetModel( "weapons/rust_crossbow/rust_crossbow_bolt.vmdl" ); 15 | } 16 | 17 | 18 | [Event.Tick.Server] 19 | public virtual void Tick() 20 | { 21 | if ( !IsServer ) 22 | return; 23 | 24 | if ( Stuck ) 25 | return; 26 | 27 | float Speed = 10000.0f; 28 | var velocity = Rotation.Forward * Speed; 29 | 30 | var start = Position; 31 | var end = start + velocity * Time.Delta; 32 | 33 | var tr = Trace.Ray( start, end ) 34 | .UseHitboxes() 35 | //.HitLayer( CollisionLayer.Water, !InWater ) 36 | .Ignore( Owner ) 37 | .Ignore( this ) 38 | .Size( 1.0f ) 39 | .Run(); 40 | 41 | 42 | if ( tr.Hit ) 43 | { 44 | // TODO: CLINK NOISE (unless flesh) 45 | 46 | // TODO: SPARKY PARTICLES (unless flesh) 47 | 48 | Stuck = true; 49 | Position = tr.EndPos + Rotation.Forward * -1; 50 | 51 | if ( tr.Entity.IsValid() ) 52 | { 53 | var damageInfo = DamageInfo.FromBullet( tr.EndPos, tr.Direction * 200, 60.0f ) 54 | .UsingTraceResult( tr ) 55 | .WithAttacker( Owner ) 56 | .WithWeapon( this ); 57 | 58 | tr.Entity.TakeDamage( damageInfo ); 59 | } 60 | 61 | // TODO: Parent to bone so this will stick in the meaty heads 62 | SetParent( tr.Entity, tr.Bone ); 63 | Owner = null; 64 | 65 | // 66 | // Surface impact effect 67 | // 68 | tr.Normal = Rotation.Forward * -1; 69 | tr.Surface.DoBulletImpact( tr ); 70 | velocity = default; 71 | 72 | // delete self in 60 seconds 73 | _ = DeleteAsync( 60.0f ); 74 | } 75 | else 76 | { 77 | Position = end; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /code/ui/InventoryBar.scss: -------------------------------------------------------------------------------- 1 | inventorybar { 2 | position: absolute; 3 | left: 200px; 4 | top: 80px; 5 | font-family: Roboto; 6 | opacity: 0; 7 | transition: all 0.2s ease-out; 8 | 9 | 10 | &.active { 11 | top: 100px; 12 | transition: all 0.1s ease-out; 13 | opacity: 1; 14 | } 15 | } 16 | 17 | inventorycolumn { 18 | margin-right: 5px; 19 | mix-blend-mode: lighten; 20 | color: #fc0; 21 | flex-direction: column; 22 | align-items: flex-start; // align to the left, don't stretch 23 | 24 | .slot-number { 25 | background-color: #fa02; 26 | padding: 22px 15px 0px 10px; 27 | font-size: 20px; 28 | font-weight: bolder; 29 | width: 40px; 30 | height: 40px; 31 | } 32 | 33 | inventoryicon { 34 | opacity: 1; 35 | padding: 25px 15px 10px 2px; 36 | width: 40px; 37 | height: 40px; 38 | overflow: hidden; 39 | transition: all 0.1s ease-out; 40 | 41 | .icon { 42 | opacity: 0; 43 | } 44 | } 45 | 46 | opacity: 0.4; 47 | 48 | &.active { 49 | opacity: 1; 50 | 51 | inventoryicon { 52 | transition: all 0.1s ease-out; 53 | opacity: 0.2; 54 | display: flex; 55 | width: 200px; 56 | height: 90px; 57 | 58 | .icon { 59 | opacity: 1; 60 | } 61 | 62 | &.active { 63 | opacity: 1; 64 | } 65 | } 66 | } 67 | } 68 | 69 | inventoryicon { 70 | margin-top: 5px; 71 | mix-blend-mode: lighten; 72 | color: #fc0; 73 | background-color: #fa04; 74 | font-size: 40px; 75 | font-weight: lighter; 76 | padding: 10px; 77 | align-items: center; 78 | justify-content: center; 79 | overflow: hidden; 80 | 81 | .icon { 82 | width: 150px; 83 | height: 150px; 84 | background-size: 100%; 85 | background-image-tint: #fa0; 86 | position: absolute; 87 | } 88 | 89 | &.active { 90 | opacity: 1; 91 | 92 | .icon { 93 | // filter: drop-shadow additive glow 94 | opacity: 1; 95 | } 96 | } 97 | 98 | &.empty { 99 | opacity: 0.5; 100 | background-color: #c004; 101 | color: #f002; 102 | 103 | .icon { 104 | background-color: #f50; 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /code/ui/StartScreen.cs: -------------------------------------------------------------------------------- 1 | using Sandbox; 2 | using Sandbox.UI; 3 | using Sandbox.UI.Construct; 4 | using System; 5 | using System.Collections.Generic; 6 | 7 | public partial class StartScreen : Panel 8 | { 9 | public static StartScreen Instance; 10 | 11 | protected Panel userPanel; 12 | protected Dictionary players = new Dictionary(); 13 | 14 | public StartScreen() : base() 15 | { 16 | Instance = this; 17 | 18 | StyleSheet.Load( "/ui/StartScreen.scss" ); 19 | 20 | var p = Add.Panel( "sscontainer" ); 21 | 22 | var l = p.AddChild