diff --git a/Content.Client/GameObjects/Components/Items/ClientHandsComponent.cs b/Content.Client/GameObjects/Components/Items/ClientHandsComponent.cs index 74e040398e..f7f1e3c815 100644 --- a/Content.Client/GameObjects/Components/Items/ClientHandsComponent.cs +++ b/Content.Client/GameObjects/Components/Items/ClientHandsComponent.cs @@ -101,6 +101,7 @@ namespace Content.Client.GameObjects ActiveIndex = cast.ActiveIndex; _gui?.UpdateHandIcons(); + RefreshInHands(); } private void _setHand(string hand, IEntity entity) @@ -116,7 +117,19 @@ namespace Content.Client.GameObjects return; } - var item = entity.GetComponent(); + SetInHands(hand, entity); + } + + private void SetInHands(string hand, IEntity entity) + { + if (entity == null) + { + _sprite.LayerSetVisible($"hand-{hand}", false); + + return; + } + + if (!entity.TryGetComponent(out ItemComponent item)) return; var maybeInhands = item.GetInHandStateInfo(hand); if (!maybeInhands.HasValue) { @@ -130,6 +143,16 @@ namespace Content.Client.GameObjects } } + public void RefreshInHands() + { + if (!Initialized) return; + + foreach (var (hand, entity) in _hands) + { + SetInHands(hand, entity); + } + } + public override void ExposeData(ObjectSerializer serializer) { base.ExposeData(serializer); diff --git a/Content.Client/GameObjects/Components/Items/ItemComponent.cs b/Content.Client/GameObjects/Components/Items/ItemComponent.cs index 4140314c55..4c6a1d0402 100644 --- a/Content.Client/GameObjects/Components/Items/ItemComponent.cs +++ b/Content.Client/GameObjects/Components/Items/ItemComponent.cs @@ -3,8 +3,10 @@ using Content.Shared.GameObjects.Components.Items; using Robust.Client.Graphics; using Robust.Client.Interfaces.ResourceManagement; using Robust.Client.ResourceManagement; +using Robust.Shared.Containers; using Robust.Shared.GameObjects; using Robust.Shared.GameObjects.Components.Renderable; +using Robust.Shared.Interfaces.GameObjects.Components; using Robust.Shared.IoC; using Robust.Shared.Serialization; using Robust.Shared.Utility; @@ -26,7 +28,13 @@ namespace Content.Client.GameObjects public string EquippedPrefix { get => _equippedPrefix; - set => _equippedPrefix = value; + set + { + _equippedPrefix = value; + if (!ContainerHelpers.TryGetContainer(Owner, out IContainer container)) return; + if(container.Owner.TryGetComponent(out HandsComponent hands)) + hands.RefreshInHands(); + } } public (RSI rsi, RSI.StateId stateId)? GetInHandStateInfo(string hand) diff --git a/Content.Client/GameObjects/Components/Mobs/SpeciesVisualizer2D.cs b/Content.Client/GameObjects/Components/Mobs/SpeciesVisualizer2D.cs index 9c4400588d..46a31f069f 100644 --- a/Content.Client/GameObjects/Components/Mobs/SpeciesVisualizer2D.cs +++ b/Content.Client/GameObjects/Components/Mobs/SpeciesVisualizer2D.cs @@ -1,6 +1,11 @@ -using Content.Shared.GameObjects.Components.Mobs; +using System; +using Content.Shared.GameObjects.Components.Mobs; +using Microsoft.CodeAnalysis.Completion; +using Robust.Client.Animations; using Robust.Client.GameObjects; +using Robust.Client.GameObjects.Components.Animations; using Robust.Client.Interfaces.GameObjects.Components; +using Robust.Shared.Animations; using Robust.Shared.Maths; namespace Content.Client.GameObjects.Components.Mobs @@ -11,19 +16,51 @@ namespace Content.Client.GameObjects.Components.Mobs { base.OnChangeData(component); - var sprite = component.Owner.GetComponent(); if (component.TryGetData(SharedSpeciesComponent.MobVisuals.RotationState, out var state)) { switch (state) { - case SharedSpeciesComponent.MobState.Stand: - sprite.Rotation = 0; + case SharedSpeciesComponent.MobState.Standing: + SetRotation(component, 0); break; case SharedSpeciesComponent.MobState.Down: - sprite.Rotation = Angle.FromDegrees(90); + SetRotation(component, Angle.FromDegrees(90)); break; } } } + + private void SetRotation(AppearanceComponent component, Angle rotation) + { + var sprite = component.Owner.GetComponent(); + + if (!sprite.Owner.TryGetComponent(out AnimationPlayerComponent animation)) + { + sprite.Rotation = rotation; + return; + } + + if (animation.HasRunningAnimation("rotate")) + animation.Stop("rotate"); + + animation.Play(new Animation + { + Length = TimeSpan.FromSeconds(0.125), + AnimationTracks = + { + new AnimationTrackComponentProperty + { + ComponentType = typeof(ISpriteComponent), + Property = nameof(ISpriteComponent.Rotation), + InterpolationMode = AnimationInterpolationMode.Linear, + KeyFrames = + { + new AnimationTrackProperty.KeyFrame(sprite.Rotation, 0), + new AnimationTrackProperty.KeyFrame(rotation, 0.125f) + } + } + } + }, "rotate"); + } } } diff --git a/Content.Client/Interfaces/GameObjects/Components/Items/IHandsComponent.cs b/Content.Client/Interfaces/GameObjects/Components/Items/IHandsComponent.cs index 031f760198..0915f66599 100644 --- a/Content.Client/Interfaces/GameObjects/Components/Items/IHandsComponent.cs +++ b/Content.Client/Interfaces/GameObjects/Components/Items/IHandsComponent.cs @@ -14,5 +14,6 @@ namespace Content.Client.Interfaces.GameObjects void AttackByInHand(string index); void UseActiveHand(); void ActivateItemInHand(string handIndex); + void RefreshInHands(); } } diff --git a/Content.Server/GameObjects/Components/GUI/InventoryComponent.cs b/Content.Server/GameObjects/Components/GUI/InventoryComponent.cs index 6244af156a..db412a8467 100644 --- a/Content.Server/GameObjects/Components/GUI/InventoryComponent.cs +++ b/Content.Server/GameObjects/Components/GUI/InventoryComponent.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks.Dataflow; using Content.Server.GameObjects.EntitySystems; using Content.Shared.GameObjects; using Robust.Server.GameObjects.Components.Container; @@ -129,6 +130,9 @@ namespace Content.Server.GameObjects { var pass = false; + if (!ActionBlockerSystem.CanEquip(Owner)) + return false; + if (item is ClothingComponent clothing) { if (clothing.SlotFlags != SlotFlags.PREVENTEQUIP && (clothing.SlotFlags & SlotMasks[slot]) != 0) @@ -185,6 +189,9 @@ namespace Content.Server.GameObjects /// public bool CanUnequip(Slots slot) { + if (!ActionBlockerSystem.CanUnequip(Owner)) + return false; + var InventorySlot = SlotContainers[slot]; return InventorySlot.ContainedEntity != null && InventorySlot.CanRemove(InventorySlot.ContainedEntity); } diff --git a/Content.Server/GameObjects/Components/Items/Storage/ItemComponent.cs b/Content.Server/GameObjects/Components/Items/Storage/ItemComponent.cs index d50c346adc..4c8cde4ae8 100644 --- a/Content.Server/GameObjects/Components/Items/Storage/ItemComponent.cs +++ b/Content.Server/GameObjects/Components/Items/Storage/ItemComponent.cs @@ -13,6 +13,7 @@ using Robust.Shared.Containers; using Robust.Shared.GameObjects; using Robust.Shared.GameObjects.Components; using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.GameObjects.Components; using Robust.Shared.Interfaces.Map; using Robust.Shared.Interfaces.Physics; using Robust.Shared.Interfaces.Random; @@ -48,8 +49,8 @@ namespace Content.Server.GameObjects } set { - Dirty(); _equippedPrefix = value; + Dirty(); } } diff --git a/Content.Server/GameObjects/Components/Kitchen/KitchenMicrowaveComponent.cs b/Content.Server/GameObjects/Components/Kitchen/KitchenMicrowaveComponent.cs index 36a52aa7a5..d5e4aa8191 100644 --- a/Content.Server/GameObjects/Components/Kitchen/KitchenMicrowaveComponent.cs +++ b/Content.Server/GameObjects/Components/Kitchen/KitchenMicrowaveComponent.cs @@ -274,7 +274,7 @@ namespace Content.Server.GameObjects.Components.Kitchen (_currentCookTimerTime == (uint)recipeToCook.CookTime) ? true : false; SetAppearance(MicrowaveVisualState.Cooking); - _audioSystem.Play(_startCookingSound, AudioParams.Default); + _audioSystem.Play(_startCookingSound,Owner, AudioParams.Default); Timer.Spawn((int)(_currentCookTimerTime * _cookTimeMultiplier), () => { @@ -290,7 +290,7 @@ namespace Content.Server.GameObjects.Components.Kitchen var entityToSpawn = goodMeal ? recipeToCook.Result : _badRecipeName; _entityManager.SpawnEntity(entityToSpawn, Owner.Transform.GridPosition); - _audioSystem.Play(_cookingCompleteSound, AudioParams.Default); + _audioSystem.Play(_cookingCompleteSound,Owner, AudioParams.Default); SetAppearance(MicrowaveVisualState.Idle); _busy = false; UpdateUserInterface(); @@ -395,7 +395,7 @@ namespace Content.Server.GameObjects.Components.Kitchen private void ClickSound() { - _audioSystem.Play("/Audio/machines/machine_switch.ogg", AudioParams.Default.WithVolume(-2f)); + _audioSystem.Play("/Audio/machines/machine_switch.ogg",Owner, AudioParams.Default.WithVolume(-2f)); } diff --git a/Content.Server/GameObjects/Components/Mobs/DamageStates.cs b/Content.Server/GameObjects/Components/Mobs/DamageStates.cs index 515cf77e60..74ce8cf13e 100644 --- a/Content.Server/GameObjects/Components/Mobs/DamageStates.cs +++ b/Content.Server/GameObjects/Components/Mobs/DamageStates.cs @@ -1,8 +1,14 @@ -using Content.Server.GameObjects.EntitySystems; +using Content.Server.GameObjects.Components.Mobs; +using Content.Server.GameObjects.EntitySystems; +using Content.Server.Mobs; +using Content.Shared.Audio; using Content.Shared.GameObjects.Components.Mobs; +using Microsoft.EntityFrameworkCore.Metadata.Builders; using Robust.Server.GameObjects; +using Robust.Server.GameObjects.EntitySystems; using Robust.Shared.GameObjects.Components; using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.IoC; namespace Content.Server.GameObjects { @@ -77,6 +83,21 @@ namespace Content.Server.GameObjects { return true; } + + bool IActionBlocker.CanEquip() + { + return true; + } + + bool IActionBlocker.CanUnequip() + { + return true; + } + + bool IActionBlocker.CanChangeDirection() + { + return true; + } } /// @@ -86,10 +107,15 @@ namespace Content.Server.GameObjects { public void EnterState(IEntity entity) { + if(entity.TryGetComponent(out StunnableComponent stun)) + stun.CancelAll(); + + StandingStateHelper.Down(entity); } public void ExitState(IEntity entity) { + StandingStateHelper.Standing(entity); } public bool IsConscious => false; @@ -138,6 +164,21 @@ namespace Content.Server.GameObjects { return false; } + + bool IActionBlocker.CanEquip() + { + return false; + } + + bool IActionBlocker.CanUnequip() + { + return false; + } + + bool IActionBlocker.CanChangeDirection() + { + return true; + } } /// @@ -147,11 +188,10 @@ namespace Content.Server.GameObjects { public void EnterState(IEntity entity) { - if (entity.TryGetComponent(out AppearanceComponent appearance)) - { - var newState = SharedSpeciesComponent.MobState.Down; - appearance.SetData(SharedSpeciesComponent.MobVisuals.RotationState, newState); - } + if(entity.TryGetComponent(out StunnableComponent stun)) + stun.CancelAll(); + + StandingStateHelper.Down(entity, playSound:false); if (entity.TryGetComponent(out CollidableComponent collidable)) { @@ -161,11 +201,7 @@ namespace Content.Server.GameObjects public void ExitState(IEntity entity) { - if (entity.TryGetComponent(out AppearanceComponent appearance)) - { - var newState = SharedSpeciesComponent.MobState.Stand; - appearance.SetData(SharedSpeciesComponent.MobVisuals.RotationState, newState); - } + StandingStateHelper.Standing(entity); if (entity.TryGetComponent(out CollidableComponent collidable)) { @@ -219,5 +255,20 @@ namespace Content.Server.GameObjects { return false; } + + bool IActionBlocker.CanEquip() + { + return false; + } + + bool IActionBlocker.CanUnequip() + { + return false; + } + + bool IActionBlocker.CanChangeDirection() + { + return false; + } } } diff --git a/Content.Server/GameObjects/Components/Mobs/StunnableComponent.cs b/Content.Server/GameObjects/Components/Mobs/StunnableComponent.cs new file mode 100644 index 0000000000..3ec94fa9b7 --- /dev/null +++ b/Content.Server/GameObjects/Components/Mobs/StunnableComponent.cs @@ -0,0 +1,332 @@ +using System; +using System.Threading; +using Content.Server.GameObjects.Components.Movement; +using Content.Server.GameObjects.EntitySystems; +using Content.Server.Interfaces.GameObjects; +using Content.Server.Mobs; +using Content.Shared.Audio; +using Content.Shared.GameObjects.Components.Mobs; +using Robust.Server.GameObjects; +using Robust.Server.GameObjects.EntitySystems; +using Robust.Shared.Audio; +using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Timers; +using Robust.Shared.Interfaces.Timing; +using Robust.Shared.IoC; +using Robust.Shared.Maths; +using Robust.Shared.Serialization; +using Robust.Shared.ViewVariables; +using Timer = Robust.Shared.Timers.Timer; + +namespace Content.Server.GameObjects.Components.Mobs +{ + [RegisterComponent] + public class StunnableComponent : Component, IActionBlocker, IAttackHand, IMoveSpeedModifier + { + public override string Name => "Stunnable"; + +#pragma warning disable 649 + [Dependency] private IEntitySystemManager _entitySystemManager; + [Dependency] private IGameTiming _gameTiming; +#pragma warning restore 649 + + private TimeSpan? _lastStun; + + [ViewVariables] + public TimeSpan? StunStart => _lastStun; + + [ViewVariables] + public TimeSpan? StunEnd => _lastStun == null + ? (TimeSpan?) null + : _gameTiming.CurTime + TimeSpan.FromSeconds(_stunnedTimer + _knockdownTimer + _slowdownTimer); + + private const int StunLevels = 8; + + private bool _canHelp = true; + private float _stunCap = 20f; + private float _knockdownCap = 20f; + private float _slowdownCap = 20f; + private float _helpKnockdownRemove = 1f; + private float _helpInterval = 1f; + + private float _stunnedTimer = 0f; + private float _knockdownTimer = 0f; + private float _slowdownTimer = 0f; + + private float _walkModifierOverride = 0f; + private float _runModifierOverride = 0f; + private readonly string[] _texturesStunOverlay = new string[StunLevels]; + + [ViewVariables] public bool Stunned => _stunnedTimer > 0f; + [ViewVariables] public bool KnockedDown => _knockdownTimer > 0f; + [ViewVariables] public bool SlowedDown => _slowdownTimer > 0f; + [ViewVariables] public float StunCap => _stunCap; + [ViewVariables] public float KnockdownCap => _knockdownCap; + [ViewVariables] public float SlowdownCap => _slowdownCap; + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + serializer.DataField(ref _stunCap, "stunCap", 20f); + serializer.DataField(ref _knockdownCap, "knockdownCap", 20f); + serializer.DataField(ref _slowdownCap, "slowdownCap", 20f); + serializer.DataField(ref _helpInterval, "helpInterval", 1f); + serializer.DataField(ref _helpKnockdownRemove, "helpKnockdownRemove", 1f); + } + + public override void Initialize() + { + base.Initialize(); + + for (var i = 0; i < StunLevels; i++) + { + _texturesStunOverlay[i] = $"/Textures/UserInterface/Inventory/cooldown-{i}.png"; + } + } + + /// + /// Stuns the entity, disallowing it from doing many interactions temporarily. + /// + /// How many seconds the mob will stay stunned + public void Stun(float seconds) + { + seconds = MathF.Min(_stunnedTimer + (seconds * StunTimeModifier), _stunCap); + + if (seconds <= 0f) + return; + + StandingStateHelper.DropAllItemsInHands(Owner); + + _stunnedTimer = seconds; + _lastStun = _gameTiming.CurTime; + } + + /// + /// Knocks down the mob, making it fall to the ground. + /// + /// How many seconds the mob will stay on the ground + public void Knockdown(float seconds) + { + seconds = MathF.Min(_knockdownTimer + (seconds * KnockdownTimeModifier), _knockdownCap); + + if (seconds <= 0f) + return; + + StandingStateHelper.Down(Owner); + + _knockdownTimer = seconds; + _lastStun = _gameTiming.CurTime; + } + + /// + /// Applies knockdown and stun to the mob temporarily + /// + /// How many seconds the mob will be paralyzed + public void Paralyze(float seconds) + { + Stun(seconds); + Knockdown(seconds); + } + + /// + /// Slows down the mob's walking/running speed temporarily + /// + /// How many seconds the mob will be slowed down + /// Walk speed modifier. Set to 0 or negative for default value. (0.5f) + /// Run speed modifier. Set to 0 or negative for default value. (0.5f) + public void Slowdown(float seconds, float walkModifierOverride = 0f, float runModifierOverride = 0f) + { + seconds = MathF.Min(_slowdownTimer + (seconds * SlowdownTimeModifier), _slowdownCap); + + if (seconds <= 0f) + return; + + _walkModifierOverride = walkModifierOverride; + _runModifierOverride = runModifierOverride; + + _slowdownTimer = seconds; + _lastStun = _gameTiming.CurTime; + + if(Owner.TryGetComponent(out MovementSpeedModifierComponent movement)) + movement.RefreshMovementSpeedModifiers(); + } + + /// + /// Used when + /// + public void CancelAll() + { + _knockdownTimer = 0f; + _stunnedTimer = 0f; + } + + public bool AttackHand(AttackHandEventArgs eventArgs) + { + if (!_canHelp || !KnockedDown) + return false; + + _canHelp = false; + Timer.Spawn(((int)_helpInterval*1000), () => _canHelp = true); + + _entitySystemManager.GetEntitySystem() + .Play("/Audio/effects/thudswoosh.ogg", Owner, AudioHelpers.WithVariation(0.25f)); + + _knockdownTimer -= _helpKnockdownRemove; + + return true; + } + + public void Update(float delta) + { + if (Stunned) + { + _stunnedTimer -= delta; + + if (_stunnedTimer <= 0) + { + _stunnedTimer = 0f; + } + } + + if (KnockedDown) + { + _knockdownTimer -= delta; + + if (_knockdownTimer <= 0f) + { + StandingStateHelper.Standing(Owner); + + _knockdownTimer = 0f; + } + } + + if (SlowedDown) + { + _slowdownTimer -= delta; + + if (_slowdownTimer <= 0f) + { + _slowdownTimer = 0f; + + if(Owner.TryGetComponent(out MovementSpeedModifierComponent movement)) + movement.RefreshMovementSpeedModifiers(); + } + } + + if (!_lastStun.HasValue || !StunEnd.HasValue || !Owner.TryGetComponent(out ServerStatusEffectsComponent status)) + return; + + var start = _lastStun.Value; + var end = StunEnd.Value; + + var length = (end - start).TotalSeconds; + var progress = (_gameTiming.CurTime - start).TotalSeconds; + var ratio = (float)(progress / length); + + var textureIndex = CalculateStunLevel(ratio); + if (textureIndex == StunLevels) + { + _lastStun = null; + status.RemoveStatus(StatusEffect.Stun); + } + else + { + status.ChangeStatus(StatusEffect.Stun, _texturesStunOverlay[textureIndex]); + } + } + + private static int CalculateStunLevel(float stunValue) + { + var val = stunValue.Clamp(0, 1); + val *= StunLevels; + return (int)Math.Floor(val); + } + + #region ActionBlockers + public bool CanMove() => (!Stunned); + + public bool CanInteract() => (!Stunned); + + public bool CanUse() => (!Stunned); + + public bool CanThrow() => (!Stunned); + + public bool CanSpeak() => true; + + public bool CanDrop() => (!Stunned); + + public bool CanPickup() => (!Stunned); + + public bool CanEmote() => true; + + public bool CanAttack() => (!Stunned); + + public bool CanEquip() => (!Stunned); + + public bool CanUnequip() => (!Stunned); + public bool CanChangeDirection() => true; + #endregion + + public float StunTimeModifier + { + get + { + var modifier = 1.0f; + var components = Owner.GetAllComponents(); + + foreach (var component in components) + { + modifier *= component.StunTimeModifier; + } + + return modifier; + } + } + + public float KnockdownTimeModifier + { + get + { + var modifier = 1.0f; + var components = Owner.GetAllComponents(); + + foreach (var component in components) + { + modifier *= component.KnockdownTimeModifier; + } + + return modifier; + } + } + + public float SlowdownTimeModifier + { + get + { + var modifier = 1.0f; + var components = Owner.GetAllComponents(); + + foreach (var component in components) + { + modifier *= component.SlowdownTimeModifier; + } + + return modifier; + } + } + + public float WalkSpeedModifier => (SlowedDown ? (_walkModifierOverride <= 0f ? 0.5f : _walkModifierOverride) : 1f); + public float SprintSpeedModifier => (SlowedDown ? (_runModifierOverride <= 0f ? 0.5f : _runModifierOverride) : 1f); + } + + /// + /// This interface allows components to multiply the time in seconds of various stuns by a number. + /// + public interface IStunModifier + { + float StunTimeModifier => 1.0f; + float KnockdownTimeModifier => 1.0f; + float SlowdownTimeModifier => 1.0f; + } +} diff --git a/Content.Server/GameObjects/Components/Power/PowerGeneratorComponent.cs b/Content.Server/GameObjects/Components/Power/PowerGeneratorComponent.cs index e9285e7541..df10e90df4 100644 --- a/Content.Server/GameObjects/Components/Power/PowerGeneratorComponent.cs +++ b/Content.Server/GameObjects/Components/Power/PowerGeneratorComponent.cs @@ -65,7 +65,7 @@ namespace Content.Server.GameObjects.Components.Power { _supply = value; var node = Owner.GetComponent(); - node.Parent.UpdateGenerator(this); + node?.Parent?.UpdateGenerator(this); } /// diff --git a/Content.Server/GameObjects/Components/Power/SolarPanelComponent.cs b/Content.Server/GameObjects/Components/Power/SolarPanelComponent.cs new file mode 100644 index 0000000000..4707180c59 --- /dev/null +++ b/Content.Server/GameObjects/Components/Power/SolarPanelComponent.cs @@ -0,0 +1,91 @@ +using System; +using Content.Server.GameObjects.Components.Damage; +using Content.Server.GameObjects.EntitySystems; +using Content.Shared.Audio; +using Robust.Server.GameObjects; +using Robust.Server.GameObjects.EntitySystems; +using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Random; +using Robust.Shared.IoC; +using Robust.Shared.Maths; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; +using Robust.Shared.Serialization; +using Robust.Shared.ViewVariables; + +namespace Content.Server.GameObjects.Components.Power +{ + + /// + /// This is a solar panel. + /// It generates power from the sun based on coverage. + /// + [RegisterComponent] + public class SolarPanelComponent : Component, IBreakAct + { + public override string Name => "SolarPanel"; + + private PowerGeneratorComponent _powerGenerator; + + /// + /// Maximum supply output by this panel (coverage = 1) + /// + private float _maxSupply = 1500; + [ViewVariables(VVAccess.ReadWrite)] + public float MaxSupply + { + get => _maxSupply; + set { + _maxSupply = value; + UpdateSupply(); + } + } + + /// + /// Current coverage of this panel (from 0 to 1). + /// This is updated by . + /// + private float _coverage = 0; + [ViewVariables] + public float Coverage + { + get => _coverage; + set { + // This gets updated once-per-tick, so avoid updating it if truly unnecessary + if (_coverage != value) { + _coverage = value; + UpdateSupply(); + } + } + } + + private void UpdateSupply() + { + if (_powerGenerator != null) + _powerGenerator.Supply = _maxSupply * _coverage; + } + + public override void Initialize() + { + base.Initialize(); + + _powerGenerator = Owner.GetComponent(); + UpdateSupply(); + } + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + + serializer.DataField(ref _maxSupply, "maxsupply", 1500); + } + + public void OnBreak(BreakageEventArgs args) + { + var sprite = Owner.GetComponent(); + sprite.LayerSetState(0, "broken"); + MaxSupply = 0; + } + } +} diff --git a/Content.Server/GameObjects/Components/Research/ResearchConsoleComponent.cs b/Content.Server/GameObjects/Components/Research/ResearchConsoleComponent.cs index 9b10ad832a..89f3d2ddd4 100644 --- a/Content.Server/GameObjects/Components/Research/ResearchConsoleComponent.cs +++ b/Content.Server/GameObjects/Components/Research/ResearchConsoleComponent.cs @@ -7,6 +7,7 @@ using Robust.Server.GameObjects.Components.UserInterface; using Robust.Server.GameObjects.EntitySystems; using Robust.Server.Interfaces.GameObjects; using Robust.Server.Interfaces.Player; +using Robust.Shared.Audio; using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Random; @@ -33,7 +34,7 @@ namespace Content.Server.GameObjects.Components.Research private const string _soundCollectionName = "keyboard"; private bool Powered => _powerDevice.Powered; - + public override void Initialize() { base.Initialize(); @@ -119,7 +120,7 @@ namespace Content.Server.GameObjects.Components.Research var soundCollection = _prototypeManager.Index(_soundCollectionName); var file = _random.Pick(soundCollection.PickFiles); var audioSystem = _entitySystemManager.GetEntitySystem(); - audioSystem.Play(file); + audioSystem.Play(file,Owner,AudioParams.Default); } diff --git a/Content.Server/GameObjects/Components/Weapon/Melee/MeleeWeaponComponent.cs b/Content.Server/GameObjects/Components/Weapon/Melee/MeleeWeaponComponent.cs index 32c9f4c127..c2f24bb97f 100644 --- a/Content.Server/GameObjects/Components/Weapon/Melee/MeleeWeaponComponent.cs +++ b/Content.Server/GameObjects/Components/Weapon/Melee/MeleeWeaponComponent.cs @@ -75,6 +75,11 @@ namespace Content.Server.GameObjects.Components.Weapon.Melee serializer.DataField(ref _cooldownTime, "cooldownTime", 1f); } + public virtual bool OnHitEntities(IReadOnlyList entities) + { + return false; + } + void IAttack.Attack(AttackEventArgs eventArgs) { var curTime = IoCManager.Resolve().CurTime; @@ -101,6 +106,8 @@ namespace Content.Server.GameObjects.Components.Weapon.Melee } } + if(OnHitEntities(hitEntities)) return; + var audioSystem = _entitySystemManager.GetEntitySystem(); var emitter = hitEntities.Count == 0 ? eventArgs.User : hitEntities[0]; audioSystem.Play(hitEntities.Count > 0 ? _hitSound : "/Audio/weapons/punchmiss.ogg", emitter); diff --git a/Content.Server/GameObjects/Components/Weapon/Melee/StunbatonComponent.cs b/Content.Server/GameObjects/Components/Weapon/Melee/StunbatonComponent.cs new file mode 100644 index 0000000000..21000d1e39 --- /dev/null +++ b/Content.Server/GameObjects/Components/Weapon/Melee/StunbatonComponent.cs @@ -0,0 +1,93 @@ +using System.Collections.Generic; +using Content.Server.GameObjects.Components.Mobs; +using Content.Server.GameObjects.EntitySystems; +using Content.Shared.Audio; +using Robust.Server.GameObjects; +using Robust.Server.GameObjects.EntitySystems; +using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Random; +using Robust.Shared.IoC; +using Robust.Shared.Random; +using Robust.Shared.Serialization; +using Robust.Shared.ViewVariables; + +namespace Content.Server.GameObjects.Components.Weapon.Melee +{ + [RegisterComponent] + public class StunbatonComponent : MeleeWeaponComponent, IUse + { + [Dependency] private IRobustRandom _robustRandom; + [Dependency] private IEntitySystemManager _entitySystemManager; + + public override string Name => "Stunbaton"; + + private bool _activated = false; + + [ViewVariables(VVAccess.ReadWrite)] + private float _paralyzeChance = 0.25f; + + [ViewVariables(VVAccess.ReadWrite)] + private float _paralyzeTime = 10f; + + [ViewVariables(VVAccess.ReadWrite)] + private float _slowdownTime = 5f; + + [ViewVariables] + public bool Activated => _activated; + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + + serializer.DataField(ref _paralyzeChance, "paralyzeChance", 0.25f); + serializer.DataField(ref _paralyzeTime, "paralyzeTime", 10f); + serializer.DataField(ref _slowdownTime, "slowdownTime", 5f); + } + + public override bool OnHitEntities(IReadOnlyList entities) + { + if (!Activated || entities.Count == 0) + return false; + + _entitySystemManager.GetEntitySystem() + .Play("/Audio/weapons/egloves.ogg", Owner.Transform.GridPosition, AudioHelpers.WithVariation(0.25f)); + + foreach (var entity in entities) + { + if (!entity.TryGetComponent(out StunnableComponent stunnable)) continue; + + if(_robustRandom.Prob(_paralyzeChance)) + stunnable.Paralyze(_paralyzeTime); + else + stunnable.Slowdown(_slowdownTime); + } + + return false; + } + + public bool UseEntity(UseEntityEventArgs eventArgs) + { + var sprite = Owner.GetComponent(); + var item = Owner.GetComponent(); + + if (_activated) + { + item.EquippedPrefix = "off"; + sprite.LayerSetState(0, "stunbaton_off"); + _activated = false; + } + else + { + _entitySystemManager.GetEntitySystem() + .Play(AudioHelpers.GetRandomFileFromSoundCollection("sparks"), Owner.Transform.GridPosition, AudioHelpers.WithVariation(0.25f)); + + item.EquippedPrefix = "on"; + sprite.LayerSetState(0, "stunbaton_on"); + _activated = true; + } + + return true; + } + } +} diff --git a/Content.Server/GameObjects/EntitySystems/ActionBlockerSystem.cs b/Content.Server/GameObjects/EntitySystems/ActionBlockerSystem.cs index 36de02bb79..90bb573228 100644 --- a/Content.Server/GameObjects/EntitySystems/ActionBlockerSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/ActionBlockerSystem.cs @@ -22,6 +22,9 @@ namespace Content.Server.GameObjects.EntitySystems bool CanEmote() => true; bool CanAttack() => true; + bool CanEquip() => true; + bool CanUnequip() => true; + bool CanChangeDirection() => true; } public class ActionBlockerSystem : EntitySystem @@ -119,5 +122,41 @@ namespace Content.Server.GameObjects.EntitySystems return canattack; } + + public static bool CanEquip(IEntity entity) + { + bool canequip = true; + + foreach (var actionblockercomponents in entity.GetAllComponents()) + { + canequip &= actionblockercomponents.CanEquip(); + } + + return canequip; + } + + public static bool CanUnequip(IEntity entity) + { + bool canunequip = true; + + foreach (var actionblockercomponents in entity.GetAllComponents()) + { + canunequip &= actionblockercomponents.CanUnequip(); + } + + return canunequip; + } + + public static bool CanChangeDirection(IEntity entity) + { + bool canchangedirection = true; + + foreach (var actionblockercomponents in entity.GetAllComponents()) + { + canchangedirection &= actionblockercomponents.CanChangeDirection(); + } + + return canchangedirection; + } } } diff --git a/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs b/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs index cab0f56f11..a888858988 100644 --- a/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs @@ -454,13 +454,14 @@ namespace Content.Server.GameObjects.EntitySystems var item = hands.GetActiveHand?.Owner; + if(ActionBlockerSystem.CanChangeDirection(player)) + playerTransform.LocalRotation = new Angle(coordinates.ToMapPos(_mapManager) - playerTransform.MapPosition.Position); + if (!ActionBlockerSystem.CanInteract(player)) { return; } - playerTransform.LocalRotation = new Angle(coordinates.ToMapPos(_mapManager) - playerTransform.MapPosition.Position); - // TODO: Check if client should be able to see that object to click on it in the first place // Clicked on empty space behavior, try using ranged attack diff --git a/Content.Server/GameObjects/EntitySystems/PowerSolarSystem.cs b/Content.Server/GameObjects/EntitySystems/PowerSolarSystem.cs new file mode 100644 index 0000000000..4108748e48 --- /dev/null +++ b/Content.Server/GameObjects/EntitySystems/PowerSolarSystem.cs @@ -0,0 +1,65 @@ +using Content.Server.GameObjects.Components.Power; +using JetBrains.Annotations; +using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Systems; +using Robust.Shared.Maths; +using System; + +namespace Content.Server.GameObjects.EntitySystems +{ + /// + /// Responsible for maintaining the solar-panel sun angle and updating coverage. + /// + [UsedImplicitly] + public class PowerSolarSystem: EntitySystem + { + /// + /// The current sun angle. + /// + public Angle TowardsSun = Angle.South; + + public override void Initialize() + { + EntityQuery = new TypeEntityQuery(typeof(SolarPanelComponent)); + } + + public override void Update(float frameTime) + { + TowardsSun += Angle.FromDegrees(frameTime / 10); + TowardsSun = TowardsSun.Reduced(); + foreach (var entity in RelevantEntities) + { + // In the 'sunRelative' coordinate system: + // the sun is considered to be an infinite distance directly up. + // this is the rotation of the panel relative to that. + // directly upwards (theta = 0) = coverage 1 + // left/right 90 degrees (abs(theta) = (pi / 2)) = coverage 0 + // directly downwards (abs(theta) = pi) = coverage -1 + // as TowardsSun + = CCW, + // panelRelativeToSun should - = CW + var panelRelativeToSun = entity.Transform.WorldRotation - TowardsSun; + // essentially, given cos = X & sin = Y & Y is 'downwards', + // then for the first 90 degrees of rotation in either direction, + // this plots the lower-right quadrant of a circle. + // now basically assume a line going from the negated X/Y to there, + // and that's the hypothetical solar panel. + // + // since, again, the sun is considered to be an infinite distance upwards, + // this essentially means Cos(panelRelativeToSun) is half of the cross-section, + // and since the full cross-section has a max of 2, effectively-halving it is fine. + // + // as for when it goes negative, it only does that when (abs(theta) > pi) + // and that's expected behavior. + float coverage = (float) Math.Max(0, Math.Cos(panelRelativeToSun)); + + // Would determine occlusion, but that requires raytraces. + // And I'm not sure where those are in the codebase. + // Luckily, auto-rotation isn't in yet, so it won't matter anyway. + + // Total coverage calculated; apply it to the panel. + var panel = entity.GetComponent(); + panel.Coverage = coverage; + } + } + } +} diff --git a/Content.Server/GameObjects/EntitySystems/StunSystem.cs b/Content.Server/GameObjects/EntitySystems/StunSystem.cs new file mode 100644 index 0000000000..fea2a19502 --- /dev/null +++ b/Content.Server/GameObjects/EntitySystems/StunSystem.cs @@ -0,0 +1,28 @@ +using Content.Server.GameObjects.Components.Mobs; +using Content.Shared.GameObjects.Components.Mobs; +using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Systems; +using Robust.Shared.Interfaces.GameObjects; + +namespace Content.Server.GameObjects.EntitySystems +{ + public class StunSystem : EntitySystem + { + public override void Initialize() + { + base.Initialize(); + + EntityQuery = new TypeEntityQuery(typeof(StunnableComponent)); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + foreach (var entity in RelevantEntities) + { + entity.GetComponent().Update(frameTime); + } + } + } +} diff --git a/Content.Server/Mobs/StandingStateHelper.cs b/Content.Server/Mobs/StandingStateHelper.cs new file mode 100644 index 0000000000..36e1c584c2 --- /dev/null +++ b/Content.Server/Mobs/StandingStateHelper.cs @@ -0,0 +1,74 @@ +using Content.Server.Interfaces.GameObjects; +using Content.Shared.Audio; +using Content.Shared.GameObjects.Components.Mobs; +using Robust.Server.GameObjects; +using Robust.Server.GameObjects.EntitySystems; +using Robust.Shared.Audio; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Random; +using Robust.Shared.IoC; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; + +namespace Content.Server.Mobs +{ + public static class StandingStateHelper + { + /// + /// Set's the mob standing state to down. + /// + /// The mob in question + /// Whether to play a sound when falling down or not + /// Whether to make the mob drop all the items on his hands + /// False if the mob was already downed or couldn't set the state + public static bool Down(IEntity entity, bool playSound = true, bool dropItems = true) + { + if (!entity.TryGetComponent(out AppearanceComponent appearance)) return false; + + appearance.TryGetData(SharedSpeciesComponent.MobVisuals.RotationState, out var oldState); + + var newState = SharedSpeciesComponent.MobState.Down; + if (newState == oldState) + return false; + + appearance.SetData(SharedSpeciesComponent.MobVisuals.RotationState, newState); + + if (playSound) + IoCManager.Resolve().GetEntitySystem() + .Play(AudioHelpers.GetRandomFileFromSoundCollection("bodyfall"), entity, AudioHelpers.WithVariation(0.25f)); + + if(dropItems) + DropAllItemsInHands(entity); + + return true; + } + + /// + /// Sets the mob's standing state to standing. + /// + /// The mob in question. + /// False if the mob was already standing or couldn't set the state + public static bool Standing(IEntity entity) + { + if (!entity.TryGetComponent(out AppearanceComponent appearance)) return false; + appearance.TryGetData(SharedSpeciesComponent.MobVisuals.RotationState, out var oldState); + var newState = SharedSpeciesComponent.MobState.Standing; + if (newState == oldState) + return false; + + appearance.SetData(SharedSpeciesComponent.MobVisuals.RotationState, newState); + + return true; + } + + public static void DropAllItemsInHands(IEntity entity) + { + if (!entity.TryGetComponent(out IHandsComponent hands)) return; + + foreach (var heldItem in hands.GetAllHeldItems()) + { + hands.Drop(heldItem.Owner); + } + } + } +} diff --git a/Content.Shared/Audio/AudioHelpers.cs b/Content.Shared/Audio/AudioHelpers.cs index f6a920f479..4e2a68b0fb 100644 --- a/Content.Shared/Audio/AudioHelpers.cs +++ b/Content.Shared/Audio/AudioHelpers.cs @@ -2,9 +2,12 @@ using System; using Content.Shared.GameObjects.Components.Sound; using Robust.Shared.Audio; using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Random; using Robust.Shared.IoC; using Robust.Shared.Log; +using Robust.Shared.Map; +using Robust.Shared.Prototypes; using Robust.Shared.Random; namespace Content.Shared.Audio @@ -18,5 +21,11 @@ namespace Content.Shared.Audio var scale = (float)(IoCManager.Resolve().NextGaussian(1, amplitude)); return AudioParams.Default.WithPitchScale(scale); } + + public static string GetRandomFileFromSoundCollection(string name) + { + var soundCollection = IoCManager.Resolve().Index(name); + return IoCManager.Resolve().Pick(soundCollection.PickFiles); + } } } diff --git a/Content.Shared/GameObjects/Components/Mobs/SharedSpeciesComponent.cs b/Content.Shared/GameObjects/Components/Mobs/SharedSpeciesComponent.cs index 0396410c9a..eed7165fbb 100644 --- a/Content.Shared/GameObjects/Components/Mobs/SharedSpeciesComponent.cs +++ b/Content.Shared/GameObjects/Components/Mobs/SharedSpeciesComponent.cs @@ -20,7 +20,7 @@ namespace Content.Shared.GameObjects.Components.Mobs /// /// Mob is standing up /// - Stand, + Standing, /// /// Mob is laying down diff --git a/Content.Shared/GameObjects/Components/Mobs/SharedStatusEffectsComponent.cs b/Content.Shared/GameObjects/Components/Mobs/SharedStatusEffectsComponent.cs index 48c2e1c2fd..482b24fc17 100644 --- a/Content.Shared/GameObjects/Components/Mobs/SharedStatusEffectsComponent.cs +++ b/Content.Shared/GameObjects/Components/Mobs/SharedStatusEffectsComponent.cs @@ -32,5 +32,6 @@ namespace Content.Shared.GameObjects.Components.Mobs Health, Hunger, Thirst, + Stun, } } diff --git a/Resources/Audio/effects/Egloves.ogg b/Resources/Audio/effects/Egloves.ogg new file mode 100644 index 0000000000..c3ebddf0ff Binary files /dev/null and b/Resources/Audio/effects/Egloves.ogg differ diff --git a/Resources/Audio/effects/bodyfall1.ogg b/Resources/Audio/effects/bodyfall1.ogg new file mode 100644 index 0000000000..6a8eecefee Binary files /dev/null and b/Resources/Audio/effects/bodyfall1.ogg differ diff --git a/Resources/Audio/effects/bodyfall2.ogg b/Resources/Audio/effects/bodyfall2.ogg new file mode 100644 index 0000000000..b41bd746c5 Binary files /dev/null and b/Resources/Audio/effects/bodyfall2.ogg differ diff --git a/Resources/Audio/effects/bodyfall3.ogg b/Resources/Audio/effects/bodyfall3.ogg new file mode 100644 index 0000000000..ef2d5ca700 Binary files /dev/null and b/Resources/Audio/effects/bodyfall3.ogg differ diff --git a/Resources/Audio/effects/bodyfall4.ogg b/Resources/Audio/effects/bodyfall4.ogg new file mode 100644 index 0000000000..8951e75b40 Binary files /dev/null and b/Resources/Audio/effects/bodyfall4.ogg differ diff --git a/Resources/Audio/effects/sparks1.ogg b/Resources/Audio/effects/sparks1.ogg new file mode 100644 index 0000000000..3cfecb9235 Binary files /dev/null and b/Resources/Audio/effects/sparks1.ogg differ diff --git a/Resources/Audio/effects/sparks2.ogg b/Resources/Audio/effects/sparks2.ogg new file mode 100644 index 0000000000..8dd8af7066 Binary files /dev/null and b/Resources/Audio/effects/sparks2.ogg differ diff --git a/Resources/Audio/effects/sparks3.ogg b/Resources/Audio/effects/sparks3.ogg new file mode 100644 index 0000000000..feb933cd54 Binary files /dev/null and b/Resources/Audio/effects/sparks3.ogg differ diff --git a/Resources/Audio/effects/sparks4.ogg b/Resources/Audio/effects/sparks4.ogg new file mode 100644 index 0000000000..3faca5ec2e Binary files /dev/null and b/Resources/Audio/effects/sparks4.ogg differ diff --git a/Resources/Audio/effects/thudswoosh.ogg b/Resources/Audio/effects/thudswoosh.ogg new file mode 100644 index 0000000000..934ec6350f Binary files /dev/null and b/Resources/Audio/effects/thudswoosh.ogg differ diff --git a/Resources/Audio/weapons/egloves.ogg b/Resources/Audio/weapons/egloves.ogg new file mode 100644 index 0000000000..bd92ec294e Binary files /dev/null and b/Resources/Audio/weapons/egloves.ogg differ diff --git a/Resources/Prototypes/Entities/Buildings/airlock_access.yml b/Resources/Prototypes/Entities/Buildings/airlock_access.yml new file mode 100644 index 0000000000..6bffa1c88a --- /dev/null +++ b/Resources/Prototypes/Entities/Buildings/airlock_access.yml @@ -0,0 +1,8 @@ +- type: entity + parent: airlock_engineering + id: airlock_engineering_locked + name: Engineering Airlock + suffix: Locked + components: + - type: AccessReader + required: ["engineering"] diff --git a/Resources/Prototypes/Entities/Buildings/airlock_types.yml b/Resources/Prototypes/Entities/Buildings/airlock_types.yml index d3b2f17098..f5407b8007 100644 --- a/Resources/Prototypes/Entities/Buildings/airlock_types.yml +++ b/Resources/Prototypes/Entities/Buildings/airlock_types.yml @@ -40,16 +40,6 @@ - type: Icon sprite: Buildings/Doors/airlock_engineering_glass.rsi - -- type: entity - parent: airlock_engineering - id: airlock_engineering_locked - name: Locked Engineering Airlock - components: - - type: AccessReader - required: ["engineering"] - - - type: entity parent: Airlock id: airlock_medical diff --git a/Resources/Prototypes/Entities/Buildings/instruments.yml b/Resources/Prototypes/Entities/Buildings/instruments.yml index 22826ba0fe..671e6fc1df 100644 --- a/Resources/Prototypes/Entities/Buildings/instruments.yml +++ b/Resources/Prototypes/Entities/Buildings/instruments.yml @@ -47,7 +47,7 @@ id: MinimoogInstrument components: - type: Instrument - program: 7 + program: 81 - type: Sprite sprite: Objects/Instruments/otherinstruments.rsi state: minimoog diff --git a/Resources/Prototypes/Entities/Buildings/power.yml b/Resources/Prototypes/Entities/Buildings/power.yml index 5191c4e1f0..a599ba1842 100644 --- a/Resources/Prototypes/Entities/Buildings/power.yml +++ b/Resources/Prototypes/Entities/Buildings/power.yml @@ -62,6 +62,35 @@ - type: SnapGrid offset: Center +- type: entity + id: SolarPanel + name: Solar Panel + description: Generates power from sunlight. Usually used to power replacements for sunlight. Fragile. + placement: + mode: SnapgridCenter + components: + - type: Clickable + - type: InteractionOutline + - type: Collidable + shapes: + - !type:PhysShapeAabb + layer: 31 + - type: Sprite + sprite: Buildings/solar_panel.rsi + state: normal + - type: Icon + sprite: Buildings/solar_panel.rsi + state: normal + - type: PowerGenerator + - type: SolarPanel + supply: 1500 + - type: Rotatable + - type: SnapGrid + offset: Center + - type: Damageable + - type: Breakable + thresholdvalue: 100 + - type: entity id: WPPnobattery name: WPPnobattery diff --git a/Resources/Prototypes/Entities/Items/Instruments.yml b/Resources/Prototypes/Entities/Items/Instruments.yml index 24b8e5565e..b083775970 100644 --- a/Resources/Prototypes/Entities/Items/Instruments.yml +++ b/Resources/Prototypes/Entities/Items/Instruments.yml @@ -17,7 +17,7 @@ id: SynthesizerInstrument components: - type: Instrument - program: 2 + program: 62 - type: Sprite sprite: Objects/Instruments/h_synthesizer.rsi state: icon diff --git a/Resources/Prototypes/Entities/Items/Weapons/security.yml b/Resources/Prototypes/Entities/Items/Weapons/security.yml new file mode 100644 index 0000000000..cc0a69562b --- /dev/null +++ b/Resources/Prototypes/Entities/Items/Weapons/security.yml @@ -0,0 +1,25 @@ +- type: entity + name: Stun baton + parent: BaseItem + id: Stunbaton + components: + - type: Sprite + sprite: Objects/Melee/stunbaton.rsi + state: stunbaton_off + + - type: Icon + sprite: Objects/Melee/stunbaton.rsi + state: stunbaton_off + + - type: Stunbaton + damage: 1 + range: 0.75 + arcwidth: 0 + arc: default + + - type: Item + Size: 10 + sprite: Objects/Melee/stunbaton.rsi + HeldPrefix: off + + - type: ItemCooldown diff --git a/Resources/Prototypes/Entities/Mobs/human.yml b/Resources/Prototypes/Entities/Mobs/human.yml index 34bebab229..45c3755f3e 100644 --- a/Resources/Prototypes/Entities/Mobs/human.yml +++ b/Resources/Prototypes/Entities/Mobs/human.yml @@ -131,6 +131,8 @@ - type: FootstepSound - type: HumanoidAppearance - type: HumanInventoryController + - type: Stunnable + - type: AnimationPlayer - type: entity save: false diff --git a/Resources/Prototypes/Entities/water_tank.yml b/Resources/Prototypes/Entities/water_tank.yml index 34be446a38..66c8a90136 100644 --- a/Resources/Prototypes/Entities/water_tank.yml +++ b/Resources/Prototypes/Entities/water_tank.yml @@ -2,13 +2,14 @@ parent: ReagentItem id: WaterTank name: Water Tank + suffix: Empty description: "A water tank. It is used to store high amounts of water." components: - type: Sprite - texture: Buildings/WaterTank.png + texture: Buildings/watertank.png - type: Icon - texture: Buildings/WaterTank.png + texture: Buildings/watertank.png - type: Clickable - type: InteractionOutline @@ -37,6 +38,7 @@ - type: entity parent: WaterTank id: WaterTankFull + suffix: Full components: - type: Solution contents: diff --git a/Resources/Prototypes/SoundCollections/body_fall.yml b/Resources/Prototypes/SoundCollections/body_fall.yml new file mode 100644 index 0000000000..a3338c94e4 --- /dev/null +++ b/Resources/Prototypes/SoundCollections/body_fall.yml @@ -0,0 +1,7 @@ +- type: sound_collection + id: bodyfall + files: + - /Audio/effects/bodyfall1.ogg + - /Audio/effects/bodyfall2.ogg + - /Audio/effects/bodyfall3.ogg + - /Audio/effects/bodyfall4.ogg diff --git a/Resources/Prototypes/SoundCollections/sparks.yml b/Resources/Prototypes/SoundCollections/sparks.yml new file mode 100644 index 0000000000..8f81b6055c --- /dev/null +++ b/Resources/Prototypes/SoundCollections/sparks.yml @@ -0,0 +1,7 @@ +- type: sound_collection + id: sparks + files: + - /Audio/effects/sparks1.ogg + - /Audio/effects/sparks2.ogg + - /Audio/effects/sparks3.ogg + - /Audio/effects/sparks4.ogg diff --git a/Resources/Textures/Buildings/solar_panel.rsi/broken.png b/Resources/Textures/Buildings/solar_panel.rsi/broken.png new file mode 100644 index 0000000000..ded81ca75f Binary files /dev/null and b/Resources/Textures/Buildings/solar_panel.rsi/broken.png differ diff --git a/Resources/Textures/Buildings/solar_panel.rsi/meta.json b/Resources/Textures/Buildings/solar_panel.rsi/meta.json new file mode 100644 index 0000000000..377fa6ccb7 --- /dev/null +++ b/Resources/Textures/Buildings/solar_panel.rsi/meta.json @@ -0,0 +1 @@ +{"version":1,"license":"CC-BY-SA-3.0","copyright":"Taken from https://github.com/discordia-space/CEV-Eris/blob/d1e0161af146835f4fb79d21a6200caa9cc842d0/icons/obj/power.dmi and modified.","size":{"x":32,"y":32},"states":[{"name":"normal","select":[],"flags":{},"directions":8},{"name":"broken","select":[],"flags":{},"directions":1}]} diff --git a/Resources/Textures/Buildings/solar_panel.rsi/normal.png b/Resources/Textures/Buildings/solar_panel.rsi/normal.png new file mode 100644 index 0000000000..1e1c620029 Binary files /dev/null and b/Resources/Textures/Buildings/solar_panel.rsi/normal.png differ diff --git a/Resources/Textures/Objects/Melee/stunbaton.rsi/meta.json b/Resources/Textures/Objects/Melee/stunbaton.rsi/meta.json new file mode 100644 index 0000000000..a49523ff93 --- /dev/null +++ b/Resources/Textures/Objects/Melee/stunbaton.rsi/meta.json @@ -0,0 +1,143 @@ +{ + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "license": "CC BY-SA 3.0", + "copyright": "Taken from https://github.com/vgstation-coders/vgstation13 at b8758256b31013946fdbd320ca043a663c399656", + "states": + [ + { + "name": "off-inhand-left", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "off-inhand-right", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "on-inhand-left", + "directions": 4, + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + ], + [ + 0.1, + 0.1, + 0.1, + 0.1, + ], + [ + 0.1, + 0.1, + 0.1, + 0.1, + ], + [ + 0.1, + 0.1, + 0.1, + 0.1, + ], + ] + }, + { + "name": "on-inhand-right", + "directions": 4, + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + ], + [ + 0.1, + 0.1, + 0.1, + 0.1, + ], + [ + 0.1, + 0.1, + 0.1, + 0.1, + ], + [ + 0.1, + 0.1, + 0.1, + 0.1, + ], + ] + }, + { + "name": "stunbaton_off", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "stunbaton_nocell", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "stunbaton_on", + "directions": 1, + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + ] + ] + } + + ] +} + diff --git a/Resources/Textures/Objects/Melee/stunbaton.rsi/off-inhand-left.png b/Resources/Textures/Objects/Melee/stunbaton.rsi/off-inhand-left.png new file mode 100644 index 0000000000..fd0560130a Binary files /dev/null and b/Resources/Textures/Objects/Melee/stunbaton.rsi/off-inhand-left.png differ diff --git a/Resources/Textures/Objects/Melee/stunbaton.rsi/off-inhand-right.png b/Resources/Textures/Objects/Melee/stunbaton.rsi/off-inhand-right.png new file mode 100644 index 0000000000..0c01f273cb Binary files /dev/null and b/Resources/Textures/Objects/Melee/stunbaton.rsi/off-inhand-right.png differ diff --git a/Resources/Textures/Objects/Melee/stunbaton.rsi/on-inhand-left.png b/Resources/Textures/Objects/Melee/stunbaton.rsi/on-inhand-left.png new file mode 100644 index 0000000000..540811b676 Binary files /dev/null and b/Resources/Textures/Objects/Melee/stunbaton.rsi/on-inhand-left.png differ diff --git a/Resources/Textures/Objects/Melee/stunbaton.rsi/on-inhand-right.png b/Resources/Textures/Objects/Melee/stunbaton.rsi/on-inhand-right.png new file mode 100644 index 0000000000..d983fde6fc Binary files /dev/null and b/Resources/Textures/Objects/Melee/stunbaton.rsi/on-inhand-right.png differ diff --git a/Resources/Textures/Objects/Melee/stunbaton.rsi/stunbaton_nocell.png b/Resources/Textures/Objects/Melee/stunbaton.rsi/stunbaton_nocell.png new file mode 100644 index 0000000000..8c4fbac7fd Binary files /dev/null and b/Resources/Textures/Objects/Melee/stunbaton.rsi/stunbaton_nocell.png differ diff --git a/Resources/Textures/Objects/Melee/stunbaton.rsi/stunbaton_off.png b/Resources/Textures/Objects/Melee/stunbaton.rsi/stunbaton_off.png new file mode 100644 index 0000000000..59940f44ca Binary files /dev/null and b/Resources/Textures/Objects/Melee/stunbaton.rsi/stunbaton_off.png differ diff --git a/Resources/Textures/Objects/Melee/stunbaton.rsi/stunbaton_on.png b/Resources/Textures/Objects/Melee/stunbaton.rsi/stunbaton_on.png new file mode 100644 index 0000000000..2132190ad4 Binary files /dev/null and b/Resources/Textures/Objects/Melee/stunbaton.rsi/stunbaton_on.png differ diff --git a/RobustToolbox b/RobustToolbox index a72dd8c85b..feb5050b32 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit a72dd8c85bc1903ef98e709fc1a735bd5e667bdc +Subproject commit feb5050b32499c5f9e1e61ceae63bf74f6797a27 diff --git a/SpaceStation14.sln.DotSettings b/SpaceStation14.sln.DotSettings index 7d2c5a9648..316ab3918a 100644 --- a/SpaceStation14.sln.DotSettings +++ b/SpaceStation14.sln.DotSettings @@ -58,3 +58,5 @@ True True True + True + True