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
This commit is contained in:
clusterfack
2018-12-13 07:47:19 -06:00
committed by Pieter-Jan Briers
parent b8becf4a56
commit 37df61113e
52 changed files with 1172 additions and 183 deletions

View File

@@ -72,8 +72,12 @@
</ItemGroup>
<ItemGroup>
<Compile Include="EntryPoint.cs" />
<Compile Include="GameObjects\Components\Actor\CharacterInterface.cs" />
<Compile Include="GameObjects\Components\DamageableComponent.cs" />
<Compile Include="GameObjects\Components\HUD\Inventory\ClientInventoryComponent.cs" />
<Compile Include="GameObjects\Components\Mobs\ICharacterUI.cs" />
<Compile Include="GameObjects\Components\Mobs\SpeciesUI.cs" />
<Compile Include="GameObjects\Components\Clothing\ClothingComponent.cs" />
<Compile Include="GameObjects\Components\Inventory\ClientInventoryComponent.cs" />
<Compile Include="GameObjects\Components\Items\ItemComponent.cs" />
<Compile Include="GameObjects\Components\Power\ApcBoundUserInterface.cs" />
<Compile Include="GameObjects\Components\Power\PowerCellVisualizer2D.cs" />
@@ -81,6 +85,8 @@
<Compile Include="GameObjects\EntitySystems\ClientNotifySystem.cs" />
<Compile Include="GameObjects\EntitySystems\VerbSystem.cs" />
<Compile Include="GameTicking\ClientGameTicker.cs" />
<Compile Include="Graphics\Overlays\CircleMaskOverlay.cs" />
<Compile Include="Graphics\Overlays\GradientCircleMask.cs" />
<Compile Include="Input\ContentContexts.cs" />
<Compile Include="Interfaces\IClientGameTicker.cs" />
<Compile Include="Interfaces\IClientNotifyManager.cs" />

View File

@@ -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<IPrototypeManager>();
factory.RegisterIgnore("Interactable");
factory.RegisterIgnore("Damageable");
factory.RegisterIgnore("Destructible");
factory.RegisterIgnore("Temperature");
factory.RegisterIgnore("PowerTransfer");
@@ -68,15 +70,22 @@ namespace Content.Client
factory.Register<ConstructorComponent>();
factory.Register<ConstructionGhostComponent>();
factory.Register<IconSmoothComponent>();
factory.Register<DamageableComponent>();
factory.Register<ClothingComponent>();
factory.Register<ItemComponent>();
factory.RegisterReference<ClothingComponent, ItemComponent>();
factory.Register<SpeciesUI>();
factory.Register<CharacterInterface>();
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<IParallaxManager>().LoadParallax();
IoCManager.Resolve<IBaseClient>().PlayerJoinedServer += SubscribePlayerAttachmentEvents;
}
/// <summary>
/// Subscribe events to the player manager after the player manager is set up
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
public void SubscribePlayerAttachmentEvents(object sender, EventArgs args)
{
IoCManager.Resolve<IPlayerManager>().LocalPlayer.EntityAttached += AttachPlayerToEntity;
IoCManager.Resolve<IPlayerManager>().LocalPlayer.EntityDetached += DetachPlayerFromEntity;
AttachPlayerToEntity(IoCManager.Resolve<IPlayerManager>().LocalPlayer, EventArgs.Empty);
}
/// <summary>
/// Add the character interface master which combines all character interfaces into one window
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
public void AttachPlayerToEntity(object sender, EventArgs args)
{
var localplayer = (LocalPlayer)sender;
localplayer.ControlledEntity?.AddComponent<CharacterInterface>();
}
/// <summary>
/// Remove the character interface master from this entity now that we have detached ourselves from it
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
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<CharacterInterface>();
}
public override void PostInit()

View File

