Shitty combat mode & animations. (#367)

* Combat mode UI & targeting zones.

* Fix inventory hud positioning.

* Crappy attack animations.

* Import TG combat sounds

* More work on arcs.

* Implement hit sounds.

* Lunging, hit effects, some more stuff.
This commit is contained in:
Pieter-Jan Briers
2019-09-26 22:32:32 +02:00
committed by GitHub
parent ac55ccf46e
commit 02d509fc5f
47 changed files with 1231 additions and 35 deletions

View File

@@ -62,7 +62,6 @@ namespace Content.Client
"EmitSoundOnUse", "EmitSoundOnUse",
"FootstepModifier", "FootstepModifier",
"HeatResistance", "HeatResistance",
"CombatMode",
"Teleportable", "Teleportable",
"ItemTeleporter", "ItemTeleporter",
"Portal", "Portal",

View File

@@ -29,6 +29,10 @@ namespace Content.Client.GameObjects.Components.Mobs
private EyeComponent _eye; private EyeComponent _eye;
// Basically I needed a way to chain this effect for the attack lunge animation.
// Sorry!
public Vector2 BaseOffset { get; set; }
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
@@ -95,7 +99,7 @@ namespace Content.Client.GameObjects.Components.Mobs
private void _updateEye() private void _updateEye()
{ {
_eye.Offset = _currentKick; _eye.Offset = BaseOffset + _currentKick;
} }
} }
} }

View File

@@ -0,0 +1,59 @@
using Content.Client.UserInterface;
using Content.Shared.GameObjects.Components.Mobs;
using Robust.Client.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.IoC;
using Robust.Shared.ViewVariables;
namespace Content.Client.GameObjects.Components.Mobs
{
[RegisterComponent]
public sealed class CombatModeComponent : SharedCombatModeComponent
{
[ViewVariables(VVAccess.ReadWrite)]
public bool IsInCombatMode { get; private set; }
[ViewVariables(VVAccess.ReadWrite)]
public TargetingZone ActiveZone { get; private set; }
#pragma warning disable 649
[Dependency] private readonly IGameHud _gameHud;
#pragma warning restore 649
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
{
base.HandleComponentState(curState, nextState);
var state = (CombatModeComponentState) curState;
IsInCombatMode = state.IsInCombatMode;
ActiveZone = state.TargetingZone;
UpdateHud();
}
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null)
{
base.HandleMessage(message, netChannel, component);
switch (message)
{
case PlayerAttachedMsg _:
_gameHud.CombatPanelVisible = true;
UpdateHud();
break;
case PlayerDetachedMsg _:
_gameHud.CombatPanelVisible = false;
break;
}
}
private void UpdateHud()
{
_gameHud.CombatModeActive = IsInCombatMode;
_gameHud.TargetingZone = ActiveZone;
}
}
}

View File

@@ -0,0 +1,65 @@
using Robust.Client.GameObjects;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.Maths;
namespace Content.Client.GameObjects.Components.Mobs
{
[RegisterComponent]
public sealed class MeleeLungeComponent : Component
{
public override string Name => "MeleeLunge";
private const float ResetTime = 0.3f;
private const float BaseOffset = 0.25f;
private Angle _angle;
private float _time;
public void SetData(Angle angle)
{
_angle = angle;
_time = 0;
}
public void Update(float frameTime)
{
_time += frameTime;
var offset = Vector2.Zero;
var deleteSelf = false;
if (_time > ResetTime)
{
deleteSelf = true;
}
else
{
offset = _angle.RotateVec((BaseOffset, 0));
offset *= (ResetTime - _time) / ResetTime;
}
if (Owner.TryGetComponent(out CameraRecoilComponent recoilComponent))
{
recoilComponent.BaseOffset = offset;
}
else if (Owner.TryGetComponent(out EyeComponent eyeComponent))
{
eyeComponent.Offset = offset;
}
if (Owner.TryGetComponent(out ISpriteComponent spriteComponent))
{
// We have to account for rotation so the offset still checks out.
// SpriteComponent.Offset is applied before transform rotation (as expected).
var worldRotation = Owner.Transform.WorldRotation;
spriteComponent.Offset = new Angle(-worldRotation).RotateVec(offset);
}
if (deleteSelf)
{
Owner.RemoveComponent<MeleeLungeComponent>();
}
}
}
}

View File

@@ -0,0 +1,67 @@
using Content.Shared.GameObjects.Components.Weapons.Melee;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Shared.GameObjects;
using Robust.Shared.Maths;
namespace Content.Client.GameObjects.Components.Weapons.Melee
{
[RegisterComponent]
public sealed class MeleeWeaponArcAnimationComponent : Component
{
public override string Name => "MeleeWeaponArcAnimation";
private MeleeWeaponAnimationPrototype _meleeWeaponAnimation;
private float _timer;
private SpriteComponent _sprite;
private Angle _baseAngle;
public override void Initialize()
{
base.Initialize();
_sprite = Owner.GetComponent<SpriteComponent>();
}
public void SetData(MeleeWeaponAnimationPrototype prototype, Angle baseAngle)
{
_meleeWeaponAnimation = prototype;
_sprite.AddLayer(new RSI.StateId(prototype.State));
_baseAngle = baseAngle;
}
internal void Update(float frameTime)
{
if (_meleeWeaponAnimation == null)
{
return;
}
_timer += frameTime;
var (r, g, b, a) =
Vector4.Clamp(_meleeWeaponAnimation.Color + _meleeWeaponAnimation.ColorDelta * _timer, Vector4.Zero, Vector4.One);
_sprite.Color = new Color(r, g, b, a);
switch (_meleeWeaponAnimation.ArcType)
{
case WeaponArcType.Slash:
var angle = Angle.FromDegrees(_meleeWeaponAnimation.Width)/2;
Owner.Transform.LocalRotation =
_baseAngle + Angle.Lerp(-angle, angle, (float) (_timer / _meleeWeaponAnimation.Length.TotalSeconds));
break;
case WeaponArcType.Poke:
_sprite.Offset += (_meleeWeaponAnimation.Speed * frameTime, 0);
break;
}
if (_meleeWeaponAnimation.Length.TotalSeconds <= _timer)
{
Owner.Delete();
}
}
}
}

