Adds weapons (#48)

* Adds weapons

- Adds melee weapons
- Adds projectile weapons
- Adds hitscan weapons (like lasers)

* Adds a separate sprite for projectile weapons
This commit is contained in:
clusterfack
2018-04-05 17:32:51 -05:00
committed by Pieter-Jan Briers
parent 63b5d83728
commit 128728bfcb
16 changed files with 329 additions and 8 deletions

View File

@@ -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<HandsComponent>();
factory.RegisterReference<HandsComponent, IHandsComponent>();

View File

@@ -72,6 +72,11 @@
<Compile Include="GameObjects\Components\Power\PowerNodeComponent.cs" />
<Compile Include="GameObjects\Components\Power\PowerProviderComponent.cs" />
<Compile Include="GameObjects\Components\Power\PowerTransferComponent.cs" />
<Compile Include="GameObjects\Components\Projectiles\ProjectileComponent.cs" />
<Compile Include="GameObjects\Components\Weapon\Melee\MeleeWeaponComponent.cs" />
<Compile Include="GameObjects\Components\Weapon\Ranged\Hitscan\HitscanWeaponComponent.cs" />
<Compile Include="GameObjects\Components\Weapon\Ranged\Projectile\ProjectileWeapon.cs" />
<Compile Include="GameObjects\Components\Weapon\Ranged\RangedWeapon.cs" />
<Compile Include="GameObjects\EntitySystems\InteractionSystem.cs" />
<Compile Include="GameObjects\EntitySystems\PowerSystem.cs" />
<Compile Include="Interfaces\GameObjects\Components\Items\IHandsComponent.cs" />

View File

@@ -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<WelderComponent>();
factory.Register<ScrewdriverComponent>();
factory.Register<CrowbarComponent>();
factory.Register<HitscanWeaponComponent>();
factory.Register<ProjectileWeaponComponent>();
factory.Register<ProjectileComponent>();
factory.Register<MeleeWeaponComponent>();
}
/// <inheritdoc />

View File

@@ -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<DamageType, int> damages = new Dictionary<DamageType, int>();
/// <summary>
/// Function that makes the collision of this object ignore a specific entity so we don't collide with ourselves
/// </summary>
/// <param name="shooter"></param>
public void IgnoreEntity(IEntity shooter)
{
Shooter = shooter.Uid;
}
/// <summary>
/// Special collision override, can be used to give custom behaviors deciding when to collide
/// </summary>
/// <param name="collidedwith"></param>
/// <returns></returns>
bool ICollideSpecial.PreventCollide(ICollidable collidedwith)
{
if (IgnoreShooter && collidedwith.Owner.Uid == Shooter)
return true;
return false;
}
/// <summary>
/// Applys the damage when our projectile collides with its victim
/// </summary>
/// <param name="collidedwith"></param>
void ICollideBehavior.CollideWith(List<IEntity> collidedwith)
{
foreach(var entity in collidedwith)
{
if(entity.TryGetComponent(out DamageableComponent damage))
{
damage.TakeDamage(DamageType.Brute, 10);
}
}
if (collidedwith.Count > 0)
Owner.Delete();
}
}
}

View File

@@ -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<TransformComponent>().LocalPosition;
var angle = new Angle(clicklocation.ToWorld().Position - location.ToWorld().Position);
var entities = IoCManager.Resolve<IServerEntityManager>().GetEntitiesInArc(user.GetComponent<TransformComponent>().LocalPosition, Range, angle, ArcWidth);
foreach(var entity in entities)
{
if (!entity.GetComponent<TransformComponent>().IsMapTransform || entity == user)
continue;
if(entity.TryGetComponent(out DamageableComponent damagecomponent))
{
damagecomponent.TakeDamage(DamageType.Brute, Damage);
}
}
}
}
}

View File

@@ -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<TransformComponent>().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<ICollisionManager>().IntersectRay(ray, 20, Owner.GetComponent<TransformComponent>().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<IGameTiming>().CurTime;
EffectSystemMessage message = new EffectSystemMessage
{
EffectSprite = spritename,
Born = time,
DeathTime = time + TimeSpan.FromSeconds(1),
Size = new Vector2(ray.Distance, 1f),
Coordinates = user.GetComponent<TransformComponent>().LocalPosition,
//Rotated from east facing
Rotation = (float)theta,
ColorDelta = new Vector4(0, 0, 0, -1500f),
Color = new Vector4(255,255,255,750)
};
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<EffectSystem>().CreateParticle(message);
}
}
}

View File

@@ -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<TransformComponent>().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<IServerEntityManager>().ForceSpawnEntityAt(_ProjectilePrototype, userposition);
//Give it the velocity we fire from this weapon, and make sure it doesn't shoot our character
projectile.GetComponent<ProjectileComponent>().IgnoreEntity(user);
//Give it the velocity this weapon gives to things it fires from itself
projectile.GetComponent<PhysicsComponent>().LinearVelocity = angle.ToVec() * _velocity;
//Rotate the bullets sprite to the correct direction, from north facing I guess
projectile.GetComponent<TransformComponent>().LocalRotation = angle.Theta;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -216,10 +216,11 @@ namespace Content.Server.GameObjects.EntitySystems
/// <param name="clicklocation"></param>
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<IAfterAttack> afterattacks = weapon.GetComponents<IAfterAttack>().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<IAfterAttack> afterattacks = weapon.GetComponents<IAfterAttack>().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<IAfterAttack> afterattacks = weapon.GetComponents<IAfterAttack>().ToList();
//See if we have a ranged attack interaction
for (var i = 0; i < afterattacks.Count; i++)
{
afterattacks[i].Afterattack(user, clicklocation);
}
}
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 B