Re-organize all projects (#4166)

This commit is contained in:
DrSmugleaf
2021-06-09 22:19:39 +02:00
committed by GitHub
parent 9f50e4061b
commit ff1a2d97ea
1773 changed files with 5258 additions and 5508 deletions

View File

@@ -0,0 +1,340 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using Content.Server.Alert;
using Content.Server.DoAfter;
using Content.Server.Hands.Components;
using Content.Shared.ActionBlocker;
using Content.Shared.Alert;
using Content.Shared.Cuffs.Components;
using Content.Shared.Interaction.Helpers;
using Content.Shared.Notification;
using Content.Shared.Verbs;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.Localization;
using Robust.Shared.Log;
using Robust.Shared.Maths;
using Robust.Shared.Player;
using Robust.Shared.Players;
using Robust.Shared.ViewVariables;
namespace Content.Server.Cuffs.Components
{
[RegisterComponent]
public class CuffableComponent : SharedCuffableComponent
{
/// <summary>
/// How many of this entity's hands are currently cuffed.
/// </summary>
[ViewVariables]
public int CuffedHandCount => Container.ContainedEntities.Count * 2;
protected IEntity LastAddedCuffs => Container.ContainedEntities[^1];
public IReadOnlyList<IEntity> StoredEntities => Container.ContainedEntities;
/// <summary>
/// Container of various handcuffs currently applied to the entity.
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
public Container Container { get; set; } = default!;
// TODO: Make a component message
public event Action? OnCuffedStateChanged;
private bool _uncuffing;
public override void Initialize()
{
base.Initialize();
Container = ContainerHelpers.EnsureContainer<Container>(Owner, Name);
Owner.EnsureComponentWarn<HandsComponent>();
}
public override ComponentState GetComponentState(ICommonSession player)
{
// there are 2 approaches i can think of to handle the handcuff overlay on players
// 1 - make the current RSI the handcuff type that's currently active. all handcuffs on the player will appear the same.
// 2 - allow for several different player overlays for each different cuff type.
// approach #2 would be more difficult/time consuming to do and the payoff doesn't make it worth it.
// right now we're doing approach #1.
if (CuffedHandCount > 0)
{
if (LastAddedCuffs.TryGetComponent<HandcuffComponent>(out var cuffs))
{
return new CuffableComponentState(CuffedHandCount,
CanStillInteract,
cuffs.CuffedRSI,
$"{cuffs.OverlayIconState}-{CuffedHandCount}",
cuffs.Color);
// the iconstate is formatted as blah-2, blah-4, blah-6, etc.
// the number corresponds to how many hands are cuffed.
}
}
return new CuffableComponentState(CuffedHandCount,
CanStillInteract,
"/Objects/Misc/handcuffs.rsi",
"body-overlay-2",
Color.White);
}
/// <summary>
/// Add a set of cuffs to an existing CuffedComponent.
/// </summary>
/// <param name="prototype"></param>
public bool TryAddNewCuffs(IEntity user, IEntity handcuff)
{
if (!handcuff.HasComponent<HandcuffComponent>())
{
Logger.Warning($"Handcuffs being applied to player are missing a {nameof(HandcuffComponent)}!");
return false;
}
if (!handcuff.InRangeUnobstructed(Owner))
{
Logger.Warning("Handcuffs being applied to player are obstructed or too far away! This should not happen!");
return true;
}
// Success!
if (user.TryGetComponent(out HandsComponent? handsComponent) && handsComponent.IsHolding(handcuff))
{
// Good lord handscomponent is scuffed, I hope some smug person will fix it someday
handsComponent.Drop(handcuff);
}
Container.Insert(handcuff);
CanStillInteract = Owner.TryGetComponent(out HandsComponent? ownerHands) && ownerHands.Hands.Count() > CuffedHandCount;
OnCuffedStateChanged?.Invoke();
UpdateAlert();
UpdateHeldItems();
Dirty();
return true;
}
public void CuffedStateChanged()
{
UpdateAlert();
OnCuffedStateChanged?.Invoke();
}
/// <summary>
/// Check how many items the user is holding and if it's more than the number of cuffed hands, drop some items.
/// </summary>
public void UpdateHeldItems()
{
if (!Owner.TryGetComponent(out HandsComponent? handsComponent)) return;
var itemCount = handsComponent.GetAllHeldItems().Count();
var freeHandCount = handsComponent.Hands.Count() - CuffedHandCount;
if (freeHandCount < itemCount)
{
foreach (var item in handsComponent.GetAllHeldItems())
{
if (freeHandCount < itemCount)
{
freeHandCount++;
handsComponent.Drop(item.Owner, false);
}
else
{
break;
}
}
}
}
/// <summary>
/// Updates the status effect indicator on the HUD.
/// </summary>
private void UpdateAlert()
{
if (Owner.TryGetComponent(out ServerAlertsComponent? status))
{
if (CanStillInteract)
{
status.ClearAlert(AlertType.Handcuffed);
}
else
{
status.ShowAlert(AlertType.Handcuffed);
}
}
}
/// <summary>
/// Attempt to uncuff a cuffed entity. Can be called by the cuffed entity, or another entity trying to help uncuff them.
/// If the uncuffing succeeds, the cuffs will drop on the floor.
/// </summary>
/// <param name="user">The cuffed entity</param>
/// <param name="cuffsToRemove">Optional param for the handcuff entity to remove from the cuffed entity. If null, uses the most recently added handcuff entity.</param>
public async void TryUncuff(IEntity user, IEntity? cuffsToRemove = null)
{
if (_uncuffing) return;
var isOwner = user == Owner;
if (cuffsToRemove == null)
{
if (Container.ContainedEntities.Count == 0)
{
return;
}
cuffsToRemove = LastAddedCuffs;
}
else
{
if (!Container.ContainedEntities.Contains(cuffsToRemove))
{
Logger.Warning("A user is trying to remove handcuffs that aren't in the owner's container. This should never happen!");
}
}
if (!cuffsToRemove.TryGetComponent<HandcuffComponent>(out var cuff))
{
Logger.Warning($"A user is trying to remove handcuffs without a {nameof(HandcuffComponent)}. This should never happen!");
return;
}
if (!ActionBlockerSystem.CanInteract(user))
{
user.PopupMessage(Loc.GetString("You can't do that!"));
return;
}
if (!isOwner && !user.InRangeUnobstructed(Owner))
{
user.PopupMessage(Loc.GetString("You are too far away to remove the cuffs."));
return;
}
if (!cuffsToRemove.InRangeUnobstructed(Owner))
{
Logger.Warning("Handcuffs being removed from player are obstructed or too far away! This should not happen!");
return;
}
user.PopupMessage(Loc.GetString("You start removing the cuffs."));
var audio = EntitySystem.Get<AudioSystem>();
if (isOwner)
{
if (cuff.StartBreakoutSound != null)
SoundSystem.Play(Filter.Pvs(Owner), cuff.StartBreakoutSound, Owner);
}
else
{
if (cuff.StartUncuffSound != null)
SoundSystem.Play(Filter.Pvs(Owner), cuff.StartUncuffSound, Owner);
}
var uncuffTime = isOwner ? cuff.BreakoutTime : cuff.UncuffTime;
var doAfterEventArgs = new DoAfterEventArgs(user, uncuffTime)
{
BreakOnUserMove = true,
BreakOnDamage = true,
BreakOnStun = true,
NeedHand = true
};
var doAfterSystem = EntitySystem.Get<DoAfterSystem>();
_uncuffing = true;
var result = await doAfterSystem.DoAfter(doAfterEventArgs);
_uncuffing = false;
if (result != DoAfterStatus.Cancelled)
{
if (cuff.EndUncuffSound != null)
SoundSystem.Play(Filter.Pvs(Owner), cuff.EndUncuffSound, Owner);
Container.ForceRemove(cuffsToRemove);
cuffsToRemove.Transform.AttachToGridOrMap();
cuffsToRemove.Transform.WorldPosition = Owner.Transform.WorldPosition;
if (cuff.BreakOnRemove)
{
cuff.Broken = true;
cuffsToRemove.Name = cuff.BrokenName;
cuffsToRemove.Description = cuff.BrokenDesc;
if (cuffsToRemove.TryGetComponent<SpriteComponent>(out var sprite) && cuff.BrokenState != null)
{
sprite.LayerSetState(0, cuff.BrokenState); // TODO: safety check to see if RSI contains the state?
}
}
CanStillInteract = Owner.TryGetComponent(out HandsComponent? handsComponent) && handsComponent.Hands.Count() > CuffedHandCount;
OnCuffedStateChanged?.Invoke();
UpdateAlert();
Dirty();
if (CuffedHandCount == 0)
{
user.PopupMessage(Loc.GetString("You successfully remove the cuffs."));
if (!isOwner)
{
user.PopupMessage(Owner, Loc.GetString("{0:theName} uncuffs your hands.", user));
}
}
else
{
if (!isOwner)
{
user.PopupMessage(Loc.GetString("You successfully remove the cuffs. {0} of {1:theName}'s hands remain cuffed.", CuffedHandCount, user));
user.PopupMessage(Owner, Loc.GetString("{0:theName} removes your cuffs. {1} of your hands remain cuffed.", user, CuffedHandCount));
}
else
{
user.PopupMessage(Loc.GetString("You successfully remove the cuffs. {0} of your hands remain cuffed.", CuffedHandCount));
}
}
}
else
{
user.PopupMessage(Loc.GetString("You fail to remove the cuffs."));
}
return;
}
/// <summary>
/// Allows the uncuffing of a cuffed person. Used by other people and by the component owner to break out of cuffs.
/// </summary>
[Verb]
private sealed class UncuffVerb : Verb<CuffableComponent>
{
protected override void GetData(IEntity user, CuffableComponent component, VerbData data)
{
if ((user != component.Owner && !ActionBlockerSystem.CanInteract(user)) || component.CuffedHandCount == 0)
{
data.Visibility = VerbVisibility.Invisible;
return;
}
data.Text = Loc.GetString("Uncuff");
}
protected override void Activate(IEntity user, CuffableComponent component)
{
if (component.CuffedHandCount > 0)
{
component.TryUncuff(user);
}
}
}
}
}

View File

@@ -0,0 +1,238 @@
#nullable enable
using System;
using System.Threading.Tasks;
using Content.Server.DoAfter;
using Content.Server.Hands.Components;
using Content.Server.Stunnable.Components;
using Content.Shared.ActionBlocker;
using Content.Shared.Cuffs.Components;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Helpers;
using Content.Shared.Notification;
using Robust.Shared.Audio;
using Robust.Shared.GameObjects;
using Robust.Shared.Localization;
using Robust.Shared.Maths;
using Robust.Shared.Player;
using Robust.Shared.Players;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
namespace Content.Server.Cuffs.Components
{
[RegisterComponent]
[ComponentReference(typeof(SharedHandcuffComponent))]
public class HandcuffComponent : SharedHandcuffComponent, IAfterInteract
{
/// <summary>
/// The time it takes to apply a <see cref="CuffedComponent"/> to an entity.
/// </summary>
[ViewVariables]
[DataField("cuffTime")]
public float CuffTime { get; set; } = 5f;
/// <summary>
/// The time it takes to remove a <see cref="CuffedComponent"/> from an entity.
/// </summary>
[ViewVariables]
[DataField("uncuffTime")]
public float UncuffTime { get; set; } = 5f;
/// <summary>
/// The time it takes for a cuffed entity to remove <see cref="CuffedComponent"/> from itself.
/// </summary>
[ViewVariables]
[DataField("breakoutTime")]
public float BreakoutTime { get; set; } = 30f;
/// <summary>
/// If an entity being cuffed is stunned, this amount of time is subtracted from the time it takes to add/remove their cuffs.
/// </summary>
[ViewVariables]
[DataField("stunBonus")]
public float StunBonus { get; set; } = 2f;
/// <summary>
/// Will the cuffs break when removed?
/// </summary>
[ViewVariables]
[DataField("breakOnRemove")]
public bool BreakOnRemove { get; set; }
/// <summary>
/// The path of the RSI file used for the player cuffed overlay.
/// </summary>
[ViewVariables]
[DataField("cuffedRSI")]
public string? CuffedRSI { get; set; } = "Objects/Misc/handcuffs.rsi";
/// <summary>
/// The iconstate used with the RSI file for the player cuffed overlay.
/// </summary>
[ViewVariables]
[DataField("bodyIconState")]
public string? OverlayIconState { get; set; } = "body-overlay";
/// <summary>
/// The iconstate used for broken handcuffs
/// </summary>
[ViewVariables]
[DataField("brokenIconState")]
public string? BrokenState { get; set; }
/// <summary>
/// The iconstate used for broken handcuffs
/// </summary>
[ViewVariables]
[DataField("brokenName")]
public string BrokenName { get; set; } = default!;
/// <summary>
/// The iconstate used for broken handcuffs
/// </summary>
[ViewVariables]
[DataField("brokenDesc")]
public string BrokenDesc { get; set; } = default!;
[ViewVariables]
public bool Broken
{
get
{
return _isBroken;
}
set
{
if (_isBroken != value)
{
_isBroken = value;
Dirty();
}
}
}
[DataField("startCuffSound")]
public string StartCuffSound { get; set; } = "/Audio/Items/Handcuffs/cuff_start.ogg";
[DataField("endCuffSound")] public string EndCuffSound { get; set; } = "/Audio/Items/Handcuffs/cuff_end.ogg";
[DataField("startBreakoutSound")]
public string StartBreakoutSound { get; set; } = "/Audio/Items/Handcuffs/cuff_breakout_start.ogg";
[DataField("startUncuffSound")]
public string StartUncuffSound { get; set; } = "/Audio/Items/Handcuffs/cuff_takeoff_start.ogg";
[DataField("endUncuffSound")]
public string EndUncuffSound { get; set; } = "/Audio/Items/Handcuffs/cuff_takeoff_end.ogg";
[DataField("color")]
public Color Color { get; set; } = Color.White;
// Non-exposed data fields
private bool _isBroken = false;
/// <summary>
/// Used to prevent DoAfter getting spammed.
/// </summary>
private bool _cuffing;
public override ComponentState GetComponentState(ICommonSession player)
{
return new HandcuffedComponentState(Broken ? BrokenState : string.Empty);
}
async Task<bool> IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
{
if (_cuffing) return true;
if (eventArgs.Target == null || !ActionBlockerSystem.CanUse(eventArgs.User) || !eventArgs.Target.TryGetComponent<CuffableComponent>(out var cuffed))
{
return false;
}
if (eventArgs.Target == eventArgs.User)
{
eventArgs.User.PopupMessage(Loc.GetString("You can't cuff yourself!"));
return true;
}
if (Broken)
{
eventArgs.User.PopupMessage(Loc.GetString("The cuffs are broken!"));
return true;
}
if (!eventArgs.Target.TryGetComponent<HandsComponent>(out var hands))
{
eventArgs.User.PopupMessage(Loc.GetString("{0:theName} has no hands!", eventArgs.Target));
return true;
}
if (cuffed.CuffedHandCount == hands.Count)
{
eventArgs.User.PopupMessage(Loc.GetString("{0:theName} has no free hands to handcuff!", eventArgs.Target));
return true;
}
if (!eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true))
{
eventArgs.User.PopupMessage(Loc.GetString("You are too far away to use the cuffs!"));
return true;
}
eventArgs.User.PopupMessage(Loc.GetString("You start cuffing {0:theName}.", eventArgs.Target));
eventArgs.User.PopupMessage(eventArgs.Target, Loc.GetString("{0:theName} starts cuffing you!", eventArgs.User));
if (StartCuffSound != null)
SoundSystem.Play(Filter.Pvs(Owner), StartCuffSound, Owner);
TryUpdateCuff(eventArgs.User, eventArgs.Target, cuffed);
return true;
}
/// <summary>
/// Update the cuffed state of an entity
/// </summary>
private async void TryUpdateCuff(IEntity user, IEntity target, CuffableComponent cuffs)
{
var cuffTime = CuffTime;
if (target.TryGetComponent<StunnableComponent>(out var stun) && stun.Stunned)
{
cuffTime = MathF.Max(0.1f, cuffTime - StunBonus);
}
var doAfterEventArgs = new DoAfterEventArgs(user, cuffTime, default, target)
{
BreakOnTargetMove = true,
BreakOnUserMove = true,
BreakOnDamage = true,
BreakOnStun = true,
NeedHand = true
};
_cuffing = true;
var result = await EntitySystem.Get<DoAfterSystem>().DoAfter(doAfterEventArgs);
_cuffing = false;
if (result != DoAfterStatus.Cancelled)
{
if (cuffs.TryAddNewCuffs(user, Owner))
{
if (EndCuffSound != null)
SoundSystem.Play(Filter.Pvs(Owner), EndCuffSound, Owner);
user.PopupMessage(Loc.GetString("You successfully cuff {0:theName}.", target));
target.PopupMessage(Loc.GetString("You have been cuffed by {0:theName}!", user));
}
}
else
{
user.PopupMessage(Loc.GetString("You were interrupted while cuffing {0:theName}!", target));
target.PopupMessage(Loc.GetString("You interrupt {0:theName} while they are cuffing you!", user));
}
}
}
}