diff --git a/Content.Server/AI/AimShootLifeProcessor.cs b/Content.Server/AI/AimShootLifeProcessor.cs index f5c7b88ba0..6d0c165e76 100644 --- a/Content.Server/AI/AimShootLifeProcessor.cs +++ b/Content.Server/AI/AimShootLifeProcessor.cs @@ -1,8 +1,8 @@ using System; using System.Collections.Generic; using Content.Server.Interfaces.GameObjects.Components.Movement; +using Content.Shared.Physics; using Robust.Server.AI; -using Robust.Server.GameObjects; using Robust.Server.Interfaces.GameObjects; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.GameObjects.Components; @@ -119,7 +119,7 @@ namespace Content.Server.AI // build the ray var dir = entity.GetComponent().WorldPosition - myTransform.WorldPosition; - var ray = new Ray(myTransform.WorldPosition, dir.Normalized,0); //TODO verify if 0 is the correct Collision Mask + var ray = new Ray(myTransform.WorldPosition, dir.Normalized, (int)(CollisionGroup.Mob | CollisionGroup.Grid)); // cast the ray var result = _physMan.IntersectRay(ray, maxRayLen); diff --git a/Content.Server/Content.Server.csproj b/Content.Server/Content.Server.csproj index 895ec78e7e..8ec12bac63 100644 --- a/Content.Server/Content.Server.csproj +++ b/Content.Server/Content.Server.csproj @@ -55,6 +55,7 @@ + @@ -71,6 +72,7 @@ + @@ -93,6 +95,7 @@ + diff --git a/Content.Server/EntryPoint.cs b/Content.Server/EntryPoint.cs index 5b60875594..f22666d7bc 100644 --- a/Content.Server/EntryPoint.cs +++ b/Content.Server/EntryPoint.cs @@ -76,6 +76,7 @@ namespace Content.Server factory.Register(); factory.RegisterReference(); factory.RegisterReference(); + factory.Register(); factory.Register(); factory.Register(); @@ -117,6 +118,7 @@ namespace Content.Server factory.Register(); factory.RegisterReference(); + factory.Register(); factory.Register(); factory.Register(); diff --git a/Content.Server/GameObjects/Components/EntityStorageComponent.cs b/Content.Server/GameObjects/Components/EntityStorageComponent.cs new file mode 100644 index 0000000000..cf508b37bd --- /dev/null +++ b/Content.Server/GameObjects/Components/EntityStorageComponent.cs @@ -0,0 +1,160 @@ +using Content.Server.GameObjects.EntitySystems; +using Robust.Server.GameObjects.Components.Container; +using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.GameObjects.Components; +using Robust.Shared.Interfaces.Network; +using Robust.Shared.Maths; +using Robust.Shared.Serialization; +using Robust.Shared.ViewVariables; +using System.Linq; + +namespace Content.Server.GameObjects.Components +{ + public class EntityStorageComponent : Component, IAttackHand + { + public override string Name => "EntityStorage"; + + private ServerStorageComponent StorageComponent; + private int StorageCapacityMax; + private bool IsCollidableWhenOpen; + private Container Contents; + private IEntityQuery entityQuery; + + public override void Initialize() + { + base.Initialize(); + Contents = ContainerManagerComponent.Ensure($"{typeof(EntityStorageComponent).FullName}{Owner.Uid.ToString()}", Owner); + StorageComponent = Owner.AddComponent(); + StorageComponent.Initialize(); + entityQuery = new IntersectingEntityQuery(Owner); + } + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + + serializer.DataField(ref StorageCapacityMax, "Capacity", 30); + serializer.DataField(ref IsCollidableWhenOpen, "IsCollidableWhenOpen", false); + } + + [ViewVariables(VVAccess.ReadWrite)] + public bool Open + { + get => StorageComponent.Open; + set => StorageComponent.Open = value; + } + + public bool AttackHand(AttackHandEventArgs eventArgs) + { + if (Open) + { + CloseStorage(); + } + else + { + OpenStorage(); + } + return true; + } + + private void CloseStorage() + { + Open = false; + var entities = Owner.EntityManager.GetEntities(entityQuery); + int count = 0; + foreach (var entity in entities) + { + if (!AddToContents(entity)) + { + continue; + } + count++; + if (count >= StorageCapacityMax) + { + break; + } + } + ModifyComponents(); + } + + private void OpenStorage() + { + Open = true; + EmptyContents(); + ModifyComponents(); + } + + private void ModifyComponents() + { + if (Owner.TryGetComponent(out var collidableComponent)) + { + collidableComponent.CollisionEnabled = IsCollidableWhenOpen || !Open; + } + if (Owner.TryGetComponent(out var placeableSurfaceComponent)) + { + placeableSurfaceComponent.IsPlaceable = Open; + } + } + + private bool AddToContents(IEntity entity) + { + var collidableComponent = Owner.GetComponent(); + if(entity.TryGetComponent(out var entityCollidableComponent)) + { + if(collidableComponent.WorldAABB.Size.X < entityCollidableComponent.WorldAABB.Size.X + || collidableComponent.WorldAABB.Size.Y < entityCollidableComponent.WorldAABB.Size.Y) + { + return false; + } + + if (collidableComponent.WorldAABB.Left > entityCollidableComponent.WorldAABB.Left) + { + entity.Transform.WorldPosition += new Vector2(collidableComponent.WorldAABB.Left - entityCollidableComponent.WorldAABB.Left, 0); + } + else if (collidableComponent.WorldAABB.Right < entityCollidableComponent.WorldAABB.Right) + { + entity.Transform.WorldPosition += new Vector2(collidableComponent.WorldAABB.Right - entityCollidableComponent.WorldAABB.Right, 0); + } + if (collidableComponent.WorldAABB.Bottom > entityCollidableComponent.WorldAABB.Bottom) + { + entity.Transform.WorldPosition += new Vector2(0, collidableComponent.WorldAABB.Bottom - entityCollidableComponent.WorldAABB.Bottom); + } + else if (collidableComponent.WorldAABB.Top < entityCollidableComponent.WorldAABB.Top) + { + entity.Transform.WorldPosition += new Vector2(0, collidableComponent.WorldAABB.Top - entityCollidableComponent.WorldAABB.Top); + } + } + if (Contents.CanInsert(entity)) + { + Contents.Insert(entity); + return true; + } + return false; + } + + private void EmptyContents() + { + while (Contents.ContainedEntities.Count > 0 ) + { + var containedEntity = Contents.ContainedEntities.First(); + Contents.Remove(containedEntity); + } + } + + public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null) + { + base.HandleMessage(message, netChannel, component); + + switch (message) + { + case RelayMovementEntityMessage msg: + if(msg.Entity.TryGetComponent(out var handsComponent)) + { + OpenStorage(); + } + break; + } + } + } +} diff --git a/Content.Server/GameObjects/Components/GUI/ServerHandsComponent.cs b/Content.Server/GameObjects/Components/GUI/ServerHandsComponent.cs index 98f4a0cc07..2f77d9ed7c 100644 --- a/Content.Server/GameObjects/Components/GUI/ServerHandsComponent.cs +++ b/Content.Server/GameObjects/Components/GUI/ServerHandsComponent.cs @@ -248,6 +248,10 @@ namespace Content.Server.GameObjects // TODO: The item should be dropped to the container our owner is in, if any. item.Owner.Transform.GridPosition = Owner.Transform.GridPosition; + if (item.Owner.TryGetComponent(out var spriteComponent)) + { + spriteComponent.RenderOrder = item.Owner.EntityManager.CurrentTick.Value; + } Dirty(); return true; diff --git a/Content.Server/GameObjects/Components/Items/Storage/ItemComponent.cs b/Content.Server/GameObjects/Components/Items/Storage/ItemComponent.cs index eb46eb6e1b..5fa3ad9df2 100644 --- a/Content.Server/GameObjects/Components/Items/Storage/ItemComponent.cs +++ b/Content.Server/GameObjects/Components/Items/Storage/ItemComponent.cs @@ -6,10 +6,13 @@ using Content.Server.GameObjects.EntitySystems; using Robust.Shared.GameObjects; using System; using Content.Shared.GameObjects.Components.Items; +using Content.Server.GameObjects.Components; +using Robust.Server.GameObjects; +using Robust.Shared.Maths; namespace Content.Server.GameObjects { - public class ItemComponent : StoreableComponent, IAttackHand + public class ItemComponent : StoreableComponent, IAttackHand, IAfterAttack { public override string Name => "Item"; public override uint? NetID => ContentNetIDs.ITEM; @@ -87,5 +90,38 @@ namespace Content.Server.GameObjects { return new ItemComponentState(EquippedPrefix); } + + public void AfterAttack(AfterAttackEventArgs eventArgs) + { + if (!eventArgs.User.TryGetComponent(out var handComponent)) + { + return; + } + if (!eventArgs.Attacked.TryGetComponent(out var placeableSurfaceComponent)) + { + return; + } + handComponent.Drop(handComponent.ActiveIndex); + Owner.Transform.WorldPosition = eventArgs.ClickLocation.Position; + return; + } + + public void Fumble() + { + if (Owner.TryGetComponent(out var physicsComponent)) + { + physicsComponent.LinearVelocity += RandomOffset(); + } + } + + private Vector2 RandomOffset() + { + return new Vector2(RandomOffset(), RandomOffset()); + float RandomOffset() + { + var size = 15.0F; + return (new Random().NextFloat() * size) - size / 2; + } + } } } diff --git a/Content.Server/GameObjects/Components/Items/Storage/ServerStorageComponent.cs b/Content.Server/GameObjects/Components/Items/Storage/ServerStorageComponent.cs index 3973619d47..4c28e07c8f 100644 --- a/Content.Server/GameObjects/Components/Items/Storage/ServerStorageComponent.cs +++ b/Content.Server/GameObjects/Components/Items/Storage/ServerStorageComponent.cs @@ -17,6 +17,7 @@ using System.Collections.Generic; using Content.Shared.Interfaces; using Robust.Shared.GameObjects.EntitySystemMessages; using Robust.Shared.ViewVariables; +using Content.Server.GameObjects.Components; namespace Content.Server.GameObjects { @@ -131,13 +132,20 @@ namespace Content.Server.GameObjects /// /// /// - bool IAttackBy.AttackBy(AttackByEventArgs eventArgs) + public bool AttackBy(AttackByEventArgs eventArgs) { _ensureInitialCalculated(); Logger.DebugS("Storage", "Storage (UID {0}) attacked by user (UID {1}) with entity (UID {2}).", Owner.Uid, eventArgs.User.Uid, eventArgs.AttackWith.Uid); - if (!eventArgs.User.TryGetComponent(out HandsComponent hands)) + if(Owner.TryGetComponent(out var placeableSurfaceComponent)) + { return false; + } + + if (!eventArgs.User.TryGetComponent(out HandsComponent hands)) + { + return false; + } //Check that we can drop the item from our hands first otherwise we obviously cant put it inside if (CanInsert(hands.GetActiveHand.Owner) && hands.Drop(hands.ActiveIndex)) @@ -324,10 +332,5 @@ namespace Content.Server.GameObjects _storageInitialCalculated = true; } - - public bool Attackby(AttackByEventArgs eventArgs) - { - throw new System.NotImplementedException(); - } } } diff --git a/Content.Server/GameObjects/Components/PlaceableSurfaceComponent.cs b/Content.Server/GameObjects/Components/PlaceableSurfaceComponent.cs new file mode 100644 index 0000000000..645e6e5282 --- /dev/null +++ b/Content.Server/GameObjects/Components/PlaceableSurfaceComponent.cs @@ -0,0 +1,20 @@ +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization; + +namespace Content.Server.GameObjects.Components +{ + public class PlaceableSurfaceComponent : Component + { + public override string Name => "PlaceableSurface"; + + private bool _isPlaceable; + public bool IsPlaceable { get => _isPlaceable; set => _isPlaceable = value; } + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + + serializer.DataField(ref _isPlaceable, "IsPlaceable", true); + } + } +} diff --git a/Content.Server/GameObjects/Components/Projectiles/ThrownItemComponent.cs b/Content.Server/GameObjects/Components/Projectiles/ThrownItemComponent.cs index 6eac36d453..525b99e6c0 100644 --- a/Content.Server/GameObjects/Components/Projectiles/ThrownItemComponent.cs +++ b/Content.Server/GameObjects/Components/Projectiles/ThrownItemComponent.cs @@ -2,6 +2,7 @@ using Content.Server.GameObjects.Components.Projectiles; using Content.Shared.GameObjects; using Content.Shared.Physics; +using Robust.Server.GameObjects; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.GameObjects.Components; @@ -25,9 +26,10 @@ namespace Content.Server.GameObjects.Components // after impacting the first object. // For realism this should actually be changed when the velocity of the object is less than a threshold. // This would allow ricochets off walls, and weird gravity effects from slowing the object. - if (collidedwith.Count > 0 && Owner.TryGetComponent(out ICollidableComponent body)) + if (collidedwith.Count > 0 && Owner.TryGetComponent(out CollidableComponent body)) { - body.CollisionMask &= (int) ~CollisionGroup.Mob; + body.CollisionMask &= (int)~CollisionGroup.Mob; + body.IsScrapingFloor = true; // KYS, your job is finished. Owner.RemoveComponent(); diff --git a/Content.Server/GameObjects/Components/Weapon/Ranged/Hitscan/HitscanWeaponComponent.cs b/Content.Server/GameObjects/Components/Weapon/Ranged/Hitscan/HitscanWeaponComponent.cs index f539a37955..c18906e904 100644 --- a/Content.Server/GameObjects/Components/Weapon/Ranged/Hitscan/HitscanWeaponComponent.cs +++ b/Content.Server/GameObjects/Components/Weapon/Ranged/Hitscan/HitscanWeaponComponent.cs @@ -16,6 +16,7 @@ using Robust.Shared.GameObjects; using Content.Server.GameObjects.EntitySystems; using Content.Server.GameObjects.Components.Power; using Content.Shared.Interfaces; +using Content.Shared.Physics; namespace Content.Server.GameObjects.Components.Weapon.Ranged.Hitscan { @@ -84,7 +85,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Hitscan var userPosition = user.Transform.WorldPosition; //Remember world positions are ephemeral and can only be used instantaneously var angle = new Angle(clickLocation.Position - userPosition); - var ray = new Ray(userPosition, angle.ToVec(), 0); //TODO set the CollsionMask for this ray. + var ray = new Ray(userPosition, angle.ToVec(), (int)(CollisionGroup.Grid | CollisionGroup.Mob)); var rayCastResults = IoCManager.Resolve().IntersectRay(ray, MaxLength, Owner.Transform.GetMapTransform().Owner); diff --git a/Content.Server/GameObjects/EntitySystems/HandsSystem.cs b/Content.Server/GameObjects/EntitySystems/HandsSystem.cs index b45d014211..37d92c7006 100644 --- a/Content.Server/GameObjects/EntitySystems/HandsSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/HandsSystem.cs @@ -109,22 +109,16 @@ namespace Content.Server.GameObjects.EntitySystems { var ent = ((IPlayerSession) session).AttachedEntity; - if(ent == null || !ent.IsValid()) + if (ent == null || !ent.IsValid()) + { return; + } if (!ent.TryGetComponent(out HandsComponent handsComp)) + { return; - - var transform = ent.Transform; - - if (transform.GridPosition.InRange(coords, InteractionSystem.INTERACTION_RANGE)) - { - handsComp.Drop(handsComp.ActiveIndex, coords); - } - else - { - handsComp.Drop(handsComp.ActiveIndex); } + handsComp.Drop(handsComp.ActiveIndex); } private static void HandleActivateItem(ICommonSession session) @@ -163,22 +157,17 @@ namespace Content.Server.GameObjects.EntitySystems if (!throwEnt.TryGetComponent(out CollidableComponent colComp)) { - colComp = throwEnt.AddComponent(); - - if(!colComp.Running) - colComp.Startup(); + return; } colComp.CollisionEnabled = true; - colComp.CollisionLayer |= (int)CollisionGroup.Items; - colComp.CollisionMask |= (int)CollisionGroup.Grid; - // I can now collide with player, so that i can do damage. - colComp.CollisionMask |= (int) CollisionGroup.Mob; if (!throwEnt.TryGetComponent(out ThrownItemComponent projComp)) { projComp = throwEnt.AddComponent(); + colComp.CollisionMask |= (int)CollisionGroup.Mob; + colComp.IsScrapingFloor = false; } projComp.IgnoreEntity(plyEnt); diff --git a/Content.Shared/Content.Shared.csproj b/Content.Shared/Content.Shared.csproj index 7daf2b646e..00dda7b7e2 100644 --- a/Content.Shared/Content.Shared.csproj +++ b/Content.Shared/Content.Shared.csproj @@ -122,5 +122,6 @@ Robust.Shared + \ No newline at end of file diff --git a/Content.Shared/GameObjects/Components/Storage/SharedStorageComponent.cs b/Content.Shared/GameObjects/Components/Storage/SharedStorageComponent.cs index 6ae4f8810c..b987fc761e 100644 --- a/Content.Shared/GameObjects/Components/Storage/SharedStorageComponent.cs +++ b/Content.Shared/GameObjects/Components/Storage/SharedStorageComponent.cs @@ -7,7 +7,7 @@ namespace Content.Shared.GameObjects.Components.Storage { public abstract class SharedStorageComponent : Component { - public sealed override string Name => "Storage"; + public override string Name => "Storage"; public override uint? NetID => ContentNetIDs.INVENTORY; public override Type StateType => typeof(StorageComponentState); diff --git a/Content.Shared/GameObjects/ContentNetIDs.cs b/Content.Shared/GameObjects/ContentNetIDs.cs index 5fc2a2d496..f25e205bad 100644 --- a/Content.Shared/GameObjects/ContentNetIDs.cs +++ b/Content.Shared/GameObjects/ContentNetIDs.cs @@ -17,5 +17,6 @@ public const uint SOUND = 1012; public const uint ITEM = 1013; public const uint CLOTHING = 1014; + public const uint ENTITYSTORAGE = 1015; } } diff --git a/Content.Shared/Physics/CollisionGroup.cs b/Content.Shared/Physics/CollisionGroup.cs index 08d481e19c..1cedeb6e83 100644 --- a/Content.Shared/Physics/CollisionGroup.cs +++ b/Content.Shared/Physics/CollisionGroup.cs @@ -8,10 +8,10 @@ namespace Content.Shared.Physics [Flags] public enum CollisionGroup { - None = 0x0000, - Grid = 0x0001, // Walls - Mob = 0x0002, // Mobs, like the player or NPCs - Fixture = 0x0004, // wall fixtures, like APC or posters - Items = 0x008, // Items on the ground + None = 0, + Grid = 1, // Walls + Mob = 2, // Mobs, like the player or NPCs + Fixture = 4, // wall fixtures, like APC or posters + Items = 8 // Items on the ground } } diff --git a/Resources/Prototypes/Entities/Items.yml b/Resources/Prototypes/Entities/Items.yml index 7a24945ac8..1ebd8abe2a 100644 --- a/Resources/Prototypes/Entities/Items.yml +++ b/Resources/Prototypes/Entities/Items.yml @@ -6,6 +6,11 @@ Size: 5 - type: Clickable - type: BoundingBox + aabb: "-0.15,-0.15,0.15,0.15" + - type: Collidable + mask: 5 + layer: 8 + IsScrapingFloor: true - type: Physics mass: 5 - type: Sprite diff --git a/Resources/Prototypes/Entities/Mobs.yml b/Resources/Prototypes/Entities/Mobs.yml index 86af247a7e..3f8c8e439b 100644 --- a/Resources/Prototypes/Entities/Mobs.yml +++ b/Resources/Prototypes/Entities/Mobs.yml @@ -43,6 +43,8 @@ mass: 85 - type: Collidable + mask: 3 + layer: 2 DebugColor: "#0000FF" - type: Input diff --git a/Resources/Prototypes/Entities/closet.yml b/Resources/Prototypes/Entities/closet.yml index 9057e27a54..2f102c1969 100644 --- a/Resources/Prototypes/Entities/closet.yml +++ b/Resources/Prototypes/Entities/closet.yml @@ -17,12 +17,13 @@ - type: BoundingBox aabb: "-0.5,-0.25,0.5,0.25" - type: Collidable - IsInteractingWithFloor: true - - type: Storage - Capacity: 80 + mask: 11 + IsScrapingFloor: true - type: Physics mass: 25 Anchored: false + - type: EntityStorage + - type: PlaceableSurface placement: snap: diff --git a/Resources/Prototypes/Entities/table.yml b/Resources/Prototypes/Entities/table.yml index 951d87aad6..9ffc7ce2d7 100644 --- a/Resources/Prototypes/Entities/table.yml +++ b/Resources/Prototypes/Entities/table.yml @@ -3,6 +3,7 @@ name: "worktop" components: - type: Clickable + - type: PlaceableSurface - type: Sprite netsync: false sprite: Buildings/table_solid.rsi