mirror of
https://github.com/corvax-team/ss14-wl.git
synced 2026-06-09 10:06:46 +02:00
Changeling gaining DNA (#43759)
* changeling-gain-DNA * review * Return IsUniqueDevour * oops * Update SharedChangelingIdentitySystem.cs * Store data in ChangelingIdentityData + fixes List of changes: - Whether an identity granted devour is now tracked in ChangelingIdentityData - Made devoured shutdown cleanup again - Made server set the mind to null if it never found one, instead of being invalid Fixes: - Fixed name being networked as empty if original was deleted --------- Co-authored-by: ScarKy0 <scarky0@onet.eu>
This commit is contained in:
@@ -33,6 +33,7 @@ public sealed class ChangelingIdentitySystem : SharedChangelingIdentitySystem
|
||||
OriginalJob = identity.OriginalJob,
|
||||
OriginalName = identity.OriginalName,
|
||||
Starting = identity.Starting,
|
||||
GrantedDna = identity.GrantedDna,
|
||||
};
|
||||
|
||||
ent.Comp.ConsumedIdentities.Add(data);
|
||||
|
||||
@@ -24,8 +24,9 @@ public sealed class ChangelingIdentitySystem : SharedChangelingIdentitySystem
|
||||
Identity = GetNetEntity(identity.Identity),
|
||||
Original = GetNetEntity(identity.Original),
|
||||
OriginalJob = identity.OriginalJob,
|
||||
OriginalName = identity.Original != null ? Name(identity.Original.Value) : string.Empty,
|
||||
OriginalName = identity.OriginalName,
|
||||
Starting = identity.Starting,
|
||||
GrantedDna = identity.GrantedDna,
|
||||
};
|
||||
|
||||
sentIdentities.Add(netData);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using Content.Shared.Changeling.Systems;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Store;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameStates;
|
||||
@@ -125,6 +127,15 @@ public sealed partial class ChangelingDevourComponent : Component
|
||||
[DataField, AutoNetworkedField]
|
||||
public float DevourPreventionPercentageThreshold = 0.1f;
|
||||
|
||||
/// <summary>
|
||||
/// DNA awarded for successfully devouring a new identity.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public Dictionary<string, FixedPoint2> DevourDnaReward = new()
|
||||
{
|
||||
{ "ChangelingDNA", 10 }
|
||||
};
|
||||
|
||||
public override bool SendOnlyToOwner => true;
|
||||
}
|
||||
|
||||
@@ -134,9 +145,10 @@ public sealed partial class ChangelingDevourComponent : Component
|
||||
/// <param name="Changeling">The changeling devouring this entity.</param>
|
||||
/// <param name="Devoured">The entity that was devoured.</param>
|
||||
/// <param name="ObtainedIdentity">Whether the changeling is going to be given the target's identity after devouring.</param>
|
||||
/// <param name="Unique">Whether this entity was eaten by the changeling before.</param>
|
||||
/// <param name="Unique">Whether the changeling has never had the identity of this target before.</param>
|
||||
/// <param name="GrantedDna">Whether this devour has granted the changeling Dna.</param>
|
||||
[ByRefEvent]
|
||||
public record struct ChangelingDevouredEvent(EntityUid Changeling, EntityUid Devoured, bool ObtainedIdentity, bool Unique);
|
||||
public record struct ChangelingDevouredEvent(EntityUid Changeling, EntityUid Devoured, bool ObtainedIdentity, bool Unique, bool GrantedDna);
|
||||
|
||||
/// <summary>
|
||||
/// Event raised on an entity when devoured by a changeling.
|
||||
@@ -144,6 +156,7 @@ public record struct ChangelingDevouredEvent(EntityUid Changeling, EntityUid Dev
|
||||
/// <param name="Changeling">The changeling devouring this entity.</param>
|
||||
/// <param name="Devoured">The entity that was devoured.</param>
|
||||
/// <param name="ObtainedIdentity">Whether the changeling is going to be given the target's identity after devouring.</param>
|
||||
/// <param name="Unique">Whether this entity was eaten by the changeling before.</param>
|
||||
/// <param name="Unique">Whether the changeling has never had the identity of this target before.</param>
|
||||
/// <param name="GrantedDna">Whether this devour has granted the changeling Dna.</param>
|
||||
[ByRefEvent]
|
||||
public record struct ChangelingGotDevouredEvent(EntityUid Changeling, EntityUid Devoured, bool ObtainedIdentity, bool Unique);
|
||||
public record struct ChangelingGotDevouredEvent(EntityUid Changeling, EntityUid Devoured, bool ObtainedIdentity, bool Unique, bool GrantedDna);
|
||||
|
||||
@@ -4,18 +4,12 @@ namespace Content.Shared.Changeling.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Component used for marking entities devoured by a changeling.
|
||||
/// Used to prevent granting the identity several times.
|
||||
/// Used to track which changelings have an identity of this entity.
|
||||
/// Used for cleanup purposes.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
public sealed partial class ChangelingDevouredComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether this entity has been devoured recently.
|
||||
/// Gets set back to False when the entity with this component becomes <see cref="MobState.Alive"/> again.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool Recent;
|
||||
|
||||
/// <summary>
|
||||
/// HashSet of all changelings that have devoured this entity.
|
||||
/// </summary>
|
||||
|
||||
@@ -112,6 +112,12 @@ public sealed partial class ChangelingIdentityData
|
||||
[DataField]
|
||||
public bool Starting = false;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this identity has granted DNA after devour.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool GrantedDna = false;
|
||||
|
||||
/// <summary>
|
||||
/// Convert to a string representation. This if for logging & debugging. This is not localized and should not be
|
||||
/// shown to players.
|
||||
@@ -143,4 +149,7 @@ public sealed partial class ChangelingNetworkedIdentityData
|
||||
|
||||
[DataField]
|
||||
public bool Starting;
|
||||
|
||||
[DataField]
|
||||
public bool GrantedDna;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Changeling.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Marker component for entities that were devoured recently and cannot be devoured again until revived.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class RecentlyDevouredComponent : Component;
|
||||
@@ -4,6 +4,7 @@ using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Armor;
|
||||
using Content.Shared.Atmos.Rotting;
|
||||
using Content.Shared.Changeling.Components;
|
||||
using Content.Shared.Store;
|
||||
using Content.Shared.Damage.Systems;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.DoAfter;
|
||||
@@ -12,6 +13,7 @@ using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Store.Components;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Network;
|
||||
@@ -20,16 +22,17 @@ namespace Content.Shared.Changeling.Systems;
|
||||
|
||||
public sealed class ChangelingDevourSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly DamageableSystem _damageable = default!;
|
||||
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
|
||||
[Dependency] private readonly INetManager _net = default!;
|
||||
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly MobStateSystem _mobState = default!;
|
||||
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedChangelingIdentitySystem _changelingIdentitySystem = default!;
|
||||
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
|
||||
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
|
||||
[Dependency] private readonly DamageableSystem _damageable = default!;
|
||||
[Dependency] private readonly MobStateSystem _mobState = default!;
|
||||
[Dependency] private readonly SharedChangelingIdentitySystem _changelingIdentitySystem = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly SharedStoreSystem _store = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -167,17 +170,24 @@ public sealed class ChangelingDevourSystem : EntitySystem
|
||||
|
||||
_adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(ent.Owner):player} successfully devoured {ToPrettyString(target):player}'s identity");
|
||||
|
||||
// If this entity has never been devoured before, it counts as unique.
|
||||
var unique = !_changelingIdentitySystem.TryGetDataFromOriginal(ent.Owner, target, out _);
|
||||
// A unique identity is separate from whether we have actually devoured this target before.
|
||||
var uniqueIdentity = IsUniqueDevour(ent.Owner, target);
|
||||
var willGrantDna = WillDevourGrantDna(ent.Owner, target);
|
||||
|
||||
// Even if not unique, target is supposed to give us an identity if it is not currently in our identity list.
|
||||
var becomesIdentity = !HasIdentity(ent.Owner, target);
|
||||
|
||||
var ev = new ChangelingDevouredEvent(ent.Owner, target, becomesIdentity, unique);
|
||||
var ev = new ChangelingDevouredEvent(ent.Owner, target, becomesIdentity, uniqueIdentity, willGrantDna);
|
||||
RaiseLocalEvent(ent, ref ev, true); // We broadcast the event to allow relevant objectives to update.
|
||||
|
||||
var devouredEv = new ChangelingGotDevouredEvent(ent.Owner, target, becomesIdentity, unique);
|
||||
var devouredEv = new ChangelingGotDevouredEvent(ent.Owner, target, becomesIdentity, uniqueIdentity, willGrantDna);
|
||||
RaiseLocalEvent(target, ref devouredEv); // Don't broadcast this one, all neccessary data is in the previous event already. Just use that one if a broadcast is needed.
|
||||
|
||||
EnsureComp<RecentlyDevouredComponent>(target);
|
||||
|
||||
// Grants the DNA reward associated with a successful unique devour.
|
||||
if (willGrantDna && TryComp<StoreComponent>(ent, out var store))
|
||||
_store.TryAddCurrency(ent.Comp.DevourDnaReward, ent.Owner, store);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -191,17 +201,6 @@ public sealed class ChangelingDevourSystem : EntitySystem
|
||||
return changeling.Comp.ConsumedIdentities.FirstOrDefault(data => data.Original == devoured && data.Identity != null) != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Has this entity been devoured by a changeling already before getting revived?
|
||||
/// </summary>
|
||||
public bool WasDevouredRecently(Entity<ChangelingDevouredComponent?> entity)
|
||||
{
|
||||
if (!Resolve(entity, ref entity.Comp, false))
|
||||
return false;
|
||||
|
||||
return entity.Comp.Recent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Can the given changeling devour the given victim?
|
||||
/// </summary>
|
||||
@@ -220,7 +219,7 @@ public sealed class ChangelingDevourSystem : EntitySystem
|
||||
return false;
|
||||
}
|
||||
|
||||
if (WasDevouredRecently(victim))
|
||||
if (HasComp<RecentlyDevouredComponent>(victim))
|
||||
{
|
||||
if (showPopup)
|
||||
_popupSystem.PopupClient(Loc.GetString("changeling-devour-attempt-failed-devoured-recently"), changeling.Owner, changeling.Owner, PopupType.Medium);
|
||||
@@ -282,16 +281,35 @@ public sealed class ChangelingDevourSystem : EntitySystem
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether this changeling has devoured the target entity at any point before.
|
||||
/// Checks whether devouring this target has never been devoured by the changeling before.
|
||||
/// </summary>
|
||||
/// <param name="ent">The changeling.</param>
|
||||
/// <param name="devoured">The target entity.</param>
|
||||
/// <returns>True if target was previously devoured, False otherwise.</returns>
|
||||
/// <returns>True if the target was never devoured before, otherwise False.</returns>
|
||||
public bool IsUniqueDevour(Entity<ChangelingIdentityComponent?> ent, EntityUid devoured)
|
||||
{
|
||||
if (!Resolve(ent, ref ent.Comp, false))
|
||||
return false;
|
||||
|
||||
return _changelingIdentitySystem.TryGetDataFromOriginal(ent, devoured, out _);
|
||||
return !_changelingIdentitySystem.TryGetDataFromOriginal(ent, devoured, out _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether devouring this entity will grant DNA to the changeling.
|
||||
/// </summary>
|
||||
/// <param name="ent">The changeling.</param>
|
||||
/// <param name="devoured">The target entity.</param>
|
||||
/// <returns>True if this target entity has granted the changeling DNA before, False otherwise.</returns>
|
||||
public bool WillDevourGrantDna(Entity<ChangelingIdentityComponent?> ent, EntityUid devoured)
|
||||
{
|
||||
if (!Resolve(ent, ref ent.Comp, false))
|
||||
return false;
|
||||
|
||||
// This target was never devoured, so obviously it can grant us DNA.
|
||||
if (!_changelingIdentitySystem.TryGetDataFromOriginal(ent, devoured, out var data))
|
||||
return true;
|
||||
|
||||
// If the entity was Devoured, it means it already granted DNA, so we return False.
|
||||
return !data.GrantedDna;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,23 +37,21 @@ public abstract class SharedChangelingIdentitySystem : EntitySystem
|
||||
SubscribeLocalEvent<ChangelingStoredIdentityComponent, ComponentRemove>(OnStoredRemove);
|
||||
|
||||
SubscribeLocalEvent<ChangelingDevouredComponent, ComponentShutdown>(OnDevouredShutdown);
|
||||
SubscribeLocalEvent<ChangelingDevouredComponent, MobStateChangedEvent>(OnDevouredMobState);
|
||||
SubscribeLocalEvent<RecentlyDevouredComponent, MobStateChangedEvent>(OnRecentlyDevouredMobState);
|
||||
}
|
||||
|
||||
private void OnDevouredEntity(Entity<ChangelingIdentityComponent> ent, ref ChangelingDevouredEvent args)
|
||||
{
|
||||
// We're not supposed to be given an identity.
|
||||
if (!args.ObtainedIdentity)
|
||||
return;
|
||||
if (args.ObtainedIdentity)
|
||||
{
|
||||
CloneToPausedMap(ent, args.Devoured);
|
||||
AddDevouredReference(ent, args.Devoured);
|
||||
}
|
||||
|
||||
CloneToPausedMap(ent, args.Devoured);
|
||||
|
||||
// We add a reference to ourselves to prevent repeated identity gain.
|
||||
var targetDevoured = EnsureComp<ChangelingDevouredComponent>(args.Devoured);
|
||||
targetDevoured.DevouredBy.Add(ent.Owner);
|
||||
targetDevoured.Recent = true;
|
||||
Dirty(args.Devoured, targetDevoured);
|
||||
Dirty(ent);
|
||||
if (args.GrantedDna && TryGetDataFromOriginal(ent.AsNullable(), args.Devoured, out var data))
|
||||
{
|
||||
data.GrantedDna = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPlayerAttached(Entity<ChangelingIdentityComponent> ent, ref PlayerAttachedEvent args)
|
||||
@@ -75,6 +73,7 @@ public abstract class SharedChangelingIdentitySystem : EntitySystem
|
||||
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;
|
||||
}
|
||||
@@ -100,14 +99,13 @@ public abstract class SharedChangelingIdentitySystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDevouredMobState(Entity<ChangelingDevouredComponent> ent, ref MobStateChangedEvent args)
|
||||
private void OnRecentlyDevouredMobState(Entity<RecentlyDevouredComponent> ent, ref MobStateChangedEvent args)
|
||||
{
|
||||
// Once we are revived the body is no longer "recent".
|
||||
// Once we are revived the body is no longer recently devoured.
|
||||
if (args.NewMobState != MobState.Alive)
|
||||
return;
|
||||
|
||||
ent.Comp.Recent = false;
|
||||
Dirty(ent);
|
||||
RemCompDeferred<RecentlyDevouredComponent>(ent);
|
||||
}
|
||||
|
||||
private void OnStoredRemove(Entity<ChangelingStoredIdentityComponent> ent, ref ComponentRemove args)
|
||||
@@ -222,6 +220,17 @@ public abstract class SharedChangelingIdentitySystem : EntitySystem
|
||||
return clone;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks that the changeling has successfully devoured the target.
|
||||
/// </summary>
|
||||
public void AddDevouredReference(Entity<ChangelingIdentityComponent> ent, EntityUid target)
|
||||
{
|
||||
var targetDevoured = EnsureComp<ChangelingDevouredComponent>(target);
|
||||
targetDevoured.DevouredBy.Add(ent.Owner);
|
||||
|
||||
Dirty(target, targetDevoured);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Drop a stored identity from the changeling's storage.
|
||||
/// </summary>
|
||||
@@ -348,10 +357,10 @@ public abstract class SharedChangelingIdentitySystem : EntitySystem
|
||||
{
|
||||
data.Identity = identity;
|
||||
data.Original = original;
|
||||
data.OriginalName = Name(identity);
|
||||
data.OriginalName = Name(original);
|
||||
|
||||
var foundMind = _mind.TryGetMind(original, out var mindId, out _);
|
||||
data.OriginalMind = mindId;
|
||||
data.OriginalMind = foundMind ? mindId : null;
|
||||
|
||||
if (foundMind)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user