From 37df61113e6a253ef2ced23e48c970e24b71144b Mon Sep 17 00:00:00 2001 From: clusterfack <8516830+clusterfack@users.noreply.github.com> Date: Thu, 13 Dec 2018 07:47:19 -0600 Subject: [PATCH] Species Component (#130) * Fix this fug oksure Creates the initial species component, damage states, and threshold templates and hooks them into the damageable component * More rebase fixes * test * Pre future rebase * Please * Lol * Lol2 * SHADERS * Update Engine * yml file * Fix an initialization issue, injects dependencies * Fix error in loading shaders * Makes a master character ui controller component added upon client attachment to entity and remove upon client detachment from entity * Fixes just about everytrhing * Address PJB's comments * geeze * Make overlays work in worldspace instead of screen space and not cover user interfaces * update submodule --- Content.Client/Content.Client.csproj | 8 +- Content.Client/EntryPoint.cs | 48 +++++- .../Components/Actor/CharacterInterface.cs | 108 ++++++++++++ .../Components/DamageableComponent.cs | 31 ++++ .../Inventory/ClientInventoryComponent.cs | 59 +++---- .../Components/Mobs/ICharacterUI.cs | 21 +++ .../GameObjects/Components/Mobs/SpeciesUI.cs | 160 ++++++++++++++++++ .../Graphics/Overlays/CircleMaskOverlay.cs | 32 ++++ .../Graphics/Overlays/GradientCircleMask.cs | 32 ++++ Content.Server/Content.Server.csproj | 6 + Content.Server/EntryPoint.cs | 1 + .../Components/Damage/DamageThreshold.cs | 61 +++++++ .../Components/Damage/DamageableComponent.cs | 90 +++------- .../Damage/DestructibleComponent.cs | 13 +- .../Components/Damage/ResistanceSet.cs | 18 +- .../Components/Mobs/DamageStates.cs | 104 ++++++++++++ .../DamageThresholdTemplates.cs | 65 +++++++ .../DamageThresholdTemplates/HumanTemplate.cs | 66 ++++++++ .../Components/Mobs/SpeciesComponent.cs | 114 +++++++++++++ .../Projectiles/ProjectileComponent.cs | 1 + .../Projectiles/ThrownItemComponent.cs | 1 + .../Weapon/Melee/MeleeWeaponComponent.cs | 1 + .../Ranged/Hitscan/HitscanWeaponComponent.cs | 19 ++- .../EntitySystems/ActionBlockerSystem.cs | 47 +++++ .../EntitySystems/Click/InteractionSystem.cs | 16 +- .../Components/Damage/IDamageableComponent.cs | 3 +- Content.Shared/Content.Shared.csproj | 4 +- .../Components/Damage/DamageableComponent.cs | 41 +++++ Content.Shared/GameObjects/ContentNetIDs.cs | 1 + .../GameObjects/Messages/Mob/HealthHud.cs | 28 +++ Resources/Prototypes/Entities/Mobs.yml | 4 + Resources/Prototypes/Entities/Weapons.yml | 2 + Resources/Prototypes/Shaders/shaders.yml | 9 + Resources/Scenes/Mobs/CharacterWindow.tscn | 32 ++++ .../{Inventory => Mobs}/HumanInventory.tscn | 47 ++--- Resources/Scenes/Mobs/Species.tscn | 34 ++++ .../{Inventory => Mobs}/StorageSlot.tres | 0 .../{Inventory => Mobs}/StorageSlot.tscn | 0 Resources/Shaders/circlemask.gdsl | 16 ++ Resources/Shaders/gradientcirclemask.gdsl | 10 ++ Resources/Textures/Mob/UI/Human/human0.png | Bin 0 -> 233 bytes Resources/Textures/Mob/UI/Human/human1.png | Bin 0 -> 214 bytes Resources/Textures/Mob/UI/Human/human2.png | Bin 0 -> 214 bytes Resources/Textures/Mob/UI/Human/human3.png | Bin 0 -> 214 bytes Resources/Textures/Mob/UI/Human/human4.png | Bin 0 -> 214 bytes Resources/Textures/Mob/UI/Human/human5.png | Bin 0 -> 214 bytes Resources/Textures/Mob/UI/Human/human6-0.png | Bin 0 -> 214 bytes Resources/Textures/Mob/UI/Human/human6-1.png | Bin 0 -> 240 bytes .../Textures/Mob/UI/Human/humancrit-0.png | Bin 0 -> 252 bytes .../Textures/Mob/UI/Human/humancrit-1.png | Bin 0 -> 252 bytes Resources/Textures/Mob/UI/Human/humandead.png | Bin 0 -> 207 bytes engine | 2 +- 52 files changed, 1172 insertions(+), 183 deletions(-) create mode 100644 Content.Client/GameObjects/Components/Actor/CharacterInterface.cs create mode 100644 Content.Client/GameObjects/Components/DamageableComponent.cs rename Content.Client/GameObjects/Components/{ => HUD}/Inventory/ClientInventoryComponent.cs (87%) create mode 100644 Content.Client/GameObjects/Components/Mobs/ICharacterUI.cs create mode 100644 Content.Client/GameObjects/Components/Mobs/SpeciesUI.cs create mode 100644 Content.Client/Graphics/Overlays/CircleMaskOverlay.cs create mode 100644 Content.Client/Graphics/Overlays/GradientCircleMask.cs create mode 100644 Content.Server/GameObjects/Components/Damage/DamageThreshold.cs create mode 100644 Content.Server/GameObjects/Components/Mobs/DamageStates.cs create mode 100644 Content.Server/GameObjects/Components/Mobs/DamageThresholdTemplates/DamageThresholdTemplates.cs create mode 100644 Content.Server/GameObjects/Components/Mobs/DamageThresholdTemplates/HumanTemplate.cs create mode 100644 Content.Server/GameObjects/Components/Mobs/SpeciesComponent.cs create mode 100644 Content.Server/GameObjects/EntitySystems/ActionBlockerSystem.cs create mode 100644 Content.Shared/GameObjects/Components/Damage/DamageableComponent.cs create mode 100644 Content.Shared/GameObjects/Messages/Mob/HealthHud.cs create mode 100644 Resources/Prototypes/Shaders/shaders.yml create mode 100644 Resources/Scenes/Mobs/CharacterWindow.tscn rename Resources/Scenes/{Inventory => Mobs}/HumanInventory.tscn (53%) create mode 100644 Resources/Scenes/Mobs/Species.tscn rename Resources/Scenes/{Inventory => Mobs}/StorageSlot.tres (100%) rename Resources/Scenes/{Inventory => Mobs}/StorageSlot.tscn (100%) create mode 100644 Resources/Shaders/circlemask.gdsl create mode 100644 Resources/Shaders/gradientcirclemask.gdsl create mode 100644 Resources/Textures/Mob/UI/Human/human0.png create mode 100644 Resources/Textures/Mob/UI/Human/human1.png create mode 100644 Resources/Textures/Mob/UI/Human/human2.png create mode 100644 Resources/Textures/Mob/UI/Human/human3.png create mode 100644 Resources/Textures/Mob/UI/Human/human4.png create mode 100644 Resources/Textures/Mob/UI/Human/human5.png create mode 100644 Resources/Textures/Mob/UI/Human/human6-0.png create mode 100644 Resources/Textures/Mob/UI/Human/human6-1.png create mode 100644 Resources/Textures/Mob/UI/Human/humancrit-0.png create mode 100644 Resources/Textures/Mob/UI/Human/humancrit-1.png create mode 100644 Resources/Textures/Mob/UI/Human/humandead.png diff --git a/Content.Client/Content.Client.csproj b/Content.Client/Content.Client.csproj index 01eac19f9b..2228239cdd 100644 --- a/Content.Client/Content.Client.csproj +++ b/Content.Client/Content.Client.csproj @@ -72,8 +72,12 @@ + + + + + - @@ -81,6 +85,8 @@ + + diff --git a/Content.Client/EntryPoint.cs b/Content.Client/EntryPoint.cs index 556f6149bb..d3df4a3c74 100644 --- a/Content.Client/EntryPoint.cs +++ b/Content.Client/EntryPoint.cs @@ -1,4 +1,5 @@ using Content.Client.GameObjects; +using Content.Client.GameObjects.Components.Actor; using Content.Client.GameObjects.Components.Clothing; using Content.Client.GameObjects.Components.Construction; using Content.Client.GameObjects.Components.Power; @@ -15,11 +16,13 @@ using SS14.Client; using SS14.Client.Interfaces; using SS14.Client.Interfaces.Graphics.Overlays; using SS14.Client.Interfaces.Input; +using SS14.Client.Player; using SS14.Client.Utility; using SS14.Shared.ContentPack; using SS14.Shared.Interfaces.GameObjects; using SS14.Shared.IoC; using SS14.Shared.Prototypes; +using System; namespace Content.Client { @@ -34,7 +37,6 @@ namespace Content.Client var prototypes = IoCManager.Resolve(); factory.RegisterIgnore("Interactable"); - factory.RegisterIgnore("Damageable"); factory.RegisterIgnore("Destructible"); factory.RegisterIgnore("Temperature"); factory.RegisterIgnore("PowerTransfer"); @@ -68,15 +70,22 @@ namespace Content.Client factory.Register(); factory.Register(); factory.Register(); + factory.Register(); factory.Register(); factory.Register(); + factory.RegisterReference(); + factory.Register(); + factory.Register(); + factory.RegisterIgnore("Construction"); factory.RegisterIgnore("Apc"); factory.RegisterIgnore("Door"); factory.RegisterIgnore("PoweredLight"); factory.RegisterIgnore("Smes"); + factory.RegisterIgnore("Powercell"); + factory.RegisterIgnore("HandheldLight"); prototypes.RegisterIgnore("material"); @@ -87,6 +96,43 @@ namespace Content.Client IoCManager.BuildGraph(); IoCManager.Resolve().LoadParallax(); + IoCManager.Resolve().PlayerJoinedServer += SubscribePlayerAttachmentEvents; + } + + /// + /// Subscribe events to the player manager after the player manager is set up + /// + /// + /// + public void SubscribePlayerAttachmentEvents(object sender, EventArgs args) + { + IoCManager.Resolve().LocalPlayer.EntityAttached += AttachPlayerToEntity; + IoCManager.Resolve().LocalPlayer.EntityDetached += DetachPlayerFromEntity; + AttachPlayerToEntity(IoCManager.Resolve().LocalPlayer, EventArgs.Empty); + } + + /// + /// Add the character interface master which combines all character interfaces into one window + /// + /// + /// + public void AttachPlayerToEntity(object sender, EventArgs args) + { + var localplayer = (LocalPlayer)sender; + + localplayer.ControlledEntity?.AddComponent(); + } + + /// + /// Remove the character interface master from this entity now that we have detached ourselves from it + /// + /// + /// + public void DetachPlayerFromEntity(object sender, EventArgs args) + { + var localplayer = (LocalPlayer)sender; + //Wont work atm, controlled entity gets nulled before this event fires + localplayer.ControlledEntity?.RemoveComponent(); } public override void PostInit() diff --git a/Content.Client/GameObjects/Components/Actor/CharacterInterface.cs b/Content.Client/GameObjects/Components/Actor/CharacterInterface.cs new file mode 100644 index 0000000000..90c3303c5a --- /dev/null +++ b/Content.Client/GameObjects/Components/Actor/CharacterInterface.cs @@ -0,0 +1,108 @@ +using Content.Client.GameObjects.Components.Mobs; +using Content.Shared.Input; +using SS14.Client.Interfaces.Input; +using SS14.Client.UserInterface.CustomControls; +using SS14.Shared.GameObjects; +using SS14.Shared.Input; +using SS14.Shared.IoC; +using SS14.Shared.Utility; +using System.Collections.Generic; +using System.Linq; + +namespace Content.Client.GameObjects.Components.Actor +{ + /// + /// A semi-abstract component which gets added to entities upon attachment and collects all character + /// user interfaces into a single window and keybind for the user + /// + public class CharacterInterface : Component + { + public override string Name => "Character Interface Component"; + + /// + /// Stored keybind to open the menu on keypress + /// + private InputCmdHandler _openMenuCmdHandler; + + /// + /// Window to hold each of the character interfaces + /// + private SS14Window _window; + + /// + /// Create the window with all character UIs and bind it to a keypress + /// + public override void Initialize() + { + base.Initialize(); + + //Use all the character ui interfaced components to create the character window + var UIcomponents = Owner.GetAllComponents(); + _window = new CharacterWindow(UIcomponents); + + //Add to screen the window and hide it + _window.AddToScreen(); + _window.Close(); + + //Toggle window visible/invisible on keypress + _openMenuCmdHandler = InputCmdHandler.FromDelegate(session => { + if (_window.Visible) + { + _window.Close(); + } + else + { + _window.Open(); + } + }); + + //Set keybind to open character menu + var inputMgr = IoCManager.Resolve(); + inputMgr.SetInputCommand(ContentKeyFunctions.OpenCharacterMenu, _openMenuCmdHandler); + } + + /// + /// Dispose of window and the keypress binding + /// + public override void OnRemove() + { + base.OnRemove(); + + _window.Dispose(); + _window = null; + + var inputMgr = IoCManager.Resolve(); + inputMgr.SetInputCommand(ContentKeyFunctions.OpenCharacterMenu, null); + } + + /// + /// A window that collects and shows all the individual character user interfaces + /// + public class CharacterWindow : SS14Window + { + protected override ResourcePath ScenePath => new ResourcePath("/Scenes/Mobs/CharacterWindow.tscn"); + + public CharacterWindow(IEnumerable windowcomponents) + { + //TODO: sort window components by priority of window component + foreach(var element in windowcomponents.OrderByDescending(x => x.Priority)) + { + Contents.AddChild(element.Scene); + } + + HideOnClose = true; + } + } + } + + /// + /// Determines ordering of the character user interface, small values come sooner + /// + public enum UIPriority + { + First = 0, + Species = 100, + Inventory = 200, + Last = 99999 + } +} diff --git a/Content.Client/GameObjects/Components/DamageableComponent.cs b/Content.Client/GameObjects/Components/DamageableComponent.cs new file mode 100644 index 0000000000..70ab4811ce --- /dev/null +++ b/Content.Client/GameObjects/Components/DamageableComponent.cs @@ -0,0 +1,31 @@ +using Content.Shared.GameObjects; +using SS14.Shared.GameObjects; +using SS14.Shared.Interfaces.GameObjects; +using SS14.Shared.Interfaces.Network; +using System.Collections.Generic; + +namespace Content.Client.GameObjects +{ + /// + /// Fuck I really hate doing this + /// TODO: make sure the client only gets damageable component on the clientside entity for its player mob + /// + public class DamageableComponent : SharedDamageableComponent + { + /// + public override string Name => "Damageable"; + + public Dictionary CurrentDamage = new Dictionary(); + + public override void HandleComponentState(ComponentState state) + { + base.HandleComponentState(state); + + if(state is DamageComponentState) + { + var damagestate = (DamageComponentState)state; + CurrentDamage = damagestate.CurrentDamage; + } + } + } +} diff --git a/Content.Client/GameObjects/Components/Inventory/ClientInventoryComponent.cs b/Content.Client/GameObjects/Components/HUD/Inventory/ClientInventoryComponent.cs similarity index 87% rename from Content.Client/GameObjects/Components/Inventory/ClientInventoryComponent.cs rename to Content.Client/GameObjects/Components/HUD/Inventory/ClientInventoryComponent.cs index ab66cbee0b..1655160e98 100644 --- a/Content.Client/GameObjects/Components/Inventory/ClientInventoryComponent.cs +++ b/Content.Client/GameObjects/Components/HUD/Inventory/ClientInventoryComponent.cs @@ -1,15 +1,9 @@ using Content.Shared.GameObjects; -using Content.Shared.Input; -using SS14.Client.GameObjects; using SS14.Client.Interfaces.GameObjects.Components; -using SS14.Client.Interfaces.Input; using SS14.Client.UserInterface; using SS14.Client.UserInterface.Controls; -using SS14.Client.UserInterface.CustomControls; 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.Serialization; using SS14.Shared.Utility; @@ -20,21 +14,36 @@ using Content.Client.GameObjects.Components.Clothing; using SS14.Shared.Interfaces.Reflection; using static Content.Shared.GameObjects.Components.Inventory.EquipmentSlotDefines; using static Content.Shared.GameObjects.SharedInventoryComponent.ClientInventoryMessage; +using Content.Client.GameObjects.Components.Mobs; +using Content.Client.GameObjects.Components.Actor; namespace Content.Client.GameObjects { - public class ClientInventoryComponent : SharedInventoryComponent + /// + /// A character UI which shows items the user has equipped within his inventory + /// + public class ClientInventoryComponent : SharedInventoryComponent, ICharacterUI { private Dictionary _slots = new Dictionary(); + /// + /// Holds the godot control for the inventory window + /// private InventoryWindow _window; + private string _templateName = "HumanInventory"; //stored for serialization purposes - private InputCmdHandler _openMenuCmdHandler; + /// + /// Inventory template after being loaded from instance creator and string name + /// private Inventory _inventory; private ISpriteComponent _sprite; + //Relevant interface implementation for the character UI controller + public Control Scene => _window; + public UIPriority Priority => UIPriority.Inventory; + public override void OnRemove() { base.OnRemove(); @@ -42,22 +51,17 @@ namespace Content.Client.GameObjects _window.Dispose(); } - public override void OnAdd() - { - base.OnAdd(); - - _openMenuCmdHandler = InputCmdHandler.FromDelegate(session => { _window.AddToScreen(); _window.Open(); }); - } - public override void Initialize() { base.Initialize(); + //Loads inventory template var reflectionManager = IoCManager.Resolve(); var type = reflectionManager.LooseGetType(_templateName); DebugTools.Assert(type != null); _inventory = (Inventory)Activator.CreateInstance(type); + //Creates godot control class for inventory _window = new InventoryWindow(this); _window.CreateInventory(_inventory); @@ -120,21 +124,6 @@ namespace Content.Client.GameObjects } } - public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null) - { - var inputMgr = IoCManager.Resolve(); - switch (message) - { - case PlayerAttachedMsg _: - inputMgr.SetInputCommand(ContentKeyFunctions.OpenCharacterMenu, _openMenuCmdHandler); - break; - - case PlayerDetachedMsg _: - inputMgr.SetInputCommand(ContentKeyFunctions.OpenCharacterMenu, null); - break; - } - } - private void _setSlot(Slots slot, IEntity entity) { if (_sprite != null && entity.TryGetComponent(out ClothingComponent clothing)) @@ -178,7 +167,7 @@ namespace Content.Client.GameObjects /// /// Temporary window to hold the basis for inventory hud /// - private class InventoryWindow : SS14Window + private class InventoryWindow : Control { private int elements_x; @@ -187,13 +176,11 @@ namespace Content.Client.GameObjects private Dictionary InventorySlots = new Dictionary(); //ordered dictionary? private ClientInventoryComponent InventoryComponent; - protected override ResourcePath ScenePath => new ResourcePath("/Scenes/Inventory/HumanInventory.tscn"); + protected override ResourcePath ScenePath => new ResourcePath("/Scenes/Mobs/HumanInventory.tscn"); public InventoryWindow(ClientInventoryComponent inventory) { InventoryComponent = inventory; - - HideOnClose = true; } /// @@ -203,7 +190,7 @@ namespace Content.Client.GameObjects { elements_x = inventory.Columns; - GridContainer = (GridContainer)Contents.GetChild("PanelContainer").GetChild("CenterContainer").GetChild("GridContainer"); + GridContainer = (GridContainer)GetChild("CenterContainer").GetChild("GridContainer"); GridContainer.Columns = elements_x; IndexedSlots = new List(inventory.SlotMasks); @@ -304,7 +291,7 @@ namespace Content.Client.GameObjects public Slots Slot; public EntityUid EntityUid; - protected override ResourcePath ScenePath => new ResourcePath("/Scenes/Inventory/StorageSlot.tscn"); + protected override ResourcePath ScenePath => new ResourcePath("/Scenes/Mobs/StorageSlot.tscn"); public InventoryButton(Slots slot) { diff --git a/Content.Client/GameObjects/Components/Mobs/ICharacterUI.cs b/Content.Client/GameObjects/Components/Mobs/ICharacterUI.cs new file mode 100644 index 0000000000..e65e448110 --- /dev/null +++ b/Content.Client/GameObjects/Components/Mobs/ICharacterUI.cs @@ -0,0 +1,21 @@ +using Content.Client.GameObjects.Components.Actor; +using SS14.Client.UserInterface; + +namespace Content.Client.GameObjects.Components.Mobs +{ + /// + /// An interface which is gathered to assemble the character window from multiple components + /// + public interface ICharacterUI + { + /// + /// The godot control which holds the character user interface to be included in the window + /// + Control Scene { get; } + + /// + /// The order it will appear in the character UI, higher is lower + /// + UIPriority Priority { get; } + } +} diff --git a/Content.Client/GameObjects/Components/Mobs/SpeciesUI.cs b/Content.Client/GameObjects/Components/Mobs/SpeciesUI.cs new file mode 100644 index 0000000000..a8f498a168 --- /dev/null +++ b/Content.Client/GameObjects/Components/Mobs/SpeciesUI.cs @@ -0,0 +1,160 @@ +using Content.Client.GameObjects.Components.Actor; +using Content.Client.GameObjects.Components.Mobs; +using Content.Client.Graphics.Overlays; +using Content.Shared.GameObjects; +using SS14.Client.GameObjects; +using SS14.Client.Interfaces.Graphics.Overlays; +using SS14.Client.Interfaces.ResourceManagement; +using SS14.Client.Player; +using SS14.Client.ResourceManagement; +using SS14.Client.UserInterface; +using SS14.Client.UserInterface.Controls; +using SS14.Shared.GameObjects; +using SS14.Shared.Interfaces.GameObjects; +using SS14.Shared.Interfaces.Network; +using SS14.Shared.IoC; +using SS14.Shared.Log; +using SS14.Shared.Utility; +using System.Collections.Generic; + +namespace Content.Client.GameObjects +{ + /// + /// A character UI component which shows the current damage state of the mob (living/dead) + /// + public class SpeciesUI : Component, ICharacterUI + { + public override string Name => "Species"; + + public override uint? NetID => ContentNetIDs.SPECIES; + + /// + /// Holds the godot control for the species window + /// + private SpeciesWindow _window; + + /// + /// An enum representing the current state being applied to the user + /// + private ScreenEffects _currentEffect = ScreenEffects.None; + + // Required dependencies + [Dependency] private readonly IOverlayManager _overlayManager; + [Dependency] private readonly IPlayerManager _playerManager; + + //Relevant interface implementation for the character UI controller + public Control Scene => _window; + public UIPriority Priority => UIPriority.Species; + + /// + /// Allows calculating if we need to act due to this component being controlled by the current mob + /// + private bool CurrentlyControlled => _playerManager.LocalPlayer.ControlledEntity == Owner; + + /// + /// Holds the screen effects that can be applied mapped ot their relevant overlay + /// + private Dictionary EffectsDictionary; + + public override void OnRemove() + { + base.OnRemove(); + + _window.Dispose(); + } + + public override void OnAdd() + { + base.OnAdd(); + + IoCManager.InjectDependencies(this); + _window = new SpeciesWindow(); + + EffectsDictionary = new Dictionary() + { + { ScreenEffects.CircleMask, new CircleMaskOverlay() }, + { ScreenEffects.GradientCircleMask, new GradientCircleMask() } + }; + } + + public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null) + { + switch (message) + { + case HudStateChange msg: + if(CurrentlyControlled) + { + ChangeHudIcon(msg); + } + break; + + case PlayerAttachedMsg _: + ApplyOverlay(); + break; + + case PlayerDetachedMsg _: + RemoveOverlay(); + break; + } + } + + private void ChangeHudIcon(HudStateChange changemessage) + { + _window.SetIcon(changemessage); + SetOverlay(changemessage); + } + + private void SetOverlay(HudStateChange message) + { + RemoveOverlay(); + + _currentEffect = message.effect; + + ApplyOverlay(); + } + + private void RemoveOverlay() + { + if (_currentEffect != ScreenEffects.None) + { + var appliedeffect = EffectsDictionary[_currentEffect]; + _overlayManager.RemoveOverlay(nameof(appliedeffect)); + } + + _currentEffect = ScreenEffects.None; + } + + private void ApplyOverlay() + { + if (_currentEffect != ScreenEffects.None) + { + _overlayManager.AddOverlay(EffectsDictionary[_currentEffect]); + } + } + + private class SpeciesWindow : Control + { + private TextureRect _textureRect; + + protected override ResourcePath ScenePath => new ResourcePath("/Scenes/Mobs/Species.tscn"); + + protected override void Initialize() + { + base.Initialize(); + + _textureRect = (TextureRect)GetChild("TextureRect"); + } + + public void SetIcon(HudStateChange changemessage) + { + if (!IoCManager.Resolve().TryGetResource(new ResourcePath("/Textures") / changemessage.StateSprite, out var newtexture)) + { + Logger.Info("The Species Health Sprite {0} Does Not Exist", new ResourcePath("/Textures") / changemessage.StateSprite); + return; + } + + _textureRect.Texture = newtexture; + } + } + } +} diff --git a/Content.Client/Graphics/Overlays/CircleMaskOverlay.cs b/Content.Client/Graphics/Overlays/CircleMaskOverlay.cs new file mode 100644 index 0000000000..df36c995ce --- /dev/null +++ b/Content.Client/Graphics/Overlays/CircleMaskOverlay.cs @@ -0,0 +1,32 @@ +using SS14.Client.Graphics.Drawing; +using SS14.Client.Graphics.Overlays; +using SS14.Client.Graphics.Shaders; +using SS14.Client.Interfaces.Graphics.ClientEye; +using SS14.Client.Interfaces.Graphics.Overlays; +using SS14.Shared.IoC; +using SS14.Shared.Maths; +using SS14.Shared.Prototypes; + +namespace Content.Client.Graphics.Overlays +{ + public class CircleMaskOverlay : Overlay + { + [Dependency] private readonly IPrototypeManager _prototypeManager; + [Dependency] private readonly IEyeManager _eyeManager; + + public override OverlaySpace Space => OverlaySpace.WorldSpace; + + public CircleMaskOverlay() : base(nameof(CircleMaskOverlay)) + { + IoCManager.InjectDependencies(this); + Shader = _prototypeManager.Index("circlemask").Instance(); + } + + protected override void Draw(DrawingHandle handle) + { + var worldHandle = (DrawingHandleWorld)handle; + var viewport = _eyeManager.GetWorldViewport(); + worldHandle.DrawRect(viewport, Color.White); + } + } +} diff --git a/Content.Client/Graphics/Overlays/GradientCircleMask.cs b/Content.Client/Graphics/Overlays/GradientCircleMask.cs new file mode 100644 index 0000000000..9897f06cbd --- /dev/null +++ b/Content.Client/Graphics/Overlays/GradientCircleMask.cs @@ -0,0 +1,32 @@ +using SS14.Client.Graphics.Drawing; +using SS14.Client.Graphics.Overlays; +using SS14.Client.Graphics.Shaders; +using SS14.Client.Interfaces.Graphics.ClientEye; +using SS14.Client.Interfaces.Graphics.Overlays; +using SS14.Shared.IoC; +using SS14.Shared.Maths; +using SS14.Shared.Prototypes; + +namespace Content.Client.Graphics.Overlays +{ + public class GradientCircleMask : Overlay + { + [Dependency] private readonly IPrototypeManager _prototypeManager; + [Dependency] private readonly IEyeManager _eyeManager; + + public override OverlaySpace Space => OverlaySpace.WorldSpace; + + public GradientCircleMask() : base(nameof(GradientCircleMask)) + { + IoCManager.InjectDependencies(this); + Shader = _prototypeManager.Index("gradientcirclemask").Instance(); + } + + protected override void Draw(DrawingHandle handle) + { + var worldHandle = (DrawingHandleWorld)handle; + var viewport = _eyeManager.GetWorldViewport(); + worldHandle.DrawRect(viewport, Color.White); + } + } +} diff --git a/Content.Server/Content.Server.csproj b/Content.Server/Content.Server.csproj index 95f3b9189b..b51ba01b2f 100644 --- a/Content.Server/Content.Server.csproj +++ b/Content.Server/Content.Server.csproj @@ -66,6 +66,7 @@ + @@ -79,7 +80,11 @@ + + + + @@ -96,6 +101,7 @@ + diff --git a/Content.Server/EntryPoint.cs b/Content.Server/EntryPoint.cs index 91e858cb8c..75ba452a70 100644 --- a/Content.Server/EntryPoint.cs +++ b/Content.Server/EntryPoint.cs @@ -115,6 +115,7 @@ namespace Content.Server factory.RegisterIgnore("ConstructionGhost"); factory.Register(); + factory.Register(); IoCManager.Register(); IoCManager.Register(); diff --git a/Content.Server/GameObjects/Components/Damage/DamageThreshold.cs b/Content.Server/GameObjects/Components/Damage/DamageThreshold.cs new file mode 100644 index 0000000000..46e8a94a78 --- /dev/null +++ b/Content.Server/GameObjects/Components/Damage/DamageThreshold.cs @@ -0,0 +1,61 @@ +using Content.Shared.GameObjects; +using System; + +namespace Content.Server.GameObjects +{ + /// + /// Triggers an event when values rise above or drop below this threshold + /// + public struct DamageThreshold + { + public DamageType DamageType { get; } + public int Value { get; } + public ThresholdType ThresholdType { get; } + + public DamageThreshold(DamageType damageType, int value, ThresholdType thresholdType) + { + DamageType = damageType; + Value = value; + ThresholdType = thresholdType; + } + + public override bool Equals(Object obj) + { + return obj is DamageThreshold && this == (DamageThreshold)obj; + } + public override int GetHashCode() + { + return DamageType.GetHashCode() ^ Value.GetHashCode(); + } + public static bool operator ==(DamageThreshold x, DamageThreshold y) + { + return x.DamageType == y.DamageType && x.Value == y.Value; + } + public static bool operator !=(DamageThreshold x, DamageThreshold y) + { + return !(x == y); + } + } + + public enum ThresholdType + { + None, + Destruction, + Death, + Critical, + HUDUpdate + } + + public class DamageThresholdPassedEventArgs : EventArgs + { + public DamageThreshold DamageThreshold { get; } + public bool Passed { get; } + + public DamageThresholdPassedEventArgs(DamageThreshold threshold, bool passed) + { + DamageThreshold = threshold; + Passed = passed; + } + } +} + diff --git a/Content.Server/GameObjects/Components/Damage/DamageableComponent.cs b/Content.Server/GameObjects/Components/Damage/DamageableComponent.cs index cc8d76d428..b65f77c4e2 100644 --- a/Content.Server/GameObjects/Components/Damage/DamageableComponent.cs +++ b/Content.Server/GameObjects/Components/Damage/DamageableComponent.cs @@ -18,14 +18,11 @@ namespace Content.Server.GameObjects /// A component that handles receiving damage and healing, /// as well as informing other components of it. /// - public class DamageableComponent : Component, IDamageableComponent + public class DamageableComponent : SharedDamageableComponent, IDamageableComponent { /// public override string Name => "Damageable"; - /// - public override uint? NetID => ContentNetIDs.DAMAGEABLE; - /// /// The resistance set of this object. /// Affects receiving damage of various types. @@ -33,11 +30,17 @@ namespace Content.Server.GameObjects [ViewVariables] public ResistanceSet Resistances { get; private set; } - Dictionary CurrentDamage = new Dictionary(); - Dictionary> Thresholds = new Dictionary>(); + public IReadOnlyDictionary CurrentDamage => _currentDamage; + private Dictionary _currentDamage = new Dictionary(); + + Dictionary> Thresholds = new Dictionary>(); public event EventHandler DamageThresholdPassed; + public override ComponentState GetComponentState() + { + return new DamageComponentState(_currentDamage); + } public override void ExposeData(ObjectSerializer serializer) { @@ -55,9 +58,10 @@ namespace Content.Server.GameObjects { base.Initialize(); InitializeDamageType(DamageType.Total); - if (Owner is IOnDamageBehavior damageBehavior) + + foreach (var damagebehavior in Owner.GetAllComponents()) { - AddThresholdsFrom(damageBehavior); + AddThresholdsFrom(damagebehavior); } RecalculateComponentThresholds(); @@ -72,7 +76,7 @@ namespace Content.Server.GameObjects } InitializeDamageType(damageType); - int oldValue = CurrentDamage[damageType]; + int oldValue = _currentDamage[damageType]; int oldTotalValue = -1; if (amount == 0) @@ -81,13 +85,13 @@ namespace Content.Server.GameObjects } amount = Resistances.CalculateDamage(damageType, amount); - CurrentDamage[damageType] = Math.Max(0, CurrentDamage[damageType] + amount); + _currentDamage[damageType] = Math.Max(0, _currentDamage[damageType] + amount); UpdateForDamageType(damageType, oldValue); if (Resistances.AppliesToTotal(damageType)) { - oldTotalValue = CurrentDamage[DamageType.Total]; - CurrentDamage[DamageType.Total] = Math.Max(0, CurrentDamage[DamageType.Total] + amount); + oldTotalValue = _currentDamage[DamageType.Total]; + _currentDamage[DamageType.Total] = Math.Max(0, _currentDamage[DamageType.Total] + amount); UpdateForDamageType(DamageType.Total, oldTotalValue); } } @@ -104,7 +108,7 @@ namespace Content.Server.GameObjects void UpdateForDamageType(DamageType damageType, int oldValue) { - int change = CurrentDamage[damageType] - oldValue; + int change = _currentDamage[damageType] - oldValue; if (change == 0) { @@ -113,11 +117,12 @@ namespace Content.Server.GameObjects int changeSign = Math.Sign(change); - foreach (int value in Thresholds[damageType]) + foreach (var threshold in Thresholds[damageType]) { - if (((value * changeSign) > (oldValue * changeSign)) && ((value * changeSign) <= (CurrentDamage[damageType] * changeSign))) + var value = threshold.Value; + if (((value * changeSign) > (oldValue * changeSign)) && ((value * changeSign) <= (_currentDamage[damageType] * changeSign))) { - var args = new DamageThresholdPassedEventArgs(new DamageThreshold(damageType, value), (changeSign > 0)); + var args = new DamageThresholdPassedEventArgs(threshold, (changeSign > 0)); DamageThresholdPassed?.Invoke(this, args); } } @@ -142,62 +147,23 @@ namespace Content.Server.GameObjects foreach (DamageThreshold threshold in thresholds) { - if (!Thresholds[threshold.DamageType].Contains(threshold.Value)) + if (!Thresholds[threshold.DamageType].Contains(threshold)) { - Thresholds[threshold.DamageType].Add(threshold.Value); + Thresholds[threshold.DamageType].Add(threshold); } } + + DamageThresholdPassed += onDamageBehavior.OnDamageThresholdPassed; } void InitializeDamageType(DamageType damageType) { - if (!CurrentDamage.ContainsKey(damageType)) + if (!_currentDamage.ContainsKey(damageType)) { - CurrentDamage.Add(damageType, 0); - Thresholds.Add(damageType, new List()); + _currentDamage.Add(damageType, 0); + Thresholds.Add(damageType, new List()); } } } - - public struct DamageThreshold - { - public DamageType DamageType { get; } - public int Value { get; } - - public DamageThreshold(DamageType damageType, int value) - { - DamageType = damageType; - Value = value; - } - - public override bool Equals(Object obj) - { - return obj is DamageThreshold && this == (DamageThreshold)obj; - } - public override int GetHashCode() - { - return DamageType.GetHashCode() ^ Value.GetHashCode(); - } - public static bool operator ==(DamageThreshold x, DamageThreshold y) - { - return x.DamageType == y.DamageType && x.Value == y.Value; - } - public static bool operator !=(DamageThreshold x, DamageThreshold y) - { - return !(x == y); - } - } - - public class DamageThresholdPassedEventArgs : EventArgs - { - public DamageThreshold DamageThreshold { get; } - public bool Passed { get; } - - public DamageThresholdPassedEventArgs(DamageThreshold threshold, bool passed) - { - DamageThreshold = threshold; - Passed = passed; - } - } } diff --git a/Content.Server/GameObjects/Components/Damage/DestructibleComponent.cs b/Content.Server/GameObjects/Components/Damage/DestructibleComponent.cs index 7eef3e39f8..4045c4589c 100644 --- a/Content.Server/GameObjects/Components/Damage/DestructibleComponent.cs +++ b/Content.Server/GameObjects/Components/Damage/DestructibleComponent.cs @@ -43,18 +43,7 @@ namespace Content.Server.GameObjects serializer.DataReadFunction("thresholdtype", DamageType.Total, type => damageType = type); serializer.DataReadFunction("thresholdvalue", 0, val => damageValue = val); - Threshold = new DamageThreshold(damageType, damageValue); - } - } - - /// - public override void Initialize() - { - base.Initialize(); - - if (Owner.TryGetComponent(out DamageableComponent damageable)) - { - damageable.DamageThresholdPassed += OnDamageThresholdPassed; + Threshold = new DamageThreshold(damageType, damageValue, ThresholdType.Destruction); } } diff --git a/Content.Server/GameObjects/Components/Damage/ResistanceSet.cs b/Content.Server/GameObjects/Components/Damage/ResistanceSet.cs index dd4a860f66..f344c84cca 100644 --- a/Content.Server/GameObjects/Components/Damage/ResistanceSet.cs +++ b/Content.Server/GameObjects/Components/Damage/ResistanceSet.cs @@ -1,23 +1,9 @@ -using System; +using Content.Shared.GameObjects; +using System; using System.Collections.Generic; namespace Content.Server.GameObjects { - /// - /// Damage types used in-game. - /// Total should never be used directly - it's a derived value. - /// - public enum DamageType - { - Total, - Brute, - Heat, - Cold, - Acid, - Toxic, - Electric - } - /// /// Resistance set used by damageable objects. /// For each damage type, has a coefficient, damage reduction and "included in total" value. diff --git a/Content.Server/GameObjects/Components/Mobs/DamageStates.cs b/Content.Server/GameObjects/Components/Mobs/DamageStates.cs new file mode 100644 index 0000000000..3dc711531d --- /dev/null +++ b/Content.Server/GameObjects/Components/Mobs/DamageStates.cs @@ -0,0 +1,104 @@ +using Content.Server.GameObjects.EntitySystems; +using SS14.Server.GameObjects; +using SS14.Shared.Interfaces.GameObjects; +using SS14.Shared.Maths; + +namespace Content.Server.GameObjects +{ + /// + /// Defines the blocking effect of each damage state, and what effects to apply upon entering or exiting the state + /// + public interface DamageState : IActionBlocker + { + void EnterState(IEntity entity); + + void ExitState(IEntity entity); + } + + /// + /// Standard state that a species is at with no damage or negative effect + /// + public struct NormalState : DamageState + { + public void EnterState(IEntity entity){} + + public void ExitState(IEntity entity){} + + bool IActionBlocker.CanInteract() + { + return true; + } + + bool IActionBlocker.CanMove() + { + return true; + } + + bool IActionBlocker.CanUse() + { + return true; + } + } + + /// + /// A state in which you are disabled from acting due to damage + /// + public struct CriticalState : DamageState + { + public void EnterState(IEntity entity) { } + + public void ExitState(IEntity entity) { } + + bool IActionBlocker.CanInteract() + { + return false; + } + + bool IActionBlocker.CanMove() + { + return false; + } + + bool IActionBlocker.CanUse() + { + return false; + } + } + + /// + /// A damage state which will allow ghosting out of mobs + /// + public struct DeadState : DamageState + { + public void EnterState(IEntity entity) + { + if(entity.TryGetComponent(out SpriteComponent sprite)) + { + sprite.Rotation = sprite.Rotation + Angle.FromDegrees(90); + } + } + + public void ExitState(IEntity entity) + { + if (entity.TryGetComponent(out SpriteComponent sprite)) + { + sprite.Rotation = sprite.Rotation - Angle.FromDegrees(90); + } + } + + bool IActionBlocker.CanInteract() + { + return false; + } + + bool IActionBlocker.CanMove() + { + return false; + } + + bool IActionBlocker.CanUse() + { + return false; + } + } +} diff --git a/Content.Server/GameObjects/Components/Mobs/DamageThresholdTemplates/DamageThresholdTemplates.cs b/Content.Server/GameObjects/Components/Mobs/DamageThresholdTemplates/DamageThresholdTemplates.cs new file mode 100644 index 0000000000..b31e1edc77 --- /dev/null +++ b/Content.Server/GameObjects/Components/Mobs/DamageThresholdTemplates/DamageThresholdTemplates.cs @@ -0,0 +1,65 @@ +using System.Collections.Generic; +using Content.Shared.GameObjects; + +namespace Content.Server.GameObjects +{ + /// + /// Defines the threshold values for each damage state for any kind of species + /// + public abstract class DamageTemplates + { + public abstract List HealthHudThresholds { get; } + + /// + /// Changes the hud state when a threshold is reached + /// + /// + /// + /// + public abstract HudStateChange ChangeHudState(DamageableComponent damage); + + //public abstract ResistanceSet resistanceset { get; } + + /// + /// Shows allowed states, ordered by priority, closest to last value to have threshold reached is preferred + /// + public abstract List<(DamageType, int, ThresholdType)> AllowedStates { get; } + + /// + /// Map of ALL POSSIBLE damage states to the threshold enum value that will trigger them, normal state wont be triggered by this value but is a default that is fell back onto + /// + public static Dictionary StateThresholdMap = new Dictionary() + { + { ThresholdType.None, new NormalState() }, + { ThresholdType.Critical, new CriticalState() }, + { ThresholdType.Death, new DeadState() } + }; + + public List DamageThresholds + { + get + { + List thresholds = new List(); + foreach (var element in AllowedStates) + { + thresholds.Add(new DamageThreshold(element.Item1, element.Item2, element.Item3)); + } + return thresholds; + } + } + + public ThresholdType CalculateDamageState(DamageableComponent damage) + { + ThresholdType healthstate = ThresholdType.None; + foreach(var element in AllowedStates) + { + if(damage.CurrentDamage[element.Item1] >= element.Item2) + { + healthstate = element.Item3; + } + } + + return healthstate; + } + } +} diff --git a/Content.Server/GameObjects/Components/Mobs/DamageThresholdTemplates/HumanTemplate.cs b/Content.Server/GameObjects/Components/Mobs/DamageThresholdTemplates/HumanTemplate.cs new file mode 100644 index 0000000000..e1d903dd19 --- /dev/null +++ b/Content.Server/GameObjects/Components/Mobs/DamageThresholdTemplates/HumanTemplate.cs @@ -0,0 +1,66 @@ +using Content.Shared.GameObjects; +using System.Collections.Generic; + +namespace Content.Server.GameObjects +{ + public class Human : DamageTemplates + { + int critvalue = 200; + int normalstates = 6; + //string startsprite = "human0"; + + public override List<(DamageType, int, ThresholdType)> AllowedStates => new List<(DamageType, int, ThresholdType)>() + { + (DamageType.Total, critvalue, ThresholdType.Critical), + (DamageType.Total, 300, ThresholdType.Death), + }; + + public override List HealthHudThresholds + { + get + { + List thresholds = new List(); + thresholds.Add(new DamageThreshold(DamageType.Total, 1, ThresholdType.HUDUpdate)); + for (var i = 1; i <= normalstates; i++) + { + thresholds.Add(new DamageThreshold(DamageType.Total, i * critvalue / normalstates, ThresholdType.HUDUpdate)); + } + return thresholds; //we don't need to respecify the state damage thresholds since we'll update hud on damage state changes as well + } + } + + public override HudStateChange ChangeHudState(DamageableComponent damage) + { + ThresholdType healthstate = CalculateDamageState(damage); + switch (healthstate) + { + case ThresholdType.None: + var totaldamage = damage.CurrentDamage[DamageType.Total]; + if (totaldamage > critvalue) + { + throw new System.InvalidOperationException(); //these should all be below the crit value, possibly going over multiple thresholds at once? + } + var modifier = totaldamage / (critvalue / normalstates); //integer division floors towards zero + return new HudStateChange() + { + StateSprite = "Mob/UI/Human/human" + modifier.ToString() + ".png", + effect = ScreenEffects.None + }; + case ThresholdType.Critical: + return new HudStateChange() + { + StateSprite = "Mob/UI/Human/humancrit-0.png", //TODO: display as gif or alternate with -0 and -1 as frames + effect = ScreenEffects.GradientCircleMask + }; + case ThresholdType.Death: + return new HudStateChange() + { + StateSprite = "Mob/UI/Human/humandead.png", + effect = ScreenEffects.CircleMask + }; + default: + throw new System.InvalidOperationException(); + } + } + } +} diff --git a/Content.Server/GameObjects/Components/Mobs/SpeciesComponent.cs b/Content.Server/GameObjects/Components/Mobs/SpeciesComponent.cs new file mode 100644 index 0000000000..106f3e514c --- /dev/null +++ b/Content.Server/GameObjects/Components/Mobs/SpeciesComponent.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using Content.Server.GameObjects.EntitySystems; +using Content.Server.Interfaces; +using Content.Shared.GameObjects; +using SS14.Server.GameObjects; +using SS14.Shared.ContentPack; +using SS14.Shared.GameObjects; +using SS14.Shared.Interfaces.GameObjects; +using SS14.Shared.Interfaces.Network; +using SS14.Shared.Serialization; + +namespace Content.Server.GameObjects +{ + public class SpeciesComponent : Component, IActionBlocker, IOnDamageBehavior + { + public override string Name => "Species"; + + public override uint? NetID => ContentNetIDs.SPECIES; + + /// + /// Damagestates are reached by reaching a certain damage threshold, they will block actions after being reached + /// + public DamageState CurrentDamageState { get; private set; } = new NormalState(); + + /// + /// Damage state enum for current health, set only via change damage state //TODO: SETTER + /// + private ThresholdType currentstate = ThresholdType.None; + + /// + /// Holds the damage template which controls the threshold and resistance settings for this species type + /// + private DamageTemplates DamageTemplate; + + /// + /// Variable for serialization + /// + private string templatename; + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + + serializer.DataField(ref templatename, "Template", "Human"); + + Type type = AppDomain.CurrentDomain.GetAssemblyByName("Content.Server").GetType("Content.Server.GameObjects." + templatename); + DamageTemplate = (DamageTemplates)Activator.CreateInstance(type); + } + + public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null) + { + switch (message) + { + case PlayerAttachedMsg _: + var hudstatechange = DamageTemplate.ChangeHudState(Owner.GetComponent()); + SendNetworkMessage(hudstatechange); + break; + } + } + + bool IActionBlocker.CanMove() + { + return CurrentDamageState.CanMove(); + } + + bool IActionBlocker.CanInteract() + { + return CurrentDamageState.CanInteract(); + } + + bool IActionBlocker.CanUse() + { + return CurrentDamageState.CanUse(); + } + + List IOnDamageBehavior.GetAllDamageThresholds() + { + var thresholdlist = DamageTemplate.DamageThresholds; + thresholdlist.AddRange(DamageTemplate.HealthHudThresholds); + return thresholdlist; + } + + void IOnDamageBehavior.OnDamageThresholdPassed(object damageable, DamageThresholdPassedEventArgs e) + { + DamageableComponent damage = (DamageableComponent)damageable; + + if(e.DamageThreshold.ThresholdType != ThresholdType.HUDUpdate) + { + ChangeDamageState(DamageTemplate.CalculateDamageState(damage)); + } + + if (Owner.TryGetComponent(out BasicActorComponent actor)) //specifies if we have a client to update the hud for + { + var hudstatechange = DamageTemplate.ChangeHudState(damage); + SendNetworkMessage(hudstatechange); + } + } + + private void ChangeDamageState(ThresholdType threshold) + { + if(threshold == currentstate) + { + return; + } + + CurrentDamageState.ExitState(Owner); + CurrentDamageState = DamageTemplates.StateThresholdMap[threshold]; + CurrentDamageState.EnterState(Owner); + + currentstate = threshold; + } + } +} diff --git a/Content.Server/GameObjects/Components/Projectiles/ProjectileComponent.cs b/Content.Server/GameObjects/Components/Projectiles/ProjectileComponent.cs index 77034b14a5..a1d752b018 100644 --- a/Content.Server/GameObjects/Components/Projectiles/ProjectileComponent.cs +++ b/Content.Server/GameObjects/Components/Projectiles/ProjectileComponent.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using YamlDotNet.RepresentationModel; using SS14.Shared.Utility; +using Content.Shared.GameObjects; namespace Content.Server.GameObjects.Components.Projectiles { diff --git a/Content.Server/GameObjects/Components/Projectiles/ThrownItemComponent.cs b/Content.Server/GameObjects/Components/Projectiles/ThrownItemComponent.cs index 54005c96cc..54d01e30c3 100644 --- a/Content.Server/GameObjects/Components/Projectiles/ThrownItemComponent.cs +++ b/Content.Server/GameObjects/Components/Projectiles/ThrownItemComponent.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using Content.Server.GameObjects.Components.Projectiles; +using Content.Shared.GameObjects; using Content.Shared.Physics; using SS14.Shared.Interfaces.GameObjects; using SS14.Shared.Interfaces.GameObjects.Components; diff --git a/Content.Server/GameObjects/Components/Weapon/Melee/MeleeWeaponComponent.cs b/Content.Server/GameObjects/Components/Weapon/Melee/MeleeWeaponComponent.cs index a756c9cbf3..bfb20f080d 100644 --- a/Content.Server/GameObjects/Components/Weapon/Melee/MeleeWeaponComponent.cs +++ b/Content.Server/GameObjects/Components/Weapon/Melee/MeleeWeaponComponent.cs @@ -11,6 +11,7 @@ using SS14.Shared.Interfaces.Timing; using SS14.Shared.GameObjects.EntitySystemMessages; using SS14.Shared.Serialization; using SS14.Shared.Interfaces.GameObjects.Components; +using Content.Shared.GameObjects; namespace Content.Server.GameObjects.Components.Weapon.Melee { diff --git a/Content.Server/GameObjects/Components/Weapon/Ranged/Hitscan/HitscanWeaponComponent.cs b/Content.Server/GameObjects/Components/Weapon/Ranged/Hitscan/HitscanWeaponComponent.cs index 1d9025356d..1606f9eaa4 100644 --- a/Content.Server/GameObjects/Components/Weapon/Ranged/Hitscan/HitscanWeaponComponent.cs +++ b/Content.Server/GameObjects/Components/Weapon/Ranged/Hitscan/HitscanWeaponComponent.cs @@ -1,4 +1,5 @@ -using SS14.Server.GameObjects.EntitySystems; +using Content.Shared.GameObjects; +using SS14.Server.GameObjects.EntitySystems; using SS14.Shared.Audio; using SS14.Shared.GameObjects.EntitySystemMessages; using SS14.Shared.Interfaces.GameObjects; @@ -9,6 +10,7 @@ using SS14.Shared.IoC; using SS14.Shared.Map; using SS14.Shared.Maths; using SS14.Shared.Physics; +using SS14.Shared.Serialization; using System; namespace Content.Server.GameObjects.Components.Weapon.Ranged.Hitscan @@ -18,7 +20,16 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Hitscan private const float MaxLength = 20; public override string Name => "HitscanWeapon"; - private const string SpriteName = "Objects/laser.png"; + string Spritename = "Objects/laser.png"; + int Damage = 10; + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + + serializer.DataField(ref Spritename, "sprite", "Objects/laser.png"); + serializer.DataField(ref Damage, "damage", 10); + } protected override void Fire(IEntity user, GridLocalCoordinates clicklocation) { @@ -37,7 +48,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Hitscan { if (ray.HitEntity != null && ray.HitEntity.TryGetComponent(out DamageableComponent damage)) { - damage.TakeDamage(DamageType.Heat, 10); + damage.TakeDamage(DamageType.Heat, Damage); } } @@ -48,7 +59,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Hitscan 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), diff --git a/Content.Server/GameObjects/EntitySystems/ActionBlockerSystem.cs b/Content.Server/GameObjects/EntitySystems/ActionBlockerSystem.cs new file mode 100644 index 0000000000..087bc864ca --- /dev/null +++ b/Content.Server/GameObjects/EntitySystems/ActionBlockerSystem.cs @@ -0,0 +1,47 @@ +using SS14.Shared.GameObjects.Systems; +using SS14.Shared.Interfaces.GameObjects; + +namespace Content.Server.GameObjects.EntitySystems +{ + public interface IActionBlocker + { + bool CanMove(); + + bool CanInteract(); + + bool CanUse(); + } + + public class ActionBlockerSystem : EntitySystem + { + public static bool CanMove(IEntity entity) + { + bool canmove = true; + foreach(var actionblockercomponents in entity.GetAllComponents()) + { + canmove &= actionblockercomponents.CanMove(); //sets var to false if false + } + return canmove; + } + + public static bool CanInteract(IEntity entity) + { + bool caninteract = true; + foreach (var actionblockercomponents in entity.GetAllComponents()) + { + caninteract &= actionblockercomponents.CanInteract(); + } + return caninteract; + } + + public static bool CanUse(IEntity entity) + { + bool canuse = true; + foreach (var actionblockercomponents in entity.GetAllComponents()) + { + canuse &= actionblockercomponents.CanUse(); + } + return canuse; + } + } +} diff --git a/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs b/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs index 9a8b1faccf..8db802801e 100644 --- a/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs @@ -178,10 +178,9 @@ namespace Content.Server.GameObjects.EntitySystems var item = hands.GetActiveHand?.Owner; - if (!MobCanInteract(player)) + if (!ActionBlockerSystem.CanInteract(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 + //TODO: 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 //Clicked on empty space behavior, try using ranged attack @@ -239,15 +238,6 @@ namespace Content.Server.GameObjects.EntitySystems } } - /// - /// TODO function for blocking activity based on mob status - /// - /// - private static bool MobCanInteract(IEntity user) - { - return true; //Hook into future planned mob status system - } - /// /// We didn't click on any entity, try doing an afterattack on the click location /// @@ -324,7 +314,7 @@ namespace Content.Server.GameObjects.EntitySystems /// public static void TryUseInteraction(IEntity user, IEntity used) { - if (user != null && used != null && MobCanInteract(user)) + if (user != null && used != null && ActionBlockerSystem.CanUse(user)) { UseInteraction(user, used); } diff --git a/Content.Server/Interfaces/GameObjects/Components/Damage/IDamageableComponent.cs b/Content.Server/Interfaces/GameObjects/Components/Damage/IDamageableComponent.cs index a9021feb77..14f929f291 100644 --- a/Content.Server/Interfaces/GameObjects/Components/Damage/IDamageableComponent.cs +++ b/Content.Server/Interfaces/GameObjects/Components/Damage/IDamageableComponent.cs @@ -1,4 +1,5 @@ -using Content.Server.GameObjects; +using Content.Server.GameObjects; +using Content.Shared.GameObjects; using SS14.Shared.Interfaces.GameObjects; using System; diff --git a/Content.Shared/Content.Shared.csproj b/Content.Shared/Content.Shared.csproj index 9d2d3b124f..4f86c7a7fb 100644 --- a/Content.Shared/Content.Shared.csproj +++ b/Content.Shared/Content.Shared.csproj @@ -63,6 +63,7 @@ + @@ -71,6 +72,7 @@ + @@ -117,4 +119,4 @@ - \ No newline at end of file + diff --git a/Content.Shared/GameObjects/Components/Damage/DamageableComponent.cs b/Content.Shared/GameObjects/Components/Damage/DamageableComponent.cs new file mode 100644 index 0000000000..41ed371db9 --- /dev/null +++ b/Content.Shared/GameObjects/Components/Damage/DamageableComponent.cs @@ -0,0 +1,41 @@ +using SS14.Shared.GameObjects; +using SS14.Shared.Serialization; +using System; +using System.Collections.Generic; + +namespace Content.Shared.GameObjects +{ + public abstract class SharedDamageableComponent : Component + { + public override string Name => "Damageable"; + public sealed override uint? NetID => ContentNetIDs.DAMAGEABLE; + public sealed override Type StateType => typeof(DamageComponentState); + } + + // The IDs of the items get synced over the network. + [Serializable, NetSerializable] + public class DamageComponentState : ComponentState + { + public Dictionary CurrentDamage = new Dictionary(); + + public DamageComponentState(Dictionary damage) : base(ContentNetIDs.DAMAGEABLE) + { + CurrentDamage = damage; + } + } + + /// + /// Damage types used in-game. + /// Total should never be used directly - it's a derived value. + /// + public enum DamageType + { + Total, + Brute, + Heat, + Cold, + Acid, + Toxic, + Electric + } +} diff --git a/Content.Shared/GameObjects/ContentNetIDs.cs b/Content.Shared/GameObjects/ContentNetIDs.cs index a0dfdeab31..bd544e218b 100644 --- a/Content.Shared/GameObjects/ContentNetIDs.cs +++ b/Content.Shared/GameObjects/ContentNetIDs.cs @@ -11,5 +11,6 @@ public const uint INVENTORY = 1006; public const uint POWER_DEBUG_TOOL = 1007; public const uint CONSTRUCTOR = 1008; + public const uint SPECIES = 1009; } } diff --git a/Content.Shared/GameObjects/Messages/Mob/HealthHud.cs b/Content.Shared/GameObjects/Messages/Mob/HealthHud.cs new file mode 100644 index 0000000000..b477d53a9e --- /dev/null +++ b/Content.Shared/GameObjects/Messages/Mob/HealthHud.cs @@ -0,0 +1,28 @@ +using SS14.Shared.GameObjects; +using SS14.Shared.Serialization; +using System; + +namespace Content.Shared.GameObjects +{ + /// + /// Sends updates to the standard species health hud with the sprite to change the hud to + /// + [Serializable, NetSerializable] + public class HudStateChange : ComponentMessage + { + public string StateSprite; + public ScreenEffects effect; + + public HudStateChange() + { + Directed = true; + } + } + + public enum ScreenEffects + { + None, + CircleMask, + GradientCircleMask, + } +} diff --git a/Resources/Prototypes/Entities/Mobs.yml b/Resources/Prototypes/Entities/Mobs.yml index 0b97100ae1..76641d5435 100644 --- a/Resources/Prototypes/Entities/Mobs.yml +++ b/Resources/Prototypes/Entities/Mobs.yml @@ -47,6 +47,10 @@ - type: Input context: "human" + + - type: Species + Template: Human + - type: Damageable - type: Eye zoom: 0.5, 0.5 diff --git a/Resources/Prototypes/Entities/Weapons.yml b/Resources/Prototypes/Entities/Weapons.yml index 21ca07414d..0321e95a6b 100644 --- a/Resources/Prototypes/Entities/Weapons.yml +++ b/Resources/Prototypes/Entities/Weapons.yml @@ -11,6 +11,8 @@ sprite: Objects/laser_retro.rsi state: 100 - type: HitscanWeapon + damage: 30 + sprite: "Objects/laser.png" - type: Item Size: 24 sprite: Objects/laser_retro.rsi diff --git a/Resources/Prototypes/Shaders/shaders.yml b/Resources/Prototypes/Shaders/shaders.yml new file mode 100644 index 0000000000..f7920f1cd5 --- /dev/null +++ b/Resources/Prototypes/Shaders/shaders.yml @@ -0,0 +1,9 @@ +- type: shader + id: circlemask + kind: source + path: "/Shaders/circlemask.gdsl" + +- type: shader + id: gradientcirclemask + kind: source + path: "/Shaders/gradientcirclemask.gdsl" \ No newline at end of file diff --git a/Resources/Scenes/Mobs/CharacterWindow.tscn b/Resources/Scenes/Mobs/CharacterWindow.tscn new file mode 100644 index 0000000000..a5020062fd --- /dev/null +++ b/Resources/Scenes/Mobs/CharacterWindow.tscn @@ -0,0 +1,32 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://Scenes/SS14Window/SS14Window.tscn" type="PackedScene" id=1] + +[node name="SS14Window" index="0" instance=ExtResource( 1 )] + +anchor_bottom = 1.0 +margin_left = 0.0 +margin_top = 0.0 +margin_right = 240.0 +margin_bottom = 0.0 +rect_clip_content = false +_sections_unfolded = [ "Anchor", "Margin" ] + +[node name="Contents" parent="." index="0"] + +rect_clip_content = false + +[node name="Header" parent="." index="1"] + +rect_clip_content = false + +[node name="Header Text" parent="Header" index="0"] + +rect_clip_content = false +text = "Character Info" + +[node name="CloseButton" parent="Header" index="1"] + +rect_clip_content = false + + diff --git a/Resources/Scenes/Inventory/HumanInventory.tscn b/Resources/Scenes/Mobs/HumanInventory.tscn similarity index 53% rename from Resources/Scenes/Inventory/HumanInventory.tscn rename to Resources/Scenes/Mobs/HumanInventory.tscn index 03adb5012f..53b8f6ad8b 100644 --- a/Resources/Scenes/Inventory/HumanInventory.tscn +++ b/Resources/Scenes/Mobs/HumanInventory.tscn @@ -1,22 +1,12 @@ -[gd_scene load_steps=2 format=2] +[gd_scene format=2] -[ext_resource path="res://Scenes/SS14Window/SS14Window.tscn" type="PackedScene" id=1] - -[node name="SS14Window" index="0" instance=ExtResource( 1 )] - -margin_right = 403.0 -rect_clip_content = false - -[node name="Contents" parent="." index="0"] - -rect_clip_content = false - -[node name="PanelContainer" type="PanelContainer" parent="Contents" index="0"] +[node name="PanelContainer" type="PanelContainer"] anchor_left = 0.0 anchor_top = 0.0 anchor_right = 1.0 -anchor_bottom = 1.0 +anchor_bottom = 0.0 +margin_bottom = 400.0 rect_pivot_offset = Vector2( 0, 0 ) rect_clip_content = false mouse_filter = 0 @@ -25,7 +15,7 @@ size_flags_horizontal = 3 size_flags_vertical = 3 _sections_unfolded = [ "Anchor", "Grow Direction", "Margin", "Mouse", "Rect", "Size Flags", "Theme" ] -[node name="CenterContainer" type="CenterContainer" parent="Contents/PanelContainer" index="0"] +[node name="CenterContainer" type="CenterContainer" parent="." index="0"] anchor_left = 0.0 anchor_top = 0.0 @@ -33,8 +23,8 @@ anchor_right = 0.0 anchor_bottom = 0.0 margin_left = 7.0 margin_top = 7.0 -margin_right = 276.0 -margin_bottom = 424.0 +margin_right = 1017.0 +margin_bottom = 393.0 rect_pivot_offset = Vector2( 0, 0 ) rect_clip_content = false mouse_filter = 0 @@ -44,16 +34,16 @@ size_flags_vertical = 1 use_top_left = false _sections_unfolded = [ "Anchor", "Focus", "Grow Direction", "Hint", "Margin", "Mouse", "Rect", "Size Flags", "Theme" ] -[node name="GridContainer" type="GridContainer" parent="Contents/PanelContainer/CenterContainer" index="0"] +[node name="GridContainer" type="GridContainer" parent="CenterContainer" index="0"] anchor_left = 0.0 anchor_top = 0.0 anchor_right = 0.0 anchor_bottom = 0.0 -margin_left = 134.0 -margin_top = 208.0 -margin_right = 134.0 -margin_bottom = 208.0 +margin_left = 505.0 +margin_top = 193.0 +margin_right = 505.0 +margin_bottom = 193.0 rect_pivot_offset = Vector2( 0, 0 ) rect_clip_content = false mouse_filter = 0 @@ -63,17 +53,4 @@ size_flags_vertical = 1 columns = 3 _sections_unfolded = [ "Anchor", "Focus", "Grow Direction", "Hint", "Margin", "Material", "Mouse", "Rect", "Size Flags", "Theme", "custom_constants" ] -[node name="Header" parent="." index="1"] - -rect_clip_content = false - -[node name="Header Text" parent="Header" index="0"] - -rect_clip_content = false -text = "I dont how to not use a window" - -[node name="CloseButton" parent="Header" index="1"] - -rect_clip_content = false - diff --git a/Resources/Scenes/Mobs/Species.tscn b/Resources/Scenes/Mobs/Species.tscn new file mode 100644 index 0000000000..7f7b70f0d6 --- /dev/null +++ b/Resources/Scenes/Mobs/Species.tscn @@ -0,0 +1,34 @@ +[gd_scene format=2] + +[node name="Control" type="Control" index="0"] + +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 1.0 +anchor_bottom = 0.0 +margin_bottom = 64.0 +rect_pivot_offset = Vector2( 0, 0 ) +rect_clip_content = false +mouse_filter = 0 +mouse_default_cursor_shape = 0 +size_flags_horizontal = 1 +size_flags_vertical = 1 +_sections_unfolded = [ "Anchor", "Grow Direction", "Margin", "Rect" ] + +[node name="TextureRect" type="TextureRect" parent="." index="0"] + +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +rect_pivot_offset = Vector2( 0, 0 ) +rect_clip_content = false +mouse_filter = 1 +mouse_default_cursor_shape = 0 +size_flags_horizontal = 1 +size_flags_vertical = 1 +expand = true +stretch_mode = 6 +_sections_unfolded = [ "Anchor", "Grow Direction", "Margin" ] + + diff --git a/Resources/Scenes/Inventory/StorageSlot.tres b/Resources/Scenes/Mobs/StorageSlot.tres similarity index 100% rename from Resources/Scenes/Inventory/StorageSlot.tres rename to Resources/Scenes/Mobs/StorageSlot.tres diff --git a/Resources/Scenes/Inventory/StorageSlot.tscn b/Resources/Scenes/Mobs/StorageSlot.tscn similarity index 100% rename from Resources/Scenes/Inventory/StorageSlot.tscn rename to Resources/Scenes/Mobs/StorageSlot.tscn diff --git a/Resources/Shaders/circlemask.gdsl b/Resources/Shaders/circlemask.gdsl new file mode 100644 index 0000000000..c29fd7657c --- /dev/null +++ b/Resources/Shaders/circlemask.gdsl @@ -0,0 +1,16 @@ +shader_type canvas_item; + +float circle(in vec2 _st, in float _radius){ + vec2 dist = _st-vec2(0.5); + return smoothstep(_radius-(_radius*0.01), + _radius+(_radius*0.01), + dot(dist,dist)*4.0); +} + +void fragment(){ + vec2 st = FRAGCOORD.xy*SCREEN_PIXEL_SIZE.xy; + + COLOR = texture(TEXTURE, UV); + COLOR.rgb = vec3(0); + COLOR.a = circle(st,0.05); +} diff --git a/Resources/Shaders/gradientcirclemask.gdsl b/Resources/Shaders/gradientcirclemask.gdsl new file mode 100644 index 0000000000..e5c662ff06 --- /dev/null +++ b/Resources/Shaders/gradientcirclemask.gdsl @@ -0,0 +1,10 @@ +shader_type canvas_item; +uniform float percentagedistanceshow = 0.1; +uniform float gradientfalloffwidth = 0.2; + +void fragment() { + float dist = distance(UV, vec2(0.5, 0.5)); + float fromdiskdist = dist - percentagedistanceshow; + COLOR.a = max(0, min(1.0, 2.0*dist/percentagedistanceshow - 2.0)); + COLOR.rgb = vec3(1,1,1) - vec3(fromdiskdist,fromdiskdist,fromdiskdist)/gradientfalloffwidth; +} diff --git a/Resources/Textures/Mob/UI/Human/human0.png b/Resources/Textures/Mob/UI/Human/human0.png new file mode 100644 index 0000000000000000000000000000000000000000..ca94b41e6eab6eb22c7cc7e2b53c3d4cf445f065 GIT binary patch literal 233 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvi2$Dv*8>L*FvKx1tYBc+%fL{| zu!Ni8vjD^Y|Nr^f=0pJ1FqQ=Q1v5B2yO9Ru)O)%(hG(35+AOYr zI}o=e%)n6H@8hNeb1bxHas6P_FxFaNoVuD_xkbQcOSizu_6E~!%vpc9H!w_`gTe~DWM4f_gGV{ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mob/UI/Human/human1.png b/Resources/Textures/Mob/UI/Human/human1.png new file mode 100644 index 0000000000000000000000000000000000000000..68b189bc6aab8a202cd1e343456c09f870831e5d GIT binary patch literal 214 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv{s5m4*8>L*FvKx1tYBc+%fL{| z@K>SxB~Xg7B*-tA!Qt7BG$5zc)5S4FW8%^Y7kQZ#d7MA8&G2iu`M~I)=NtRrhtK(f zSBBpH^Ymq8-=s_D55KVvk)5Iz?j}k z_yKQ4XU8P52_5%-b6UIn6WDUgY=ZoMCclKW%xm^47-#DLPMfFxSfF*|wYi&s4q)(f L^>bP0l+XkK*E~-B literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mob/UI/Human/human2.png b/Resources/Textures/Mob/UI/Human/human2.png new file mode 100644 index 0000000000000000000000000000000000000000..ad2acb4ad030cbfd656f6724b9a3c3512e9eed92 GIT binary patch literal 214 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv{s5m4*8>L*XvHyjuV4t>%V1i{ zpvyFSDo~2CB*-tA!Qt7BG$5zc)5S4FW8%^Y7kQZ#d7MA8&G2iu`M~I)=NtRrhtK(f zSBBpH^Ymq8-=s_D55KVvk)5Iz?j}k z_yKQ4XU8P52_5%-b6UIn6WDUgY=ZoMCclKW%xm^47-#DLPMfFxSfF*|wYi&s4q)(f L^>bP0l+XkKzw}PZ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mob/UI/Human/human3.png b/Resources/Textures/Mob/UI/Human/human3.png new file mode 100644 index 0000000000000000000000000000000000000000..0888dd79a781750f47ea693ffd152f5e59d3f932 GIT binary patch literal 214 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv{s5m4*8>L*1jR8-Ucsp&^Sk|4ie28U-i(tw;&PZ!4!jfqPqT;yd|co*42@`b4iBOcBaCHC_PqD z;Rn1GogI_JCUo5U&1voOPhiU}vkCJ5nfwygGOyXIV4SJ{J8ho&V}aI<*XC{lI)K5` L)z4*}Q$iB}xuj53 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mob/UI/Human/human4.png b/Resources/Textures/Mob/UI/Human/human4.png new file mode 100644 index 0000000000000000000000000000000000000000..8eed659e972d0a660bc330c22c8bef3976f7c93b GIT binary patch literal 214 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv{s5m4*8>L*#5pjmC}h}M$586W z@MUfJHJ}t@NswPKgTu2MX+Tb?r;B5V#>Ax)F7h%f@;HBFo8i}R^MTPp&o}nL51;b| zuMEBY=jqGHzDbwPAAVyUB0EJb+)HMD!#0jzFMk`nP!-H(>*`3}xggTe~DWM4f2TV}c literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mob/UI/Human/human5.png b/Resources/Textures/Mob/UI/Human/human5.png new file mode 100644 index 0000000000000000000000000000000000000000..8b4e8a4b9ef741da6ac8b33ade2ceb6a5ddefb1f GIT binary patch literal 214 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv{s5m4*8>L*#3?bXuwmHi#89fq z!0}k z_yKQ4XU8P52_5%-b6UIn6WDUgY=ZoMCclKW%xm^47-#DLPMfFxSfF*|wYi&s4q)(f L^>bP0l+XkK{l`x* literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mob/UI/Human/human6-0.png b/Resources/Textures/Mob/UI/Human/human6-0.png new file mode 100644 index 0000000000000000000000000000000000000000..05f0233a7328a5719b3f0462c44e817be2cb726d GIT binary patch literal 214 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv{s5m4*8>L*#IZ1}U}xCN#!$+_ zU~S&13zT9k3GxeOaCmkj4ah0=ba4#Pn7DMpMP6n_9_Np2GyEEEJ}^4y`Nlr@;d8#= zm7%x)JbfA2H|f&(!*8rZWT&Wwd&$gi*v9eeL*G%zsy|Ns9MkYr#u zz`!7;!YKumWh@Eu3ubV5b|VeQ>G5=N4AGdlbizU2!wNjEZ+#B%%PuHke!ZEYOZ-IR z-*C+sq2*JnJZe8*KJ4%G-LRrFv}xNz_HI`>qo!yNaOqt@0neEA`s l(Bxg(a3fTKal-7+Y@0qw`EVRKZvu1|gQu&X%Q~loCIIX;UM&Cs literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mob/UI/Human/humancrit-0.png b/Resources/Textures/Mob/UI/Human/humancrit-0.png new file mode 100644 index 0000000000000000000000000000000000000000..252d22c75013f8f0c500a941b07c8f9e7352c5a7 GIT binary patch literal 252 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvi2$Dv*8>L*G%zsy|Ns9MkYr#u z03;iLT!z43*Ij{X7)yfuf*Bm1-ADs+CV9FzhGC^SNm0PHU3bmQqs$%VH+aMq z{G4(V^OuS%JpAP}<*R<#>-QCvhyCp?zT2?$B~QLGccoozBe&6Sj;_ny&pEu9R|z(p zxMr~ZbxZw>EQ4)37HECel98KV^5KlD_oa|`vP-7LFqkBByCvJ4%vUb`5PbXD1S^hr u!qW^Gs{e==FpEWNABa1gGjnFyGgfg{?M2R86z>3C$KdJe=d#Wzp$P!|Z(kAs literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mob/UI/Human/humancrit-1.png b/Resources/Textures/Mob/UI/Human/humancrit-1.png new file mode 100644 index 0000000000000000000000000000000000000000..c3102a638cbe201a47989f77fa98aab2c65d0fcc GIT binary patch literal 252 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvi2$Dv*8>L*G%zsy|Ns9MkYr#u z02FTk(*LttW3qs3#*!evU*OxbkD0=YMs7ySKfy1{g2N^|2xBOA+yDR^{d&I#^aa8cer$)_bROUHe;$%gQd2^ z7V|S-jb7UO7zb?Mam44e`;7PROFn3rUXe)JTPmQsR7>HQT+gwJpg6ZT50-~LTVU34 vPdzu2q3o~t0*2Mm(iir!T~tz%IU~IGphn*Pj{CoWu4C|Y^>bP0l+XkKK{R6P literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mob/UI/Human/humandead.png b/Resources/Textures/Mob/UI/Human/humandead.png new file mode 100644 index 0000000000000000000000000000000000000000..75e6fb07af08de8d9b0ec4d95861b0ce00a2e0a1 GIT binary patch literal 207 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvp#Yx{*8>L*$jHdp3JC=V2hC() z`2YXEv+Gtnpe$oakY6x^!?PP{Ku)%&i(`n!#N-48X%4OhpCqS>1hTeFnZlwW>R>)a zaD|hAFMF3Xi@^%Lyh$38sY^Oc_y54p9PaNfF#dJB3!= z-pIkh5vZlWAtc}s#vtObtt~Jhz#%|D^@Il_L-=E