From 128728bfcb2dcfe676b2b4c726070ee64661b6f1 Mon Sep 17 00:00:00 2001 From: clusterfack Date: Thu, 5 Apr 2018 17:32:51 -0500 Subject: [PATCH] Adds weapons (#48) * Adds weapons - Adds melee weapons - Adds projectile weapons - Adds hitscan weapons (like lasers) * Adds a separate sprite for projectile weapons --- Content.Client/EntryPoint.cs | 4 ++ Content.Server/Content.Server.csproj | 5 ++ Content.Server/EntryPoint.cs | 9 +++ .../Projectiles/ProjectileComponent.cs | 61 ++++++++++++++++++ .../Weapon/Melee/MeleeWeaponComponent.cs | 52 +++++++++++++++ .../Ranged/Hitscan/HitscanWeaponComponent.cs | 61 ++++++++++++++++++ .../Ranged/Projectile/ProjectileWeapon.cs | 40 ++++++++++++ .../Components/Weapon/Ranged/RangedWeapon.cs | 35 ++++++++++ .../EntitySystems/InteractionSystem.cs | 25 ++++--- .../Prototypes/Entities/HitscanWeapons.yml | 23 +++++++ Resources/Prototypes/Entities/Projectiles.yml | 17 +++++ Resources/Prototypes/Entities/Tools.yml | 5 ++ Resources/textures/Objects/gun.png | Bin 0 -> 282 bytes Resources/textures/Objects/laser.png | Bin 0 -> 153 bytes .../textures/Objects/projectilebullet.png | Bin 0 -> 263 bytes .../textures/Objects/projectileweapon.png | Bin 0 -> 308 bytes 16 files changed, 329 insertions(+), 8 deletions(-) create mode 100644 Content.Server/GameObjects/Components/Projectiles/ProjectileComponent.cs create mode 100644 Content.Server/GameObjects/Components/Weapon/Melee/MeleeWeaponComponent.cs create mode 100644 Content.Server/GameObjects/Components/Weapon/Ranged/Hitscan/HitscanWeaponComponent.cs create mode 100644 Content.Server/GameObjects/Components/Weapon/Ranged/Projectile/ProjectileWeapon.cs create mode 100644 Content.Server/GameObjects/Components/Weapon/Ranged/RangedWeapon.cs create mode 100644 Resources/Prototypes/Entities/HitscanWeapons.yml create mode 100644 Resources/Prototypes/Entities/Projectiles.yml create mode 100644 Resources/textures/Objects/gun.png create mode 100644 Resources/textures/Objects/laser.png create mode 100644 Resources/textures/Objects/projectilebullet.png create mode 100644 Resources/textures/Objects/projectileweapon.png diff --git a/Content.Client/EntryPoint.cs b/Content.Client/EntryPoint.cs index 6711cb3dc7..4fa5e40b21 100644 --- a/Content.Client/EntryPoint.cs +++ b/Content.Client/EntryPoint.cs @@ -31,6 +31,10 @@ namespace Content.Client factory.RegisterIgnore("Welder"); factory.RegisterIgnore("Wrench"); factory.RegisterIgnore("Crowbar"); + factory.RegisterIgnore("HitscanWeapon"); + factory.RegisterIgnore("ProjectileWeapon"); + factory.RegisterIgnore("Projectile"); + factory.RegisterIgnore("MeleeWeapon"); factory.Register(); factory.RegisterReference(); diff --git a/Content.Server/Content.Server.csproj b/Content.Server/Content.Server.csproj index 7206cee6ea..2fb2c2511d 100644 --- a/Content.Server/Content.Server.csproj +++ b/Content.Server/Content.Server.csproj @@ -72,6 +72,11 @@ + + + + + diff --git a/Content.Server/EntryPoint.cs b/Content.Server/EntryPoint.cs index 368cab7cb5..af22a9e22d 100644 --- a/Content.Server/EntryPoint.cs +++ b/Content.Server/EntryPoint.cs @@ -21,6 +21,10 @@ using SS14.Shared.Map; using SS14.Shared.Timers; using SS14.Shared.Interfaces.Timing; using SS14.Shared.Maths; +using Content.Server.GameObjects.Components.Weapon.Ranged.Hitscan; +using Content.Server.GameObjects.Components.Weapon.Ranged.Projectile; +using Content.Server.GameObjects.Components.Projectiles; +using Content.Server.GameObjects.Components.Weapon.Melee; namespace Content.Server { @@ -74,6 +78,11 @@ namespace Content.Server factory.Register(); factory.Register(); factory.Register(); + + factory.Register(); + factory.Register(); + factory.Register(); + factory.Register(); } /// diff --git a/Content.Server/GameObjects/Components/Projectiles/ProjectileComponent.cs b/Content.Server/GameObjects/Components/Projectiles/ProjectileComponent.cs new file mode 100644 index 0000000000..77034b14a5 --- /dev/null +++ b/Content.Server/GameObjects/Components/Projectiles/ProjectileComponent.cs @@ -0,0 +1,61 @@ +using SS14.Shared.GameObjects; +using SS14.Shared.Interfaces.GameObjects; +using SS14.Shared.Interfaces.Physics; +using SS14.Shared.Interfaces.GameObjects.Components; +using System; +using System.Collections.Generic; +using YamlDotNet.RepresentationModel; +using SS14.Shared.Utility; + +namespace Content.Server.GameObjects.Components.Projectiles +{ + public class ProjectileComponent : Component, ICollideSpecial, ICollideBehavior + { + public override string Name => "Projectile"; + + public bool IgnoreShooter = true; + + private EntityUid Shooter = EntityUid.Invalid; + + public Dictionary damages = new Dictionary(); + + /// + /// Function that makes the collision of this object ignore a specific entity so we don't collide with ourselves + /// + /// + public void IgnoreEntity(IEntity shooter) + { + Shooter = shooter.Uid; + } + + /// + /// Special collision override, can be used to give custom behaviors deciding when to collide + /// + /// + /// + bool ICollideSpecial.PreventCollide(ICollidable collidedwith) + { + if (IgnoreShooter && collidedwith.Owner.Uid == Shooter) + return true; + return false; + } + + /// + /// Applys the damage when our projectile collides with its victim + /// + /// + void ICollideBehavior.CollideWith(List collidedwith) + { + foreach(var entity in collidedwith) + { + if(entity.TryGetComponent(out DamageableComponent damage)) + { + damage.TakeDamage(DamageType.Brute, 10); + } + } + + if (collidedwith.Count > 0) + Owner.Delete(); + } + } +} diff --git a/Content.Server/GameObjects/Components/Weapon/Melee/MeleeWeaponComponent.cs b/Content.Server/GameObjects/Components/Weapon/Melee/MeleeWeaponComponent.cs new file mode 100644 index 0000000000..a8a8307aa9 --- /dev/null +++ b/Content.Server/GameObjects/Components/Weapon/Melee/MeleeWeaponComponent.cs @@ -0,0 +1,52 @@ +using System; +using SS14.Shared.GameObjects; +using SS14.Shared.GameObjects.Serialization; +using Content.Server.GameObjects.EntitySystems; +using SS14.Shared.Interfaces.GameObjects; +using SS14.Shared.Map; +using SS14.Shared.IoC; +using SS14.Server.GameObjects; +using SS14.Shared.Maths; +using SS14.Server.Interfaces.GameObjects; +using SS14.Shared.Interfaces.Timing; +using SS14.Shared.GameObjects.EntitySystemMessages; + +namespace Content.Server.GameObjects.Components.Weapon.Melee +{ + public class MeleeWeaponComponent : Component, IAfterAttack + { + public override string Name => "MeleeWeapon"; + + public int Damage = 1; + public float Range = 1; + public float ArcWidth = 90; + + public override void ExposeData(EntitySerializer serializer) + { + base.ExposeData(serializer); + + serializer.DataField(ref Damage, "damage", 5); + serializer.DataField(ref Range, "damage", 1); + serializer.DataField(ref ArcWidth, "damage", 90); + + } + + void IAfterAttack.Afterattack(IEntity user, LocalCoordinates clicklocation) + { + var location = user.GetComponent().LocalPosition; + var angle = new Angle(clicklocation.ToWorld().Position - location.ToWorld().Position); + var entities = IoCManager.Resolve().GetEntitiesInArc(user.GetComponent().LocalPosition, Range, angle, ArcWidth); + + foreach(var entity in entities) + { + if (!entity.GetComponent().IsMapTransform || entity == user) + continue; + + if(entity.TryGetComponent(out DamageableComponent damagecomponent)) + { + damagecomponent.TakeDamage(DamageType.Brute, Damage); + } + } + } + } +} diff --git a/Content.Server/GameObjects/Components/Weapon/Ranged/Hitscan/HitscanWeaponComponent.cs b/Content.Server/GameObjects/Components/Weapon/Ranged/Hitscan/HitscanWeaponComponent.cs new file mode 100644 index 0000000000..21629972e1 --- /dev/null +++ b/Content.Server/GameObjects/Components/Weapon/Ranged/Hitscan/HitscanWeaponComponent.cs @@ -0,0 +1,61 @@ +using SS14.Server.GameObjects; +using SS14.Server.GameObjects.EntitySystems; +using SS14.Shared.GameObjects; +using SS14.Shared.GameObjects.EntitySystemMessages; +using SS14.Shared.Interfaces.GameObjects; +using SS14.Shared.Interfaces.Physics; +using SS14.Shared.Interfaces.Timing; +using SS14.Shared.IoC; +using SS14.Shared.Map; +using SS14.Shared.Maths; +using SS14.Shared.Physics; +using System; + +namespace Content.Server.GameObjects.Components.Weapon.Ranged.Hitscan +{ + public class HitscanWeaponComponent : RangedWeaponComponent + { + public override string Name => "HitscanWeapon"; + + string spritename = "laser"; + + protected override void Fire(IEntity user, LocalCoordinates clicklocation) + { + var userposition = user.GetComponent().WorldPosition; //Remember world positions are ephemeral and can only be used instantaneously + var angle = new Angle(clicklocation.Position - userposition); + var theta = angle.Theta; + + var ray = new Ray(userposition, angle.ToVec()); + var raycastresults = IoCManager.Resolve().IntersectRay(ray, 20, Owner.GetComponent().GetMapTransform().Owner); + + Hit(raycastresults); + AfterEffects(user, raycastresults, theta); + } + + protected virtual void Hit(RayCastResults ray) + { + if(ray.HitEntity != null && ray.HitEntity.TryGetComponent(out DamageableComponent damage)) + { + damage.TakeDamage(DamageType.Heat, 10); + } + } + + protected virtual void AfterEffects(IEntity user, RayCastResults ray, double theta) + { + var time = IoCManager.Resolve().CurTime; + EffectSystemMessage message = new EffectSystemMessage + { + EffectSprite = spritename, + Born = time, + DeathTime = time + TimeSpan.FromSeconds(1), + Size = new Vector2(ray.Distance, 1f), + Coordinates = user.GetComponent().LocalPosition, + //Rotated from east facing + Rotation = (float)theta, + ColorDelta = new Vector4(0, 0, 0, -1500f), + Color = new Vector4(255,255,255,750) + }; + IoCManager.Resolve().GetEntitySystem().CreateParticle(message); + } + } +} diff --git a/Content.Server/GameObjects/Components/Weapon/Ranged/Projectile/ProjectileWeapon.cs b/Content.Server/GameObjects/Components/Weapon/Ranged/Projectile/ProjectileWeapon.cs new file mode 100644 index 0000000000..7482a2015f --- /dev/null +++ b/Content.Server/GameObjects/Components/Weapon/Ranged/Projectile/ProjectileWeapon.cs @@ -0,0 +1,40 @@ +using Content.Server.GameObjects.Components.Projectiles; +using SS14.Server.GameObjects; +using SS14.Server.Interfaces.GameObjects; +using SS14.Shared.Interfaces.GameObjects; +using SS14.Shared.IoC; +using SS14.Shared.Map; +using SS14.Shared.Maths; + +namespace Content.Server.GameObjects.Components.Weapon.Ranged.Projectile +{ + public class ProjectileWeaponComponent : RangedWeaponComponent + { + public override string Name => "ProjectileWeapon"; + + private string _ProjectilePrototype = "ProjectileBullet"; + + private float _velocity = 20f; + + protected override void Fire(IEntity user, LocalCoordinates clicklocation) + { + var userposition = user.GetComponent().LocalPosition; //Remember world positions are ephemeral and can only be used instantaneously + var angle = new Angle(clicklocation.Position - userposition.Position); + + var theta = angle.Theta; + + + //Spawn the projectileprototype + IEntity projectile = IoCManager.Resolve().ForceSpawnEntityAt(_ProjectilePrototype, userposition); + + //Give it the velocity we fire from this weapon, and make sure it doesn't shoot our character + projectile.GetComponent().IgnoreEntity(user); + + //Give it the velocity this weapon gives to things it fires from itself + projectile.GetComponent().LinearVelocity = angle.ToVec() * _velocity; + + //Rotate the bullets sprite to the correct direction, from north facing I guess + projectile.GetComponent().LocalRotation = angle.Theta; + } + } +} diff --git a/Content.Server/GameObjects/Components/Weapon/Ranged/RangedWeapon.cs b/Content.Server/GameObjects/Components/Weapon/Ranged/RangedWeapon.cs new file mode 100644 index 0000000000..d9aae20c2e --- /dev/null +++ b/Content.Server/GameObjects/Components/Weapon/Ranged/RangedWeapon.cs @@ -0,0 +1,35 @@ +using SS14.Shared.GameObjects; +using Content.Server.GameObjects.EntitySystems; +using SS14.Shared.Interfaces.GameObjects; +using SS14.Shared.Map; + +namespace Content.Server.GameObjects.Components.Weapon.Ranged +{ + public class RangedWeaponComponent : Component, IAfterAttack + { + public override string Name => "RangedWeapon"; + + void IAfterAttack.Afterattack(IEntity user, LocalCoordinates clicklocation) + { + if(UserCanFire(user) && WeaponCanFire()) + { + Fire(user, clicklocation); + } + } + + protected virtual bool WeaponCanFire() + { + return true; + } + + protected virtual bool UserCanFire(IEntity user) + { + return true; + } + + protected virtual void Fire(IEntity user, LocalCoordinates clicklocation) + { + return; + } + } +} diff --git a/Content.Server/GameObjects/EntitySystems/InteractionSystem.cs b/Content.Server/GameObjects/EntitySystems/InteractionSystem.cs index d2631b7539..5b94fe8232 100644 --- a/Content.Server/GameObjects/EntitySystems/InteractionSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/InteractionSystem.cs @@ -216,10 +216,11 @@ namespace Content.Server.GameObjects.EntitySystems /// private void InteractAfterattack(IEntity user, IEntity weapon, LocalCoordinates clicklocation) { - //If not lets attempt to use afterattack from the held item on the click location - if (weapon.TryGetComponent(out IAfterAttack attacker)) + List afterattacks = weapon.GetComponents().ToList(); + + for (var i = 0; i < afterattacks.Count; i++) { - attacker.Afterattack(user, clicklocation); + afterattacks[i].Afterattack(user, clicklocation); } } @@ -244,10 +245,13 @@ namespace Content.Server.GameObjects.EntitySystems //Else check damage component to see if we damage if not attackby, and if so can we attack object + //If we aren't directly attacking the nearby object, lets see if our item has an after attack we can do - if (weapon.TryGetComponent(out IAfterAttack attacker)) + List afterattacks = weapon.GetComponents().ToList(); + + for (var i = 0; i < afterattacks.Count; i++) { - attacker.Afterattack(user, clicklocation); + afterattacks[i].Afterattack(user, clicklocation); } } @@ -312,10 +316,15 @@ namespace Content.Server.GameObjects.EntitySystems } } - //If not lets attempt to use afterattack from the held item on the click location - if (weapon != null && weapon.TryGetComponent(out IAfterAttack attacker)) + if(weapon != null) { - attacker.Afterattack(user, clicklocation); + List afterattacks = weapon.GetComponents().ToList(); + + //See if we have a ranged attack interaction + for (var i = 0; i < afterattacks.Count; i++) + { + afterattacks[i].Afterattack(user, clicklocation); + } } } } diff --git a/Resources/Prototypes/Entities/HitscanWeapons.yml b/Resources/Prototypes/Entities/HitscanWeapons.yml new file mode 100644 index 0000000000..9bb823ddc5 --- /dev/null +++ b/Resources/Prototypes/Entities/HitscanWeapons.yml @@ -0,0 +1,23 @@ +- type: entity + name: "LASER" + parent: BaseItem + id: LaserItem + components: + - type: WearableAnimatedSprite + notWornSprite: gun + sprite: gun + - type: Icon + icon: gun + - type: HitscanWeapon + +- type: entity + name: GUN + parent: BaseItem + id: GUNITEM + components: + - type: WearableAnimatedSprite + notWornSprite: projectileweapon + sprite: projectileweapon + - type: Icon + icon: gun + - type: ProjectileWeapon \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Projectiles.yml b/Resources/Prototypes/Entities/Projectiles.yml new file mode 100644 index 0000000000..9ab7b0bcb6 --- /dev/null +++ b/Resources/Prototypes/Entities/Projectiles.yml @@ -0,0 +1,17 @@ +- type: entity + id: ProjectileBullet + name: ProjectileBullet + components: + - type: Transform + - type: Sprite + drawdepth: FloorPlaceable + sprites: + - projectilebullet + - type: Icon + icon: projectilebullet + - type: BoundingBox + - type: Physics + edgeslide: false + - type: Projectile + - type: Collidable + hard: false diff --git a/Resources/Prototypes/Entities/Tools.yml b/Resources/Prototypes/Entities/Tools.yml index 284f634bac..fc01ca3ee3 100644 --- a/Resources/Prototypes/Entities/Tools.yml +++ b/Resources/Prototypes/Entities/Tools.yml @@ -9,6 +9,7 @@ sprite: wirecutter - type: Icon icon: wirecutter + - type: MeleeWeapon - type: entity name: Screwdriver @@ -21,6 +22,7 @@ sprite: screwdriver - type: Icon icon: screwdriver + - type: MeleeWeapon - type: entity name: Welder @@ -33,6 +35,7 @@ sprite: welder - type: Icon icon: welder + - type: MeleeWeapon - type: entity name: Wrench @@ -45,6 +48,7 @@ sprite: wrench - type: Icon icon: wrench + - type: MeleeWeapon - type: entity name: Crowbar @@ -57,6 +61,7 @@ sprite: crowbar - type: Icon icon: crowbar + - type: MeleeWeapon - type: entity name: Multitool diff --git a/Resources/textures/Objects/gun.png b/Resources/textures/Objects/gun.png new file mode 100644 index 0000000000000000000000000000000000000000..65859157798bf1520d8a064bfa489e387daefc63 GIT binary patch literal 282 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyP60k4t_Kbr(A3mSOHOuoa|;Lv z$j;3CrownvhT*Uv!&DxIbQXrt;NZl>#H_5Wm30jE_V#9GW-C{&%=*uuATMuhZl0Z$ z#TT`?7-$+}NswPKgTu2MX+Tb!r;B5VMeo}Qr-hmfcwBe-awV+^x%mHo=w*}ChJ%H# zn>L?*^5kZzx^#nlA;$vN2FsRGJF$iGZQVwF;TF1QWfYlqSuUA)amoo!F*BEUlLK4g zUa#Ju7P`mz7We6O`<%~d+01kL>|$^;i94 c?|9YalN;GyeP-)62fB#C)78&qol`;+095Q}0ssI2 literal 0 HcmV?d00001 diff --git a/Resources/textures/Objects/laser.png b/Resources/textures/Objects/laser.png new file mode 100644 index 0000000000000000000000000000000000000000..00432972d135828a8dc1fab5a33f30a203cb6c3a GIT binary patch literal 153 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJ1Wy;okcie~n}P=nhaH%fF>bBB z!nl}EqV&KDW;fXbK=8v}ujKFntA^dd92U;l0l4(txcQxEyLW#k^>n^ tDTy3BwT2V;5@d3x^jRf9jc~ALVBowgAd%m+=oQc|22WQ%mvv4FO#q>?F-ZUb literal 0 HcmV?d00001 diff --git a/Resources/textures/Objects/projectilebullet.png b/Resources/textures/Objects/projectilebullet.png new file mode 100644 index 0000000000000000000000000000000000000000..0ba9c8b86f1134761317233ab1f94babaa44d4e5 GIT binary patch literal 263 zcmV+i0r>ujP)08V#`uu#A@0C147a7+OrREGft1p7DK`@!qU%y_CIDbw$$z1G1)77uZ1)ltPGyPN zN3f4T^Ao;`z+7@$fnSK|Vk)5Nfc6fsvR>DLl+xQ&fQZhf0_Gj4!3T#tKn<0mgl_-< N002ovPDHLkV1iR`ZovQm literal 0 HcmV?d00001 diff --git a/Resources/textures/Objects/projectileweapon.png b/Resources/textures/Objects/projectileweapon.png new file mode 100644 index 0000000000000000000000000000000000000000..404463fce888adb0d8815842b9e43c5c7717fba2 GIT binary patch literal 308 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dy_5nU2t_KbrFg7+03kwqw5wWqc zNli_Sj*ga-lM4t4h>eX63=DL0bJN$?*VWavwY3!#6m)QK5D*aX3yElGX!!JrVd>X( zdO)KXOM?7@862M7NCR>fd%8G=SoFS~c#*HkfQRAw)_@dYjUWH-XIbkgC>npfSRtwM zrO!a2#r{6y2h9$K>|ZhK$_bS{IZtDnm{r-UW| Df<$t& literal 0 HcmV?d00001