View File

@@ -0,0 +1,33 @@
using Content.Client.UserInterface;
using Content.Shared.GameObjects.Components.Mobs;
using Content.Shared.GameObjects.EntitySystemMessages;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.IoC;
namespace Content.Client.GameObjects.EntitySystems
{
public sealed class CombatModeSystem : EntitySystem
{
#pragma warning disable 649
[Dependency] private readonly IGameHud _gameHud;
#pragma warning restore 649
public override void Initialize()
{
base.Initialize();
_gameHud.OnCombatModeChanged = OnCombatModeChanged;
_gameHud.OnTargetingZoneChanged = OnTargetingZoneChanged;
}
private void OnTargetingZoneChanged(TargetingZone obj)
{
RaiseNetworkEvent(new CombatModeSystemMessages.SetTargetZoneMessage(obj));
}
private void OnCombatModeChanged(bool obj)
{
RaiseNetworkEvent(new CombatModeSystemMessages.SetCombatModeActiveMessage(obj));
}
}
}

View File

@@ -0,0 +1,28 @@
using Content.Client.GameObjects.Components.Mobs;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
namespace Content.Client.GameObjects.EntitySystems
{
[UsedImplicitly]
public sealed class MeleeLungeSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
EntityQuery = new TypeEntityQuery<MeleeLungeComponent>();
}
public override void FrameUpdate(float frameTime)
{
base.FrameUpdate(frameTime);
foreach (var entity in RelevantEntities)
{
entity.GetComponent<MeleeLungeComponent>().Update(frameTime);
}
}
}
}

View File

@@ -0,0 +1,101 @@
using System.Linq;
using Content.Client.GameObjects.Components.Mobs;
using Content.Client.GameObjects.Components.Weapons.Melee;
using Content.Shared.GameObjects.Components.Weapons.Melee;
using JetBrains.Annotations;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes;
using Robust.Shared.Timers;
using static Content.Shared.GameObjects.EntitySystemMessages.MeleeWeaponSystemMessages;
namespace Content.Client.GameObjects.EntitySystems
{
[UsedImplicitly]
public sealed class MeleeWeaponSystem : EntitySystem
{
#pragma warning disable 649
[Dependency] private readonly IPrototypeManager _prototypeManager;
#pragma warning restore 649
public override void Initialize()
{
base.Initialize();
EntityQuery = new TypeEntityQuery(typeof(MeleeWeaponArcAnimationComponent));
}
public override void RegisterMessageTypes()
{
base.RegisterMessageTypes();
RegisterMessageType<PlayMeleeWeaponAnimationMessage>();
}
public override void HandleNetMessage(INetChannel channel, EntitySystemMessage message)
{
base.HandleNetMessage(channel, message);
switch (message)
{
case PlayMeleeWeaponAnimationMessage playMsg:
PlayWeaponArc(playMsg);
break;
}
}
public override void FrameUpdate(float frameTime)
{
base.FrameUpdate(frameTime);
foreach (var entity in RelevantEntities)
{
entity.GetComponent<MeleeWeaponArcAnimationComponent>().Update(frameTime);
}
}
private void PlayWeaponArc(PlayMeleeWeaponAnimationMessage msg)
{
if (!_prototypeManager.TryIndex(msg.ArcPrototype, out MeleeWeaponAnimationPrototype weaponArc))
{
Logger.Error("Tried to play unknown weapon arc prototype '{0}'", msg.ArcPrototype);
return;
}
var attacker = EntityManager.GetEntity(msg.Attacker);
var lunge = attacker.EnsureComponent<MeleeLungeComponent>();
lunge.SetData(msg.Angle);
var entity = EntityManager.SpawnEntityAt("WeaponArc", attacker.Transform.GridPosition);
entity.Transform.LocalRotation = msg.Angle;
var weaponArcAnimation = entity.GetComponent<MeleeWeaponArcAnimationComponent>();
weaponArcAnimation.SetData(weaponArc, msg.Angle);
foreach (var hitEntity in msg.Hits.Select(u => EntityManager.GetEntity(u)))
{
if (!hitEntity.TryGetComponent(out ISpriteComponent sprite)) continue;
var originalColor = sprite.Color;
var newColor = Color.Red * originalColor;
sprite.Color = newColor;
Timer.Spawn(100, () =>
{
// Only reset back to the original color if something else didn't change the color in the mean time.
if (sprite.Color == newColor)
{
sprite.Color = originalColor;
}
});
}
}
}
}

View File

@@ -27,8 +27,7 @@ namespace Content.Client.Input
human.AddFunction(ContentKeyFunctions.OpenCraftingMenu); human.AddFunction(ContentKeyFunctions.OpenCraftingMenu);
human.AddFunction(ContentKeyFunctions.OpenInventoryMenu); human.AddFunction(ContentKeyFunctions.OpenInventoryMenu);
human.AddFunction(ContentKeyFunctions.MouseMiddle); human.AddFunction(ContentKeyFunctions.MouseMiddle);
// Disabled until there is feedback, so hitting tab doesn't suddenly break interaction. human.AddFunction(ContentKeyFunctions.ToggleCombatMode);
// human.AddFunction(ContentKeyFunctions.ToggleCombatMode);
var ghost = contexts.New("ghost", "common"); var ghost = contexts.New("ghost", "common");
ghost.AddFunction(EngineKeyFunctions.MoveUp); ghost.AddFunction(EngineKeyFunctions.MoveUp);

