mirror of
https://github.com/space-syndicate/space-station-14.git
synced 2026-10-09 10:21:18 +03:00
Changeling Devour Most Objective (#44169)
* loving it * wording ig * first pass * makes more sense * Update changeling.yml * issuers to prototypes * *screams!!!!!!!!!!!!! * functional * yes im salty about the order * works! * screw this text * add it to rolls * nah * review * partial review * no more roundend text * remove append stuff * guh * var
This commit is contained in:
parent
1951b00875
commit
b65eb25098
10 changed files with 142 additions and 37 deletions
|
|
@ -0,0 +1,10 @@
|
|||
using Content.Server.Objectives.Systems;
|
||||
|
||||
namespace Content.Server.Objectives.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Requires that a changeling has obtained the highest amount of unique devours out of all other changelings.
|
||||
/// Checks against identities stored on the mind.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(ChangelingObjectiveSystem))]
|
||||
public sealed partial class ChangelingDevourMostConditionComponent : Component;
|
||||
|
|
@ -3,15 +3,8 @@ using Content.Server.Objectives.Systems;
|
|||
namespace Content.Server.Objectives.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Requires that a changeling has obtained X unique identities.
|
||||
/// Requires that a changeling has obtained X unique identities. Checks against identities stored on the mind.
|
||||
/// Depends on <see cref="NumberObjectiveComponent"/> to function.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(ChangelingObjectiveSystem))]
|
||||
public sealed partial class ChangelingUniqueIdentityConditionComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The amount of identities that have been already devoured.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public int UniqueIdentities;
|
||||
}
|
||||
public sealed partial class ChangelingUniqueIdentityConditionComponent : Component;
|
||||
|
|
|
|||
|
|
@ -122,6 +122,10 @@ public sealed partial class ObjectivesSystem : SharedObjectivesSystem
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a summary for a list of antag minds based on their agent.
|
||||
/// Contains the objective issuer, objectives and completion rate.
|
||||
/// </summary>
|
||||
private void AddSummary(StringBuilder result, string agent, List<(EntityUid, string)> minds)
|
||||
{
|
||||
var agentSummaries = new List<(string summary, float successRate, int completedObjectives)>();
|
||||
|
|
|
|||
|
|
@ -10,42 +10,89 @@ public sealed partial class ChangelingObjectiveSystem : EntitySystem
|
|||
[Dependency] private NumberObjectiveSystem _number = default!;
|
||||
[Dependency] private SharedMindSystem _mind = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ChangelingUniqueIdentityConditionComponent, ObjectiveGetProgressEvent>(OnGetProgress);
|
||||
SubscribeLocalEvent<ChangelingDevouredEvent>(OnChangelingDevoured);
|
||||
}
|
||||
|
||||
[SubscribeLocalEvent]
|
||||
private void OnChangelingDevoured(ref ChangelingDevouredEvent args)
|
||||
{
|
||||
// We check if the devour granted us Dna.
|
||||
// We do this because we could already have gotten the identity by other means before, such as the Dna sting.
|
||||
// Dna grant basically ensures this is the first time valid devour has happened on a target.
|
||||
if (!_mind.TryGetMind(args.Changeling, out var mind, out _))
|
||||
return;
|
||||
|
||||
if (!args.GrantedDna)
|
||||
return;
|
||||
|
||||
if (!_mind.TryGetObjectiveComp<ChangelingUniqueIdentityConditionComponent>(args.Changeling, out var obj))
|
||||
return;
|
||||
|
||||
obj.UniqueIdentities++;
|
||||
EnsureComp<ChangelingMindIdentityTrackerComponent>(mind, out var tracker);
|
||||
tracker.Devoured++;
|
||||
}
|
||||
|
||||
private void OnGetProgress(Entity<ChangelingUniqueIdentityConditionComponent> ent, ref ObjectiveGetProgressEvent args)
|
||||
[SubscribeLocalEvent]
|
||||
private void OnGetUniqueIdentitiesProgress(Entity<ChangelingUniqueIdentityConditionComponent> ent, ref ObjectiveGetProgressEvent args)
|
||||
{
|
||||
args.Progress = GetProgress(ent.Comp, _number.GetTarget(ent));
|
||||
args.Progress = GetUniqueIdentitiesProgress(args.MindId, _number.GetTarget(ent));
|
||||
}
|
||||
|
||||
private float GetProgress(ChangelingUniqueIdentityConditionComponent comp, int target)
|
||||
[SubscribeLocalEvent]
|
||||
private void OnGetMostIdentitiesProgress(Entity<ChangelingDevourMostConditionComponent> ent, ref ObjectiveGetProgressEvent args)
|
||||
{
|
||||
args.Progress = GetMostIdentitiesProgress(args.MindId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the progress for <see cref="ChangelingUniqueIdentityConditionComponent"/>.
|
||||
/// Uses data stored on the mind.
|
||||
/// </summary>
|
||||
/// <returns>Objective progress, between 0 and 1.</returns>
|
||||
private float GetUniqueIdentitiesProgress(EntityUid mind, int target)
|
||||
{
|
||||
// We've never actually gained an identity.
|
||||
if (!TryComp<ChangelingMindIdentityTrackerComponent>(mind, out var tracker))
|
||||
return 0f;
|
||||
|
||||
// prevent divide-by-zero
|
||||
if (target == 0)
|
||||
return 1f;
|
||||
|
||||
if (comp.UniqueIdentities >= target)
|
||||
var uniqueCount = tracker.Devoured;
|
||||
|
||||
if (uniqueCount >= target)
|
||||
return 1f;
|
||||
|
||||
return (float)comp.UniqueIdentities / (float)target;
|
||||
return (float)uniqueCount / (float)target;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the progress for <see cref="ChangelingDevourMostConditionComponent"/> for a given mind.
|
||||
/// Uses data stored on the mind.
|
||||
/// </summary>
|
||||
/// <returns>Objective progress, between 0 and 1.</returns>
|
||||
private float GetMostIdentitiesProgress(EntityUid mind)
|
||||
{
|
||||
// Can't progress if we've never eaten anyone.
|
||||
if (!TryComp<ChangelingMindIdentityTrackerComponent>(mind, out var selfTracker))
|
||||
return 0f;
|
||||
|
||||
// We never actually devoured anyone.
|
||||
// We don't want to grant greentext if 0 is technically the highest.
|
||||
if (selfTracker.Devoured is var selfUniqueCount && selfUniqueCount < 1)
|
||||
return 0f;
|
||||
|
||||
var query = AllEntityQuery<ChangelingMindIdentityTrackerComponent>();
|
||||
|
||||
var highest = 0;
|
||||
|
||||
while (query.MoveNext(out var uid, out var tracker))
|
||||
{
|
||||
// Skip our own tracker. We only care for the highest others have.
|
||||
if (uid == mind)
|
||||
continue;
|
||||
|
||||
if (tracker.Devoured > highest)
|
||||
highest = tracker.Devoured;
|
||||
}
|
||||
|
||||
// No equal check. Only one can win.
|
||||
if (selfUniqueCount > highest)
|
||||
return 1f;
|
||||
|
||||
// highest+1 because we aim to be 1 above the highest.
|
||||
return (float)selfUniqueCount / (float)(highest+1);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -154,3 +154,13 @@ public sealed partial class ChangelingNetworkedIdentityData
|
|||
[DataField]
|
||||
public bool GrantedDna;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event raised on the changeling and broadcast when it gains a new identity, or an existing identity data is updated when gaining an identity.
|
||||
/// Identities are never removed, so any future grants of an identity (after dropping it, for example) is an update.
|
||||
/// </summary>
|
||||
/// <param name="Changeling">The changeling that gained an identity.</param>
|
||||
/// <param name="Identity">The identity data that was obtained.</param>
|
||||
/// <param name="NewIdentity">Whether this is the first time an identity was gained.</param>
|
||||
[ByRefEvent]
|
||||
public record struct ChangelingGainedOrUpdatedIdentityEvent(EntityUid Changeling, ChangelingIdentityData Identity, bool NewIdentity);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Changeling.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Tracks devoured identities on the mind of the changeling, used for objectives.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class ChangelingMindIdentityTrackerComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Amount of unique entities devoured by this changeling.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public int Devoured;
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Content.Shared.Changeling.Components;
|
||||
using Content.Shared.Cloning;
|
||||
|
|
@ -44,7 +44,7 @@ public abstract partial class SharedChangelingIdentitySystem : EntitySystem
|
|||
{
|
||||
if (args.ObtainedIdentity)
|
||||
{
|
||||
GrantIdentity(ent.Owner, args.Devoured);
|
||||
GrantIdentity(ent.Owner, args.Devoured, true);
|
||||
}
|
||||
|
||||
if (args.GrantedDna && TryGetDataFromOriginal(ent.AsNullable(), args.Devoured, out var data))
|
||||
|
|
@ -71,9 +71,6 @@ public abstract partial class SharedChangelingIdentitySystem : EntitySystem
|
|||
if (!TryGetDataFromOriginal(ent.AsNullable(), ent, out var data))
|
||||
return;
|
||||
|
||||
data.Starting = true;
|
||||
data.GrantedDna = true; // I have no idea how you're supposed to ever get DNA from yourself, but just in case.
|
||||
|
||||
ent.Comp.CurrentIdentity = data.Identity;
|
||||
}
|
||||
|
||||
|
|
@ -196,7 +193,8 @@ public abstract partial class SharedChangelingIdentitySystem : EntitySystem
|
|||
/// </summary>
|
||||
/// <param name="ent">The Changeling.</param>
|
||||
/// <param name="target">The target to clone.</param>
|
||||
public EntityUid? GrantIdentity(Entity<ChangelingIdentityComponent?> ent, EntityUid target)
|
||||
/// <param name="devoured">Whether this identity was granted via devouring, aka granted DNA to the changeling.</param>
|
||||
public EntityUid? GrantIdentity(Entity<ChangelingIdentityComponent?> ent, EntityUid target, bool devoured = false)
|
||||
{
|
||||
if (!Resolve(ent.Owner, ref ent.Comp))
|
||||
return null;
|
||||
|
|
@ -206,17 +204,32 @@ public abstract partial class SharedChangelingIdentitySystem : EntitySystem
|
|||
if (clone == null)
|
||||
return null;
|
||||
|
||||
var isNew = false;
|
||||
|
||||
// We see if we already have a identity slot for this entity.
|
||||
// This can happen if we devoured them before, but then dropped their stored identity.
|
||||
if (!TryGetDataFromOriginal(ent.AsNullable(), target, out var newIdentity))
|
||||
{
|
||||
newIdentity = new ChangelingIdentityData();
|
||||
ent.Comp.ConsumedIdentities.Add(newIdentity);
|
||||
isNew = true; // Data didn't exist before so its not an update.
|
||||
}
|
||||
|
||||
UpdateIdentityData(newIdentity, clone.Value, target);
|
||||
AddDevouredReference(ent!, target);
|
||||
|
||||
newIdentity.GrantedDna = devoured;
|
||||
|
||||
if (newIdentity.Original == ent)
|
||||
{
|
||||
// This is an identity of us, we don't wanna count it for objectives n stuff.
|
||||
newIdentity.Starting = true;
|
||||
newIdentity.GrantedDna = true;
|
||||
}
|
||||
|
||||
var ev = new ChangelingGainedOrUpdatedIdentityEvent(ent, newIdentity, isNew);
|
||||
RaiseLocalEvent(ent, ref ev, true); // Broadcast it to allow the mind tracker to update.
|
||||
|
||||
HandlePvsOverride(ent, clone.Value);
|
||||
Dirty(ent);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
using Content.Shared.Mind;
|
||||
using Content.Shared.Objectives;
|
||||
using Content.Shared.Objectives.Prototypes;
|
||||
using Content.Shared.Objectives.Systems;
|
||||
using Robust.Shared.Utility;
|
||||
|
|
|
|||
|
|
@ -80,3 +80,16 @@
|
|||
- type: KillPersonCondition
|
||||
requireDead: true
|
||||
requireMaroon: true
|
||||
|
||||
- type: entity
|
||||
parent: BaseChangelingObjective
|
||||
id: ChangelingDevourMostObjective
|
||||
name: Devour the most humanoids.
|
||||
description: We must devour the most unique humanoids out of all other changelings.
|
||||
components:
|
||||
- type: Objective
|
||||
difficulty: 1
|
||||
icon:
|
||||
sprite: Mobs/Aliens/flesh.rsi
|
||||
state: golem
|
||||
- type: ChangelingDevourMostCondition
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@
|
|||
id: ChangelingObjectiveGroupDevour
|
||||
weights:
|
||||
ChangelingUniqueIdentityObjective: 1
|
||||
# eventually: devour most
|
||||
ChangelingDevourMostObjective: 1
|
||||
|
||||
- type: weightedRandom
|
||||
id: ChangelingObjectiveGroupEscapeDummy
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue