Skip to content

Commit

Permalink
add syndicate recruiter midround event (DeltaV-Station#1345)
Browse files Browse the repository at this point in the history
* add SignAttemptEvent and minor signature refactor

* add recruiter pen

* add the recruiter ship

* add recruiter antag and event

* fixes

* real

* bad popup spam

* more fix

* fix blood visual maybe

* fix greentext

* more fixy

* fix fill state not being coloured

* give btamp instead of paper in pocket 2

* f

* brighter filled state

* fix greentext

* fix error on spawn

* pro

* prevent using a syringe to inject anything into the pen

* update stuff after upstream merge

---------

Signed-off-by: deltanedas <[email protected]>
Co-authored-by: deltanedas <@deltanedas:kde.org>
  • Loading branch information
deltanedas authored Jul 9, 2024
1 parent e47d912 commit 4237fc9
Show file tree
Hide file tree
Showing 24 changed files with 1,183 additions and 14 deletions.
5 changes: 5 additions & 0 deletions Content.Client/DeltaV/Recruiter/RecruiterPenSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
using Content.Shared.DeltaV.Recruiter;

namespace Content.Client.DeltaV.Recruiter;

public sealed class RecruiterPenSystem : SharedRecruiterPenSystem;
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Content.Server.Objectives.Systems;
using Content.Shared.DeltaV.Recruiter;

namespace Content.Server.Objectives.Components;

/// <summary>
/// Objective condition that requires the recruiter's pen to be used by a number of people to sign paper.
/// Requires <see cref="NumberObjectiveComponent"/> to function.
/// </summary>
[RegisterComponent, Access(typeof(RecruitingConditionSystem), typeof(SharedRecruiterPenSystem))]
public sealed partial class RecruitingConditionComponent : Component
{
[DataField]
public int Recruited;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using Content.Server.Objectives.Components;
using Content.Shared.Objectives.Components;

namespace Content.Server.Objectives.Systems;

public sealed class RecruitingConditionSystem : EntitySystem
{
[Dependency] private readonly NumberObjectiveSystem _number = default!;

public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<RecruitingConditionComponent, ObjectiveGetProgressEvent>(OnGetProgress);
}

private void OnGetProgress(Entity<RecruitingConditionComponent> ent, ref ObjectiveGetProgressEvent args)
{
args.Progress = Progress(ent.Comp.Recruited, _number.GetTarget(ent.Owner));
}

private float Progress(int recruited, int target)
{
// prevent divide-by-zero
if (target == 0)
return 1f;

return MathF.Min(recruited / (float) target, 1f);
}
}
30 changes: 17 additions & 13 deletions Content.Server/DeltaV/Paper/SignatureSystem.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Content.Server.Access.Systems;
using Content.Server.Paper;
using Content.Server.Popups;
using Content.Shared.DeltaV.Paper;
using Content.Shared.Paper;
using Content.Shared.Popups;
using Content.Shared.Tag;
Expand All @@ -26,20 +27,20 @@ public override void Initialize()
SubscribeLocalEvent<PaperComponent, GetVerbsEvent<AlternativeVerb>>(OnGetAltVerbs);
}

private void OnGetAltVerbs(EntityUid uid, PaperComponent component, GetVerbsEvent<AlternativeVerb> args)
private void OnGetAltVerbs(Entity<PaperComponent> ent, ref GetVerbsEvent<AlternativeVerb> args)
{
if (!args.CanAccess || !args.CanInteract)
return;

var pen = args.Using;
if (pen == null || !_tagSystem.HasTag(pen.Value, "Write"))
if (args.Using is not {} pen || !_tagSystem.HasTag(pen, "Write"))
return;

var user = args.User;
AlternativeVerb verb = new()
{
Act = () =>
{
TrySignPaper((uid, component), args.User);
TrySignPaper(ent, user, pen);
},
Text = Loc.GetString("paper-sign-verb"),
DoContactInteraction = true,
Expand All @@ -51,10 +52,13 @@ private void OnGetAltVerbs(EntityUid uid, PaperComponent component, GetVerbsEven
/// <summary>
/// Tries add add a signature to the paper with signer's name.
/// </summary>
public bool TrySignPaper(Entity<PaperComponent?> paper, EntityUid signer)
public bool TrySignPaper(Entity<PaperComponent> paper, EntityUid signer, EntityUid pen)
{
var paperComp = paper.Comp;
if (!Resolve(paper, ref paperComp))
var comp = paper.Comp;

var ev = new SignAttemptEvent(paper, signer);
RaiseLocalEvent(pen, ref ev);
if (ev.Cancelled)
return false;

var signatureName = DetermineEntitySignature(signer);
Expand All @@ -65,25 +69,25 @@ public bool TrySignPaper(Entity<PaperComponent?> paper, EntityUid signer)
StampedColor = Color.DarkSlateGray, // TODO: make configurable? Perhaps it should depend on the pen.
};

if (!paperComp.StampedBy.Contains(stampInfo) && _paper.TryStamp(paper, stampInfo, SignatureStampState, paperComp))
if (!comp.StampedBy.Contains(stampInfo) && _paper.TryStamp(paper, stampInfo, SignatureStampState, comp))
{
// Show popups and play a paper writing sound
var signedOtherMessage = Loc.GetString("paper-signed-other", ("user", signer), ("target", paper));
var signedOtherMessage = Loc.GetString("paper-signed-other", ("user", signer), ("target", paper.Owner));
_popup.PopupEntity(signedOtherMessage, signer, Filter.PvsExcept(signer, entityManager: EntityManager), true);

var signedSelfMessage = Loc.GetString("paper-signed-self", ("target", paper));
var signedSelfMessage = Loc.GetString("paper-signed-self", ("target", paper.Owner));
_popup.PopupEntity(signedSelfMessage, signer, signer);

_audio.PlayPvs(paperComp.Sound, signer);
_audio.PlayPvs(comp.Sound, signer);

_paper.UpdateUserInterface(paper, paperComp);
_paper.UpdateUserInterface(paper, comp);

return true;
}
else
{
// Show an error popup
_popup.PopupEntity(Loc.GetString("paper-signed-failure", ("target", paper)), signer, signer, PopupType.SmallCaution);
_popup.PopupEntity(Loc.GetString("paper-signed-failure", ("target", paper.Owner)), signer, signer, PopupType.SmallCaution);

return false;
}
Expand Down
54 changes: 54 additions & 0 deletions Content.Server/DeltaV/Recruiter/RecruiterPenSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using Content.Server.Body.Components;
using Content.Server.Forensics;
using Content.Server.Objectives.Components;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.DeltaV.Recruiter;
using Content.Shared.Popups;

namespace Content.Server.DeltaV.Recruiter;

/// <summary>
/// Handles bloodstream related code since that isn't in shared.
/// </summary>
public sealed class RecruiterPenSystem : SharedRecruiterPenSystem
{
[Dependency] private readonly ForensicsSystem _forensics = default!;
[Dependency] private readonly SolutionTransferSystem _transfer = default!;

protected override void DrawBlood(EntityUid uid, Entity<SolutionComponent> dest, EntityUid user)
{
// how did you even use this mr plushie...
if (CompOrNull<BloodstreamComponent>(user)?.BloodSolution is not {} blood)
return;

var desired = dest.Comp.Solution.AvailableVolume;
// TODO: when bloodstream is shared put the transfer in shared so PopupClient is actually used, and this popup isnt needed
if (desired == 0)
{
Popup.PopupEntity(Loc.GetString("recruiter-pen-prick-full", ("pen", uid)), user, user);
return;
}

if (_transfer.Transfer(user, user, blood, uid, dest, desired) != desired)
return;

// this is why you have to keep the pen safe, it has the dna of everyone you recruited!
_forensics.TransferDna(uid, user, canDnaBeCleaned: false);

Popup.PopupEntity(Loc.GetString("recruiter-pen-pricked", ("pen", uid)), user, user, PopupType.LargeCaution);
}

protected override void Recruit(Entity<RecruiterPenComponent> ent, EntityUid user)
{
// only increment count once if 1 person signs multiple papers
if (!ent.Comp.Recruited.Add(user))
return;

if (ent.Comp.RecruiterMind is {} mindId &&
Mind.TryGetObjectiveComp<RecruitingConditionComponent>(mindId, out var obj, null))
{
obj.Recruited++;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ public FixedPoint2 Transfer(EntityUid user,
var actualAmount = FixedPoint2.Min(amount, FixedPoint2.Min(sourceSolution.Volume, targetSolution.AvailableVolume));

var solution = _solution.SplitSolution(source, actualAmount);
_solution.Refill(targetEntity, target, solution);
_solution.AddSolution(target, solution); // DeltaV - make RefillableSolution optional for transfer target until its upstream and merged

_adminLogger.Add(LogType.Action, LogImpact.Medium,
$"{ToPrettyString(user):player} transferred {SharedSolutionContainerSystem.ToPrettyString(solution)} to {ToPrettyString(targetEntity):target}, which now contains {SharedSolutionContainerSystem.ToPrettyString(targetSolution)}");
Expand Down
8 changes: 8 additions & 0 deletions Content.Shared/DeltaV/Paper/SignatureEvents.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Content.Shared.DeltaV.Paper;

/// <summary>
/// Raised on the pen when trying to sign a paper.
/// If it's cancelled the signature isn't made.
/// </summary>
[ByRefEvent]
public record struct SignAttemptEvent(EntityUid Paper, EntityUid User, bool Cancelled = false);
52 changes: 52 additions & 0 deletions Content.Shared/DeltaV/Recruiter/RecruiterPenComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using Content.Shared.FixedPoint;
using Content.Shared.Whitelist;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;

namespace Content.Shared.DeltaV.Recruiter;

/// <summary>
/// Pen that can be pricked with the user's blood, and requires blood to sign papers.
/// </summary>
[RegisterComponent, NetworkedComponent, Access(typeof(SharedRecruiterPenSystem))]
[AutoGenerateComponentState]
public sealed partial class RecruiterPenComponent : Component
{
/// <summary>
/// Solution on the pen to draw blood to and use for signing.
/// </summary>
[DataField]
public string Solution = "blood";

/// <summary>
/// Mind of the recruiter this pen belongs to.
/// Used for objective and is set when first picked up.
/// </summary>
[DataField]
public EntityUid? RecruiterMind;

/// <summary>
/// Lets other clients predict recruiter being bound without knowing who it is.
/// </summary>
[DataField, AutoNetworkedField]
public bool Bound;

/// <summary>
/// Entities of every person that signed paper with this pen.
/// Used to prevent someone gaming it by signing multiple papers.
/// </summary>
[DataField]
public HashSet<EntityUid> Recruited = new();

/// <summary>
/// If the user matches this blacklist they can't use this pen.
/// </summary>
[DataField]
public EntityWhitelist? Blacklist;

/// <summary>
/// If the user's mind matches this blacklist they can't use this pen.
/// </summary>
[DataField]
public EntityWhitelist? MindBlacklist;
}
Loading

0 comments on commit 4237fc9

Please sign in to comment.