Storage Component (toolboxes, backpacks, etc) (#57)
* Fix Finish and update submodule Comments code, changes network messages FIXES THE USE INTERACTION SO YOU CAN ACTUALLY ACTIVATE THINGS NOW Need engine commits We'll figure out what to do about this shit later eh mates? Maybe have a script in the build process that automatically moves them over to godot/scenes Changes some prototypes and fixes stuff Fixes some more bugs, makes storage values show up properly Part 3 Part 2 Storage Take 1 * Doneso
This commit is contained in:
committed by
Pieter-Jan Briers
parent
74193d1182
commit
ea05c593aa
@@ -74,6 +74,8 @@
|
||||
<Compile Include="GameObjects\Components\Interactable\Tools\WelderComponent.cs" />
|
||||
<Compile Include="GameObjects\Components\Interactable\Tools\WirecutterComponent.cs" />
|
||||
<Compile Include="GameObjects\Components\Interactable\Tools\WrenchComponent.cs" />
|
||||
<Compile Include="GameObjects\Components\Items\Storage\StoreableComponent.cs" />
|
||||
<Compile Include="GameObjects\Components\Items\Storage\ServerStorageComponent.cs" />
|
||||
<Compile Include="GameObjects\Components\Power\PowerStorageComponent.cs" />
|
||||
<Compile Include="GameObjects\Components\Power\PowerGeneratorComponent.cs" />
|
||||
<Compile Include="GameObjects\Components\Power\PowerDevice.cs" />
|
||||
@@ -93,7 +95,7 @@
|
||||
<Compile Include="Interfaces\GameObjects\Components\Items\IItemComponent.cs" />
|
||||
<Compile Include="GameObjects\Components\Items\ServerHandsComponent.cs" />
|
||||
<Compile Include="GameObjects\Components\Items\InventoryComponent.cs" />
|
||||
<Compile Include="GameObjects\Components\Items\ItemComponent.cs" />
|
||||
<Compile Include="GameObjects\Components\Items\Storage\ItemComponent.cs" />
|
||||
<Compile Include="GameObjects\Components\Damage\DamageableComponent.cs" />
|
||||
<Compile Include="GameObjects\Components\Damage\DestructibleComponent.cs" />
|
||||
<Compile Include="GameObjects\Components\Damage\ResistanceSet.cs" />
|
||||
|
||||
@@ -55,9 +55,11 @@ namespace Content.Server
|
||||
factory.Register<InventoryComponent>();
|
||||
factory.RegisterReference<InventoryComponent, IInventoryComponent>();
|
||||
|
||||
factory.Register<StoreableComponent>();
|
||||
factory.Register<ItemComponent>();
|
||||
factory.RegisterReference<ItemComponent, IItemComponent>();
|
||||
|
||||
factory.RegisterReference<ItemComponent, StoreableComponent>();
|
||||
|
||||
factory.Register<DamageableComponent>();
|
||||
factory.Register<DestructibleComponent>();
|
||||
factory.Register<TemperatureComponent>();
|
||||
@@ -83,6 +85,8 @@ namespace Content.Server
|
||||
factory.Register<ProjectileWeaponComponent>();
|
||||
factory.Register<ProjectileComponent>();
|
||||
factory.Register<MeleeWeaponComponent>();
|
||||
|
||||
factory.Register<ServerStorageComponent>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Server.Interfaces.GameObjects;
|
||||
using Content.Shared.GameObjects;
|
||||
using SS14.Server.Interfaces.GameObjects;
|
||||
using SS14.Server.Interfaces.Player;
|
||||
using SS14.Shared.GameObjects;
|
||||
using SS14.Shared.Input;
|
||||
using SS14.Shared.Interfaces.GameObjects;
|
||||
using SS14.Shared.Interfaces.Network;
|
||||
using SS14.Shared.IoC;
|
||||
using SS14.Shared.Utility;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
@@ -81,6 +86,8 @@ namespace Content.Server.GameObjects
|
||||
return slot.Item;
|
||||
}
|
||||
|
||||
public IItemComponent GetActiveHand => GetHand(ActiveIndex);
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates over the hand keys, returning the active hand first.
|
||||
/// </summary>
|
||||
@@ -233,19 +240,40 @@ namespace Content.Server.GameObjects
|
||||
ActiveIndex = orderedHands[index];
|
||||
}
|
||||
|
||||
public override void HandleMessage(object owner, ComponentMessage message)
|
||||
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null)
|
||||
{
|
||||
base.HandleMessage(owner, message);
|
||||
|
||||
base.HandleMessage(message, netChannel, component);
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case ClientChangedHandMsg msg:
|
||||
if (HasHand(msg.Index))
|
||||
ActiveIndex = msg.Index;
|
||||
break;
|
||||
{
|
||||
var playerMan = IoCManager.Resolve<IPlayerManager>();
|
||||
var session = playerMan.GetSessionByChannel(netChannel);
|
||||
var playerentity = session.AttachedEntity;
|
||||
|
||||
if (playerentity == Owner && HasHand(msg.Index))
|
||||
ActiveIndex = msg.Index;
|
||||
break;
|
||||
}
|
||||
|
||||
case ActivateInhandMsg msg:
|
||||
{
|
||||
var playerMan = IoCManager.Resolve<IPlayerManager>();
|
||||
var session = playerMan.GetSessionByChannel(netChannel);
|
||||
var playerentity = session.AttachedEntity;
|
||||
var used = GetActiveHand?.Owner;
|
||||
|
||||
if (playerentity == Owner && used != null)
|
||||
{
|
||||
InteractionSystem.TryUseInteraction(Owner, used);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
//Boundkeychangedmsg only works for the player entity
|
||||
case BoundKeyChangedMsg msg:
|
||||
if(msg.State != BoundKeyState.Down)
|
||||
if (msg.State != BoundKeyState.Down)
|
||||
return;
|
||||
switch (msg.Function)
|
||||
{
|
||||
@@ -255,6 +283,13 @@ namespace Content.Server.GameObjects
|
||||
case BoundKeyFunctions.Drop:
|
||||
Drop(ActiveIndex);
|
||||
break;
|
||||
case BoundKeyFunctions.ActivateItemInHand:
|
||||
var used = GetActiveHand?.Owner;
|
||||
if(used != null)
|
||||
{
|
||||
InteractionSystem.TryUseInteraction(Owner, used);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
using Content.Server.Interfaces.GameObjects;
|
||||
using SS14.Server.Interfaces.GameObjects;
|
||||
using SS14.Shared.GameObjects;
|
||||
using System;
|
||||
using SS14.Shared.Interfaces.GameObjects;
|
||||
|
||||
namespace Content.Server.GameObjects
|
||||
{
|
||||
public class ItemComponent : Component, IItemComponent, EntitySystems.IAttackHand
|
||||
public class ItemComponent : StoreableComponent, IItemComponent, EntitySystems.IAttackHand
|
||||
{
|
||||
public override string Name => "Item";
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Server.Interfaces.GameObjects;
|
||||
using Content.Shared.GameObjects.Components.Storage;
|
||||
using SS14.Server.GameObjects;
|
||||
using SS14.Server.GameObjects.Components.Container;
|
||||
using SS14.Server.Interfaces.Player;
|
||||
using SS14.Shared.GameObjects;
|
||||
using SS14.Shared.GameObjects.Serialization;
|
||||
using SS14.Shared.Interfaces.GameObjects;
|
||||
using SS14.Shared.Interfaces.Network;
|
||||
using SS14.Shared.IoC;
|
||||
using SS14.Shared.Log;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Content.Server.GameObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// Storage component for containing entities within this one, matches a UI on the client which shows stored entities
|
||||
/// </summary>
|
||||
public class ServerStorageComponent : SharedStorageComponent, IAttackby, IUse
|
||||
{
|
||||
private Container storage;
|
||||
|
||||
private int StorageUsed = 0;
|
||||
private int StorageCapacityMax = 10000;
|
||||
|
||||
public override void OnAdd()
|
||||
{
|
||||
base.OnAdd();
|
||||
|
||||
storage = Container.Create("storagebase", Owner);
|
||||
}
|
||||
|
||||
public override void ExposeData(EntitySerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
|
||||
serializer.DataField(ref StorageCapacityMax, "Capacity", 10000);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes from the storage container and updates the stored value
|
||||
/// </summary>
|
||||
/// <param name="toremove"></param>
|
||||
/// <returns></returns>
|
||||
bool Remove(IEntity toremove)
|
||||
{
|
||||
if(storage.Remove(toremove))
|
||||
{
|
||||
StorageUsed -= toremove.GetComponent<StoreableComponent>().ObjectSize;
|
||||
UpdateClientInventory();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts into the storage container
|
||||
/// </summary>
|
||||
/// <param name="toinsert"></param>
|
||||
/// <returns></returns>
|
||||
bool Insert(IEntity toinsert)
|
||||
{
|
||||
if(CanInsert(toinsert) && storage.Insert(toinsert))
|
||||
{
|
||||
StorageUsed += toinsert.GetComponent<StoreableComponent>().ObjectSize;
|
||||
UpdateClientInventory();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies the object can be inserted by checking if it is storeable and if it keeps under the capacity limit
|
||||
/// </summary>
|
||||
/// <param name="toinsert"></param>
|
||||
/// <returns></returns>
|
||||
bool CanInsert(IEntity toinsert)
|
||||
{
|
||||
if(toinsert.TryGetComponent(out StoreableComponent store))
|
||||
{
|
||||
if (store.ObjectSize <= (StorageCapacityMax - StorageUsed))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts storeable entities into this storage container if possible, otherwise return to the hand of the user
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <param name="attackwith"></param>
|
||||
/// <returns></returns>
|
||||
bool IAttackby.Attackby(IEntity user, IEntity attackwith)
|
||||
{
|
||||
var hands = user.GetComponent<HandsComponent>();
|
||||
//Check that we can drop the item from our hands first otherwise we obviously cant put it inside
|
||||
if(hands.Drop(hands.ActiveIndex))
|
||||
{
|
||||
var inserted = Insert(attackwith);
|
||||
if (inserted)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Return the object to the hand since its too big or something like that
|
||||
hands.PutInHand(attackwith.GetComponent<IItemComponent>());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a message to open the storage UI
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <returns></returns>
|
||||
bool IUse.UseEntity(IEntity user)
|
||||
{
|
||||
SendNetworkMessage(new OpenStorageUIMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the storage UI on all clients telling them of the entities stored in this container
|
||||
/// </summary>
|
||||
private void UpdateClientInventory()
|
||||
{
|
||||
Dictionary<EntityUid, int> storedentities = new Dictionary<EntityUid, int>();
|
||||
foreach (var entities in storage.ContainedEntities)
|
||||
{
|
||||
storedentities.Add(entities.Uid, entities.GetComponent<StoreableComponent>().ObjectSize);
|
||||
}
|
||||
SendNetworkMessage(new StorageHeldItemsMessage(storedentities, StorageUsed, StorageCapacityMax));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Receives messages to remove entities from storage, verifies the player can do them,
|
||||
/// and puts the removed entity in hand or on the ground
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="netChannel"></param>
|
||||
/// <param name="component"></param>
|
||||
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null)
|
||||
{
|
||||
base.HandleMessage(message, netChannel, component);
|
||||
|
||||
switch (message)
|
||||
{
|
||||
|
||||
case RemoveEntityMessage msg:
|
||||
var playerMan = IoCManager.Resolve<IPlayerManager>();
|
||||
var session = playerMan.GetSessionByChannel(netChannel);
|
||||
var playerentity = session.AttachedEntity;
|
||||
|
||||
var ourtransform = Owner.GetComponent<TransformComponent>();
|
||||
var playertransform = playerentity.GetComponent<TransformComponent>();
|
||||
|
||||
if (playertransform.LocalPosition.InRange(ourtransform.LocalPosition, 2)
|
||||
&& (ourtransform.IsMapTransform || playertransform.ContainsEntity(ourtransform)))
|
||||
{
|
||||
var remove = (RemoveEntityMessage)message;
|
||||
var entity = IoCManager.Resolve<IEntityManager>().GetEntity(remove.EntityUid);
|
||||
if (entity != null && storage.Contains(entity))
|
||||
{
|
||||
Remove(entity);
|
||||
UpdateClientInventory();
|
||||
|
||||
var item = entity.GetComponent<IItemComponent>();
|
||||
if (item != null && playerentity.TryGetComponent(out HandsComponent hands))
|
||||
{
|
||||
if (hands.PutInHand(item))
|
||||
return;
|
||||
}
|
||||
|
||||
entity.GetComponent<TransformComponent>().WorldPosition = Owner.GetComponent<TransformComponent>().WorldPosition;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using SS14.Shared.GameObjects;
|
||||
using SS14.Shared.GameObjects.Serialization;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Content.Server.GameObjects
|
||||
{
|
||||
public class StoreableComponent : Component
|
||||
{
|
||||
public override string Name => "Storeable";
|
||||
|
||||
public int ObjectSize = 0;
|
||||
|
||||
public override void ExposeData(EntitySerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
|
||||
serializer.DataField(ref ObjectSize, "Size", 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enum for the storage capacity of various containers
|
||||
/// </summary>
|
||||
public enum ReferenceSizes
|
||||
{
|
||||
Wallet = 4,
|
||||
Pocket = 12,
|
||||
Box = 24,
|
||||
Toolbox = 60,
|
||||
Backpack = 100,
|
||||
NoStoring = 9999
|
||||
}
|
||||
}
|
||||
@@ -148,32 +148,36 @@ namespace Content.Server.GameObjects.EntitySystems
|
||||
{
|
||||
return;
|
||||
}
|
||||
var item = hands.GetHand(hands.ActiveIndex)?.Owner;
|
||||
var item = hands.GetActiveHand?.Owner;
|
||||
|
||||
|
||||
if (!MobCanInteract(player))
|
||||
return;
|
||||
//TODO: Mob status code that allows or rejects interactions based on current mob status
|
||||
//Check if client should be able to see that object to click on it in the first place, prevent using locaters by firing a laser or something
|
||||
|
||||
//Off entity click handling
|
||||
if (attacked == null)
|
||||
|
||||
//Clicked on empty space behavior, try using ranged attack
|
||||
if (attacked == null && item != null)
|
||||
{
|
||||
if(item != null)
|
||||
{
|
||||
//AFTERATTACK: Check if we clicked on an empty location, if so the only interaction we can do is afterattack
|
||||
InteractAfterattack(player, item, msg.Coordinates);
|
||||
return;
|
||||
}
|
||||
//AFTERATTACK: Check if we clicked on an empty location, if so the only interaction we can do is afterattack
|
||||
InteractAfterattack(player, item, msg.Coordinates);
|
||||
return;
|
||||
}
|
||||
//USE: Check if we clicked on the item we are holding in our active hand to use it
|
||||
else if(attacked == item && item != null)
|
||||
else if(attacked == null)
|
||||
{
|
||||
UseInteraction(player, item);
|
||||
return;
|
||||
}
|
||||
|
||||
//Verify attacked object is on the map if we managed to click on it somehow
|
||||
if (!attacked.GetComponent<TransformComponent>().IsMapTransform)
|
||||
{
|
||||
Logger.Warning(string.Format("Player named {0} clicked on object {1} that isn't currently on the map somehow", player.Name, attacked.Name));
|
||||
return;
|
||||
}
|
||||
|
||||
//Check if ClickLocation is in object bounds here, if not lets log as warning and see why
|
||||
if(attacked != null && attacked.TryGetComponent(out BoundingBoxComponent boundingbox))
|
||||
if (attacked.TryGetComponent(out BoundingBoxComponent boundingbox))
|
||||
{
|
||||
if (!boundingbox.WorldAABB.Contains(msg.Coordinates.Position))
|
||||
{
|
||||
@@ -208,6 +212,15 @@ namespace Content.Server.GameObjects.EntitySystems
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// TODO function for blocking activity based on mob status
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private static bool MobCanInteract(IEntity user)
|
||||
{
|
||||
return true; //Hook into future planned mob status system
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// We didn't click on any entity, try doing an afterattack on the click location
|
||||
/// </summary>
|
||||
@@ -276,6 +289,20 @@ namespace Content.Server.GameObjects.EntitySystems
|
||||
//Else check damage component to see if we damage if not attackby, and if so can we attack object
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Activates the Use behavior of an object
|
||||
/// Verifies that the user is capable of doing the use interaction first
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <param name="used"></param>
|
||||
public static void TryUseInteraction(IEntity user, IEntity used)
|
||||
{
|
||||
if(user != null && used != null && MobCanInteract(user))
|
||||
{
|
||||
UseInteraction(user, used);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Activates/Uses an object in control/possession of a user
|
||||
/// If the item has the IUse interface on one of its components we use the object in our hand
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using SS14.Shared.Interfaces.GameObjects;
|
||||
using SS14.Shared.Interfaces.GameObjects;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Content.Server.Interfaces.GameObjects
|
||||
@@ -22,6 +22,11 @@ namespace Content.Server.Interfaces.GameObjects
|
||||
/// <returns>The item in the held, null if no item is held</returns>
|
||||
IItemComponent GetHand(string index);
|
||||
|
||||
/// <summary>
|
||||
/// Gets item held by the current active hand
|
||||
/// </summary>
|
||||
IItemComponent GetActiveHand { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Puts an item into any empty hand, preferring the active hand.
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user