@@ -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
{
/// <summary>
/// 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
/// </summary>
public class CharacterInterface : Component
{
public override string Name => "Character Interface Component";
/// <summary>
/// Stored keybind to open the menu on keypress
/// </summary>
private InputCmdHandler _openMenuCmdHandler;
/// <summary>
/// Window to hold each of the character interfaces
/// </summary>
private SS14Window _window;
/// <summary>
/// Create the window with all character UIs and bind it to a keypress
/// </summary>
public override void Initialize()
{
base.Initialize();
//Use all the character ui interfaced components to create the character window
var UIcomponents = Owner.GetAllComponents<ICharacterUI>();
_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<IInputManager>();
inputMgr.SetInputCommand(ContentKeyFunctions.OpenCharacterMenu, _openMenuCmdHandler);
}
/// <summary>
/// Dispose of window and the keypress binding
/// </summary>
public override void OnRemove()
{
base.OnRemove();
_window.Dispose();
_window = null;
var inputMgr = IoCManager.Resolve<IInputManager>();
inputMgr.SetInputCommand(ContentKeyFunctions.OpenCharacterMenu, null);
}
/// <summary>
/// A window that collects and shows all the individual character user interfaces
/// </summary>
public class CharacterWindow : SS14Window
{
protected override ResourcePath ScenePath => new ResourcePath("/Scenes/Mobs/CharacterWindow.tscn");
public CharacterWindow(IEnumerable<ICharacterUI> 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;
}
}
}
/// <summary>
/// Determines ordering of the character user interface, small values come sooner
/// </summary>
public enum UIPriority
{
First = 0,
Species = 100,
Inventory = 200,
Last = 99999
}
}

View File

@@ -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
{
/// <summary>
/// Fuck I really hate doing this
/// TODO: make sure the client only gets damageable component on the clientside entity for its player mob
/// </summary>
public class DamageableComponent : SharedDamageableComponent
{
/// <inheritdoc />
public override string Name => "Damageable";
public Dictionary<DamageType, int> CurrentDamage = new Dictionary<DamageType, int>();
public override void HandleComponentState(ComponentState state)
{
base.HandleComponentState(state);
if(state is DamageComponentState)
{
var damagestate = (DamageComponentState)state;
CurrentDamage = damagestate.CurrentDamage;
}
}
}
}

View File

@@ -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
/// <summary>
/// A character UI which shows items the user has equipped within his inventory
/// </summary>
public class ClientInventoryComponent : SharedInventoryComponent, ICharacterUI
{
private Dictionary<Slots, IEntity> _slots = new Dictionary<Slots, IEntity>();
/// <summary>
/// Holds the godot control for the inventory window
/// </summary>
private InventoryWindow _window;
private string _templateName = "HumanInventory"; //stored for serialization purposes
private InputCmdHandler _openMenuCmdHandler;
/// <summary>
/// Inventory template after being loaded from instance creator and string name
/// </summary>
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<IReflectionManager>();
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<IInputManager>();
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
/// <summary>
/// Temporary window to hold the basis for inventory hud
/// </summary>
private class InventoryWindow : SS14Window
private class InventoryWindow : Control
{
private int elements_x;
@@ -187,13 +176,11 @@ namespace Content.Client.GameObjects
private Dictionary<Slots, InventoryButton> InventorySlots = new Dictionary<Slots, InventoryButton>(); //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;
}
/// <summary>
@@ -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<Slots>(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)
{

View File

@@ -0,0 +1,21 @@
using Content.Client.GameObjects.Components.Actor;
using SS14.Client.UserInterface;
namespace Content.Client.GameObjects.Components.Mobs
{
/// <summary>
/// An interface which is gathered to assemble the character window from multiple components
/// </summary>
public interface ICharacterUI
{
/// <summary>
/// The godot control which holds the character user interface to be included in the window
/// </summary>
Control Scene { get; }
/// <summary>
/// The order it will appear in the character UI, higher is lower
/// </summary>
UIPriority Priority { get; }
}
}

View File

@@ -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
{
/// <summary>
/// A character UI component which shows the current damage state of the mob (living/dead)
/// </summary>
public class SpeciesUI : Component, ICharacterUI
{
public override string Name => "Species";
public override uint? NetID => ContentNetIDs.SPECIES;
/// <summary>
/// Holds the godot control for the species window
/// </summary>
private SpeciesWindow _window;
/// <summary>
/// An enum representing the current state being applied to the user
/// </summary>
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;
/// <summary>
/// Allows calculating if we need to act due to this component being controlled by the current mob
/// </summary>
private bool CurrentlyControlled => _playerManager.LocalPlayer.ControlledEntity == Owner;
/// <summary>
/// Holds the screen effects that can be applied mapped ot their relevant overlay
/// </summary>
private Dictionary<ScreenEffects, IOverlay> 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, IOverlay>()
{
{ 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<IResourceCache>().TryGetResource<TextureResource>(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;
}
}
}
}

View File

@@ -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<ShaderPrototype>("circlemask").Instance();
}
protected override void Draw(DrawingHandle handle)
{
var worldHandle = (DrawingHandleWorld)handle;
var viewport = _eyeManager.GetWorldViewport();
worldHandle.DrawRect(viewport, Color.White);
}
}
}

