Predict thieving beacon (#39610)

predict thieving beacon
This commit is contained in:
slarticodefast
2026-01-05 13:17:17 +01:00
committed by GitHub
parent e572d75f04
commit 71c3fa8fd7
6 changed files with 73 additions and 51 deletions

View File

@@ -1,23 +0,0 @@
using Content.Server.Objectives.Systems;
using Content.Server.Thief.Systems;
namespace Content.Server.Objectives.Components;
/// <summary>
/// An abstract component that allows other systems to count adjacent objects as "stolen" when controlling other systems
/// </summary>
[RegisterComponent, Access(typeof(StealConditionSystem), typeof(ThiefBeaconSystem))]
public sealed partial class StealAreaComponent : Component
{
[DataField]
public bool Enabled = true;
[DataField]
public float Range = 1f;
/// <summary>
/// all the minds that will be credited with stealing from this area.
/// </summary>
[DataField]
public HashSet<EntityUid> Owners = new();
}

View File

@@ -37,7 +37,7 @@ public sealed class DeployFoldableSystem : EntitySystem
private void OnDragDropDragged(Entity<DeployFoldableComponent> ent, ref DragDropDraggedEvent args)
{
if (!TryComp<FoldableComponent>(ent, out var foldable)
|| !_foldable.TrySetFolded(ent, foldable, true))
|| !_foldable.TrySetFolded(ent, foldable, true, args.User))
return;
_hands.PickupOrDrop(args.User, ent.Owner);
@@ -77,7 +77,7 @@ public sealed class DeployFoldableSystem : EntitySystem
|| !_hands.TryDrop((args.User, hands), args.Used, targetDropLocation: args.ClickLocation))
return;
if (!_foldable.TrySetFolded(ent, foldable, false))
if (!_foldable.TrySetFolded(ent, foldable, false, args.User))
{
_hands.TryPickup(args.User, args.Used, handsComp: hands);
return;

View File

@@ -78,14 +78,14 @@ public sealed class FoldableSystem : EntitySystem
/// <summary>
/// Set the folded state of the given <see cref="FoldableComponent"/>
/// </summary>
public void SetFolded(EntityUid uid, FoldableComponent component, bool folded)
public void SetFolded(EntityUid uid, FoldableComponent component, bool folded, EntityUid? user = null)
{
component.IsFolded = folded;
Dirty(uid, component);
_appearance.SetData(uid, FoldedVisuals.State, folded);
_buckle.StrapSetEnabled(uid, !component.IsFolded);
var ev = new FoldedEvent(folded);
var ev = new FoldedEvent(folded, user);
RaiseLocalEvent(uid, ref ev);
}
@@ -97,7 +97,7 @@ public sealed class FoldableSystem : EntitySystem
public bool TryToggleFold(EntityUid uid, FoldableComponent comp, EntityUid? folder = null)
{
var result = TrySetFolded(uid, comp, !comp.IsFolded);
var result = TrySetFolded(uid, comp, !comp.IsFolded, folder);
if (!result && folder != null)
{
if (comp.IsFolded)
@@ -129,7 +129,7 @@ public sealed class FoldableSystem : EntitySystem
/// <summary>
/// Try to fold/unfold
/// </summary>
public bool TrySetFolded(EntityUid uid, FoldableComponent comp, bool state)
public bool TrySetFolded(EntityUid uid, FoldableComponent comp, bool state, EntityUid? user = null)
{
if (state == comp.IsFolded)
return false;
@@ -137,7 +137,7 @@ public sealed class FoldableSystem : EntitySystem
if (!CanToggleFold(uid, comp))
return false;
SetFolded(uid, comp, state);
SetFolded(uid, comp, state, user);
return true;
}
@@ -180,6 +180,7 @@ public record struct FoldAttemptEvent(FoldableComponent Comp, bool Cancelled = f
/// <summary>
/// Event raised on an entity after it has been folded.
/// </summary>
/// <param name="IsFolded"></param>
/// <param name="IsFolded">True is it has been folded, false if it has been unfolded.</param>
/// <param name="User">The player who did the folding.</param>
[ByRefEvent]
public readonly record struct FoldedEvent(bool IsFolded);
public readonly record struct FoldedEvent(bool IsFolded, EntityUid? User);

View File

@@ -0,0 +1,38 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Objectives.Components;
/// <summary>
/// An abstract component that allows other systems to count adjacent objects as "stolen" when controlling other systems
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class StealAreaComponent : Component
{
/// <summary>
/// Is the component currently enabled?
/// </summary>
[DataField, AutoNetworkedField]
public bool Enabled = true;
/// <summary>
/// The range to check for items in.
/// </summary>
[DataField, AutoNetworkedField]
public float Range = 1f;
/// <summary>
/// All the minds that will be credited with stealing from this area.
/// </summary>
/// <remarks>
/// TODO: Network this when we have WeakEntityReference.
/// </remarks>
[DataField]
public HashSet<EntityUid> Owners = new();
/// <summary>
/// The count of the owner hashset.
/// This is a separate datafield because networking the list would cause PVS errors if an entity inside would be deleted and networked.
/// </summary>
[DataField, AutoNetworkedField]
public int OwnerCount = 0;
}

View File

@@ -1,12 +1,14 @@
using Content.Server.Thief.Systems;
using Content.Shared.Thief.Systems;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
namespace Content.Server.Thief.Components;
namespace Content.Shared.Thief.Components;
/// <summary>
/// working together with StealAreaComponent, allows the thief to count objects near the beacon as stolen when setting up.
/// </summary>
[RegisterComponent, Access(typeof(ThiefBeaconSystem))]
[RegisterComponent, NetworkedComponent]
[Access(typeof(ThiefBeaconSystem))]
public sealed partial class ThiefBeaconComponent : Component
{
[DataField]

View File

@@ -1,15 +1,15 @@
using Content.Server.Mind;
using Content.Server.Objectives.Components;
using Content.Server.Thief.Components;
using Content.Shared.Mind;
using Content.Shared.Objectives.Components;
using Content.Shared.Roles;
using Content.Shared.Thief.Components;
using Content.Shared.Examine;
using Content.Shared.Foldable;
using Content.Shared.Popups;
using Content.Shared.Verbs;
using Content.Shared.Roles;
using Content.Shared.Roles.Components;
using Robust.Shared.Audio.Systems;
namespace Content.Server.Thief.Systems;
namespace Content.Shared.Thief.Systems;
/// <summary>
/// <see cref="ThiefBeaconComponent"/>
@@ -18,7 +18,7 @@ public sealed class ThiefBeaconSystem : EntitySystem
{
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly MindSystem _mind = default!;
[Dependency] private readonly SharedMindSystem _mind = default!;
[Dependency] private readonly SharedRoleSystem _roles = default!;
public override void Initialize()
{
@@ -46,7 +46,7 @@ public sealed class ThiefBeaconSystem : EntitySystem
{
Act = () =>
{
SetCoordinate(beacon, mind.Value);
SetCoordinate(beacon, mind.Value, user);
},
Message = Loc.GetString("thief-fulton-verb-message"),
Text = Loc.GetString("thief-fulton-verb-text"),
@@ -56,7 +56,7 @@ public sealed class ThiefBeaconSystem : EntitySystem
private void OnFolded(Entity<ThiefBeaconComponent> beacon, ref FoldedEvent args)
{
if (args.IsFolded)
ClearCoordinate(beacon);
ClearCoordinate(beacon, args.User);
}
private void OnExamined(Entity<ThiefBeaconComponent> beacon, ref ExaminedEvent args)
@@ -64,23 +64,25 @@ public sealed class ThiefBeaconSystem : EntitySystem
if (!TryComp<StealAreaComponent>(beacon, out var area))
return;
args.PushText(Loc.GetString(area.Owners.Count == 0
args.PushText(Loc.GetString(area.OwnerCount == 0
? "thief-fulton-examined-unset"
: "thief-fulton-examined-set"));
}
private void SetCoordinate(Entity<ThiefBeaconComponent> beacon, EntityUid mind)
private void SetCoordinate(Entity<ThiefBeaconComponent> beacon, EntityUid mind, EntityUid? user = null)
{
if (!TryComp<StealAreaComponent>(beacon, out var area))
return;
_audio.PlayPvs(beacon.Comp.LinkSound, beacon);
_popup.PopupEntity(Loc.GetString("thief-fulton-set"), beacon);
area.Owners.Clear(); //We only reconfigure the beacon for ourselves, we don't need multiple thieves to steal from the same beacon.
_audio.PlayPredicted(beacon.Comp.LinkSound, beacon, user);
_popup.PopupClient(Loc.GetString("thief-fulton-set"), beacon, user);
area.Owners.Clear(); // We only reconfigure the beacon for ourselves, we don't need multiple thieves to steal from the same beacon.
area.Owners.Add(mind);
area.OwnerCount = area.Owners.Count;
Dirty(beacon.Owner, area);
}
private void ClearCoordinate(Entity<ThiefBeaconComponent> beacon)
private void ClearCoordinate(Entity<ThiefBeaconComponent> beacon, EntityUid? user = null)
{
if (!TryComp<StealAreaComponent>(beacon, out var area))
return;
@@ -88,8 +90,10 @@ public sealed class ThiefBeaconSystem : EntitySystem
if (area.Owners.Count == 0)
return;
_audio.PlayPvs(beacon.Comp.UnlinkSound, beacon);
_popup.PopupEntity(Loc.GetString("thief-fulton-clear"), beacon);
_audio.PlayPredicted(beacon.Comp.UnlinkSound, beacon, user);
_popup.PopupClient(Loc.GetString("thief-fulton-clear"), beacon, user);
area.Owners.Clear();
area.OwnerCount = area.Owners.Count;
Dirty(beacon.Owner, area);
}
}