diff --git a/Content.Client/Entry/IgnoredComponents.cs b/Content.Client/Entry/IgnoredComponents.cs index c052a17946..2f4117bc1e 100644 --- a/Content.Client/Entry/IgnoredComponents.cs +++ b/Content.Client/Entry/IgnoredComponents.cs @@ -346,6 +346,7 @@ namespace Content.Client.Entry "DoorRemote", "InteractionPopup", "HealthAnalyzer", + "BodyReassemble", "Thirst", "CanEscapeInventory", "PowerSink", diff --git a/Content.Client/Preferences/UI/HumanoidProfileEditor.xaml.cs b/Content.Client/Preferences/UI/HumanoidProfileEditor.xaml.cs index 690b995b5e..46936b7a56 100644 --- a/Content.Client/Preferences/UI/HumanoidProfileEditor.xaml.cs +++ b/Content.Client/Preferences/UI/HumanoidProfileEditor.xaml.cs @@ -541,6 +541,23 @@ namespace Content.Client.Preferences.UI Profile = Profile.WithCharacterAppearance(Profile.Appearance.WithSkinColor(color)); break; } + case SpeciesSkinColor.TintedHues: + { + if (!_rgbSkinColorContainer.Visible) + { + _skinColor.Visible = false; + _rgbSkinColorContainer.Visible = true; + } + + // a little hacky in order to convert rgb --> hsv --> rgb + var color = new Color(_rgbSkinColorSelector.Color.R, _rgbSkinColorSelector.Color.G, _rgbSkinColorSelector.Color.B); + var newColor = Color.ToHsv(color); + newColor.Y = .1f; + color = Color.FromHsv(newColor); + + Profile = Profile.WithCharacterAppearance(Profile.Appearance.WithSkinColor(color)); + break; + } } IsDirty = true; @@ -753,6 +770,18 @@ namespace Content.Client.Preferences.UI _rgbSkinColorContainer.Visible = true; } + // set the RGB values to the direct values otherwise + _rgbSkinColorSelector.Color = Profile.Appearance.SkinColor; + break; + } + case SpeciesSkinColor.TintedHues: + { + if (!_rgbSkinColorContainer.Visible) + { + _skinColor.Visible = false; + _rgbSkinColorContainer.Visible = true; + } + // set the RGB values to the direct values otherwise _rgbSkinColorSelector.Color = Profile.Appearance.SkinColor; break; diff --git a/Content.Server/Body/Components/BodyComponent.cs b/Content.Server/Body/Components/BodyComponent.cs index f41a11cabc..695e651f81 100644 --- a/Content.Server/Body/Components/BodyComponent.cs +++ b/Content.Server/Body/Components/BodyComponent.cs @@ -83,11 +83,20 @@ namespace Content.Server.Body.Components } } - public override void Gib(bool gibParts = false) + public override HashSet Gib(bool gibParts = false) { - base.Gib(gibParts); + var gibs = base.Gib(gibParts); - SoundSystem.Play(Filter.Pvs(Owner), _gibSound.GetSound(), _entMan.GetComponent(Owner).Coordinates, AudioHelpers.WithVariation(0.025f)); + var xform = _entMan.GetComponent(Owner); + var coordinates = xform.Coordinates; + + // These have already been forcefully removed from containers so run it here. + foreach (var part in gibs) + { + _entMan.EventBus.RaiseLocalEvent(part, new PartGibbedEvent(Owner, gibs)); + } + + SoundSystem.Play(Filter.Pvs(Owner, entityManager: _entMan), _gibSound.GetSound(), coordinates, AudioHelpers.WithVariation(0.025f)); if (_entMan.TryGetComponent(Owner, out ContainerManagerComponent? container)) { @@ -96,18 +105,41 @@ namespace Content.Server.Body.Components foreach (var ent in cont.ContainedEntities) { cont.ForceRemove(ent); - _entMan.GetComponent(ent).Coordinates = _entMan.GetComponent(Owner).Coordinates; + _entMan.GetComponent(ent).Coordinates = coordinates; ent.RandomOffset(0.25f); } } } - _entMan.EventBus.RaiseLocalEvent(Owner, new BeingGibbedEvent(), false); + _entMan.EventBus.RaiseLocalEvent(Owner, new BeingGibbedEvent(gibs), false); _entMan.QueueDeleteEntity(Owner); + + return gibs; } } public sealed class BeingGibbedEvent : EntityEventArgs { + public readonly HashSet GibbedParts; + + public BeingGibbedEvent(HashSet gibbedParts) + { + GibbedParts = gibbedParts; + } + } + + /// + /// An event raised on all the parts of an entity when it's gibbed + /// + public sealed class PartGibbedEvent : EntityEventArgs + { + public EntityUid EntityToGib; + public readonly HashSet GibbedParts; + + public PartGibbedEvent(EntityUid entityToGib, HashSet gibbedParts) + { + EntityToGib = entityToGib; + GibbedParts = gibbedParts; + } } } diff --git a/Content.Server/Body/Components/BodyReassembleComponent.cs b/Content.Server/Body/Components/BodyReassembleComponent.cs new file mode 100644 index 0000000000..cefe87d089 --- /dev/null +++ b/Content.Server/Body/Components/BodyReassembleComponent.cs @@ -0,0 +1,35 @@ +using System.Threading; +using Content.Server.Cloning; +using Content.Shared.Actions.ActionTypes; + +namespace Content.Server.Body.Components +{ + [RegisterComponent] + public sealed class BodyReassembleComponent : Component + { + /// + /// The dna entry used for reassembling the skeleton + /// updated before the entity is gibbed. + /// + [ViewVariables] + public ClonerDNAEntry? DNA = null; + + /// + /// The default time it takes to reassemble itself + /// + [ViewVariables] + [DataField("delay")] + public float DoAfterTime = 5f; + + /// + /// The list of body parts that are needed for reassembly + /// + [ViewVariables] + public HashSet? BodyParts = null; + + [DataField("action")] + public InstantAction? ReassembleAction = null; + + public CancellationTokenSource? CancelToken = null; + } +} diff --git a/Content.Server/Body/Systems/BodyReassembleSystem.cs b/Content.Server/Body/Systems/BodyReassembleSystem.cs new file mode 100644 index 0000000000..439cc89dab --- /dev/null +++ b/Content.Server/Body/Systems/BodyReassembleSystem.cs @@ -0,0 +1,234 @@ +using System.Threading; +using Content.Server.Body.Components; +using Content.Server.Cloning; +using Content.Server.DoAfter; +using Content.Server.Mind.Components; +using Content.Server.Popups; +using Content.Server.Preferences.Managers; +using Content.Shared.Actions; +using Content.Shared.CharacterAppearance.Systems; +using Content.Shared.Preferences; +using Content.Shared.Species; +using Content.Shared.Verbs; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; + +/// +/// Fair warning, this is all kinda shitcode, but it'll have to wait for a major +/// refactor until proper body systems get added. The current implementation is +/// definitely not ideal and probably will be prone to weird bugs. +/// + +namespace Content.Server.Body.Systems +{ + public sealed class BodyReassembleSystem : EntitySystem + { + [Dependency] private readonly IServerPreferencesManager _prefsManager = null!; + [Dependency] private readonly IPrototypeManager _prototype = default!; + [Dependency] private readonly SharedActionsSystem _actions = default!; + [Dependency] private readonly PopupSystem _popupSystem = default!; + [Dependency] private readonly DoAfterSystem _doAfterSystem = default!; + [Dependency] private readonly SharedHumanoidAppearanceSystem _humanoidAppearance = default!; + + private const float SelfReassembleMultiplier = 2f; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnPartGibbed); + SubscribeLocalEvent(StartReassemblyAction); + + SubscribeLocalEvent>(AddReassembleVerbs); + SubscribeLocalEvent(ReassembleComplete); + SubscribeLocalEvent(ReassembleCancelled); + } + + private void StartReassemblyAction(EntityUid uid, BodyReassembleComponent component, ReassembleActionEvent args) + { + args.Handled = true; + StartReassembly(uid, component, SelfReassembleMultiplier); + } + + private void ReassembleCancelled(EntityUid uid, BodyReassembleComponent component, ReassembleCancelledEvent args) + { + component.CancelToken = null; + } + + private void OnPartGibbed(EntityUid uid, BodyReassembleComponent component, PartGibbedEvent args) + { + if (!TryComp(args.EntityToGib, out var mindComp) || mindComp?.Mind == null) + return; + + component.BodyParts = args.GibbedParts; + UpdateDNAEntry(uid, args.EntityToGib); + mindComp.Mind.TransferTo(uid); + + if (component.ReassembleAction == null) + return; + + _actions.AddAction(uid, component.ReassembleAction, null); + } + + private void StartReassembly(EntityUid uid, BodyReassembleComponent component, float multiplier = 1f) + { + if (component.CancelToken != null) + return; + + if (!GetNearbyParts(uid, component, out var partList)) + return; + + if (partList == null) + return; + + var doAfterTime = component.DoAfterTime * multiplier; + var cancelToken = new CancellationTokenSource(); + component.CancelToken = cancelToken; + + var doAfterEventArgs = new DoAfterEventArgs(component.Owner, doAfterTime, cancelToken.Token, component.Owner) + { + BreakOnTargetMove = true, + BreakOnUserMove = true, + BreakOnDamage = true, + BreakOnStun = true, + NeedHand = false, + TargetCancelledEvent = new ReassembleCancelledEvent(), + TargetFinishedEvent = new ReassembleCompleteEvent(uid, uid, partList), + }; + + _doAfterSystem.DoAfter(doAfterEventArgs); + } + + /// + /// Adds the custom verb for reassembling body parts + /// + private void AddReassembleVerbs(EntityUid uid, BodyReassembleComponent component, GetVerbsEvent args) + { + if (!args.CanAccess || !args.CanInteract) + return; + + if (!TryComp(uid, out var mind) || + !mind.HasMind || + component.CancelToken != null) + return; + + // doubles the time if you reconstruct yourself + var multiplier = args.User == uid ? SelfReassembleMultiplier : 1f; + + // Custom verb + AlternativeVerb custom = new() + { + Text = Loc.GetString("reassemble-action"), + Act = () => + { + StartReassembly(uid, component, multiplier); + }, + IconEntity = uid, + Priority = 1 + }; + args.Verbs.Add(custom); + } + + private bool GetNearbyParts(EntityUid uid, BodyReassembleComponent component, out HashSet? partList) + { + partList = new HashSet(); + + if (component.BodyParts == null) + return false; + + // Ensures all of the old body part pieces are there + var xformQuery = GetEntityQuery(); + var notFound = true; + var bodyXform = xformQuery.GetComponent(uid); + + foreach (var part in component.BodyParts) + { + if (!xformQuery.TryGetComponent(part, out var xform) || + !bodyXform.Coordinates.InRange(EntityManager, xform.Coordinates, 2f)) continue; + + notFound = false; + partList.Add(part); + } + + if (notFound) + { + _popupSystem.PopupEntity(Loc.GetString("reassemble-fail"), uid, Filter.Entities(uid)); + return false; + } + + return true; + } + + private void ReassembleComplete(EntityUid uid, BodyReassembleComponent component, ReassembleCompleteEvent args) + { + component.CancelToken = null; + + if (component.DNA == null) + return; + + // Creates the new entity and transfers the mind component + var speciesProto = _prototype.Index(component.DNA.Value.Profile.Species).Prototype; + var mob = EntityManager.SpawnEntity(speciesProto, EntityManager.GetComponent(component.Owner).MapPosition); + + _humanoidAppearance.UpdateFromProfile(mob, component.DNA.Value.Profile); + MetaData(mob).EntityName = component.DNA.Value.Profile.Name; + + if (TryComp(uid, out var mindcomp) && mindcomp.Mind != null) + mindcomp.Mind.TransferTo(mob); + + // Cleans up all the body part pieces + foreach (var entity in args.PartList) + { + EntityManager.DeleteEntity(entity); + } + + _popupSystem.PopupEntity(Loc.GetString("reassemble-success", ("user", mob)), mob, Filter.Entities(mob)); + } + + /// + /// Called before the skeleton entity is gibbed in order to save + /// the dna for reassembly later + /// + /// the entity that the player will transfer to + /// the entity whose DNA is being saved + private void UpdateDNAEntry(EntityUid uid, EntityUid body) + { + if (!TryComp(uid, out var skelBodyComp) || !TryComp(body, out var mindcomp)) + return; + + if (mindcomp.Mind == null) + return; + + if (mindcomp.Mind.UserId == null) + return; + + var profile = (HumanoidCharacterProfile) _prefsManager.GetPreferences(mindcomp.Mind.UserId.Value).SelectedCharacter; + skelBodyComp.DNA = new ClonerDNAEntry(mindcomp.Mind, profile); + } + + private sealed class ReassembleCompleteEvent : EntityEventArgs + { + /// + /// The entity being reassembled + /// + public readonly EntityUid Uid; + + /// + /// The user performing the reassembly + /// + public readonly EntityUid User; + public readonly HashSet PartList; + + public ReassembleCompleteEvent(EntityUid uid, EntityUid user, HashSet partList) + { + Uid = uid; + User = user; + PartList = partList; + } + } + + private sealed class ReassembleCancelledEvent : EntityEventArgs {} + } +} + +public sealed class ReassembleActionEvent : InstantActionEvent { } diff --git a/Content.Server/Cloning/CloningSystem.cs b/Content.Server/Cloning/CloningSystem.cs index 381094f4d2..dc56b6738e 100644 --- a/Content.Server/Cloning/CloningSystem.cs +++ b/Content.Server/Cloning/CloningSystem.cs @@ -137,7 +137,7 @@ namespace Content.Server.Cloning // For example, GameTicker should be using this, and this should be using ICharacterProfile rather than HumanoidCharacterProfile. // It should carry a reference or copy of itself with the mobs that it affects. // See TODO in MedicalScannerComponent. - struct ClonerDNAEntry { + public struct ClonerDNAEntry { public Mind.Mind Mind; public HumanoidCharacterProfile Profile; diff --git a/Content.Shared/Body/Components/SharedBodyComponent.cs b/Content.Shared/Body/Components/SharedBodyComponent.cs index a73fdf620c..9598784484 100644 --- a/Content.Shared/Body/Components/SharedBodyComponent.cs +++ b/Content.Shared/Body/Components/SharedBodyComponent.cs @@ -394,15 +394,19 @@ namespace Content.Shared.Body.Components } } - public virtual void Gib(bool gibParts = false) + public virtual HashSet Gib(bool gibParts = false) { + var gibs = new HashSet(); foreach (var part in SlotParts.Keys) { + gibs.Add(part.Owner); RemovePart(part); if (gibParts) part.Gib(); } + + return gibs; } } diff --git a/Content.Shared/Body/Part/BodyPartCompatibility.cs b/Content.Shared/Body/Part/BodyPartCompatibility.cs index 91a24819cf..1af9d20148 100644 --- a/Content.Shared/Body/Part/BodyPartCompatibility.cs +++ b/Content.Shared/Body/Part/BodyPartCompatibility.cs @@ -1,4 +1,4 @@ -using System; +using System; using Content.Shared.Body.Components; using Robust.Shared.Serialization; @@ -14,6 +14,6 @@ namespace Content.Shared.Body.Part Universal = 0, Biological, Mechanical, - Slime + Slime, } } diff --git a/Content.Shared/Species/SpeciesPrototype.cs b/Content.Shared/Species/SpeciesPrototype.cs index fd0fab0dda..e4221c75d6 100644 --- a/Content.Shared/Species/SpeciesPrototype.cs +++ b/Content.Shared/Species/SpeciesPrototype.cs @@ -49,4 +49,5 @@ public enum SpeciesSkinColor { HumanToned, Hues, + TintedHues, //This gives a color tint to a humanoid's skin (10% saturation with full hue range). } diff --git a/Resources/Audio/Effects/bone_rattle.ogg b/Resources/Audio/Effects/bone_rattle.ogg new file mode 100644 index 0000000000..a4f25ff2cd Binary files /dev/null and b/Resources/Audio/Effects/bone_rattle.ogg differ diff --git a/Resources/Audio/Effects/license.txt b/Resources/Audio/Effects/license.txt deleted file mode 100644 index 5f63492667..0000000000 --- a/Resources/Audio/Effects/license.txt +++ /dev/null @@ -1,27 +0,0 @@ -adminhelp.ogg taken from https://github.com/tgstation/tgstation/blob/d775e1ac804eb9d0259573f5f29a18d320c97ef3/sound/effects/adminhelp.ogg - (available in master, therefore see master license: "All assets including icons and sound are under a Creative Commons 3.0 BY-SA license unless otherwise indicated.") - "Changed the adminhelpsound to some creative commons sound I pinched. Until somebody can get a better one. I'm sick of MAAAAAAAAOOOOOOW." - Actual source is https://freesound.org/people/martian/sounds/19261/ (CC0) - The sound had been reversed and the volume altered. - -hit_kick.ogg is made by Taira Komori -(https://taira-komori.jpn.org/freesounden.html) - -fire.ogg taken and edited from https://freesound.org/people/raremess/sounds/222557/ - -holy.ogg taken from https://freesound.org/people/random_intruder/sounds/392172/ and edited - -poster_broken.ogg taken from https://github.com/tgstation/tgstation/blob/2834383245d2129a106acef3afd17b81e1e64777/sound/items/poster_ripped.ogg - -poster_being_set.ogg taken from https://github.com/tgstation/tgstation/blob/2834383245d2129a106acef3afd17b81e1e64777/sound/items/poster_ripped.ogg - -saw.ogg taken from https://freesound.org/people/domiscz/sounds/461728/ and clipped - CC0-1.0 -*It's actually an angle grinder - -smoke.ogg taken from https://github.com/tgstation/tgstation/blob/a5d362ce84e4f0c61026236d5ec84d3c81553664/sound/effects/smoke.ogg - -voteding.ogg taken from "Bike, Bell Ding, Single, 01-01.wav" by InspectorJ (www.jshaw.co.uk) of Freesound.org at https://freesound.org/people/InspectorJ/sounds/484344/ under CC BY 3.0. The volume has been reduced. - -minibombcountdown.ogg taken from Thomas Evdokimoff at https://freesound.org/people/thomas_evdokimoff/sounds/202193/ - -bite.ogg take from https://github.com/tgstation/tgstation/commit/d4f678a1772007ff8d7eddd21cf7218c8e07bfc0 diff --git a/Resources/Audio/Effects/licenses.txt b/Resources/Audio/Effects/licenses.txt index 6a6c0f662c..ce352cf03d 100644 --- a/Resources/Audio/Effects/licenses.txt +++ b/Resources/Audio/Effects/licenses.txt @@ -1 +1,31 @@ +adminhelp.ogg taken from https://github.com/tgstation/tgstation/blob/d775e1ac804eb9d0259573f5f29a18d320c97ef3/sound/effects/adminhelp.ogg + (available in master, therefore see master license: "All assets including icons and sound are under a Creative Commons 3.0 BY-SA license unless otherwise indicated.") + "Changed the adminhelpsound to some creative commons sound I pinched. Until somebody can get a better one. I'm sick of MAAAAAAAAOOOOOOW." + Actual source is https://freesound.org/people/martian/sounds/19261/ (CC0) + The sound had been reversed and the volume altered. + +hit_kick.ogg is made by Taira Komori +(https://taira-komori.jpn.org/freesounden.html) + +fire.ogg taken and edited from https://freesound.org/people/raremess/sounds/222557/ + +holy.ogg taken from https://freesound.org/people/random_intruder/sounds/392172/ and edited + hug.ogg taken from /tg/station at https://github.com/tgstation/tgstation/blob/e6624ac8aed150668bb4e61c3a48372cc757bfd4/sound/weapons/thudswoosh.ogg - Under license: CC BY-SA 3.0 + +poster_broken.ogg taken from https://github.com/tgstation/tgstation/blob/2834383245d2129a106acef3afd17b81e1e64777/sound/items/poster_ripped.ogg + +poster_being_set.ogg taken from https://github.com/tgstation/tgstation/blob/2834383245d2129a106acef3afd17b81e1e64777/sound/items/poster_ripped.ogg + +saw.ogg taken from https://freesound.org/people/domiscz/sounds/461728/ and clipped - CC0-1.0 +*It's actually an angle grinder + +smoke.ogg taken from https://github.com/tgstation/tgstation/blob/a5d362ce84e4f0c61026236d5ec84d3c81553664/sound/effects/smoke.ogg + +voteding.ogg taken from "Bike, Bell Ding, Single, 01-01.wav" by InspectorJ (www.jshaw.co.uk) of Freesound.org at https://freesound.org/people/InspectorJ/sounds/484344/ under CC BY 3.0. The volume has been reduced. + +minibombcountdown.ogg taken from Thomas Evdokimoff at https://freesound.org/people/thomas_evdokimoff/sounds/202193/ + +bite.ogg take from https://github.com/tgstation/tgstation/commit/d4f678a1772007ff8d7eddd21cf7218c8e07bfc0 + +bone_rattle.ogg licensed under CC0 1.0 and taken from spookymodem at https://freesound.org/people/spookymodem/sounds/202102/ diff --git a/Resources/Audio/Voice/Skeleton/license.txt b/Resources/Audio/Voice/Skeleton/license.txt new file mode 100644 index 0000000000..b057ff96e3 --- /dev/null +++ b/Resources/Audio/Voice/Skeleton/license.txt @@ -0,0 +1 @@ +skeleton_scream.ogg licensed under CC0 1.0 taken from AntumDeluge at https://freesound.org/people/AntumDeluge/sounds/188034/ diff --git a/Resources/Audio/Voice/Skeleton/skeleton_scream.ogg b/Resources/Audio/Voice/Skeleton/skeleton_scream.ogg new file mode 100644 index 0000000000..12bb0b4f42 Binary files /dev/null and b/Resources/Audio/Voice/Skeleton/skeleton_scream.ogg differ diff --git a/Resources/Locale/en-US/body/behavior/reassemble.ftl b/Resources/Locale/en-US/body/behavior/reassemble.ftl new file mode 100644 index 0000000000..b13ac67ad0 --- /dev/null +++ b/Resources/Locale/en-US/body/behavior/reassemble.ftl @@ -0,0 +1,4 @@ +reassemble-action = Reassemble +reassemble-description = Reassemble the pieces of your body. +reassemble-fail = Parts are missing! +reassemble-success = {CAPITALIZE(THE($user))} was put back together. \ No newline at end of file diff --git a/Resources/Prototypes/Body/Parts/skeleton.yml b/Resources/Prototypes/Body/Parts/skeleton.yml new file mode 100644 index 0000000000..f40ed648c5 --- /dev/null +++ b/Resources/Prototypes/Body/Parts/skeleton.yml @@ -0,0 +1,231 @@ +# TODO BODY: Part damage +- type: entity + id: PartSkeleton + parent: BaseItem + name: "skeleton body part" + abstract: true + components: + - type: Damageable + damageContainer: Biological + +- type: entity + id: TorsoSkeleton + name: "skeleton torso" + parent: PartSkeleton + components: + - type: Sprite + netsync: false + sprite: Mobs/Species/Skeleton/parts.rsi + state: "torso_m" + - type: Icon + sprite: Mobs/Species/Skeleton/parts.rsi + state: "torso_m" + - type: BodyPart + partType: Torso + size: 14 + compatibility: Biological +# criticalThreshold: 100 +# deadThreshold: 150 + +- type: entity + id: HeadSkeleton + name: "skull" + description: Alas poor Yorick... + parent: PartSkeleton + components: + - type: Sprite + netsync: false + sprite: Mobs/Species/Skeleton/parts.rsi + state: "skull_icon" + - type: Icon + sprite: Mobs/Species/Skeleton/parts.rsi + state: "skull_icon" + - type: BodyPart + partType: Head + size: 7 + compatibility: Biological + #Unique stuff the skull has for one a skelly gets "boned" nyeheheh + - type: BodyReassemble + action: + icon: Mobs/Species/Skeleton/parts.rsi/full.png + name: reassemble-action + description: reassemble-description + itemIconStyle: NoItem + event: !type:ReassembleActionEvent + - type: Input + context: "human" + - type: Speech + - type: Vocal + - type: Emoting + - type: Grammar + attributes: + proper: true + - type: Examiner + - type: GhostRadio + - type: DoAfter + - type: Actions + - type: MobState + thresholds: + 0: !type:NormalMobState {} + # criticalThreshold: 50 + # deadThreshold: 120 + + + +- type: entity + id: LeftArmSkeleton + name: "left skeleton arm" + parent: PartSkeleton + components: + - type: Sprite + netsync: false + sprite: Mobs/Species/Skeleton/parts.rsi + state: "l_arm" + - type: Icon + sprite: Mobs/Species/Skeleton/parts.rsi + state: "l_arm" + - type: BodyPart + partType: Arm + size: 5 + compatibility: Biological + symmetry: Left + # criticalThreshold: 40 + # deadThreshold: 80 + +- type: entity + id: RightArmSkeleton + name: "right skeleton arm" + parent: PartSkeleton + components: + - type: Sprite + netsync: false + sprite: Mobs/Species/Skeleton/parts.rsi + state: "r_arm" + - type: Icon + sprite: Mobs/Species/Skeleton/parts.rsi + state: "r_arm" + - type: BodyPart + partType: Arm + size: 5 + compatibility: Biological + symmetry: Right + # criticalThreshold: 40 + # deadThreshold: 80 + +- type: entity + id: LeftHandSkeleton + name: "left skeleton hand" + parent: PartSkeleton + components: + - type: Sprite + netsync: false + sprite: Mobs/Species/Skeleton/parts.rsi + state: "l_hand" + - type: Icon + sprite: Mobs/Species/Skeleton/parts.rsi + state: "l_hand" + - type: BodyPart + partType: Hand + size: 3 + compatibility: Biological + symmetry: Left + # criticalThreshold: 30 + # deadThreshold: 60 + +- type: entity + id: RightHandSkeleton + name: "right skeleton hand" + parent: PartSkeleton + components: + - type: Sprite + netsync: false + sprite: Mobs/Species/Skeleton/parts.rsi + state: "r_hand" + - type: Icon + sprite: Mobs/Species/Skeleton/parts.rsi + state: "r_hand" + - type: BodyPart + partType: Hand + size: 3 + compatibility: Biological + symmetry: Right + # criticalThreshold: 30 + # deadThreshold: 60 + +- type: entity + id: LeftLegSkeleton + name: "left skeleton leg" + parent: PartSkeleton + components: + - type: Sprite + netsync: false + sprite: Mobs/Species/Skeleton/parts.rsi + state: "l_leg" + - type: Icon + sprite: Mobs/Species/Skeleton/parts.rsi + state: "l_leg" + - type: BodyPart + partType: Leg + size: 6 + compatibility: Biological + symmetry: Left + +- type: entity + id: RightLegSkeleton + name: "right skeleton leg" + parent: PartSkeleton + components: + - type: Sprite + netsync: false + sprite: Mobs/Species/Skeleton/parts.rsi + state: "r_leg" + - type: Icon + sprite: Mobs/Species/Skeleton/parts.rsi + state: "r_leg" + - type: BodyPart + partType: Leg + size: 6 + compatibility: Biological + symmetry: Right + # criticalThreshold: 45 + # deadThreshold: 90 + +- type: entity + id: LeftFootSkeleton + name: "left skeleton foot" + parent: PartSkeleton + components: + - type: Sprite + netsync: false + sprite: Mobs/Species/Skeleton/parts.rsi + state: "l_foot" + - type: Icon + sprite: Mobs/Species/Skeleton/parts.rsi + state: "l_foot" + - type: BodyPart + partType: Foot + size: 2 + compatibility: Biological + symmetry: Left +# criticalThreshold: 30 +# deadThreshold: 60 + +- type: entity + id: RightFootSkeleton + name: "right skeleton foot" + parent: PartSkeleton + components: + - type: Sprite + netsync: false + sprite: Mobs/Species/Skeleton/parts.rsi + state: "r_foot" + - type: Icon + sprite: Mobs/Species/Skeleton/parts.rsi + state: "r_foot" + - type: BodyPart + partType: Foot + size: 2 + compatibility: Biological + symmetry: Right +# criticalThreshold: 30 +# deadThreshold: 60 diff --git a/Resources/Prototypes/Body/Presets/skeleton.yml b/Resources/Prototypes/Body/Presets/skeleton.yml new file mode 100644 index 0000000000..07d372c2eb --- /dev/null +++ b/Resources/Prototypes/Body/Presets/skeleton.yml @@ -0,0 +1,14 @@ +- type: bodyPreset + name: "skeleton" + id: SkeletonPreset + partIDs: + head: HeadSkeleton + torso: TorsoSkeleton + right arm: RightArmSkeleton + left arm: LeftArmSkeleton + right hand: RightHandSkeleton + left hand: LeftHandSkeleton + right leg: RightLegSkeleton + left leg: LeftLegSkeleton + right foot: RightFootSkeleton + left foot: LeftFootSkeleton diff --git a/Resources/Prototypes/Damage/modifier_sets.yml b/Resources/Prototypes/Damage/modifier_sets.yml index 537655660c..0e9ea58a4a 100644 --- a/Resources/Prototypes/Damage/modifier_sets.yml +++ b/Resources/Prototypes/Damage/modifier_sets.yml @@ -118,6 +118,20 @@ Heat: 3.0 Poison: 0.0 +# immune to everything except physical and heat damage +- type: damageModifierSet + id: Skeleton + coefficients: + Blunt: 2.5 + Slash: 1.5 + Piercing: 1.2 + Cold: 0.0 + Poison: 0.0 + Radiation: 0.0 + Asphyxiation: 0.0 + Bloodloss: 0.0 + Cellular: 0.0 + # Represents which damage types should be modified # in relation to how they cause bloodloss damage. - type: damageModifierSet diff --git a/Resources/Prototypes/Entities/Mobs/Player/skeleton.yml b/Resources/Prototypes/Entities/Mobs/Player/skeleton.yml new file mode 100644 index 0000000000..f6d140151c --- /dev/null +++ b/Resources/Prototypes/Entities/Mobs/Player/skeleton.yml @@ -0,0 +1,28 @@ +- type: entity + save: false + parent: MobSkeletonPersonBase + id: MobSkeletonPerson + components: + - type: InteractionPopup + successChance: 1 + interactSuccessString: hugging-success-generic + interactSuccessSound: /Audio/Effects/hug.ogg + messagePerceivedByOthers: hugging-success-generic-others + - type: Mind + showExamineInfo: true + - type: Input + context: "human" + - type: PlayerMobMover + - type: PlayerInputMover + - type: Vocal + maleScream: /Audio/Voice/Skeleton/skeleton_scream.ogg + femaleScream: /Audio/Voice/Skeleton/skeleton_scream.ogg + - type: Alerts + - type: Actions + - type: Eye + - type: CameraRecoil + - type: Examiner + - type: CanHostGuardian + - type: AiFactionTag + factions: + - NanoTrasen diff --git a/Resources/Prototypes/Entities/Mobs/Species/skeleton.yml b/Resources/Prototypes/Entities/Mobs/Species/skeleton.yml new file mode 100644 index 0000000000..9896bab23f --- /dev/null +++ b/Resources/Prototypes/Entities/Mobs/Species/skeleton.yml @@ -0,0 +1,352 @@ +- type: entity + save: false + name: Urist McSkelly + id: MobSkeletonPersonBase + description: A miserable pile of bones. + noSpawn: true + components: + - type: Tag + tags: + - CanPilot + - FootstepSound + - DoorBumpOpener + - type: Reactive + groups: + Flammable: [ Touch ] + Extinguish: [ Touch ] + Acidic: [Touch, Ingestion] + reactions: + - reagents: [Water, SpaceCleaner] + methods: [Touch] + effects: + - !type:WashCreamPieReaction + - type: Flashable + - type: Hands + - type: MovementSpeedModifier + - type: MovedByPressure + - type: Markings + - type: Barotrauma + damage: + types: + Blunt: 0.7 #per second, scales with pressure and other constants. + - type: DamageOnHighSpeedImpact + damage: + types: + Blunt: 5 + soundHit: + path: /Audio/Effects/hit_kick.ogg + # Organs + - type: HealthExaminable + examinableTypes: + - Blunt + - Slash + - Piercing + - Heat + - Shock + - type: StatusEffects + allowed: + - Stun + - KnockedDown + - SlowedDown + - Stutter + - Electrocution + # Other + - type: Inventory + - type: Clickable + - type: InteractionOutline + - type: Icon + sprite: Mobs/Species/Skeleton/parts.rsi + state: full + - type: Sprite + netsync: false + noRot: true + drawdepth: Mobs + layers: + - map: [ "enum.HumanoidVisualLayers.Chest" ] + color: "#ffffff" + sprite: Mobs/Species/Skeleton/parts.rsi + state: torso_m + - map: [ "enum.HumanoidVisualLayers.Head" ] + color: "#ffffff" + sprite: Mobs/Species/Skeleton/parts.rsi + state: head_m + - map: [ "enum.HumanoidVisualLayers.Eyes" ] + color: "#008800" + sprite: Mobs/Customization/eyes.rsi + state: eyes + - map: [ "enum.HumanoidVisualLayers.RArm" ] + color: "#ffffff" + sprite: Mobs/Species/Skeleton/parts.rsi + state: r_arm + - map: [ "enum.HumanoidVisualLayers.LArm" ] + color: "#ffffff" + sprite: Mobs/Species/Skeleton/parts.rsi + state: l_arm + - map: [ "enum.HumanoidVisualLayers.RLeg" ] + color: "#ffffff" + sprite: Mobs/Species/Skeleton/parts.rsi + state: r_leg + - map: [ "enum.HumanoidVisualLayers.LLeg" ] + color: "#ffffff" + sprite: Mobs/Species/Skeleton/parts.rsi + state: l_leg + - shader: StencilClear + sprite: Mobs/Species/Skeleton/parts.rsi + state: l_leg + - shader: StencilMask + map: [ "enum.HumanoidVisualLayers.StencilMask" ] + sprite: Mobs/Customization/masking_helpers.rsi + state: female_full + visible: false + - map: [ "jumpsuit" ] + shader: StencilDraw + - map: [ "enum.HumanoidVisualLayers.LHand" ] + color: "#ffffff" + sprite: Mobs/Species/Skeleton/parts.rsi + state: l_hand + - map: [ "enum.HumanoidVisualLayers.RHand" ] + color: "#ffffff" + sprite: Mobs/Species/Skeleton/parts.rsi + state: r_hand + - map: [ "enum.HumanoidVisualLayers.LFoot" ] + color: "#ffffff" + sprite: Mobs/Species/Skeleton/parts.rsi + state: l_foot + - map: [ "enum.HumanoidVisualLayers.RFoot" ] + color: "#ffffff" + sprite: Mobs/Species/Skeleton/parts.rsi + state: r_foot + - map: [ "enum.HumanoidVisualLayers.Handcuffs" ] + color: "#ffffff" + sprite: Objects/Misc/handcuffs.rsi + state: body-overlay-2 + visible: false + - map: [ "id" ] + - map: [ "gloves" ] + - map: [ "shoes" ] + - map: [ "ears" ] + - map: [ "outerClothing" ] + - map: [ "eyes" ] + - map: [ "belt" ] + - map: [ "neck" ] + - map: [ "back" ] + - map: [ "mask" ] + - map: [ "head" ] + - map: [ "pocket1" ] + - map: [ "pocket2" ] + - type: Physics + bodyType: KinematicController + - type: Fixtures + fixtures: # TODO: This needs a second fixture just for mob collisions. + - shape: + !type:PhysShapeCircle + radius: 0.35 + mass: 70 + restitution: 0.0 + mask: + - MobMask + layer: + - MobLayer + - type: AtmosExposed + - type: Flammable + fireSpread: true + canResistFire: true + damage: + types: + Heat: 1 #per second, scales with number of fire 'stacks' + - type: Temperature + heatDamageThreshold: 360 + coldDamageThreshold: 260 + currentTemperature: 310.15 + specificHeat: 42 + coldDamage: + types: + Cold : 1 #per second, scales with temperature & other constants + heatDamage: + types: + Heat : 1 #per second, scales with temperature & other constants + - type: HumanoidAppearance + - type: Body + template: HumanoidTemplate + preset: SkeletonPreset + gibSound: /Audio/Effects/bone_rattle.ogg + - type: Damageable + damageContainer: Biological + damageModifierSet: Skeleton + - type: ThermalRegulator + metabolismHeat: 800 + radiatedHeat: 100 + implicitHeatRegulation: 500 + sweatHeatRegulation: 2000 + shiveringHeatRegulation: 2000 + normalBodyTemperature: 310.15 + thermalRegulationTemperatureThreshold: 25 + - type: Internals + - type: MobState + thresholds: + 0: !type:NormalMobState {} + 100: !type:DeadMobState {} + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:GibBehavior { } + - type: SlowOnDamage #modified speeds because they're so weak + speedModifierThresholds: + 60: 0.9 + 80: 0.7 + 90: 0.5 + - type: HeatResistance + - type: Appearance + visuals: + - type: RotationVisualizer + - type: BuckleVisualizer + - type: FireVisualizer + sprite: Mobs/Effects/onfire.rsi + normalState: Generic_mob_burning + alternateState: Standing + fireStackAlternateState: 3 + - type: CreamPiedVisualizer + state: creampie_human + - type: DamageVisualizer + thresholds: [20, 40, 100] + targetLayers: + - "enum.HumanoidVisualLayers.Chest" + - "enum.HumanoidVisualLayers.Head" + - "enum.HumanoidVisualLayers.LArm" + - "enum.HumanoidVisualLayers.LLeg" + - "enum.HumanoidVisualLayers.RArm" + - "enum.HumanoidVisualLayers.RLeg" + damageOverlayGroups: + Brute: + sprite: Mobs/Effects/brute_damage.rsi + color: "#8a8a8a" + Burn: + sprite: Mobs/Effects/burn_damage.rsi + - type: CombatMode + - type: Climbing + - type: Cuffable + - type: CharacterInfo + - type: AnimationPlayer + - type: Buckle + - type: UnarmedCombat + hitSound: + collection: Punch + range: 0.8 + arcwidth: 30 + arc: fist + damage: + types: + Blunt: 5 + - type: Pullable + - type: DoAfter + - type: CreamPied + - type: Stripping + - type: Strippable + - type: UserInterface + interfaces: + - key: enum.StrippingUiKey.Key + type: StrippableBoundUserInterface + - type: Puller + - type: Speech + - type: Vocal + - type: Emoting + - type: Grammar + attributes: + proper: true + - type: StandingState + +- type: entity + save: false + name: Urist McSkelly + parent: MobHumanDummy + id: MobSkeletonPersonDummy + noSpawn: true + description: A dummy skeleton meant to be used in character setup. + components: + - type: Sprite + netsync: false + drawdepth: Mobs + layers: + - map: [ "enum.HumanoidVisualLayers.Chest" ] + color: "#b8b8b8" + sprite: Mobs/Species/Skeleton/parts.rsi + state: torso_m + - map: [ "enum.HumanoidVisualLayers.Head" ] + color: "#b8b8b8" + sprite: Mobs/Species/Skeleton/parts.rsi + state: head_m + - map: [ "enum.HumanoidVisualLayers.Eyes" ] + color: "#008800" + sprite: Mobs/Customization/eyes.rsi + state: eyes + - map: [ "enum.HumanoidVisualLayers.RArm" ] + color: "#b8b8b8" + sprite: Mobs/Species/Skeleton/parts.rsi + state: r_arm + - map: [ "enum.HumanoidVisualLayers.LArm" ] + color: "#b8b8b8" + sprite: Mobs/Species/Skeleton/parts.rsi + state: l_arm + - map: [ "enum.HumanoidVisualLayers.RLeg" ] + color: "#b8b8b8" + sprite: Mobs/Species/Skeleton/parts.rsi + state: r_leg + - map: [ "enum.HumanoidVisualLayers.LLeg" ] + color: "#b8b8b8" + sprite: Mobs/Species/Skeleton/parts.rsi + state: l_leg + - shader: StencilClear + sprite: Mobs/Species/Skeleton/parts.rsi + state: l_leg + - shader: StencilMask + map: [ "enum.HumanoidVisualLayers.StencilMask" ] + sprite: Mobs/Customization/masking_helpers.rsi + state: female_full + visible: false + - map: [ "jumpsuit" ] + shader: StencilDraw + - map: [ "enum.HumanoidVisualLayers.LHand" ] + color: "#b8b8b8" + sprite: Mobs/Species/Skeleton/parts.rsi + state: l_hand + - map: [ "enum.HumanoidVisualLayers.RHand" ] + color: "#b8b8b8" + sprite: Mobs/Species/Skeleton/parts.rsi + state: r_hand + - map: [ "enum.HumanoidVisualLayers.LFoot" ] + color: "#b8b8b8" + sprite: Mobs/Species/Skeleton/parts.rsi + state: l_foot + - map: [ "enum.HumanoidVisualLayers.RFoot" ] + color: "#b8b8b8" + sprite: Mobs/Species/Skeleton/parts.rsi + state: r_foot + - map: [ "enum.HumanoidVisualLayers.Handcuffs" ] + color: "#ffffff" + sprite: Objects/Misc/handcuffs.rsi + state: body-overlay-2 + visible: false + - map: [ "id" ] + - map: [ "gloves" ] + - map: [ "shoes" ] + - map: [ "ears" ] + - map: [ "outerClothing" ] + - map: [ "eyes" ] + - map: [ "belt" ] + - map: [ "neck" ] + - map: [ "back" ] + - map: [ "enum.HumanoidVisualLayers.FacialHair" ] + state: shaved + sprite: Mobs/Customization/human_facial_hair.rsi + color: '#FFFFFF80' + - map: [ "enum.HumanoidVisualLayers.Hair" ] + state: bald + sprite: Mobs/Customization/human_hair.rsi + color: '#FFFFFF80' + - map: [ "mask" ] + - map: [ "head" ] + - map: [ "pocket1" ] + - map: [ "pocket2" ] diff --git a/Resources/Prototypes/species.yml b/Resources/Prototypes/species.yml index 65c794acf2..832a34bd3a 100644 --- a/Resources/Prototypes/species.yml +++ b/Resources/Prototypes/species.yml @@ -30,6 +30,14 @@ dollPrototype: MobSlimePersonDummy skinColoration: Hues +- type: species + id: Skeleton + name: Skeleton + roundStart: false #unspooky, change it in october + prototype: MobSkeletonPerson + dollPrototype: MobSkeletonPersonDummy + skinColoration: TintedHues + #- type: species # id: Vox # name: Vox @@ -37,4 +45,3 @@ # prototype: MobVox # dollPrototype: MobVoxDummy # skinColoration: Hues - diff --git a/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/full.png b/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/full.png new file mode 100644 index 0000000000..e457b7018e Binary files /dev/null and b/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/full.png differ diff --git a/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/head_f.png b/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/head_f.png new file mode 100644 index 0000000000..ee8bd9978b Binary files /dev/null and b/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/head_f.png differ diff --git a/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/head_m.png b/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/head_m.png new file mode 100644 index 0000000000..ee8bd9978b Binary files /dev/null and b/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/head_m.png differ diff --git a/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/l_arm.png b/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/l_arm.png new file mode 100644 index 0000000000..a0764cc340 Binary files /dev/null and b/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/l_arm.png differ diff --git a/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/l_foot.png b/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/l_foot.png new file mode 100644 index 0000000000..eba52e75d8 Binary files /dev/null and b/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/l_foot.png differ diff --git a/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/l_hand.png b/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/l_hand.png new file mode 100644 index 0000000000..f60248ad72 Binary files /dev/null and b/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/l_hand.png differ diff --git a/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/l_leg.png b/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/l_leg.png new file mode 100644 index 0000000000..b5d60ddd30 Binary files /dev/null and b/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/l_leg.png differ diff --git a/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/meta.json b/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/meta.json new file mode 100644 index 0000000000..78e0a89e04 --- /dev/null +++ b/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/meta.json @@ -0,0 +1,66 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Created by EmoGarbage", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "full" + }, + { + "name": "head_f", + "directions": 4 + }, + { + "name": "head_m", + "directions": 4 + }, + { + "name": "l_arm", + "directions": 4 + }, + { + "name": "l_foot", + "directions": 4 + }, + { + "name": "l_hand", + "directions": 4 + }, + { + "name": "l_leg", + "directions": 4 + }, + { + "name": "r_arm", + "directions": 4 + }, + { + "name": "r_foot", + "directions": 4 + }, + { + "name": "r_hand", + "directions": 4 + }, + { + "name": "r_leg", + "directions": 4 + }, + { + "name": "skull_icon", + "directions": 1 + }, + { + "name": "torso_f", + "directions": 4 + }, + { + "name": "torso_m", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/r_arm.png b/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/r_arm.png new file mode 100644 index 0000000000..6efc6bd3af Binary files /dev/null and b/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/r_arm.png differ diff --git a/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/r_foot.png b/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/r_foot.png new file mode 100644 index 0000000000..86f97661fc Binary files /dev/null and b/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/r_foot.png differ diff --git a/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/r_hand.png b/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/r_hand.png new file mode 100644 index 0000000000..09fad6c219 Binary files /dev/null and b/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/r_hand.png differ diff --git a/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/r_leg.png b/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/r_leg.png new file mode 100644 index 0000000000..4319d201d5 Binary files /dev/null and b/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/r_leg.png differ diff --git a/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/skull_icon.png b/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/skull_icon.png new file mode 100644 index 0000000000..8d86abc7aa Binary files /dev/null and b/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/skull_icon.png differ diff --git a/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/torso_f.png b/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/torso_f.png new file mode 100644 index 0000000000..6eb022a096 Binary files /dev/null and b/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/torso_f.png differ diff --git a/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/torso_m.png b/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/torso_m.png new file mode 100644 index 0000000000..22f860dc91 Binary files /dev/null and b/Resources/Textures/Mobs/Species/Skeleton/parts.rsi/torso_m.png differ