Made a fancier lasergun (#174)
Laserguns now have an internal capacitor that can be recharged by using it with a power cell Makes the final fix for #138
This commit is contained in:
committed by
Pieter-Jan Briers
parent
b94e015314
commit
1af1ee2ad4
@@ -149,6 +149,7 @@
|
|||||||
<Compile Include="ServerNotifyManager.cs" />
|
<Compile Include="ServerNotifyManager.cs" />
|
||||||
<Compile Include="StatusShell.cs" />
|
<Compile Include="StatusShell.cs" />
|
||||||
<Compile Include="GameObjects\Components\Healing\HealingComponent.cs" />
|
<Compile Include="GameObjects\Components\Healing\HealingComponent.cs" />
|
||||||
|
<Compile Include="GameObjects\Components\Weapon\Ranged\Hitscan\HitscanWeaponCapacitorComponent.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Content.Shared\Content.Shared.csproj">
|
<ProjectReference Include="..\Content.Shared\Content.Shared.csproj">
|
||||||
@@ -156,15 +157,15 @@
|
|||||||
<Name>Content.Shared</Name>
|
<Name>Content.Shared</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\RobustToolbox\SS14.Server\SS14.Server.csproj">
|
<ProjectReference Include="..\RobustToolbox\SS14.Server\SS14.Server.csproj">
|
||||||
<Project>{916C5BF0-74BF-485D-A233-C1AC30007119}</Project>
|
<Project>{B04AAE71-0000-0000-0000-000000000000}</Project>
|
||||||
<Name>SS14.Server</Name>
|
<Name>SS14.Server</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\RobustToolbox\SS14.Shared.Maths\SS14.Shared.Maths.csproj">
|
<ProjectReference Include="..\RobustToolbox\SS14.Shared.Maths\SS14.Shared.Maths.csproj">
|
||||||
<Project>{7DC961F2-A45B-4193-BE7F-77622A231943}</Project>
|
<Project>{93F23A82-00C5-4572-964E-E7C9457726D4}</Project>
|
||||||
<Name>SS14.Shared.Maths</Name>
|
<Name>SS14.Shared.Maths</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\RobustToolbox\SS14.Shared\SS14.Shared.csproj">
|
<ProjectReference Include="..\RobustToolbox\SS14.Shared\SS14.Shared.csproj">
|
||||||
<Project>{5F161008-6C48-4596-A2DE-5E7B0CEB2AA1}</Project>
|
<Project>{0529F740-0000-0000-0000-000000000000}</Project>
|
||||||
<Name>SS14.Shared</Name>
|
<Name>SS14.Shared</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -135,6 +135,8 @@ namespace Content.Server
|
|||||||
factory.Register<BallisticBulletComponent>();
|
factory.Register<BallisticBulletComponent>();
|
||||||
factory.Register<BallisticMagazineComponent>();
|
factory.Register<BallisticMagazineComponent>();
|
||||||
|
|
||||||
|
factory.Register<HitscanWeaponCapacitorComponent>();
|
||||||
|
|
||||||
factory.Register<CameraRecoilComponent>();
|
factory.Register<CameraRecoilComponent>();
|
||||||
factory.RegisterReference<CameraRecoilComponent, SharedCameraRecoilComponent>();
|
factory.RegisterReference<CameraRecoilComponent, SharedCameraRecoilComponent>();
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ namespace Content.Server.GameObjects.Components.Power
|
|||||||
base.DeductCharge(toDeduct);
|
base.DeductCharge(toDeduct);
|
||||||
|
|
||||||
_updateAppearance();
|
_updateAppearance();
|
||||||
|
ChargeChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void AddCharge(float charge)
|
public override void AddCharge(float charge)
|
||||||
@@ -38,6 +39,7 @@ namespace Content.Server.GameObjects.Components.Power
|
|||||||
base.AddCharge(charge);
|
base.AddCharge(charge);
|
||||||
|
|
||||||
_updateAppearance();
|
_updateAppearance();
|
||||||
|
ChargeChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void _updateAppearance()
|
private void _updateAppearance()
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ namespace Content.Server.GameObjects.Components.Power
|
|||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public bool Full => Charge >= Capacity;
|
public bool Full => Charge >= Capacity;
|
||||||
|
|
||||||
|
public event Action OnChargeChanged;
|
||||||
public override void ExposeData(ObjectSerializer serializer)
|
public override void ExposeData(ObjectSerializer serializer)
|
||||||
{
|
{
|
||||||
base.ExposeData(serializer);
|
base.ExposeData(serializer);
|
||||||
@@ -70,6 +71,14 @@ namespace Content.Server.GameObjects.Components.Power
|
|||||||
serializer.DataField(ref _distributionRate, "distributionrate", 1000);
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if the storage can supply the amount of charge directly requested
|
/// Checks if the storage can supply the amount of charge directly requested
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -88,6 +97,7 @@ namespace Content.Server.GameObjects.Components.Power
|
|||||||
_charge = Math.Max(0, Charge - toDeduct);
|
_charge = Math.Max(0, Charge - toDeduct);
|
||||||
LastChargeState = ChargeState.Discharging;
|
LastChargeState = ChargeState.Discharging;
|
||||||
LastChargeStateChange = DateTime.Now;
|
LastChargeStateChange = DateTime.Now;
|
||||||
|
ChargeChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void AddCharge(float charge)
|
public virtual void AddCharge(float charge)
|
||||||
@@ -95,6 +105,7 @@ namespace Content.Server.GameObjects.Components.Power
|
|||||||
_charge = Math.Min(Capacity, Charge + charge);
|
_charge = Math.Min(Capacity, Charge + charge);
|
||||||
LastChargeState = ChargeState.Charging;
|
LastChargeState = ChargeState.Charging;
|
||||||
LastChargeStateChange = DateTime.Now;
|
LastChargeStateChange = DateTime.Now;
|
||||||
|
ChargeChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -14,35 +14,74 @@ using SS14.Shared.Serialization;
|
|||||||
using System;
|
using System;
|
||||||
using Content.Server.GameObjects.Components.Sound;
|
using Content.Server.GameObjects.Components.Sound;
|
||||||
using SS14.Shared.GameObjects;
|
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
|
namespace Content.Server.GameObjects.Components.Weapon.Ranged.Hitscan
|
||||||
{
|
{
|
||||||
public class HitscanWeaponComponent : Component
|
public class HitscanWeaponComponent : Component, IAttackby
|
||||||
{
|
{
|
||||||
private const float MaxLength = 20;
|
private const float MaxLength = 20;
|
||||||
public override string Name => "HitscanWeapon";
|
public override string Name => "HitscanWeapon";
|
||||||
|
|
||||||
string Spritename = "Objects/laser.png";
|
string _spritename;
|
||||||
int Damage = 10;
|
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)
|
public override void ExposeData(ObjectSerializer serializer)
|
||||||
{
|
{
|
||||||
base.ExposeData(serializer);
|
base.ExposeData(serializer);
|
||||||
|
|
||||||
serializer.DataField(ref Spritename, "sprite", "Objects/laser.png");
|
serializer.DataField(ref _spritename, "sprite", "Objects/laser.png");
|
||||||
serializer.DataField(ref Damage, "damage", 10);
|
serializer.DataField(ref _damage, "damage", 10);
|
||||||
|
serializer.DataField(ref _baseFireCost, "baseFireCost", 300);
|
||||||
|
serializer.DataField(ref _lowerChargeLimit, "lowerChargeLimit", 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
var rangedWeapon = Owner.GetComponent<RangedWeaponComponent>();
|
var rangedWeapon = Owner.GetComponent<RangedWeaponComponent>();
|
||||||
|
capacitorComponent = Owner.GetComponent<HitscanWeaponCapacitorComponent>();
|
||||||
rangedWeapon.FireHandler = Fire;
|
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)
|
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 userPosition = user.Transform.WorldPosition; //Remember world positions are ephemeral and can only be used instantaneously
|
||||||
var angle = new Angle(clickLocation.Position - userPosition);
|
var angle = new Angle(clickLocation.Position - userPosition);
|
||||||
|
|
||||||
@@ -50,26 +89,28 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Hitscan
|
|||||||
var rayCastResults = IoCManager.Resolve<IPhysicsManager>().IntersectRay(ray, MaxLength,
|
var rayCastResults = IoCManager.Resolve<IPhysicsManager>().IntersectRay(ray, MaxLength,
|
||||||
Owner.Transform.GetMapTransform().Owner);
|
Owner.Transform.GetMapTransform().Owner);
|
||||||
|
|
||||||
Hit(rayCastResults);
|
Hit(rayCastResults, energyModifier);
|
||||||
AfterEffects(user, rayCastResults, angle);
|
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))
|
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<IGameTiming>().CurTime;
|
var time = IoCManager.Resolve<IGameTiming>().CurTime;
|
||||||
var dist = ray.DidHitObject ? ray.Distance : MaxLength;
|
var dist = ray.DidHitObject ? ray.Distance : MaxLength;
|
||||||
var offset = angle.ToVec() * dist / 2;
|
var offset = angle.ToVec() * dist / 2;
|
||||||
var message = new EffectSystemMessage
|
var message = new EffectSystemMessage
|
||||||
{
|
{
|
||||||
EffectSprite = Spritename,
|
EffectSprite = _spritename,
|
||||||
Born = time,
|
Born = time,
|
||||||
DeathTime = time + TimeSpan.FromSeconds(1),
|
DeathTime = time + TimeSpan.FromSeconds(1),
|
||||||
Size = new Vector2(dist, 1f),
|
Size = new Vector2(dist, 1f),
|
||||||
@@ -77,7 +118,8 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Hitscan
|
|||||||
//Rotated from east facing
|
//Rotated from east facing
|
||||||
Rotation = (float) angle.Theta,
|
Rotation = (float) angle.Theta,
|
||||||
ColorDelta = new Vector4(0, 0, 0, -1500f),
|
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
|
Shaded = false
|
||||||
};
|
};
|
||||||
var mgr = IoCManager.Resolve<IEntitySystemManager>();
|
var mgr = IoCManager.Resolve<IEntitySystemManager>();
|
||||||
|
|||||||
@@ -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
|
- type: entity
|
||||||
name: Spear
|
name: Spear
|
||||||
parent: BaseItem
|
parent: BaseItem
|
||||||
|
|||||||
24
Resources/Prototypes/Entities/weapons/laserguns.yml
Normal file
24
Resources/Prototypes/Entities/weapons/laserguns.yml
Normal file
@@ -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
|
||||||
Reference in New Issue
Block a user