View File

@@ -1,5 +1,6 @@
using System; using System;
using Content.Client.Utility; using Content.Client.Utility;
using Content.Shared.GameObjects.Components.Mobs;
using Content.Shared.Input; using Content.Shared.Input;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Client.Graphics.Drawing; using Robust.Client.Graphics.Drawing;
@@ -48,6 +49,13 @@ namespace Content.Client.UserInterface
Control HandsContainer { get; } Control HandsContainer { get; }
Control InventoryQuickButtonContainer { get; } Control InventoryQuickButtonContainer { get; }
bool CombatPanelVisible { get; set; }
bool CombatModeActive { get; set; }
TargetingZone TargetingZone { get; set; }
Action<bool> OnCombatModeChanged { get; set; }
Action<TargetingZone> OnTargetingZoneChanged { get; set; }
// Init logic. // Init logic.
void Initialize(); void Initialize();
} }
@@ -62,6 +70,9 @@ namespace Content.Client.UserInterface
private TopButton _buttonCraftingMenu; private TopButton _buttonCraftingMenu;
private TopButton _buttonSandboxMenu; private TopButton _buttonSandboxMenu;
private TutorialWindow _tutorialWindow; private TutorialWindow _tutorialWindow;
private TargetingDoll _targetingDoll;
private Button _combatModeButton;
private VBoxContainer _combatPanelContainer;
#pragma warning disable 649 #pragma warning disable 649
[Dependency] private readonly IResourceCache _resourceCache; [Dependency] private readonly IResourceCache _resourceCache;
@@ -71,6 +82,26 @@ namespace Content.Client.UserInterface
public Control HandsContainer { get; private set; } public Control HandsContainer { get; private set; }
public Control InventoryQuickButtonContainer { get; private set; } public Control InventoryQuickButtonContainer { get; private set; }
public bool CombatPanelVisible
{
get => _combatPanelContainer.Visible;
set => _combatPanelContainer.Visible = value;
}
public bool CombatModeActive
{
get => _combatModeButton.Pressed;
set => _combatModeButton.Pressed = value;
}
public TargetingZone TargetingZone
{
get => _targetingDoll.ActiveZone;
set => _targetingDoll.ActiveZone = value;
}
public Action<bool> OnCombatModeChanged { get; set; }
public Action<TargetingZone> OnTargetingZoneChanged { get; set; }
public void Initialize() public void Initialize()
{ {
@@ -179,16 +210,35 @@ namespace Content.Client.UserInterface
{ {
GrowHorizontal = Control.GrowDirection.Begin, GrowHorizontal = Control.GrowDirection.Begin,
GrowVertical = Control.GrowDirection.Begin, GrowVertical = Control.GrowDirection.Begin,
SizeFlagsVertical = Control.SizeFlags.ShrinkEnd
}; };
HandsContainer = new MarginContainer HandsContainer = new MarginContainer
{ {
GrowHorizontal = Control.GrowDirection.Both, GrowHorizontal = Control.GrowDirection.Both,
GrowVertical = Control.GrowDirection.Begin GrowVertical = Control.GrowDirection.Begin,
SizeFlagsVertical = Control.SizeFlags.ShrinkEnd
}; };
_combatPanelContainer = new VBoxContainer
{
Children =
{
(_combatModeButton = new Button
{
Text = _loc.GetString("Combat Mode"),
ToggleMode = true
}),
(_targetingDoll = new TargetingDoll(_resourceCache))
}
};
_combatModeButton.OnToggled += args => OnCombatModeChanged?.Invoke(args.Pressed);
_targetingDoll.OnZoneChanged += args => OnTargetingZoneChanged?.Invoke(args);
inventoryContainer.Children.Add(HandsContainer); inventoryContainer.Children.Add(HandsContainer);
inventoryContainer.Children.Add(InventoryQuickButtonContainer); inventoryContainer.Children.Add(InventoryQuickButtonContainer);
inventoryContainer.Children.Add(_combatPanelContainer);
} }
private void ButtonTutorialOnOnToggled() private void ButtonTutorialOnOnToggled()

View File

@@ -481,6 +481,23 @@ namespace Content.Client.UserInterface
{ {
new StyleProperty(Label.StylePropertyFont, notoSansDisplayBold14), new StyleProperty(Label.StylePropertyFont, notoSansDisplayBold14),
}), }),
// Targeting doll
new StyleRule(new SelectorElement(typeof(TextureButton), new []{TargetingDoll.StyleClassTargetDollZone}, null, new [] {TextureButton.StylePseudoClassNormal}), new []
{
new StyleProperty(Control.StylePropertyModulateSelf, Color.FromHex("#F00")),
}),
new StyleRule(new SelectorElement(typeof(TextureButton), new []{TargetingDoll.StyleClassTargetDollZone}, null, new [] {TextureButton.StylePseudoClassHover}), new []
{
new StyleProperty(Control.StylePropertyModulateSelf, Color.FromHex("#0F0")),
}),
new StyleRule(new SelectorElement(typeof(TextureButton), new []{TargetingDoll.StyleClassTargetDollZone}, null, new [] {TextureButton.StylePseudoClassPressed}), new []
{
new StyleProperty(Control.StylePropertyModulateSelf, Color.FromHex("#00F")),
}),
}); });
} }
} }

View File

@@ -0,0 +1,82 @@
using System;
using Content.Client.Utility;
using Content.Shared.GameObjects.Components.Mobs;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.UserInterface.Controls;
namespace Content.Client.UserInterface
{
public sealed class TargetingDoll : VBoxContainer
{
private TargetingZone _activeZone = TargetingZone.Middle;
public const string StyleClassTargetDollZone = "target-doll-zone";
private const string TextureHigh = "/Textures/UserInterface/target-doll-high.svg.96dpi.png";
private const string TextureMiddle = "/Textures/UserInterface/target-doll-middle.svg.96dpi.png";
private const string TextureLow = "/Textures/UserInterface/target-doll-low.svg.96dpi.png";
private readonly TextureButton _buttonHigh;
private readonly TextureButton _buttonMiddle;
private readonly TextureButton _buttonLow;
public TargetingZone ActiveZone
{
get => _activeZone;
set
{
if (_activeZone == value)
{
return;
}
_activeZone = value;
OnZoneChanged?.Invoke(value);
UpdateButtons();
}
}
public event Action<TargetingZone> OnZoneChanged;
public TargetingDoll(IResourceCache resourceCache)
{
_buttonHigh = new TextureButton
{
TextureNormal = resourceCache.GetTexture(TextureHigh),
StyleClasses = {StyleClassTargetDollZone},
SizeFlagsHorizontal = SizeFlags.ShrinkCenter
};
_buttonMiddle = new TextureButton
{
TextureNormal = resourceCache.GetTexture(TextureMiddle),
StyleClasses = {StyleClassTargetDollZone},
SizeFlagsHorizontal = SizeFlags.ShrinkCenter
};
_buttonLow = new TextureButton
{
TextureNormal = resourceCache.GetTexture(TextureLow),
StyleClasses = {StyleClassTargetDollZone},
SizeFlagsHorizontal = SizeFlags.ShrinkCenter
};
_buttonHigh.OnPressed += _ => ActiveZone = TargetingZone.High;
_buttonMiddle.OnPressed += _ => ActiveZone = TargetingZone.Middle;
_buttonLow.OnPressed += _ => ActiveZone = TargetingZone.Low;
AddChild(_buttonHigh);
AddChild(_buttonMiddle);
AddChild(_buttonLow);
UpdateButtons();
}
private void UpdateButtons()
{
_buttonHigh.Pressed = _activeZone == TargetingZone.High;
_buttonMiddle.Pressed = _activeZone == TargetingZone.Middle;
_buttonLow.Pressed = _activeZone == TargetingZone.Low;
}
}
}

View File

@@ -1,3 +1,4 @@
using Content.Shared.GameObjects.Components.Mobs;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
@@ -9,11 +10,36 @@ namespace Content.Server.GameObjects.Components.Mobs
/// using *everything* as a weapon. /// using *everything* as a weapon.
/// </summary> /// </summary>
[RegisterComponent] [RegisterComponent]
public sealed class CombatModeComponent : Component public sealed class CombatModeComponent : SharedCombatModeComponent
{ {
public override string Name => "CombatMode"; private bool _isInCombatMode;
private TargetingZone _activeZone;
[ViewVariables(VVAccess.ReadWrite)] [ViewVariables(VVAccess.ReadWrite)]
public bool IsInCombatMode { get; set; } public bool IsInCombatMode
{
get => _isInCombatMode;
set
{
_isInCombatMode = value;
Dirty();
}
}
[ViewVariables(VVAccess.ReadWrite)]
public TargetingZone ActiveZone
{
get => _activeZone;
set
{
_activeZone = value;
Dirty();
}
}
public override ComponentState GetComponentState()
{
return new CombatModeComponentState(IsInCombatMode, ActiveZone);
}
} }
} }

View File

