mirror of
https://github.com/space-syndicate/space-station-14.git
synced 2026-10-09 10:21:18 +03:00
Changeling Dna Sting (#43785)
* init * fix objective * Somehow this locale got lost i guess * some more flavor idk * wording * sprites * be gone copy paste * we * review * rename * explosion * oops * Apply suggestion from @slarticodefast Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>
This commit is contained in:
parent
510e9e6139
commit
b3619d28c6
12 changed files with 111 additions and 32 deletions
|
|
@ -20,7 +20,10 @@ public sealed class ChangelingObjectiveSystem : EntitySystem
|
|||
|
||||
private void OnChangelingDevoured(ref ChangelingDevouredEvent args)
|
||||
{
|
||||
if (!args.Unique)
|
||||
// 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 (!args.GrantedDna)
|
||||
return;
|
||||
|
||||
if (!_mind.TryGetObjectiveComp<ChangelingUniqueIdentityConditionComponent>(args.Changeling, out var obj))
|
||||
|
|
|
|||
|
|
@ -6,8 +6,7 @@ using Robust.Shared.GameStates;
|
|||
namespace Content.Shared.Changeling.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Allows the changeling to spawn dummy chameleon clothing items that will transform with them,
|
||||
/// mimicing the equipment of the stored disguise.
|
||||
/// Allows the changeling to vomit acid over restraints, setting themselves free and stunning whoever is pulling them.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class ChangelingBiodegradeAbilityComponent : Component
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
using System.Linq;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Atmos.Rotting;
|
||||
using Content.Shared.Changeling.Components;
|
||||
using Content.Shared.Cuffs;
|
||||
using Content.Shared.Ensnaring;
|
||||
|
|
@ -20,12 +22,15 @@ public sealed partial class ChangelingAbilitySystem : EntitySystem
|
|||
[Dependency] private readonly PullingSystem _pulling = default!;
|
||||
[Dependency] private readonly SharedStunSystem _stun = default!;
|
||||
[Dependency] private readonly SharedPuddleSystem _puddle = default!;
|
||||
[Dependency] private readonly SharedChangelingIdentitySystem _changelingIdentity = default!;
|
||||
[Dependency] private readonly ChangelingDevourSystem _changelingDevour = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ChangelingBiodegradeAbilityComponent, ChangelingBiodegradeActionEvent>(OnBiodegradeAction);
|
||||
SubscribeLocalEvent<ChangelingIdentityComponent, ChangelingStingDnaEvent>(OnStingDna);
|
||||
}
|
||||
|
||||
private void OnBiodegradeAction(Entity<ChangelingBiodegradeAbilityComponent> ent, ref ChangelingBiodegradeActionEvent args)
|
||||
|
|
@ -66,4 +71,23 @@ public sealed partial class ChangelingAbilitySystem : EntitySystem
|
|||
if (ent.Comp.SpillSolution != null)
|
||||
_puddle.TrySpillAt(args.Performer, ent.Comp.SpillSolution, out _, false);
|
||||
}
|
||||
|
||||
private void OnStingDna(Entity<ChangelingIdentityComponent> ent, ref ChangelingStingDnaEvent args)
|
||||
{
|
||||
if (args.Target == ent.Owner)
|
||||
return; // Can't sting yourself.
|
||||
|
||||
if (!_changelingDevour.CanDevour(ent.Owner, args.Target, checkDead: false, checkProtected: false))
|
||||
return;
|
||||
|
||||
_popup.PopupClient(Loc.GetString("changeling-sting-success", ("target", Identity.Entity(args.Target, EntityManager))), args.Target, ent.Owner, PopupType.Medium);
|
||||
_changelingIdentity.GrantIdentity(ent, args.Target);
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Action event for the Dna sting ability. Used to grand the changeling an identity without devouring somebody.
|
||||
/// </summary>
|
||||
public sealed partial class ChangelingStingDnaEvent : EntityTargetActionEvent;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
using System.Linq;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Armor;
|
||||
|
|
@ -29,7 +28,7 @@ public sealed class ChangelingDevourSystem : EntitySystem
|
|||
[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 SharedChangelingIdentitySystem _changelingIdentity = default!;
|
||||
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly SharedStoreSystem _store = default!;
|
||||
|
|
@ -175,7 +174,7 @@ public sealed class ChangelingDevourSystem : EntitySystem
|
|||
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 becomesIdentity = !_changelingIdentity.HasIdentity(ent.Owner, target);
|
||||
|
||||
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.
|
||||
|
|
@ -190,21 +189,10 @@ public sealed class ChangelingDevourSystem : EntitySystem
|
|||
_store.TryAddCurrency(ent.Comp.DevourDnaReward, ent.Owner, store);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the given changeling has a valid identity of the given entity.
|
||||
/// </summary>
|
||||
public bool HasIdentity(Entity<ChangelingIdentityComponent?> changeling, EntityUid devoured)
|
||||
{
|
||||
if (!Resolve(changeling, ref changeling.Comp, false))
|
||||
return false;
|
||||
|
||||
return changeling.Comp.ConsumedIdentities.FirstOrDefault(data => data.Original == devoured && data.Identity != null) != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Can the given changeling devour the given victim?
|
||||
/// </summary>
|
||||
public bool CanDevour(Entity<ChangelingDevourComponent?> changeling, EntityUid victim, bool showPopup = true)
|
||||
public bool CanDevour(Entity<ChangelingDevourComponent?> changeling, EntityUid victim, bool checkDead = true, bool checkProtected = true, bool showPopup = true)
|
||||
{
|
||||
if (!Resolve(changeling, ref changeling.Comp))
|
||||
return false;
|
||||
|
|
@ -226,7 +214,7 @@ public sealed class ChangelingDevourSystem : EntitySystem
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!_mobState.IsDead(victim))
|
||||
if (checkDead && !_mobState.IsDead(victim))
|
||||
{
|
||||
if (showPopup)
|
||||
_popupSystem.PopupClient(Loc.GetString("changeling-devour-attempt-failed-not-dead"), changeling.Owner, changeling.Owner, PopupType.Medium);
|
||||
|
|
@ -240,14 +228,14 @@ public sealed class ChangelingDevourSystem : EntitySystem
|
|||
return false;
|
||||
}
|
||||
|
||||
if (IsTargetProtected(victim, changeling!))
|
||||
if (checkProtected && IsTargetProtected(victim, changeling))
|
||||
{
|
||||
if (showPopup)
|
||||
_popupSystem.PopupClient(Loc.GetString("changeling-devour-attempt-failed-protected"), changeling.Owner, changeling.Owner, PopupType.Medium);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!HasIdentity(changeling.Owner, victim) && !_changelingIdentitySystem.HasFreeDisguiseSlot(changeling.Owner))
|
||||
if (!_changelingIdentity.HasIdentity(changeling.Owner, victim) && !_changelingIdentity.HasFreeDisguiseSlot(changeling.Owner))
|
||||
{
|
||||
if (showPopup)
|
||||
_popupSystem.PopupClient(Loc.GetString("changeling-devour-attempt-failed-no-space"), changeling.Owner, changeling.Owner, PopupType.Medium);
|
||||
|
|
@ -263,8 +251,11 @@ public sealed class ChangelingDevourSystem : EntitySystem
|
|||
/// <param name="target">The Targeted entity</param>
|
||||
/// <param name="ent">Changelings Devour Component</param>
|
||||
/// <returns>Is the target Protected from the attack</returns>
|
||||
private bool IsTargetProtected(EntityUid target, Entity<ChangelingDevourComponent> ent)
|
||||
public bool IsTargetProtected(EntityUid target, Entity<ChangelingDevourComponent?> ent)
|
||||
{
|
||||
if (!Resolve(ent, ref ent.Comp, false))
|
||||
return false;
|
||||
|
||||
var ev = new CoefficientQueryEvent(SlotFlags.OUTERCLOTHING);
|
||||
|
||||
RaiseLocalEvent(target, ev);
|
||||
|
|
@ -291,7 +282,7 @@ public sealed class ChangelingDevourSystem : EntitySystem
|
|||
if (!Resolve(ent, ref ent.Comp, false))
|
||||
return false;
|
||||
|
||||
return !_changelingIdentitySystem.TryGetDataFromOriginal(ent, devoured, out _);
|
||||
return !_changelingIdentity.TryGetDataFromOriginal(ent, devoured, out _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -306,7 +297,7 @@ public sealed class ChangelingDevourSystem : EntitySystem
|
|||
return false;
|
||||
|
||||
// This target was never devoured, so obviously it can grant us DNA.
|
||||
if (!_changelingIdentitySystem.TryGetDataFromOriginal(ent, devoured, out var data))
|
||||
if (!_changelingIdentity.TryGetDataFromOriginal(ent, devoured, out var data))
|
||||
return true;
|
||||
|
||||
// If the entity was Devoured, it means it already granted DNA, so we return False.
|
||||
|
|
|
|||
|
|
@ -44,8 +44,7 @@ public abstract class SharedChangelingIdentitySystem : EntitySystem
|
|||
{
|
||||
if (args.ObtainedIdentity)
|
||||
{
|
||||
CloneToPausedMap(ent, args.Devoured);
|
||||
AddDevouredReference(ent, args.Devoured);
|
||||
GrantIdentity(ent, args.Devoured);
|
||||
}
|
||||
|
||||
if (args.GrantedDna && TryGetDataFromOriginal(ent.AsNullable(), args.Devoured, out var data))
|
||||
|
|
@ -67,7 +66,7 @@ public abstract class SharedChangelingIdentitySystem : EntitySystem
|
|||
private void OnMapInit(Entity<ChangelingIdentityComponent> ent, ref MapInitEvent args)
|
||||
{
|
||||
// Make a backup of our current identity so we can transform back.
|
||||
CloneToPausedMap(ent, ent.Owner);
|
||||
GrantIdentity(ent, ent.Owner);
|
||||
|
||||
if (!TryGetDataFromOriginal(ent.AsNullable(), ent, out var data))
|
||||
return;
|
||||
|
|
@ -197,7 +196,7 @@ public abstract class SharedChangelingIdentitySystem : EntitySystem
|
|||
/// </summary>
|
||||
/// <param name="ent">The Changeling.</param>
|
||||
/// <param name="target">The target to clone.</param>
|
||||
public EntityUid? CloneToPausedMap(Entity<ChangelingIdentityComponent> ent, EntityUid target)
|
||||
public EntityUid? GrantIdentity(Entity<ChangelingIdentityComponent> ent, EntityUid target)
|
||||
{
|
||||
var clone = CloneToPausedMap(ent.Comp.IdentityCloningSettings, target);
|
||||
|
||||
|
|
@ -213,6 +212,7 @@ public abstract class SharedChangelingIdentitySystem : EntitySystem
|
|||
}
|
||||
|
||||
UpdateIdentityData(newIdentity, clone.Value, target);
|
||||
AddDevouredReference(ent, target);
|
||||
|
||||
HandlePvsOverride(ent, clone.Value);
|
||||
Dirty(ent);
|
||||
|
|
@ -319,6 +319,17 @@ public abstract class SharedChangelingIdentitySystem : EntitySystem
|
|||
return ent.Comp.ConsumedIdentities.Count(data => data.Identity != null) < ent.Comp.MaxStoredDisguises;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the given changeling has a valid identity of the given entity.
|
||||
/// </summary>
|
||||
public bool HasIdentity(Entity<ChangelingIdentityComponent?> changeling, EntityUid devoured)
|
||||
{
|
||||
if (!Resolve(changeling, ref changeling.Comp, false))
|
||||
return false;
|
||||
|
||||
return changeling.Comp.ConsumedIdentities.FirstOrDefault(data => data.Original == devoured && data.Identity != null) != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a paused map for storing devoured identities as a clone of the player.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ roles-antag-changeling-objective = A intelligent predator that assumes the ident
|
|||
# devour
|
||||
changeling-devour-attempt-failed-cannot-devour = We cannot devour this!
|
||||
changeling-devour-attempt-failed-already-devoured = We already consumed this body!
|
||||
changeling-devour-attempt-failed-devoured-recently = This body is too mangled to devour!
|
||||
changeling-devour-attempt-failed-not-dead = This body yet lives! We cannot consume it alive!
|
||||
changeling-devour-attempt-failed-rotting = This corpse has only rotted biomass.
|
||||
changeling-devour-attempt-failed-protected = This victim's biomass is protected by armor!
|
||||
|
|
@ -25,8 +26,8 @@ changeling-transform-attempt-others = { CAPITALIZE(POSS-ADJ($user)) } bones snap
|
|||
changeling-transform-bui-select-entity = {$entity}
|
||||
changeling-transform-bui-drop-identity-menu = Drop a devoured identity from your memory.
|
||||
changeling-transform-bui-drop-identity-entity = Drop {$entity}
|
||||
changeling-transform-bui-drop-identity-entity-popup = You dropped {$entity} from your memory.
|
||||
changeling-transform-bui-drop-identity-cannot-drop = You cannot drop your current identity.
|
||||
changeling-transform-bui-drop-identity-entity-popup = We dropped {$entity} from our memory.
|
||||
changeling-transform-bui-drop-identity-cannot-drop = We cannot drop our current identity.
|
||||
|
||||
# voice mimicry
|
||||
changeling-voice-mimic-window-title = Voice Mimicry
|
||||
|
|
@ -41,5 +42,8 @@ changeling-flesh-clothing-alert-desc = Whether clothing transformation is enable
|
|||
changeling-biodegrade-used-popup = {CAPITALIZE(THE($user))} vomits acid over {POSS-ADJ($user)} {$restraint}!
|
||||
changeling-biodegrade-used-popup-self = We vomit acid over our {$restraint}!
|
||||
|
||||
# stings
|
||||
changeling-sting-success = We silently sting {THE($target)}!
|
||||
|
||||
# other
|
||||
changeling-paused-map-name = Changeling identity storage map
|
||||
|
|
|
|||
|
|
@ -9,3 +9,6 @@ changeling-catalog-voice-mimic-desc = Change your vocal coords at will to imitat
|
|||
|
||||
changeling-catalog-biodegrade-name = Biodegrade
|
||||
changeling-catalog-biodegrade-desc = You learn to utilize acid glands within your body to vomit acid over constraints, setting yourself free.
|
||||
|
||||
changeling-catalog-sting-dna-name = Extract DNA
|
||||
changeling-catalog-sting-dna-desc = Learn to utilize your stinger to absorb the identity of unsuspecting victims. Does not grant DNA to your store or objective progress.
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@
|
|||
- type: Action
|
||||
useDelay: 60 # TODO: Short cooldown but chemical cost
|
||||
raiseOnAction: true
|
||||
itemIconStyle: BigAction
|
||||
checkCanInteract: false
|
||||
icon:
|
||||
sprite: Interface/Actions/changeling2.rsi
|
||||
|
|
@ -46,6 +45,7 @@
|
|||
- type: Action
|
||||
icon: { sprite : Interface/Actions/changeling.rsi, state: "devour" }
|
||||
iconOn: { sprite : Interface/Actions/changeling.rsi, state: "devour_on" }
|
||||
itemIconStyle: BigAction
|
||||
priority: 1
|
||||
- type: TargetAction
|
||||
- type: EntityTargetAction
|
||||
|
|
@ -80,3 +80,25 @@
|
|||
state: mimicry
|
||||
- type: InstantAction
|
||||
event: !type:VoiceMaskSetNameEvent
|
||||
|
||||
- type: entity
|
||||
parent: BaseAction
|
||||
id: ActionChangelingStingDna
|
||||
name: Extract DNA
|
||||
description: Sneakily absorb the identity of unsuspecting victims. Does not grant DNA to your store or objective progress.
|
||||
components:
|
||||
- type: Action
|
||||
icon: { sprite : Interface/Actions/changeling2.rsi, state: "extract_dna_off" }
|
||||
iconOn: { sprite : Interface/Actions/changeling2.rsi, state: "extract_dna_on" }
|
||||
itemIconStyle: BigAction
|
||||
priority: 1
|
||||
useDelay: 90 # TODO: Chemical cost, maybe this could be a DoAfter action?
|
||||
raiseOnUser: true
|
||||
- type: TargetAction
|
||||
- type: EntityTargetAction
|
||||
canTargetSelf: false
|
||||
whitelist:
|
||||
components:
|
||||
- MobState
|
||||
- HumanoidProfile
|
||||
event: !type:ChangelingStingDnaEvent
|
||||
|
|
|
|||
|
|
@ -56,3 +56,17 @@
|
|||
- !type:ListingLimitedStockCondition
|
||||
stock: 1
|
||||
productAction: ActionChangelingBiodegrade
|
||||
|
||||
- type: listing
|
||||
id: ChangelingStingDna
|
||||
name: changeling-catalog-sting-dna-name
|
||||
description: changeling-catalog-sting-dna-desc
|
||||
applyToMob: true
|
||||
cost:
|
||||
ChangelingDNA: 10
|
||||
categories:
|
||||
- ChangelingStoreStealth
|
||||
conditions:
|
||||
- !type:ListingLimitedStockCondition
|
||||
stock: 1
|
||||
productAction: ActionChangelingStingDna
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"version": 1,
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "Made by ketufaispikinut from a sprite taken from tgstation at commit https://github.com/tgstation/tgstation/commit/c838ba21dae97db345e0113f99596decd1d66039 (scientist suit), a sprite taken from tgstation at commit https://github.com/tgstation/tgstation/commit/c838ba21dae97db345e0113f99596decd1d66039 (hydro suit) and the changeling ability border/background sprites created by TiniestShark. Biodegrade and Mimicry by ketufaispikinut, based on TiniestShark's textures.",
|
||||
"copyright": "Made by ketufaispikinut from a sprite taken from tgstation at commit https://github.com/tgstation/tgstation/commit/c838ba21dae97db345e0113f99596decd1d66039 (scientist suit), a sprite taken from tgstation at commit https://github.com/tgstation/tgstation/commit/c838ba21dae97db345e0113f99596decd1d66039 (hydro suit) and the changeling ability border/background sprites created by TiniestShark. Biodegrade, Mimicry and Extract Dna On/Off by ketufaispikinut, based on TiniestShark's textures.",
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
|
|
@ -19,5 +19,13 @@
|
|||
{
|
||||
"name": "biodegrade"
|
||||
}
|
||||
,
|
||||
{
|
||||
"name": "extract_dna_on"
|
||||
}
|
||||
,
|
||||
{
|
||||
"name": "extract_dna_off"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue