Merge branch 'master' into 2020-04-28-tool-component

# Conflicts:
#	SpaceStation14.sln.DotSettings
This commit is contained in:
zumorica
2020-05-22 11:31:18 +02:00
56 changed files with 1128 additions and 43 deletions

View File

@@ -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
/// </returns>
public bool CanUnequip(Slots slot)
{
if (!ActionBlockerSystem.CanUnequip(Owner))
return false;
var InventorySlot = SlotContainers[slot];
return InventorySlot.ContainedEntity != null && InventorySlot.CanRemove(InventorySlot.ContainedEntity);
}

View File

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

View File

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

View File

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

View File

@@ -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";
}
}
/// <summary>
/// Stuns the entity, disallowing it from doing many interactions temporarily.
/// </summary>
/// <param name="seconds">How many seconds the mob will stay stunned</param>
public void Stun(float seconds)
{
seconds = MathF.Min(_stunnedTimer + (seconds * StunTimeModifier), _stunCap);
if (seconds <= 0f)
return;
StandingStateHelper.DropAllItemsInHands(Owner);
_stunnedTimer = seconds;
_lastStun = _gameTiming.CurTime;
}
/// <summary>
/// Knocks down the mob, making it fall to the ground.
/// </summary>
/// <param name="seconds">How many seconds the mob will stay on the ground</param>
public void Knockdown(float seconds)
{
seconds = MathF.Min(_knockdownTimer + (seconds * KnockdownTimeModifier), _knockdownCap);
if (seconds <= 0f)
return;
StandingStateHelper.Down(Owner);
_knockdownTimer = seconds;
_lastStun = _gameTiming.CurTime;
}
/// <summary>
/// Applies knockdown and stun to the mob temporarily
/// </summary>
/// <param name="seconds">How many seconds the mob will be paralyzed</param>
public void Paralyze(float seconds)
{
Stun(seconds);
Knockdown(seconds);
}
/// <summary>
/// Slows down the mob's walking/running speed temporarily
/// </summary>
/// <param name="seconds">How many seconds the mob will be slowed down</param>
/// <param name="walkModifierOverride">Walk speed modifier. Set to 0 or negative for default value. (0.5f)</param>
/// <param name="runModifierOverride">Run speed modifier. Set to 0 or negative for default value. (0.5f)</param>
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();
}
/// <summary>
/// Used when
/// </summary>
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<AudioSystem>()
.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<IStunModifier>();
foreach (var component in components)
{
modifier *= component.StunTimeModifier;
}
return modifier;
}
}
public float KnockdownTimeModifier
{
get
{
var modifier = 1.0f;
var components = Owner.GetAllComponents<IStunModifier>();
foreach (var component in components)
{
modifier *= component.KnockdownTimeModifier;
}
return modifier;
}
}
public float SlowdownTimeModifier
{
get
{
var modifier = 1.0f;
var components = Owner.GetAllComponents<IStunModifier>();
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);
}
/// <summary>
/// This interface allows components to multiply the time in seconds of various stuns by a number.
/// </summary>
public interface IStunModifier
{
float StunTimeModifier => 1.0f;
float KnockdownTimeModifier => 1.0f;
float SlowdownTimeModifier => 1.0f;
}
}

View File

@@ -65,7 +65,7 @@ namespace Content.Server.GameObjects.Components.Power
{
_supply = value;
var node = Owner.GetComponent<PowerNodeComponent>();
node.Parent.UpdateGenerator(this);
node?.Parent?.UpdateGenerator(this);
}
/// <summary>

View File

@@ -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
{
/// <summary>
/// This is a solar panel.
/// It generates power from the sun based on coverage.
/// </summary>
[RegisterComponent]
public class SolarPanelComponent : Component, IBreakAct
{
public override string Name => "SolarPanel";
private PowerGeneratorComponent _powerGenerator;
/// <summary>
/// Maximum supply output by this panel (coverage = 1)
/// </summary>
private float _maxSupply = 1500;
[ViewVariables(VVAccess.ReadWrite)]
public float MaxSupply
{
get => _maxSupply;
set {
_maxSupply = value;
UpdateSupply();
}
}
/// <summary>
/// Current coverage of this panel (from 0 to 1).
/// This is updated by <see cref='PowerSolarSystem'/>.
/// </summary>
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<PowerGeneratorComponent>();
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<SpriteComponent>();
sprite.LayerSetState(0, "broken");
MaxSupply = 0;
}
}
}

View File

@@ -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<SoundCollectionPrototype>(_soundCollectionName);
var file = _random.Pick(soundCollection.PickFiles);
var audioSystem = _entitySystemManager.GetEntitySystem<AudioSystem>();
audioSystem.Play(file);
audioSystem.Play(file,Owner,AudioParams.Default);
}

View File

@@ -75,6 +75,11 @@ namespace Content.Server.GameObjects.Components.Weapon.Melee
serializer.DataField(ref _cooldownTime, "cooldownTime", 1f);
}
public virtual bool OnHitEntities(IReadOnlyList<IEntity> entities)
{
return false;
}
void IAttack.Attack(AttackEventArgs eventArgs)
{
var curTime = IoCManager.Resolve<IGameTiming>().CurTime;
@@ -101,6 +106,8 @@ namespace Content.Server.GameObjects.Components.Weapon.Melee
}
}
if(OnHitEntities(hitEntities)) return;
var audioSystem = _entitySystemManager.GetEntitySystem<AudioSystem>();
var emitter = hitEntities.Count == 0 ? eventArgs.User : hitEntities[0];
audioSystem.Play(hitEntities.Count > 0 ? _hitSound : "/Audio/weapons/punchmiss.ogg", emitter);

View File

@@ -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<IEntity> entities)
{
if (!Activated || entities.Count == 0)
return false;
_entitySystemManager.GetEntitySystem<AudioSystem>()
.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<SpriteComponent>();
var item = Owner.GetComponent<ItemComponent>();
if (_activated)
{
item.EquippedPrefix = "off";
sprite.LayerSetState(0, "stunbaton_off");
_activated = false;
}
else
{
_entitySystemManager.GetEntitySystem<AudioSystem>()
.Play(AudioHelpers.GetRandomFileFromSoundCollection("sparks"), Owner.Transform.GridPosition, AudioHelpers.WithVariation(0.25f));
item.EquippedPrefix = "on";
sprite.LayerSetState(0, "stunbaton_on");
_activated = true;
}
return true;
}
}
}