@@ -1,43 +1,91 @@
using Content.Server.GameObjects.EntitySystems; using System.Collections.Generic;
using System.Linq;
using Content.Server.GameObjects.Components.Mobs;
using Content.Server.GameObjects.EntitySystems;
using Content.Shared.GameObjects; using Content.Shared.GameObjects;
using Robust.Server.GameObjects.EntitySystems;
using Robust.Server.Interfaces.GameObjects; using Robust.Server.Interfaces.GameObjects;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map; using Robust.Shared.Interfaces.Map;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Weapon.Melee namespace Content.Server.GameObjects.Components.Weapon.Melee
{ {
[RegisterComponent] [RegisterComponent]
public class MeleeWeaponComponent : Component, IAttack public class MeleeWeaponComponent : Component, IAttack
{ {
public override string Name => "MeleeWeapon";
#pragma warning disable 649 #pragma warning disable 649
[Dependency] private readonly IMapManager _mapManager; [Dependency] private readonly IMapManager _mapManager;
[Dependency] private readonly IServerEntityManager _serverEntityManager; [Dependency] private readonly IServerEntityManager _serverEntityManager;
[Dependency] private readonly IEntitySystemManager _entitySystemManager;
[Dependency] private readonly IGameTiming _gameTiming;
[Dependency] private readonly IPrototypeManager _prototypeManager;
#pragma warning restore 649 #pragma warning restore 649
public override string Name => "MeleeWeapon"; private int _damage = 1;
private float _range = 1;
private float _arcWidth = 90;
private string _arc;
private string _hitSound;
public int Damage = 1; [ViewVariables(VVAccess.ReadWrite)]
public float Range = 1; public string Arc
public float ArcWidth = 90; {
get => _arc;
set => _arc = value;
}
[ViewVariables(VVAccess.ReadWrite)]
public float ArcWidth
{
get => _arcWidth;
set => _arcWidth = value;
}
[ViewVariables(VVAccess.ReadWrite)]
public float Range
{
get => _range;
set => _range = value;
}
[ViewVariables(VVAccess.ReadWrite)]
public int Damage
{
get => _damage;
set => _damage = value;
}
public override void ExposeData(ObjectSerializer serializer) public override void ExposeData(ObjectSerializer serializer)
{ {
base.ExposeData(serializer); base.ExposeData(serializer);
serializer.DataField(ref Damage, "damage", 5); serializer.DataField(ref _damage, "damage", 5);
serializer.DataField(ref Range, "range", 1); serializer.DataField(ref _range, "range", 1);
serializer.DataField(ref ArcWidth, "arcwidth", 90); serializer.DataField(ref _arcWidth, "arcwidth", 90);
serializer.DataField(ref _arc, "arc", "default");
serializer.DataField(ref _hitSound, "hitSound", "/Audio/weapons/genhit1.ogg");
} }
void IAttack.Attack(AttackEventArgs eventArgs) void IAttack.Attack(AttackEventArgs eventArgs)
{ {
var location = eventArgs.User.Transform.GridPosition; var location = eventArgs.User.Transform.GridPosition;
var angle = new Angle(eventArgs.ClickLocation.ToWorld(_mapManager).Position - location.ToWorld(_mapManager).Position); var angle = new Angle(eventArgs.ClickLocation.ToWorld(_mapManager).Position -
var entities = _serverEntityManager.GetEntitiesInArc(eventArgs.User.Transform.GridPosition, Range, angle, ArcWidth); location.ToWorld(_mapManager).Position);
// This should really be improved. GetEntitiesInArc uses pos instead of bounding boxes.
var entities =
_serverEntityManager.GetEntitiesInArc(eventArgs.User.Transform.GridPosition, Range, angle, ArcWidth);
var hitEntities = new List<IEntity>();
foreach (var entity in entities) foreach (var entity in entities)
{ {
if (!entity.Transform.IsMapTransform || entity == eventArgs.User) if (!entity.Transform.IsMapTransform || entity == eventArgs.User)
@@ -46,8 +94,18 @@ namespace Content.Server.GameObjects.Components.Weapon.Melee
if (entity.TryGetComponent(out DamageableComponent damageComponent)) if (entity.TryGetComponent(out DamageableComponent damageComponent))
{ {
damageComponent.TakeDamage(DamageType.Brute, Damage); damageComponent.TakeDamage(DamageType.Brute, Damage);
hitEntities.Add(entity);
} }
} }
var audioSystem = _entitySystemManager.GetEntitySystem<AudioSystem>();
audioSystem.Play(hitEntities.Count > 0 ? _hitSound : "/Audio/weapons/punchmiss.ogg");
if (Arc != null)
{
var sys = _entitySystemManager.GetEntitySystem<MeleeWeaponSystem>();
sys.SendAnimation(Arc, angle, eventArgs.User, hitEntities);
}
} }
} }
} }

View File

@@ -1,15 +1,25 @@
using Content.Server.GameObjects.Components.Mobs; using Content.Server.GameObjects.Components.Mobs;
using Content.Shared.Input; using Content.Shared.Input;
using JetBrains.Annotations;
using Robust.Server.GameObjects.EntitySystems; using Robust.Server.GameObjects.EntitySystems;
using Robust.Server.Interfaces.Player; using Robust.Server.Interfaces.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems; using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Input; using Robust.Shared.Input;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.IoC;
using Robust.Shared.Players; using Robust.Shared.Players;
using static Content.Shared.GameObjects.EntitySystemMessages.CombatModeSystemMessages;
namespace Content.Server.GameObjects.EntitySystems namespace Content.Server.GameObjects.EntitySystems
{ {
[UsedImplicitly]
public sealed class CombatModeSystem : EntitySystem public sealed class CombatModeSystem : EntitySystem
{ {
#pragma warning disable 649
[Dependency] private readonly IPlayerManager _playerManager;
#pragma warning restore 649
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
@@ -19,7 +29,15 @@ namespace Content.Server.GameObjects.EntitySystems
InputCmdHandler.FromDelegate(CombatModeToggled)); InputCmdHandler.FromDelegate(CombatModeToggled));
} }
private void CombatModeToggled(ICommonSession session) public override void RegisterMessageTypes()
{
base.RegisterMessageTypes();
RegisterMessageType<SetTargetZoneMessage>();
RegisterMessageType<SetCombatModeActiveMessage>();
}
private static void CombatModeToggled(ICommonSession session)
{ {
var playerSession = (IPlayerSession) session; var playerSession = (IPlayerSession) session;
@@ -31,5 +49,28 @@ namespace Content.Server.GameObjects.EntitySystems
combatModeComponent.IsInCombatMode = !combatModeComponent.IsInCombatMode; combatModeComponent.IsInCombatMode = !combatModeComponent.IsInCombatMode;
} }
public override void HandleNetMessage(INetChannel channel, EntitySystemMessage message)
{
base.HandleNetMessage(channel, message);
var player = _playerManager.GetSessionByChannel(channel);
if (player.AttachedEntity == null
|| !player.AttachedEntity.TryGetComponent(out CombatModeComponent combatModeComponent))
{
return;
}
switch (message)
{
case SetTargetZoneMessage setTargetZone:
combatModeComponent.ActiveZone = setTargetZone.TargetZone;
break;
case SetCombatModeActiveMessage setActive:
combatModeComponent.IsInCombatMode = setActive.Active;
break;
}
}
} }
} }

View File