View File

@@ -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<ShaderPrototype>("gradientcirclemask").Instance();
}
protected override void Draw(DrawingHandle handle)
{
var worldHandle = (DrawingHandleWorld)handle;
var viewport = _eyeManager.GetWorldViewport();
worldHandle.DrawRect(viewport, Color.White);
}
}
}

View File

@@ -66,6 +66,7 @@
<Compile Include="Administration\AGhost.cs" />
<Compile Include="AI\AimShootLifeProcessor.cs" />
<Compile Include="EntryPoint.cs" />
<Compile Include="GameObjects\Components\Damage\DamageThreshold.cs" />
<Compile Include="GameObjects\Components\Doors\ServerDoorComponent.cs" />
<Compile Include="GameObjects\Components\Interactable\HandheldLightComponent.cs" />
<Compile Include="GameObjects\Components\Interactable\Tools\BaseTool.cs" />
@@ -79,7 +80,11 @@
<Compile Include="GameObjects\Components\Items\Storage\StoreableComponent.cs" />
<Compile Include="GameObjects\Components\Items\Storage\ServerStorageComponent.cs" />
<Compile Include="GameObjects\Components\Items\Storage\ItemComponent.cs" />
<Compile Include="GameObjects\Components\Mobs\DamageStates.cs" />
<Compile Include="GameObjects\Components\Mobs\DamageThresholdTemplates\DamageThresholdTemplates.cs" />
<Compile Include="GameObjects\Components\Mobs\DamageThresholdTemplates\HumanTemplate.cs" />
<Compile Include="GameObjects\Components\Mobs\MindComponent.cs" />
<Compile Include="GameObjects\Components\Mobs\SpeciesComponent.cs" />
<Compile Include="GameObjects\Components\Power\PowerCellComponent.cs" />
<Compile Include="GameObjects\Components\Power\PowerStorageComponent.cs" />
<Compile Include="GameObjects\Components\Power\PowerGeneratorComponent.cs" />
@@ -96,6 +101,7 @@
<Compile Include="GameObjects\Components\Weapon\Ranged\Projectile\ProjectileWeapon.cs" />
<Compile Include="GameObjects\Components\Weapon\Ranged\RangedWeapon.cs" />
<Compile Include="GameObjects\ContainerSlot.cs" />
<Compile Include="GameObjects\EntitySystems\ActionBlockerSystem.cs" />
<Compile Include="GameObjects\EntitySystems\Click\ExamineSystem.cs" />
<Compile Include="GameObjects\EntitySystems\Click\InteractionSystem.cs" />
<Compile Include="GameObjects\EntitySystems\DoorSystem.cs" />

View File

@@ -115,6 +115,7 @@ namespace Content.Server
factory.RegisterIgnore("ConstructionGhost");
factory.Register<MindComponent>();
factory.Register<SpeciesComponent>();
IoCManager.Register<ISharedNotifyManager, ServerNotifyManager>();
IoCManager.Register<IServerNotifyManager, ServerNotifyManager>();

View File

@@ -0,0 +1,61 @@
using Content.Shared.GameObjects;
using System;
namespace Content.Server.GameObjects
{
/// <summary>
/// Triggers an event when values rise above or drop below this threshold
/// </summary>
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;
}
}
}

View File

@@ -18,14 +18,11 @@ namespace Content.Server.GameObjects
/// A component that handles receiving damage and healing,
/// as well as informing other components of it.
/// </summary>
public class DamageableComponent : Component, IDamageableComponent
public class DamageableComponent : SharedDamageableComponent, IDamageableComponent
{
/// <inheritdoc />
public override string Name => "Damageable";
/// <inheritdoc />
public override uint? NetID => ContentNetIDs.DAMAGEABLE;
/// <summary>
/// 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<DamageType, int> CurrentDamage = new Dictionary<DamageType, int>();
Dictionary<DamageType, List<int>> Thresholds = new Dictionary<DamageType, List<int>>();
public IReadOnlyDictionary<DamageType, int> CurrentDamage => _currentDamage;
private Dictionary<DamageType, int> _currentDamage = new Dictionary<DamageType, int>();
Dictionary<DamageType, List<DamageThreshold>> Thresholds = new Dictionary<DamageType, List<DamageThreshold>>();
public event EventHandler<DamageThresholdPassedEventArgs> 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<IOnDamageBehavior>())
{
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<int>());
_currentDamage.Add(damageType, 0);
Thresholds.Add(damageType, new List<DamageThreshold>());
}
}
}
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;
}
}
}

View File

@@ -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);
}
}
/// <inheritdoc />
public override void Initialize()
{
base.Initialize();
if (Owner.TryGetComponent<DamageableComponent>(out DamageableComponent damageable))
{
damageable.DamageThresholdPassed += OnDamageThresholdPassed;
Threshold = new DamageThreshold(damageType, damageValue, ThresholdType.Destruction);
}
}

View File

@@ -1,23 +1,9 @@
using System;
using Content.Shared.GameObjects;
using System;
using System.Collections.Generic;
namespace Content.Server.GameObjects
{
/// <summary>
/// Damage types used in-game.
/// Total should never be used directly - it's a derived value.
/// </summary>
public enum DamageType
{
Total,
Brute,
Heat,
Cold,
Acid,
Toxic,
Electric
}
/// <summary>
/// Resistance set used by damageable objects.
/// For each damage type, has a coefficient, damage reduction and "included in total" value.

View File

@@ -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
{
/// <summary>
/// Defines the blocking effect of each damage state, and what effects to apply upon entering or exiting the state
/// </summary>
public interface DamageState : IActionBlocker
{
void EnterState(IEntity entity);
void ExitState(IEntity entity);
}
/// <summary>
/// Standard state that a species is at with no damage or negative effect
/// </summary>
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;
}
}
/// <summary>
/// A state in which you are disabled from acting due to damage
/// </summary>
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;
}
}
/// <summary>
/// A damage state which will allow ghosting out of mobs
/// </summary>
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;
}
}
}

View File

@@ -0,0 +1,65 @@
using System.Collections.Generic;
using Content.Shared.GameObjects;
namespace Content.Server.GameObjects
{
/// <summary>
/// Defines the threshold values for each damage state for any kind of species
/// </summary>
public abstract class DamageTemplates
{
public abstract List<DamageThreshold> HealthHudThresholds { get; }
/// <summary>
/// Changes the hud state when a threshold is reached
/// </summary>
/// <param name="state"></param>
/// <param name="damage"></param>
/// <returns></returns>
public abstract HudStateChange ChangeHudState(DamageableComponent damage);
//public abstract ResistanceSet resistanceset { get; }
/// <summary>
/// Shows allowed states, ordered by priority, closest to last value to have threshold reached is preferred
/// </summary>
public abstract List<(DamageType, int, ThresholdType)> AllowedStates { get; }
/// <summary>
/// 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
/// </summary>
public static Dictionary<ThresholdType, DamageState> StateThresholdMap = new Dictionary<ThresholdType, DamageState>()
{
{ ThresholdType.None, new NormalState() },
{ ThresholdType.Critical, new CriticalState() },
{ ThresholdType.Death, new DeadState() }
};
public List<DamageThreshold> DamageThresholds
{
get
{
List<DamageThreshold> thresholds = new List<DamageThreshold>();
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;
}
}
}

View File

@@ -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<DamageThreshold> HealthHudThresholds
{
get
{
List<DamageThreshold> thresholds = new List<DamageThreshold>();
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();
}
}
}
}

View File

