diff --git a/Content.Server/Content.Server.csproj b/Content.Server/Content.Server.csproj index ab04ae7978..e82e38bd97 100644 --- a/Content.Server/Content.Server.csproj +++ b/Content.Server/Content.Server.csproj @@ -149,6 +149,7 @@ + @@ -156,15 +157,15 @@ Content.Shared - {916C5BF0-74BF-485D-A233-C1AC30007119} + {B04AAE71-0000-0000-0000-000000000000} SS14.Server - {7DC961F2-A45B-4193-BE7F-77622A231943} + {93F23A82-00C5-4572-964E-E7C9457726D4} SS14.Shared.Maths - {5F161008-6C48-4596-A2DE-5E7B0CEB2AA1} + {0529F740-0000-0000-0000-000000000000} SS14.Shared diff --git a/Content.Server/EntryPoint.cs b/Content.Server/EntryPoint.cs index 96c671a4b3..3f70e845a1 100644 --- a/Content.Server/EntryPoint.cs +++ b/Content.Server/EntryPoint.cs @@ -135,6 +135,8 @@ namespace Content.Server factory.Register(); factory.Register(); + factory.Register(); + factory.Register(); factory.RegisterReference(); diff --git a/Content.Server/GameObjects/Components/Power/PowerCellComponent.cs b/Content.Server/GameObjects/Components/Power/PowerCellComponent.cs index ae53d48de9..6e11da5d5d 100644 --- a/Content.Server/GameObjects/Components/Power/PowerCellComponent.cs +++ b/Content.Server/GameObjects/Components/Power/PowerCellComponent.cs @@ -31,6 +31,7 @@ namespace Content.Server.GameObjects.Components.Power base.DeductCharge(toDeduct); _updateAppearance(); + ChargeChanged(); } public override void AddCharge(float charge) @@ -38,6 +39,7 @@ namespace Content.Server.GameObjects.Components.Power base.AddCharge(charge); _updateAppearance(); + ChargeChanged(); } private void _updateAppearance() diff --git a/Content.Server/GameObjects/Components/Power/PowerStorageComponent.cs b/Content.Server/GameObjects/Components/Power/PowerStorageComponent.cs index d108a76cea..02f42f3019 100644 --- a/Content.Server/GameObjects/Components/Power/PowerStorageComponent.cs +++ b/Content.Server/GameObjects/Components/Power/PowerStorageComponent.cs @@ -60,6 +60,7 @@ namespace Content.Server.GameObjects.Components.Power [ViewVariables] public bool Full => Charge >= Capacity; + public event Action OnChargeChanged; public override void ExposeData(ObjectSerializer serializer) { base.ExposeData(serializer); @@ -70,6 +71,14 @@ namespace Content.Server.GameObjects.Components.Power serializer.DataField(ref _distributionRate, "distributionrate", 1000); } + protected virtual void ChargeChanged() + { + if (OnChargeChanged != null) + { //Only fire this event if anyone actually subscribes to it + OnChargeChanged.Invoke(); + } + } + /// /// Checks if the storage can supply the amount of charge directly requested /// @@ -88,6 +97,7 @@ namespace Content.Server.GameObjects.Components.Power _charge = Math.Max(0, Charge - toDeduct); LastChargeState = ChargeState.Discharging; LastChargeStateChange = DateTime.Now; + ChargeChanged(); } public virtual void AddCharge(float charge) @@ -95,6 +105,7 @@ namespace Content.Server.GameObjects.Components.Power _charge = Math.Min(Capacity, Charge + charge); LastChargeState = ChargeState.Charging; LastChargeStateChange = DateTime.Now; + ChargeChanged(); } /// diff --git a/Content.Server/GameObjects/Components/Weapon/Ranged/Hitscan/HitscanWeaponCapacitorComponent.cs b/Content.Server/GameObjects/Components/Weapon/Ranged/Hitscan/HitscanWeaponCapacitorComponent.cs new file mode 100644 index 0000000000..be7ed87dbc --- /dev/null +++ b/Content.Server/GameObjects/Components/Weapon/Ranged/Hitscan/HitscanWeaponCapacitorComponent.cs @@ -0,0 +1,50 @@ +using System; +using Content.Server.GameObjects.Components.Power; +using SS14.Shared.Serialization; + +namespace Content.Server.GameObjects.Components.Weapon.Ranged.Hitscan +{ + public class HitscanWeaponCapacitorComponent : PowerCellComponent + { + + public override string Name => "HitscanWeaponCapacitor"; + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + } + + public override void Initialize() + { + base.Initialize(); + + Charge = Capacity; + } + + public float GetChargeFrom(float toDeduct) + { + //Use this function when you want to shoot even though you don't have enough energy for basecost + ChargeChanged(); + var chargeChangedBy = Math.Min(this.Charge, toDeduct); + this.DeductCharge(chargeChangedBy); + return chargeChangedBy; + } + + public void FillFrom(PowerStorageComponent battery) + { + var capacitorPowerDeficit = this.Capacity - this.Charge; + if (battery.CanDeductCharge(capacitorPowerDeficit)) + { + battery.DeductCharge(capacitorPowerDeficit); + this.AddCharge(capacitorPowerDeficit); + } + else + { + this.AddCharge(battery.Charge); + battery.DeductCharge(battery.Charge); + } + } + } + + +} diff --git a/Content.Server/GameObjects/Components/Weapon/Ranged/Hitscan/HitscanWeaponComponent.cs b/Content.Server/GameObjects/Components/Weapon/Ranged/Hitscan/HitscanWeaponComponent.cs index b876d4e08c..e4a4b3398f 100644 --- a/Content.Server/GameObjects/Components/Weapon/Ranged/Hitscan/HitscanWeaponComponent.cs +++ b/Content.Server/GameObjects/Components/Weapon/Ranged/Hitscan/HitscanWeaponComponent.cs @@ -14,35 +14,74 @@ using SS14.Shared.Serialization; using System; using Content.Server.GameObjects.Components.Sound; using SS14.Shared.GameObjects; +using Content.Server.GameObjects.EntitySystems; +using Content.Server.GameObjects.Components.Power; +using Content.Shared.Interfaces; namespace Content.Server.GameObjects.Components.Weapon.Ranged.Hitscan { - public class HitscanWeaponComponent : Component + public class HitscanWeaponComponent : Component, IAttackby { private const float MaxLength = 20; public override string Name => "HitscanWeapon"; - string Spritename = "Objects/laser.png"; - int Damage = 10; + string _spritename; + private int _damage; + private int _baseFireCost; + private float _lowerChargeLimit; + + //As this is a component that sits on the weapon rather than a static value + //we just declare the field and then use GetComponent later to actually get it. + //Do remember to add it in both the .yaml prototype and the factory in EntryPoint.cs + //Otherwise you will get errors + private HitscanWeaponCapacitorComponent capacitorComponent; + + public int Damage => _damage; + + public int BaseFireCost => _baseFireCost; + + public HitscanWeaponCapacitorComponent CapacitorComponent => capacitorComponent; public override void ExposeData(ObjectSerializer serializer) { base.ExposeData(serializer); - serializer.DataField(ref Spritename, "sprite", "Objects/laser.png"); - serializer.DataField(ref Damage, "damage", 10); + serializer.DataField(ref _spritename, "sprite", "Objects/laser.png"); + serializer.DataField(ref _damage, "damage", 10); + serializer.DataField(ref _baseFireCost, "baseFireCost", 300); + serializer.DataField(ref _lowerChargeLimit, "lowerChargeLimit", 10); } public override void Initialize() { base.Initialize(); - var rangedWeapon = Owner.GetComponent(); + capacitorComponent = Owner.GetComponent(); rangedWeapon.FireHandler = Fire; } + public bool Attackby(IEntity user, IEntity attackwith) + { + if (!attackwith.TryGetComponent(out PowerStorageComponent component)) + { + return false; + } + if (capacitorComponent.Full) + { + Owner.PopupMessage(user, "Capacitor at max charge"); + return false; + } + capacitorComponent.FillFrom(component); + return true; + } + private void Fire(IEntity user, GridCoordinates clickLocation) { + if (capacitorComponent.Charge < _lowerChargeLimit) + {//If capacitor has less energy than the lower limit, do nothing + return; + } + float energyModifier = capacitorComponent.GetChargeFrom(_baseFireCost) / _baseFireCost; var userPosition = user.Transform.WorldPosition; //Remember world positions are ephemeral and can only be used instantaneously var angle = new Angle(clickLocation.Position - userPosition); @@ -50,26 +89,28 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Hitscan var rayCastResults = IoCManager.Resolve().IntersectRay(ray, MaxLength, Owner.Transform.GetMapTransform().Owner); - Hit(rayCastResults); - AfterEffects(user, rayCastResults, angle); + Hit(rayCastResults, energyModifier); + AfterEffects(user, rayCastResults, angle, energyModifier); } - protected virtual void Hit(RayCastResults ray) + protected virtual void Hit(RayCastResults ray, float damageModifier) { if (ray.HitEntity != null && ray.HitEntity.TryGetComponent(out DamageableComponent damage)) { - damage.TakeDamage(DamageType.Heat, Damage); + damage.TakeDamage(DamageType.Heat, (int)Math.Round(_damage * damageModifier, MidpointRounding.AwayFromZero)); + //I used Math.Round over Convert.toInt32, as toInt32 always rounds to + //even numbers if halfway between two numbers, rather than rounding to nearest } } - protected virtual void AfterEffects(IEntity user, RayCastResults ray, Angle angle) + protected virtual void AfterEffects(IEntity user, RayCastResults ray, Angle angle, float energyModifier) { var time = IoCManager.Resolve().CurTime; var dist = ray.DidHitObject ? ray.Distance : MaxLength; var offset = angle.ToVec() * dist / 2; var message = new EffectSystemMessage { - EffectSprite = Spritename, + EffectSprite = _spritename, Born = time, DeathTime = time + TimeSpan.FromSeconds(1), Size = new Vector2(dist, 1f), @@ -77,7 +118,8 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Hitscan //Rotated from east facing Rotation = (float) angle.Theta, ColorDelta = new Vector4(0, 0, 0, -1500f), - Color = new Vector4(255, 255, 255, 750), + Color = Vector4.Multiply(new Vector4(255, 255, 255, 750), energyModifier), + Shaded = false }; var mgr = IoCManager.Resolve(); diff --git a/Resources/Prototypes/Entities/Weapons.yml b/Resources/Prototypes/Entities/Weapons.yml index e7432c623b..9c3caf8aea 100644 --- a/Resources/Prototypes/Entities/Weapons.yml +++ b/Resources/Prototypes/Entities/Weapons.yml @@ -1,25 +1,3 @@ -- type: entity - name: Laser Gun - parent: BaseItem - id: LaserItem - description: A weapon using light amplified by the stimulated emission of radiation - components: - - type: Sprite - sprite: Objects/laser_retro.rsi - state: 100 - - type: Icon - sprite: Objects/laser_retro.rsi - state: 100 - - type: RangedWeapon - - type: HitscanWeapon - damage: 30 - sprite: "Objects/laser.png" - - type: Item - Size: 24 - sprite: Objects/laser_retro.rsi - prefix: 100 - - type: Sound - - type: entity name: Spear parent: BaseItem diff --git a/Resources/Prototypes/Entities/weapons/laserguns.yml b/Resources/Prototypes/Entities/weapons/laserguns.yml new file mode 100644 index 0000000000..f26657c244 --- /dev/null +++ b/Resources/Prototypes/Entities/weapons/laserguns.yml @@ -0,0 +1,24 @@ +- type: entity + name: Laser Gun + parent: BaseItem + id: LaserItem + description: A weapon using light amplified by the stimulated emission of radiation + components: + - type: Sprite + sprite: Objects/laser_retro.rsi + state: 100 + - type: Icon + sprite: Objects/laser_retro.rsi + state: 100 + - type: RangedWeapon + - type: HitscanWeapon + damage: 30 + sprite: "Objects/laser.png" + lowerDischargeLimit: 10 + - type: HitscanWeaponCapacitor + capacity: 1000 + - type: Item + Size: 24 + sprite: Objects/laser_retro.rsi + prefix: 100 + - type: Sound \ No newline at end of file