@@ -0,0 +1,18 @@
using System.Collections.Generic;
using System.Linq;
using Content.Shared.GameObjects.EntitySystemMessages;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Maths;
namespace Content.Server.GameObjects.EntitySystems
{
public sealed class MeleeWeaponSystem : EntitySystem
{
public void SendAnimation(string arc, Angle angle, IEntity attacker, IEnumerable<IEntity> hits)
{
RaiseNetworkEvent(new MeleeWeaponSystemMessages.PlayMeleeWeaponAnimationMessage(arc, angle, attacker.Uid,
hits.Select(e => e.Uid).ToList()));
}
}
}

View File

@@ -0,0 +1,27 @@
using System;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Mobs
{
public abstract class SharedCombatModeComponent : Component
{
public sealed override uint? NetID => ContentNetIDs.COMBATMODE;
public override string Name => "CombatMode";
public sealed override Type StateType => typeof(CombatModeComponentState);
[Serializable, NetSerializable]
protected sealed class CombatModeComponentState : ComponentState
{
public bool IsInCombatMode { get; }
public TargetingZone TargetingZone { get; }
public CombatModeComponentState(bool isInCombatMode, TargetingZone targetingZone)
: base(ContentNetIDs.COMBATMODE)
{
IsInCombatMode = isInCombatMode;
TargetingZone = targetingZone;
}
}
}
}

View File

@@ -0,0 +1,27 @@
using System;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Mobs
{
/// <summary>
/// Zones the player can target for attacks.
/// </summary>
[Serializable, NetSerializable]
public enum TargetingZone
{
/// <summary>
/// Torso/arm area.
/// </summary>
Middle,
/// <summary>
/// Legs/groin area.
/// </summary>
Low,
/// <summary>
/// Go for the head.
/// </summary>
High
}
}

View File

@@ -0,0 +1,59 @@
using System;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
using YamlDotNet.RepresentationModel;
namespace Content.Shared.GameObjects.Components.Weapons.Melee
{
[Prototype("MeleeWeaponAnimation")]
public sealed class MeleeWeaponAnimationPrototype : IPrototype, IIndexedPrototype
{
private string _state;
private string _id;
private Vector4 _colorDelta;
private Vector4 _color;
private TimeSpan _length;
private float _speed;
private float _width;
private WeaponArcType _arcType;
[ViewVariables] public string ID => _id;
[ViewVariables] public string State => _state;
[ViewVariables] public TimeSpan Length => _length;
[ViewVariables] public float Speed => _speed;
[ViewVariables] public Vector4 Color => _color;
[ViewVariables] public Vector4 ColorDelta => _colorDelta;
[ViewVariables] public WeaponArcType ArcType => _arcType;
[ViewVariables] public float Width => _width;
public void LoadFrom(YamlMappingNode mapping)
{
var serializer = YamlObjectSerializer.NewReader(mapping);
serializer.DataField(ref _state, "state", null);
serializer.DataField(ref _id, "id", null);
serializer.DataField(ref _colorDelta, "colorDelta", Vector4.Zero);
serializer.DataField(ref _color, "color", new Vector4(1, 1, 1, 1));
if (serializer.TryReadDataField("length", out float length))
{
_length = TimeSpan.FromSeconds(length);
}
else
{
_length = TimeSpan.FromSeconds(0.5f);
}
serializer.DataField(ref _speed, "speed", 1);
serializer.DataField(ref _arcType, "arcType", WeaponArcType.Slash);
serializer.DataField(ref _width, "width", 90);
}
}
public enum WeaponArcType
{
Slash,
Poke,
}
}

View File

@@ -28,5 +28,6 @@
public const uint TECHNOLOGY_DATABASE = 1022; public const uint TECHNOLOGY_DATABASE = 1022;
public const uint RESEARCH_CONSOLE = 1023; public const uint RESEARCH_CONSOLE = 1023;
public const uint WIRES = 1024; public const uint WIRES = 1024;
public const uint COMBATMODE = 1025;
} }
} }

View File

@@ -0,0 +1,32 @@
using System;
using Content.Shared.GameObjects.Components.Mobs;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.EntitySystemMessages
{
public static class CombatModeSystemMessages
{
[Serializable, NetSerializable]
public sealed class SetTargetZoneMessage : EntitySystemMessage
{
public SetTargetZoneMessage(TargetingZone targetZone)
{
TargetZone = targetZone;
}
public TargetingZone TargetZone { get; }
}
[Serializable, NetSerializable]
public sealed class SetCombatModeActiveMessage : EntitySystemMessage
{
public SetCombatModeActiveMessage(bool active)
{
Active = active;
}
public bool Active { get; }
}
}
}

View File