@@ -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;
/// <summary>
/// Damagestates are reached by reaching a certain damage threshold, they will block actions after being reached
/// </summary>
public DamageState CurrentDamageState { get; private set; } = new NormalState();
/// <summary>
/// Damage state enum for current health, set only via change damage state //TODO: SETTER
/// </summary>
private ThresholdType currentstate = ThresholdType.None;
/// <summary>
/// Holds the damage template which controls the threshold and resistance settings for this species type
/// </summary>
private DamageTemplates DamageTemplate;
/// <summary>
/// Variable for serialization
/// </summary>
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<DamageableComponent>());
SendNetworkMessage(hudstatechange);
break;
}
}
bool IActionBlocker.CanMove()
{
return CurrentDamageState.CanMove();
}
bool IActionBlocker.CanInteract()
{
return CurrentDamageState.CanInteract();
}
bool IActionBlocker.CanUse()
{
return CurrentDamageState.CanUse();
}
List<DamageThreshold> 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;
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -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<IActionBlocker>())
{
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<IActionBlocker>())
{
caninteract &= actionblockercomponents.CanInteract();
}
return caninteract;
}
public static bool CanUse(IEntity entity)
{
bool canuse = true;
foreach (var actionblockercomponents in entity.GetAllComponents<IActionBlocker>())
{
canuse &= actionblockercomponents.CanUse();
}
return canuse;
}
}
}

View File

@@ -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
}
}
/// <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>
@@ -324,7 +314,7 @@ namespace Content.Server.GameObjects.EntitySystems
/// <param name="used"></param>
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);
}

View File

@@ -1,4 +1,5 @@
using Content.Server.GameObjects;
using Content.Server.GameObjects;
using Content.Shared.GameObjects;
using SS14.Shared.Interfaces.GameObjects;
using System;

View File

@@ -63,6 +63,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="EntryPoint.cs" />
<Compile Include="GameObjects\Components\Damage\DamageableComponent.cs" />
<Compile Include="GameObjects\Components\Inventory\EquipmentSlotDefinitions.cs" />
<Compile Include="GameObjects\Components\Inventory\InventoryTemplates.cs" />
<Compile Include="GameObjects\Components\Inventory\SharedInventoryComponent.cs" />
@@ -71,6 +72,7 @@
<Compile Include="GameObjects\Components\Storage\SharedStorageComponent.cs" />
<Compile Include="GameObjects\ContentNetIDs.cs" />
<Compile Include="GameObjects\EntitySystemMessages\VerbSystemMessages.cs" />
<Compile Include="GameObjects\Messages\Mob\HealthHud.cs" />
<Compile Include="GameObjects\PhysicalConstants.cs" />
<Compile Include="Interfaces\ISharedNotifyManager.cs" />
<Compile Include="GameObjects\Verb.cs" />
@@ -117,4 +119,4 @@
<Compile Include="Construction\ConstructionPrototype.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
</Project>

View File

@@ -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<DamageType, int> CurrentDamage = new Dictionary<DamageType, int>();
public DamageComponentState(Dictionary<DamageType, int> damage) : base(ContentNetIDs.DAMAGEABLE)
{
CurrentDamage = damage;
}
}
/// <summary>
/// Damage types used in-game.
/// Total should never be used directly - it's a derived value.
/// </summary>
public enum DamageType
{
Total,
Brute,
Heat,
Cold,
Acid,
Toxic,
Electric
}
}

View File

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

View File

@@ -0,0 +1,28 @@
using SS14.Shared.GameObjects;
using SS14.Shared.Serialization;
using System;
namespace Content.Shared.GameObjects
{
/// <summary>
/// Sends updates to the standard species health hud with the sprite to change the hud to
/// </summary>
[Serializable, NetSerializable]
public class HudStateChange : ComponentMessage
{
public string StateSprite;
public ScreenEffects effect;
public HudStateChange()
{
Directed = true;
}
}
public enum ScreenEffects
{
None,
CircleMask,
GradientCircleMask,
}
}

View File

@@ -47,6 +47,10 @@
- type: Input
context: "human"
- type: Species
Template: Human
- type: Damageable
- type: Eye
zoom: 0.5, 0.5

View File

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

View File

@@ -0,0 +1,9 @@
- type: shader
id: circlemask
kind: source
path: "/Shaders/circlemask.gdsl"
- type: shader
id: gradientcirclemask
kind: source
path: "/Shaders/gradientcirclemask.gdsl"

View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 B

2
engine

Submodule engine updated: c76f791799...4d9a243685