diff --git a/Content.Server/Ghost/GhostSystem.cs b/Content.Server/Ghost/GhostSystem.cs index 67ac332be89..89d24f8f29b 100644 --- a/Content.Server/Ghost/GhostSystem.cs +++ b/Content.Server/Ghost/GhostSystem.cs @@ -5,6 +5,7 @@ using Content.Server.GameTicking; using Content.Server.Mind; using Content.Server.Roles.Jobs; +using Content.Shared._ES.Changeling; // ES Change using Content.Shared.Actions; using Content.Shared.CCVar; using Content.Shared.Damage; @@ -544,6 +545,15 @@ public bool OnGhostAttempt(EntityUid mindId, bool canReturnGlobal, bool viaComma // Pass in the whole mind entity var handleEv = new GhostAttemptHandleEvent((mindId, mind), canReturnGlobal); RaiseLocalEvent(mindId, handleEv); + + if (playerEntity != null) + { + var entityCancelEv = new ESGhostAttemptEvent(mindId); + RaiseLocalEvent(playerEntity.Value, ref entityCancelEv); + + if (entityCancelEv.Cancelled) + return false; + } // ES END // Something else has handled the ghost attempt for us! We return its result. diff --git a/Content.Server/_ES/Changeling/ESChangelingDevourSystem.cs b/Content.Server/_ES/Changeling/ESChangelingDevourSystem.cs new file mode 100644 index 00000000000..5ef0bb4781c --- /dev/null +++ b/Content.Server/_ES/Changeling/ESChangelingDevourSystem.cs @@ -0,0 +1,28 @@ +using Content.Server.Polymorph.Systems; +using Content.Shared.Changeling; +using Content.Shared.Mobs.Components; +using Content.Shared.Mobs.Systems; + +namespace Content.Server._ES.Changeling; + +public sealed class ESChangelingDevourSystem : EntitySystem +{ + [Dependency] private readonly MobStateSystem _mobState = default!; + [Dependency] private readonly PolymorphSystem _polymorph = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnDevoured); + } + + public void OnDevoured(EntityUid uid, MobStateComponent component, ref ESEntityDevouredEvent args) + { + if (_mobState.IsDead(uid)) + { + _polymorph.PolymorphEntity(uid, args.HuskProto); + } + } + +} diff --git a/Content.Server/_ES/Radstorm/ESRadstormConditionComponent.cs b/Content.Server/_ES/Radstorm/ESRadstormConditionComponent.cs new file mode 100644 index 00000000000..cca82d5ff2e --- /dev/null +++ b/Content.Server/_ES/Radstorm/ESRadstormConditionComponent.cs @@ -0,0 +1,7 @@ +namespace Content.Server._ES.Radstorm; + +[RegisterComponent] +public sealed partial class ESRadstormObjectiveComponent : Component +{ + +} diff --git a/Content.Server/_ES/Radstorm/ESRadstormImmuneComponent.cs b/Content.Server/_ES/Radstorm/ESRadstormImmuneComponent.cs new file mode 100644 index 00000000000..fbb1734c38e --- /dev/null +++ b/Content.Server/_ES/Radstorm/ESRadstormImmuneComponent.cs @@ -0,0 +1,7 @@ +namespace Content.Server._ES.Radstorm; + +[RegisterComponent] +public sealed partial class ESRadstormImmuneComponent : Component +{ + +} diff --git a/Content.Server/_ES/Radstorm/ESRadstormRoundEndRuleSystem.cs b/Content.Server/_ES/Radstorm/ESRadstormRoundEndRuleSystem.cs index 85e29cea204..aa12f4cb48a 100644 --- a/Content.Server/_ES/Radstorm/ESRadstormRoundEndRuleSystem.cs +++ b/Content.Server/_ES/Radstorm/ESRadstormRoundEndRuleSystem.cs @@ -1,3 +1,4 @@ +using Content.Server._ES.Objectives; using Content.Server._ES.Radio; using Content.Server.Audio; using Content.Server.Chat.Systems; @@ -5,6 +6,7 @@ using Content.Server.GameTicking.Rules; using Content.Server.RoundEnd; using Content.Shared._ES.CCVar; +using Content.Shared._ES.Objectives.Components; using Content.Shared.Damage.Components; using Content.Shared.Damage.Systems; using Content.Shared.FixedPoint; @@ -42,6 +44,7 @@ public sealed class ESRadstormRoundEndRuleSystem : GameRuleSystem(OnGetObjectiveProgress); } + private void OnGetObjectiveProgress(Entity ent, ref ESGetObjectiveProgressEvent args) + { + foreach (var comp in EntityQuery()) + { + // Might be a bit shitty logic but from my testing it works fine I think + args.Progress = (float)(_timing.CurTime / comp.RadstormStartTime); + return; + } + } + + protected override void ActiveTick(EntityUid uid, ESRadstormRoundEndRuleComponent component, GameRuleComponent gameRule, float frameTime) { base.ActiveTick(uid, component, gameRule, frameTime); @@ -92,6 +110,9 @@ protected override void ActiveTick(EntityUid uid, ESRadstormRoundEndRuleComponen if (state.CurrentState == MobState.Dead) continue; + if (HasComp(mob)) + continue; + // if they're not in space (i.e. not parented to the map) // and we haven't technically started yet, that means we're only space-dangerous, so don't hurt them if (xform.ParentUid != mapUid && _timing.CurTime < component.RadstormStartTime) @@ -103,6 +124,8 @@ protected override void ActiveTick(EntityUid uid, ESRadstormRoundEndRuleComponen stillAlive += 1; } + _objective.RefreshObjectiveProgress(); + // show is over // (make sure we only actually do this if after time and not just deadly space) // (i kind of implemented that in a weird way huh) @@ -175,6 +198,8 @@ private void DoPhase(ESRadstormRoundEndRuleComponent comp, ESRadstormPhaseConfig if (phase.SpaceDangerous) comp.SpaceDangerous = true; + _objective.RefreshObjectiveProgress(); + phase.Completed = true; } } diff --git a/Content.Shared/Changeling/ChangelingDevourEvents.cs b/Content.Shared/Changeling/ChangelingDevourEvents.cs index ba8d25f30cb..63e336be4c8 100644 --- a/Content.Shared/Changeling/ChangelingDevourEvents.cs +++ b/Content.Shared/Changeling/ChangelingDevourEvents.cs @@ -1,5 +1,7 @@ using Content.Shared.Actions; using Content.Shared.DoAfter; +using Content.Shared.Polymorph; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; namespace Content.Shared.Changeling; @@ -7,7 +9,13 @@ namespace Content.Shared.Changeling; /// /// Action event for Devour, someone has initiated a devour on someone, begin to windup. /// -public sealed partial class ChangelingDevourActionEvent : EntityTargetActionEvent; +public sealed partial class ChangelingDevourActionEvent : EntityTargetActionEvent +// ES Start +{ + [DataField] + public bool RequireIncapacitated; +} +// ES End /// /// A windup has either successfully been completed or has been canceled. If successful start the devouring DoAfter. @@ -19,4 +27,20 @@ public sealed partial class ChangelingDevourWindupDoAfterEvent : SimpleDoAfterEv /// The Consumption DoAfter has either successfully been completed or was canceled. /// [Serializable, NetSerializable] -public sealed partial class ChangelingDevourConsumeDoAfterEvent : SimpleDoAfterEvent; +public sealed partial class ChangelingDevourConsumeDoAfterEvent : SimpleDoAfterEvent +// ES Start +{ + [DataField] + public bool DoHusk; +} + +/// +/// Raised on the person devoured +/// +[ByRefEvent] +public record struct ESEntityDevouredEvent() +{ + [DataField] + public ProtoId HuskProto = "HuskPolymorph"; +} +// ES End diff --git a/Content.Shared/Changeling/Systems/ChangelingDevourSystem.cs b/Content.Shared/Changeling/Systems/ChangelingDevourSystem.cs index a30387a807e..ddadbca4902 100644 --- a/Content.Shared/Changeling/Systems/ChangelingDevourSystem.cs +++ b/Content.Shared/Changeling/Systems/ChangelingDevourSystem.cs @@ -143,6 +143,14 @@ private void OnDevourAction(Entity ent, ref Changelin return; } + // ES Start - adds option to require a target to be crit or dead before devouring, unsure why you were just able to devour live things anyway + if (args.RequireIncapacitated && !_mobState.IsIncapacitated(args.Target)) + { + _popupSystem.PopupClient(Loc.GetString("changeling-devour-attempt-failed-incapacitated"), ent, ent, PopupType.Medium); + return; + } + // ES end + if (_net.IsServer) { var pvsSound = _audio.PlayPvs(ent.Comp.DevourWindupNoise, ent); @@ -255,11 +263,18 @@ private void OnDevourConsume(Entity ent, ref Changeli _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(ent.Owner):player} successfully devoured {ToPrettyString(args.Target):player}'s identity"); _changelingIdentitySystem.CloneToPausedMap((ent, identityStorage), target.Value); - if (_inventorySystem.TryGetSlotEntity(target.Value, "jumpsuit", out var item) - && TryComp(item, out var butcherable)) - RipClothing(target.Value, (item.Value, butcherable)); + // ES start - removes rip clothing, kind of scuffed with how job clothing is distributed + //if (_inventorySystem.TryGetSlotEntity(target.Value, "jumpsuit", out var item) + //&& TryComp(item, out var butcherable)) + //RipClothing(target.Value, (item.Value, butcherable)); + // ES End } + // ES Start + var ev = new ESEntityDevouredEvent(); + RaiseLocalEvent((EntityUid)args.Target!, ref ev); + // ES End + Dirty(ent); } diff --git a/Content.Shared/Morgue/SharedMorgueSystem.cs b/Content.Shared/Morgue/SharedMorgueSystem.cs index 3da92d798a1..357f4445f7b 100644 --- a/Content.Shared/Morgue/SharedMorgueSystem.cs +++ b/Content.Shared/Morgue/SharedMorgueSystem.cs @@ -71,11 +71,13 @@ public void CheckContents(EntityUid uid, MorgueComponent? morgue = null, EntityS if (!hasMob && HasComp(ent)) hasMob = true; - if (HasComp(ent)) - { - _appearance.SetData(uid, MorgueVisuals.Contents, MorgueContents.HasSoul, app); - return; - } + // ES Start - that stupid morgue state that I hate + //if (HasComp(ent)) + //{ + //_appearance.SetData(uid, MorgueVisuals.Contents, MorgueContents.HasSoul, app); + //return; + //} + // ES End } _appearance.SetData(uid, MorgueVisuals.Contents, hasMob ? MorgueContents.HasMob : MorgueContents.HasContents, app); diff --git a/Content.Shared/Roles/Components/ChangelingRoleComponent.cs b/Content.Shared/Roles/Components/ChangelingRoleComponent.cs index fb9bc05af30..2ed8d24eaf3 100644 --- a/Content.Shared/Roles/Components/ChangelingRoleComponent.cs +++ b/Content.Shared/Roles/Components/ChangelingRoleComponent.cs @@ -1,4 +1,5 @@ -using Robust.Shared.GameStates; +using Content.Shared.Actions.Components; +using Robust.Shared.GameStates; namespace Content.Shared.Roles.Components; @@ -6,4 +7,13 @@ namespace Content.Shared.Roles.Components; /// Added to mind role entities to tag that they are a changeling. /// [RegisterComponent, NetworkedComponent] -public sealed partial class ChangelingRoleComponent : BaseMindRoleComponent; +public sealed partial class ChangelingRoleComponent : BaseMindRoleComponent +// ES Start +{ + [DataField] + public string? StatisAction = "ActionExistStatis"; + + [DataField] + public EntityUid? StatisActionEntity; +} + diff --git a/Content.Shared/_ES/Actions/ESRandomActionGrantComponent.cs b/Content.Shared/_ES/Actions/ESRandomActionGrantComponent.cs new file mode 100644 index 00000000000..def274fb8c6 --- /dev/null +++ b/Content.Shared/_ES/Actions/ESRandomActionGrantComponent.cs @@ -0,0 +1,13 @@ +using Content.Shared.EntityTable.EntitySelectors; + +namespace Content.Shared._ES.Actions; + +[RegisterComponent] +public sealed partial class ESRandomActionGrantComponent : Component +{ + /// + /// The objectives that this troupe gives to its members + /// + [DataField] + public EntityTableSelector Actions = new NoneSelector(); +} diff --git a/Content.Shared/_ES/Actions/RandomActionGrantSystem.cs b/Content.Shared/_ES/Actions/RandomActionGrantSystem.cs new file mode 100644 index 00000000000..9dc87a76867 --- /dev/null +++ b/Content.Shared/_ES/Actions/RandomActionGrantSystem.cs @@ -0,0 +1,27 @@ +using Content.Shared.Actions; +using Content.Shared.EntityTable; + +namespace Content.Shared._ES.Actions; + +public sealed class RandomActionGrantSystem : EntitySystem +{ + [Dependency] private readonly SharedActionsSystem _actions = default!; + [Dependency] private readonly EntityTableSystem _entityTable = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnMapInit); + } + + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + var Actions = _entityTable.GetSpawns(ent.Comp.Actions); + + foreach (var action in Actions) + { + EntityUid? actionEnt = null; + _actions.AddAction(ent.Owner, ref actionEnt, action); + } + } +} diff --git a/Content.Shared/_ES/Masks/Changeling/ChemicalStingEvents.cs b/Content.Shared/_ES/Masks/Changeling/ChemicalStingEvents.cs new file mode 100644 index 00000000000..2a5bfd23622 --- /dev/null +++ b/Content.Shared/_ES/Masks/Changeling/ChemicalStingEvents.cs @@ -0,0 +1,12 @@ +using Content.Shared.Actions; + +namespace Content.Shared._ES.Changeling; + +public sealed partial class ESChemicalStingEvent : EntityTargetActionEvent +{ + /// + /// Name of the solution to draw the injection chems from (what will be injected) + /// + [DataField] + public string SolutionName = "Injector"; +} diff --git a/Content.Shared/_ES/Masks/Changeling/ChemicalStingSystem.cs b/Content.Shared/_ES/Masks/Changeling/ChemicalStingSystem.cs new file mode 100644 index 00000000000..cc9eddb5d7a --- /dev/null +++ b/Content.Shared/_ES/Masks/Changeling/ChemicalStingSystem.cs @@ -0,0 +1,29 @@ +using Content.Shared.Chemistry.EntitySystems; + +namespace Content.Shared._ES.Changeling; + +public sealed class ChemicalStingSystem : EntitySystem +{ + [Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnChemicalInjection); + } + + private void OnChemicalInjection(ESChemicalStingEvent args) + { + if (args.Handled) + return; + + if (!_solutionContainer.TryGetSolution(args.Action.Owner, args.SolutionName, out _, out var solution) || + !_solutionContainer.TryGetInjectableSolution(args.Target, out var targetSolution, out _)) + return; + + _solutionContainer.AddSolution(targetSolution.Value, solution); + + args.Handled = true; + } +} diff --git a/Content.Shared/_ES/Masks/Changeling/ESChangelingStatisEvent.cs b/Content.Shared/_ES/Masks/Changeling/ESChangelingStatisEvent.cs new file mode 100644 index 00000000000..cc2f351e521 --- /dev/null +++ b/Content.Shared/_ES/Masks/Changeling/ESChangelingStatisEvent.cs @@ -0,0 +1,14 @@ +using Content.Shared.Actions; + +namespace Content.Shared._ES.Changeling; + +public sealed partial class ESChangelingStatisEvent : InstantActionEvent +{ +} + +/// +/// Raised on an entity when Its mind is attempting to ghost out. +/// +[ByRefEvent] +public record struct ESGhostAttemptEvent(EntityUid Mind, bool Cancelled = false); + diff --git a/Content.Shared/_ES/Masks/Changeling/ESChangelingStatisSystem.cs b/Content.Shared/_ES/Masks/Changeling/ESChangelingStatisSystem.cs new file mode 100644 index 00000000000..a4f3c4d0361 --- /dev/null +++ b/Content.Shared/_ES/Masks/Changeling/ESChangelingStatisSystem.cs @@ -0,0 +1,66 @@ +using Content.Shared._ES.Mind; +using Content.Shared.Actions; +using Content.Shared.Actions.Components; +using Content.Shared.Damage.Systems; +using Content.Shared.IdentityManagement; +using Content.Shared.Mobs; +using Content.Shared.Mobs.Systems; +using Content.Shared.Popups; +using Content.Shared.Roles.Components; + +namespace Content.Shared._ES.Changeling; + +public sealed class ESChangelingStatisSystem : EntitySystem +{ + + [Dependency] private readonly SharedActionsSystem _actions = default!; + [Dependency] private readonly DamageableSystem _damage = default!; + [Dependency] private readonly MobStateSystem _mobState = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + + public override void Initialize() + { + SubscribeLocalEvent(OnMobStateChanged); + SubscribeLocalEvent(OnTryGhost); + SubscribeLocalEvent(OnChangelingStatisEvent); + } + + private void OnMobStateChanged(Entity ent, ref MobStateChangedEvent args) + { + if (_mobState.IsAlive(ent)) + { + if (ent.Comp.StatisActionEntity == null) + return; + + if (!TryComp(ent, out var action)) + return; + + _actions.RemoveAction((ent, action), ent.Comp.StatisActionEntity); + } + } + + private void OnChangelingStatisEvent(ESChangelingStatisEvent args) + { + if (_mobState.IsCritical(args.Performer)) + { + _mobState.ChangeMobState(args.Performer, MobState.Dead); + return; + } + + _damage.ClearAllDamage(args.Performer); + _mobState.ChangeMobState(args.Performer, MobState.Alive); + + var selfMessage = Loc.GetString("changeling-statis-end-self", ("user", Identity.Entity(args.Performer, EntityManager))); + var othersMessage = Loc.GetString("changeling-statis-end-others", ("user", Identity.Entity(args.Performer, EntityManager))); + + _popup.PopupPredicted(selfMessage, othersMessage, args.Performer, args.Performer, type: PopupType.MediumCaution); + } + + private void OnTryGhost(Entity ent, ref ESGhostAttemptEvent args) + { + if (_mobState.IsIncapacitated(ent)) + { + args.Cancelled = true; + } + } +} diff --git a/Resources/Locale/en-US/_ES/actions/changeling.ftl b/Resources/Locale/en-US/_ES/actions/changeling.ftl new file mode 100644 index 00000000000..907798a9381 --- /dev/null +++ b/Resources/Locale/en-US/_ES/actions/changeling.ftl @@ -0,0 +1,4 @@ +changeling-devour-attempt-failed-incapacitated = They need to be incapacitated to consume them! + +changeling-statis-end-self = We exit statis, fully restored from our damage. +changeling-statis-end-others = { CAPITALIZE(POSS-ADJ($user)) } body contorts, suddenly bursting awake diff --git a/Resources/Locale/en-US/_ES/masks/masks.ftl b/Resources/Locale/en-US/_ES/masks/masks.ftl index f3b21ddcc4f..04c6eac6840 100644 --- a/Resources/Locale/en-US/_ES/masks/masks.ftl +++ b/Resources/Locale/en-US/_ES/masks/masks.ftl @@ -87,6 +87,11 @@ es-mask-subverter-desc = As a Subverter, you have received two brain-altering ch es-mask-demolitionist-name = Demolitionist es-mask-demolitionist-desc = As a Demolitionist, you have been trained by the Syndicate for both controlled and uncontrolled "demolitions"--including in the use of explosive implants, should things turn out that way. +# Changeling Masks +es-mask-troupe-changeling-examine = They're a fellow [bold][color=maroon]Changeling[/color][/bold] and are part of our [bold][color=maroon]Hivemind[/color][/bold]. Work together to sabotage the station. + +es-mask-changeling-name = Changeling +es-mask-changeling-desc = As a changeling, use your ability to take the form of others and your armblade to decieve and sabotage the crew # Oddballs es-mask-syndie-superfan-name = Syndie Superfan es-mask-syndie-superfan-desc = As the Syndicate's biggest fan, you're a member of crew and win with them, unless the entire traitors team dies, upon which you switch sides and gain a new mask. diff --git a/Resources/Locale/en-US/_ES/masks/troupes.ftl b/Resources/Locale/en-US/_ES/masks/troupes.ftl index 1d2aeac89cf..619a4c5fb94 100644 --- a/Resources/Locale/en-US/_ES/masks/troupes.ftl +++ b/Resources/Locale/en-US/_ES/masks/troupes.ftl @@ -3,3 +3,6 @@ es-troupe-crew-description = As a member of the Crew, try to keep things running es-troupe-traitor-name = Traitor es-troupe-traitor-description = As a Traitor, seek out your fellow operatives, complete the objectives the Syndicate has assigned to you, and try to detonate the station's nuke. + +es-troupe-changeling-name = Hivemind +es-troupe-changeling-description = As a Changeling, make sure the radiation storm happens at all costs through sabotage and deception. diff --git a/Resources/Prototypes/Entities/Mobs/Species/base.yml b/Resources/Prototypes/Entities/Mobs/Species/base.yml index af06d49efae..98e55862770 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/base.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/base.yml @@ -216,6 +216,7 @@ - DoorBumpOpener - AnomalyHost # ES START + - Huskable - type: ESBlinker # ES END diff --git a/Resources/Prototypes/Entities/Mobs/Species/human.yml b/Resources/Prototypes/Entities/Mobs/Species/human.yml index bf357e1f102..fbd3847a8f2 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/human.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/human.yml @@ -28,7 +28,6 @@ sprite: Mobs/Species/Human/displacement.rsi state: jumpsuit-female - - type: entity parent: BaseSpeciesDummy id: MobHumanDummy @@ -40,4 +39,4 @@ sizeMaps: 32: sprite: Mobs/Species/Human/displacement.rsi - state: jumpsuit-female \ No newline at end of file + state: jumpsuit-female diff --git a/Resources/Prototypes/_ES/Actions/Masks/changeling.yml b/Resources/Prototypes/_ES/Actions/Masks/changeling.yml new file mode 100644 index 00000000000..088636ced01 --- /dev/null +++ b/Resources/Prototypes/_ES/Actions/Masks/changeling.yml @@ -0,0 +1,94 @@ +- type: entity + id: ESActionChangelingDevour + parent: ActionChangelingDevour + name: "[color=red]Devour[/color]" + description: Consume the essence of your victims and subsume their identity and mind into your own. + components: + - type: EntityTargetAction + whitelist: + tags: + - Huskable + event: !type:ChangelingDevourActionEvent + requireIncapacitated: true + +- type: entity + parent: BaseMentalAction + id: ESActionMaskBaseSting + name: Base Sting + description: sting sting + components: + - type: Action + useDelay: 180 + icon: { sprite : Interface/Actions/changeling.rsi, state: "devour" } + iconOn: { sprite : Interface/Actions/changeling.rsi, state: "devour_on" } + priority: 1 + - type: SolutionContainerManager + solutions: + Injector: + maxVol: 25 + reagents: + - ReagentId: Spacelube + Quantity: 1 + - type: TargetAction + - type: EntityTargetAction + event: !type:ESChemicalStingEvent + +- type: entity + parent: ESActionMaskBaseSting + id: ESActionMaskMuteSting + name: Mute Sting + description: Shut your victims up with this sting + components: + - type: SolutionContainerManager + solutions: + Injector: + maxVol: 10 + reagents: + - ReagentId: MuteToxin + Quantity: 10 + +- type: entity + parent: ESActionMaskBaseSting + id: ESActionMaskSleepSting + name: Sleep Sting + description: Shut your victims up with this sting + components: + - type: SolutionContainerManager + solutions: + Injector: + maxVol: 10 + reagents: + - ReagentId: ChloralHydrate + Quantity: 10 + +- type: entity + parent: BaseMentalAction + id: ActionExitStatis + name: Exit Statis + description: Exit your regenerative statis + components: + - type: Action + useDelay: 60 + raiseOnAction: true + itemIconStyle: BigAction + startDelay: true + icon: + sprite: _ES/Actions/masks/changeling.rsi + state: icon + - type: InstantAction + event: !type:ESChangelingStatisEvent + +- type: entity + parent: BaseCritAction + id: ActionEnterStatis + name: Enter Statis + description: Enter your regenerative statis. + components: + - type: Action + startDelay: true + useDelay: 5 + icon: + sprite: _ES/Actions/masks/changeling.rsi + state: statis + - type: InstantAction + event: !type:ESChangelingStatisEvent # It has diffrent logic I swear dont question it diff --git a/Resources/Prototypes/_ES/Damage/modifier_sets.yml b/Resources/Prototypes/_ES/Damage/modifier_sets.yml new file mode 100644 index 00000000000..aff734cf478 --- /dev/null +++ b/Resources/Prototypes/_ES/Damage/modifier_sets.yml @@ -0,0 +1,4 @@ +- type: damageModifierSet + id: Changeling + coefficients: + Heat: 3 diff --git a/Resources/Prototypes/_ES/Entities/Mobs/Player/husk.yml b/Resources/Prototypes/_ES/Entities/Mobs/Player/husk.yml new file mode 100644 index 00000000000..dcc6a387613 --- /dev/null +++ b/Resources/Prototypes/_ES/Entities/Mobs/Player/husk.yml @@ -0,0 +1,54 @@ +- type: entity + id: Husk + parent: [BaseSimpleMob, StripableInventoryBase] + name: Husk + description: A cold shell of someone, all their biomatter has been sucked dry. + components: + - type: RotationVisuals + defaultRotation: 90 + horizontalRotation: 90 + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeCircle + radius: 0.35 + density: 80 + mask: + - MobMask + layer: + - MobLayer + - type: Stripping + - type: Sprite + drawdepth: Mobs + layers: + - map: [ "enum.DamageStateVisualLayers.Base" ] + state: husk + sprite: _ES/Mobs/Effects/husk.rsi + - map: [ "jumpsuit" ] + - map: [ "enum.HumanoidVisualLayers.Handcuffs" ] + color: "#ffffff" + sprite: Objects/Misc/handcuffs.rsi + state: body-overlay-2 + visible: false + - map: [ "ears" ] + - map: [ "id" ] + - map: [ "outerClothing" ] + - map: [ "mask" ] + - map: [ "head" ] + - map: [ "clownedon" ] + sprite: "Effects/creampie.rsi" + state: "creampie_human" + visible: false + - type: Hands + - type: ComplexInteraction + - type: GenericVisualizer + visuals: + enum.CreamPiedVisuals.Creamed: + clownedon: + True: { visible: true } + False: { visible: false } + - type: Body + prototype: Human + requiredLegs: 2 + - type: CreamPied diff --git a/Resources/Prototypes/_ES/GameRules/troupes.yml b/Resources/Prototypes/_ES/GameRules/troupes.yml index 5cb17d8daf8..c13a7e6f7f2 100644 --- a/Resources/Prototypes/_ES/GameRules/troupes.yml +++ b/Resources/Prototypes/_ES/GameRules/troupes.yml @@ -30,3 +30,14 @@ - type: ESTraitorRule - type: LoadMapRule mapPath: /Maps/_ES/syndiebase.yml + +- type: entity + parent: ESBaseTroupeRule + id: ESTroupeRuleChangeling + name: The Changelings + components: + - type: ESTroupeRule + troupe: ESChangeling + minTargetMembers: 2 + maxTargetMembers: 3 + playersPerTargetMember: 8 # 1/8 Players diff --git a/Resources/Prototypes/_ES/Masks/masks.yml b/Resources/Prototypes/_ES/Masks/masks.yml index 61d72c2c0bd..973318f4e5c 100644 --- a/Resources/Prototypes/_ES/Masks/masks.yml +++ b/Resources/Prototypes/_ES/Masks/masks.yml @@ -326,8 +326,44 @@ weight: 0.3 mindComponents: - type: ESMaskCacheSpawner - cacheProto: - id: ESCrateCacheTraitorDemolitionist + +# Changeling +- type: esMask + id: ESChangeling + troupe: ESChangeling + name: es-mask-changeling-name + description: es-mask-changeling-desc + color: maroon + weight: 1.5 + components: + - type: ESTroupeFactionIcon + icon: ESChangeling + troupe: ESChangeling + examineString: es-mask-troupe-changeling-examine + - type: ChangelingDevour + changelingDevourAction: ESActionChangelingDevour + - type: ChangelingIdentity + - type: ChangelingTransform + - type: ActionGrant + actions: + - ActionRetractableItemArmBlade # just giving all changelings armblade for now + - type: ESRadstormImmune + - type: Damageable + damageContainer: Biological + damageModifierSet: Changeling + - type: ESRandomActionGrant + actions: + all: + - group: + - id: ESActionMaskMuteSting + - id: ESActionMaskSleepSting + - type: ChangelingRole + - type: MobStateActions + actions: + Critical: + - ActionEnterStatis + Dead: + - ActionExitStatis # Neutral/oddball masks - type: esMask diff --git a/Resources/Prototypes/_ES/Masks/troupes.yml b/Resources/Prototypes/_ES/Masks/troupes.yml index 29ede9da652..851304079b8 100644 --- a/Resources/Prototypes/_ES/Masks/troupes.yml +++ b/Resources/Prototypes/_ES/Masks/troupes.yml @@ -40,3 +40,24 @@ amount: 4 - id: ESObjectiveTraitorHackCryptoNukeConsoles - id: ESObjectiveTraitorDetonateNuke + +- type: esTroupe + id: ESChangeling + name: es-troupe-changeling-name + description: es-troupe-changeling-description + color: maroon + metaIcon: ESChangeling + gameRule: ESTroupeRuleChangeling + prohibitedJobs: + # Command + - ESCaptain + - ESHeadOfPersonnel + - ESHeadOfSecurity + # Security + - ESWarden + - ESSecurityOfficer + - ESDetective + showInNewsReport: false + objectives: + all: + - id: ESObjectiveRadstorm diff --git a/Resources/Prototypes/_ES/Objectives/changeling.yml b/Resources/Prototypes/_ES/Objectives/changeling.yml new file mode 100644 index 00000000000..da4f4dc163c --- /dev/null +++ b/Resources/Prototypes/_ES/Objectives/changeling.yml @@ -0,0 +1,11 @@ +- type: entity + parent: ESBaseMaskObjective + id: ESObjectiveRadstorm + name: Radstorm Objective + description: Make sure the radstorm happens at all costs. + components: + - type: ESObjective + icon: + sprite: Objects/Materials/Sheets/other.rsi + state: uranium_2 + - type: ESRadstormObjective diff --git a/Resources/Prototypes/_ES/Polymorph/husk.yml b/Resources/Prototypes/_ES/Polymorph/husk.yml new file mode 100644 index 00000000000..b24adb7e0b6 --- /dev/null +++ b/Resources/Prototypes/_ES/Polymorph/husk.yml @@ -0,0 +1,11 @@ +- type: polymorph + id: HuskPolymorph + configuration: + entity: Husk + forced: true + transferDamage: true + transferName: false + inventory: Transfer + revertOnCrit: false + revertOnDeath: false + allowRepeatedMorphs: true diff --git a/Resources/Prototypes/_ES/StatusIcon/faction.yml b/Resources/Prototypes/_ES/StatusIcon/faction.yml index a2a0429cf41..ac128a97507 100644 --- a/Resources/Prototypes/_ES/StatusIcon/faction.yml +++ b/Resources/Prototypes/_ES/StatusIcon/faction.yml @@ -11,3 +11,10 @@ icon: sprite: /Textures/Interface/Misc/job_icons.rsi state: Syndicate + +- type: factionIcon + id: ESChangeling + locationPreference: Right + icon: + sprite: /Textures/_ES/Interface/Misc/ES_job_icons.rsi + state: changeling diff --git a/Resources/Prototypes/_ES/tags.yml b/Resources/Prototypes/_ES/tags.yml index d3bf547b6ea..930550eac01 100644 --- a/Resources/Prototypes/_ES/tags.yml +++ b/Resources/Prototypes/_ES/tags.yml @@ -43,6 +43,10 @@ - type: Tag id: ESHelmetEVASec +# Used for husking +- type: Tag + id: Huskable + - type: Tag id: ESShotgunPellet @@ -72,3 +76,10 @@ - type: Tag id: ESSuitEVASec + +- type: Tag + id: ESThrusterEngineGas + +- type: Tag + id: ESThrusterEngineIon + diff --git a/Resources/Textures/_ES/Actions/masks/changeling.rsi/icon.png b/Resources/Textures/_ES/Actions/masks/changeling.rsi/icon.png new file mode 100644 index 00000000000..c7aadf67405 Binary files /dev/null and b/Resources/Textures/_ES/Actions/masks/changeling.rsi/icon.png differ diff --git a/Resources/Textures/_ES/Actions/masks/changeling.rsi/meta.json b/Resources/Textures/_ES/Actions/masks/changeling.rsi/meta.json new file mode 100644 index 00000000000..224874c3a93 --- /dev/null +++ b/Resources/Textures/_ES/Actions/masks/changeling.rsi/meta.json @@ -0,0 +1,17 @@ +{ + "version": 1, + "license": "CC-BY-SA-4.0", + "copyright": "Created by TiniestShark (github).", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "statis" + } + ] +} diff --git a/Resources/Textures/_ES/Actions/masks/changeling.rsi/statis.png b/Resources/Textures/_ES/Actions/masks/changeling.rsi/statis.png new file mode 100644 index 00000000000..56ca2b82340 Binary files /dev/null and b/Resources/Textures/_ES/Actions/masks/changeling.rsi/statis.png differ diff --git a/Resources/Textures/_ES/Interface/Misc/ES_job_icons.rsi/changeling.png b/Resources/Textures/_ES/Interface/Misc/ES_job_icons.rsi/changeling.png new file mode 100644 index 00000000000..9641d2d7c2b Binary files /dev/null and b/Resources/Textures/_ES/Interface/Misc/ES_job_icons.rsi/changeling.png differ diff --git a/Resources/Textures/_ES/Interface/Misc/ES_job_icons.rsi/meta.json b/Resources/Textures/_ES/Interface/Misc/ES_job_icons.rsi/meta.json index bf07188761e..f03ba8be3bc 100644 --- a/Resources/Textures/_ES/Interface/Misc/ES_job_icons.rsi/meta.json +++ b/Resources/Textures/_ES/Interface/Misc/ES_job_icons.rsi/meta.json @@ -18,6 +18,9 @@ }, { "name": "Soviet" + }, + { + "name": "changeling" } ] } diff --git a/Resources/Textures/_ES/Mobs/Effects/husk.rsi/husk.png b/Resources/Textures/_ES/Mobs/Effects/husk.rsi/husk.png new file mode 100644 index 00000000000..94392caca58 Binary files /dev/null and b/Resources/Textures/_ES/Mobs/Effects/husk.rsi/husk.png differ diff --git a/Resources/Textures/_ES/Mobs/Effects/husk.rsi/meta.json b/Resources/Textures/_ES/Mobs/Effects/husk.rsi/meta.json new file mode 100644 index 00000000000..4e1d2d7bb8f --- /dev/null +++ b/Resources/Textures/_ES/Mobs/Effects/husk.rsi/meta.json @@ -0,0 +1,15 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "https://github.com/imtakingabreakdontatme/tgstation/commit/537e92224941b9bf99c968370dbf2faa26285e8a", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "husk", + "directions": 4 + } + ] +}