@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.EntitySystemMessages
{
public static class MeleeWeaponSystemMessages
{
[Serializable, NetSerializable]
public sealed class PlayMeleeWeaponAnimationMessage : EntitySystemMessage
{
public PlayMeleeWeaponAnimationMessage(string arcPrototype, Angle angle, EntityUid attacker, List<EntityUid> hits)
{
ArcPrototype = arcPrototype;
Angle = angle;
Attacker = attacker;
Hits = hits;
}
public string ArcPrototype { get; }
public Angle Angle { get; }
public EntityUid Attacker { get; }
public List<EntityUid> Hits { get; }
}
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,10 @@
- type: entity
id: WeaponArc
save: false
components:
- type: Sprite
sprite: Effects/weapons/arcs.rsi
directional: false
offset: 0.85, 0
drawdepth: Overlays
- type: MeleeWeaponArcAnimation

View File

@@ -1,6 +1,17 @@
- type: entity
id: ToolboxBase
parent: BaseItem
components:
- type: Storage
Capacity: 60
- type: Item
Size: 9999
- type: MeleeWeapon
hitSound: "/Audio/weapons/smash.ogg"
- type: entity - type: entity
name: Emergency Toolbox name: Emergency Toolbox
parent: BaseItem parent: ToolboxBase
id: RedToolboxItem id: RedToolboxItem
description: A bright red toolbox, stocked with emergency tools description: A bright red toolbox, stocked with emergency tools
components: components:
@@ -8,14 +19,10 @@
texture: Objects/Tools/toolbox_r.png texture: Objects/Tools/toolbox_r.png
- type: Icon - type: Icon
texture: Objects/Tools/toolbox_r.png texture: Objects/Tools/toolbox_r.png
- type: Storage
Capacity: 60
- type: Item
Size: 9999
- type: entity - type: entity
name: Mechanical Toolbox name: Mechanical Toolbox
parent: BaseItem parent: ToolboxBase
id: BlueToolboxItem id: BlueToolboxItem
description: A blue box, stocked with mechanical tools description: A blue box, stocked with mechanical tools
components: components:
@@ -23,14 +30,10 @@
texture: Objects/Tools/Toolbox_b.png texture: Objects/Tools/Toolbox_b.png
- type: Icon - type: Icon
texture: Objects/Tools/Toolbox_b.png texture: Objects/Tools/Toolbox_b.png
- type: Storage
Capacity: 60
- type: Item
Size: 9999
- type: entity - type: entity
name: Electrical Toolbox name: Electrical Toolbox
parent: BaseItem parent: ToolboxBase
id: YellowToolboxItem id: YellowToolboxItem
description: A toolbox typically stocked with electrical gear description: A toolbox typically stocked with electrical gear
components: components:
@@ -38,10 +41,6 @@
texture: Objects/Tools/Toolbox_y.png texture: Objects/Tools/Toolbox_y.png
- type: Icon - type: Icon
texture: Objects/Tools/Toolbox_y.png texture: Objects/Tools/Toolbox_y.png
- type: Storage
Capacity: 60
- type: Item
Size: 9999
- type: entity - type: entity
id: YellowToolboxItemFilled id: YellowToolboxItemFilled

View File

@@ -12,7 +12,18 @@
state: spear state: spear
- type: MeleeWeapon - type: MeleeWeapon
range: 1.5
arcwidth: 10
arc: spear
- type: Item - type: Item
Size: 24 Size: 24
sprite: Objects/Melee/spear.rsi sprite: Objects/Melee/spear.rsi
prefix: inhand prefix: inhand
- type: MeleeWeaponAnimation
id: spear
state: spear
length: 0.10
speed: 6
arcType: Poke

View File

@@ -0,0 +1,7 @@
- type: MeleeWeaponAnimation
id: default
state: slash
arcType: Slash
length: 0.1
color: 255,255,255,1020
colorDelta: 0,0,0,-5100

View File

@@ -0,0 +1,18 @@
{
"version": 1,
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "spear",
"directions": 1
},
{
"name": "slash",
"directions": 1
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 B

View File

@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="6.1054072mm"
height="5.5663342mm"
viewBox="0 0 6.1054072 5.5663342"
version="1.1"
id="svg3494"
inkscape:version="0.92.4 5da689c313, 2019-01-14"
sodipodi:docname="target-doll-high.svg">
<defs
id="defs3488" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.4"
inkscape:cx="-27.670735"
inkscape:cy="-44.325776"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:pagecheckerboard="true"
inkscape:window-width="1920"
inkscape:window-height="1043"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1" />
<metadata
id="metadata3491">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-24.917534,-38.704927)">
<ellipse
inkscape:label="Head"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.35841459"
ry="2.7831671"
rx="3.0527036"
cy="41.488094"
cx="27.970238"
id="Oval"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 B

View File

@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="32"
height="54.584877"
viewBox="0 0 8.4666669 14.442248"
version="1.1"
id="svg2285"
inkscape:version="0.92.4 5da689c313, 2019-01-14"
sodipodi:docname="target-doll-low.svg"
inkscape:export-filename="/home/pj/Projects/space-station-14/Resources/Textures/UserInterface/target-doll-low.svg.96dpi.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96">
<defs
id="defs2279" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="7.919596"
inkscape:cx="10.248134"
inkscape:cy="37.492927"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:pagecheckerboard="true"
inkscape:window-width="1920"
inkscape:window-height="1043"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
units="px"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<metadata
id="metadata2282">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-129.69328,-72.06459)">
<path
sodipodi:nodetypes="ccssccssccc"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.35841459"
inkscape:connector-curvature="0"
id="Combined-Shape"
d="m 138.02997,72.06459 0.007,12.860185 c 0,0.932522 -0.80549,1.571841 -1.59705,1.581941 -0.8089,0.01032 -1.60425,-0.628996 -1.60425,-1.581941 V 73.365601 h -1.56762 v 11.559174 c 0,0.8692 -0.79482,1.581941 -1.77407,1.581941 -0.97993,0 -1.67772,-0.712741 -1.67772,-1.581941 l 0.0103,-12.860185 z"
inkscape:label="Legs" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 420 B

View File

@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="14.761317mm"
height="12.411281mm"
viewBox="0 0 14.761317 12.411281"
version="1.1"
id="svg2871"
sodipodi:docname="target-doll-middle.svg"
inkscape:version="0.92.4 5da689c313, 2019-01-14">
<defs
id="defs2865" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="3.959798"
inkscape:cx="12.344179"
inkscape:cy="40.151172"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:pagecheckerboard="true"
inkscape:window-width="506"
inkscape:window-height="416"
inkscape:window-x="737"
inkscape:window-y="125"
inkscape:window-maximized="0" />
<metadata
id="metadata2868">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-10.762198,-63.252693)">
<g
transform="matrix(0.35841461,0,0,0.35841461,2.5098095,57.743415)"
style="fill:none;fill-rule:evenodd;stroke:none;stroke-width:1"
id="g2227"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96">
<path
style="fill:#ffffff;fill-opacity:1"
d="m 25.617188,18.316406 c -3.668766,-0.05227 -7.262714,0.181254 -9.824219,0.681641 -2.551079,0.498351 -1.047616,0.156792 -2.542969,0.511719 -2.314857,0.549439 -3.5665384,0.789355 -4.8964844,5.476562 -1.144246,4.032732 -2.0775479,11.023827 -3.2949218,23.515625 -0.541004,4.214675 5.5660002,4.497851 6.0878902,0.478516 0.812159,-6.314445 1.251033,-14.240793 3.007813,-20.582031 l 0.01953,24.539062 c 7.628906,0 15.257813,0 22.886719,0 h 0.002 l 0.01758,-24.539062 c 1.75678,6.341238 2.197607,14.267586 3.009766,20.582031 0.52189,4.019335 6.626941,3.736159 6.085937,-0.478516 C 44.958455,36.010155 44.027106,29.01906 42.88286,24.986328 41.552915,20.299121 40.29928,20.059205 37.984423,19.509766 36.48907,19.154839 37.994483,19.496396 35.443407,18.998047 32.881902,18.49766 29.286001,18.264136 25.617236,18.316406 Z"
transform="translate(18,-2.937978)"
id="path308"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cssscccccccccssscc" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1021 B

View File

@@ -0,0 +1,116 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="55.790806"
height="128"
viewBox="0 0 14.761317 33.866665"
version="1.1"
id="svg1561"
sodipodi:docname="target-doll.svg"
inkscape:version="0.92.4 5da689c313, 2019-01-14">
<defs
id="defs1555">
<inkscape:path-effect
center_point="43.625,24.812736"
end_point="43.625,52.938449"
start_point="43.625,-3.3129772"
effect="mirror_symmetry"
id="path-effect2208"
is_visible="true"
mode="free"
discard_orig_path="false"
fuse_paths="false"
oposite_fuse="false" />
<inkscape:path-effect
effect="mirror_symmetry"
start_point="44,-2.937978"
end_point="44,92.800469"
center_point="44,44.931246"
id="path-effect2206"
is_visible="true"
mode="vertical"
discard_orig_path="false"
fuse_paths="false"
oposite_fuse="false" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.8284271"
inkscape:cx="-11.723079"
inkscape:cy="59.646363"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:pagecheckerboard="true"
inkscape:window-width="1920"
inkscape:window-height="1043"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
units="px"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<metadata
id="metadata1558">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-66.160433,-111.91867)">
<g
style="fill:none;fill-rule:evenodd;stroke:none;stroke-width:1"
id="Target-Doll"
transform="matrix(0.35841461,0,0,0.35841461,57.908044,112.67527)">
<ellipse
inkscape:label="Head"
style="fill:#ffffff;fill-opacity:1"
ry="7.7652173"
rx="8.5172415"
cy="5.6542549"
cx="43.7285"
id="Oval" />
<path
sodipodi:nodetypes="ccssccssccc"
style="fill:#ffffff;fill-opacity:1"
inkscape:connector-curvature="0"
id="Combined-Shape"
d="m 55.060955,52.084422 0.01936,35.880749 c 0,2.601799 -2.24735,4.385539 -4.455865,4.41372 -2.25688,0.0288 -4.475973,-1.754942 -4.475973,-4.41372 V 55.714329 h -4.373759 v 32.250842 c 0,2.425126 -2.2176,4.41372 -4.949759,4.41372 -2.73408,0 -4.680959,-1.988594 -4.680959,-4.41372 l 0.02877,-35.880749 z"
inkscape:label="Legs" />
<g
id="g2227">
<path
style="fill:#ffffff;fill-opacity:1"
d="m 25.617188,18.316406 c -3.668766,-0.05227 -7.262714,0.181254 -9.824219,0.681641 -2.551079,0.498351 -1.047616,0.156792 -2.542969,0.511719 -2.314857,0.549439 -3.5665384,0.789355 -4.8964844,5.476562 -1.144246,4.032732 -2.0775479,11.023827 -3.2949218,23.515625 -0.541004,4.214675 5.5660002,4.497851 6.0878902,0.478516 0.812159,-6.314445 1.251033,-14.240793 3.007813,-20.582031 l 0.01953,24.539062 c 7.628906,0 15.257813,0 22.886719,0 h 0.002 l 0.01758,-24.539062 c 1.75678,6.341238 2.197607,14.267586 3.009766,20.582031 0.52189,4.019335 6.626941,3.736159 6.085937,-0.478516 C 44.958455,36.010155 44.027106,29.01906 42.88286,24.986328 41.552915,20.299121 40.29928,20.059205 37.984423,19.509766 36.48907,19.154839 37.994483,19.496396 35.443407,18.998047 32.881902,18.49766 29.286001,18.264136 25.617236,18.316406 Z"
transform="translate(18,-2.937978)"
id="path308"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cssscccccccccssscc" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@@ -82,7 +82,7 @@ binds:
key: MouseRight key: MouseRight
canFocus: true canFocus: true
- function: ToggleCombatMode - function: ToggleCombatMode
type: Toggle type: State
key: Tab key: Tab
- function: OpenCraftingMenu - function: OpenCraftingMenu
type: state type: state