diff --git a/Content.Server/_Arcane/Flicking/TongueFlickingSystem.cs b/Content.Server/_Arcane/Flicking/TongueFlickingSystem.cs new file mode 100644 index 00000000000..bdcc1c99df6 --- /dev/null +++ b/Content.Server/_Arcane/Flicking/TongueFlickingSystem.cs @@ -0,0 +1,137 @@ +// SPDX-FileCopyrightText: 2024 ArchPigeon +// SPDX-FileCopyrightText: 2024 DrSmugleaf +// SPDX-FileCopyrightText: 2024 Krunklehorn <42424291+Krunklehorn@users.noreply.github.com> +// SPDX-FileCopyrightText: 2024 Morb <14136326+Morb0@users.noreply.github.com> +// SPDX-FileCopyrightText: 2024 metalgearsloth +// SPDX-FileCopyrightText: 2025 Aiden <28298836+Aidenkrz@users.noreply.github.com> +// +// SPDX-License-Identifier: MIT + +using Content.Server.Actions; +using Content.Server.Humanoid; +using Content.Shared.Humanoid; +using Content.Shared.Humanoid.Markings; +using Content.Shared.Mobs; +using Content.Shared.Toggleable; +using Content.Shared.Flicking; +using Robust.Shared.Prototypes; + +namespace Content.Server.Flicking; + +public sealed partial class TongueFlickingSystem : EntitySystem +{ + [Dependency] private ActionsSystem _actions = default!; + [Dependency] private HumanoidAppearanceSystem _humanoidAppearance = default!; + [Dependency] private IPrototypeManager _prototype = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnTongueFlickingStartup); + SubscribeLocalEvent(OnTongueFlickingShutdown); + SubscribeLocalEvent(OnTongueFlickingToggle); + SubscribeLocalEvent(OnMobStateChanged); + SubscribeLocalEvent(OnHumanoidStartup); + } + + private void OnHumanoidStartup(EntityUid uid, HumanoidAppearanceComponent component, ComponentStartup args) + { + Robust.Shared.Timing.Timer.Spawn(100, () => + { + if (!TryComp(uid, out var humanoid)) + return; + + if (!humanoid.MarkingSet.TryGetCategory(MarkingCategories.Face, out var faceMarkings)) + return; + + foreach (var marking in faceMarkings) + { + if (marking.MarkingId.StartsWith("ForkedTongue")) + { + EnsureComp(uid); + return; + } + } + }); + } + + private void OnTongueFlickingStartup(EntityUid uid, TongueFlickingComponent component, ComponentStartup args) + { + _actions.AddAction(uid, ref component.ActionEntity, component.Action, uid); + } + + private void OnTongueFlickingShutdown(EntityUid uid, TongueFlickingComponent component, ComponentShutdown args) + { + _actions.RemoveAction(uid, component.ActionEntity); + } + + private void OnTongueFlickingToggle(EntityUid uid, TongueFlickingComponent component, ref ToggleActionEvent args) + { + if (args.Handled) + return; + + if (args.Action != component.ActionEntity) + return; + + if (TryToggleTongueFlicking(uid, tongueFlicking: component)) + args.Handled = true; + } + + private void OnMobStateChanged(EntityUid uid, TongueFlickingComponent component, MobStateChangedEvent args) + { + if (component.TongueOut) + TryToggleTongueFlicking(uid, tongueFlicking: component); + } + + public bool TryToggleTongueFlicking(EntityUid uid, TongueFlickingComponent? tongueFlicking = null, HumanoidAppearanceComponent? humanoid = null) + { + if (!Resolve(uid, ref tongueFlicking, ref humanoid)) + return false; + + if (!humanoid.MarkingSet.Markings.TryGetValue(MarkingCategories.Face, out var markings)) + return false; + + var tongueIndex = -1; + + for (var idx = 0; idx < markings.Count; idx++) + { + if (markings[idx].MarkingId.StartsWith("ForkedTongue")) + { + tongueIndex = idx; + break; + } + } + + if (tongueIndex == -1) + return false; + + var currentMarkingId = markings[tongueIndex].MarkingId; + string newMarkingId; + + if (!tongueFlicking.TongueOut) + { + newMarkingId = $"{currentMarkingId}{tongueFlicking.Suffix}"; + } + else + { + newMarkingId = currentMarkingId[..^tongueFlicking.Suffix.Length]; + } + + if (!_prototype.HasIndex(newMarkingId)) + return false; + + tongueFlicking.TongueOut = !tongueFlicking.TongueOut; + + _actions.SetToggled(tongueFlicking.ActionEntity, tongueFlicking.TongueOut); + + _humanoidAppearance.SetMarkingId( + uid, + MarkingCategories.Face, + tongueIndex, + newMarkingId, + humanoid: humanoid); + + return true; + } +} \ No newline at end of file diff --git a/Content.Shared/_Arcane/Flicking/TongueFlickingComponent.cs b/Content.Shared/_Arcane/Flicking/TongueFlickingComponent.cs new file mode 100644 index 00000000000..1bee17e5306 --- /dev/null +++ b/Content.Shared/_Arcane/Flicking/TongueFlickingComponent.cs @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: 2024 DrSmugleaf +// SPDX-FileCopyrightText: 2024 Kara +// SPDX-FileCopyrightText: 2024 Morb <14136326+Morb0@users.noreply.github.com> +// SPDX-FileCopyrightText: 2024 Piras314 +// SPDX-FileCopyrightText: 2024 metalgearsloth +// SPDX-FileCopyrightText: 2025 Aiden <28298836+Aidenkrz@users.noreply.github.com> +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Flicking; + +[RegisterComponent, NetworkedComponent] +public sealed partial class TongueFlickingComponent : Component +{ + [DataField] + public EntProtoId Action = "ActionToggleFlicking"; + + [DataField] + public EntityUid? ActionEntity; + + public string Suffix = "Flicking"; + + [DataField] + public bool TongueOut = false; +} \ No newline at end of file diff --git a/Resources/Locale/en-US/_Arcane/actions/tongue.ftl b/Resources/Locale/en-US/_Arcane/actions/tongue.ftl new file mode 100644 index 00000000000..9b0b8094bab --- /dev/null +++ b/Resources/Locale/en-US/_Arcane/actions/tongue.ftl @@ -0,0 +1,2 @@ +ent-ActionToggleFlicking = Flicking Tongue + .desc = Start or stop flicking your tongue. \ No newline at end of file diff --git a/Resources/Locale/en-US/_Arcane/markings/tongue.ftl b/Resources/Locale/en-US/_Arcane/markings/tongue.ftl new file mode 100644 index 00000000000..f38913f3306 --- /dev/null +++ b/Resources/Locale/en-US/_Arcane/markings/tongue.ftl @@ -0,0 +1 @@ +marking-ForkedTongue = Forked Tongue (Toggleable) \ No newline at end of file diff --git a/Resources/Locale/ru-RU/_Arcane/actions/tongue.ftl b/Resources/Locale/ru-RU/_Arcane/actions/tongue.ftl new file mode 100644 index 00000000000..2e0fef02af9 --- /dev/null +++ b/Resources/Locale/ru-RU/_Arcane/actions/tongue.ftl @@ -0,0 +1,2 @@ +ent-ActionToggleFlicking = Шевелить языком + .desc = Начать/перестать шевелить языком. \ No newline at end of file diff --git a/Resources/Locale/ru-RU/_Arcane/markings/tongue.ftl b/Resources/Locale/ru-RU/_Arcane/markings/tongue.ftl new file mode 100644 index 00000000000..2cd41476e84 --- /dev/null +++ b/Resources/Locale/ru-RU/_Arcane/markings/tongue.ftl @@ -0,0 +1 @@ +marking-ForkedTongue = Раздвоенный Язык (Переключаемый) \ No newline at end of file diff --git a/Resources/Prototypes/_Arcane/Actions/flicking.yml b/Resources/Prototypes/_Arcane/Actions/flicking.yml new file mode 100644 index 00000000000..9c33773a6c8 --- /dev/null +++ b/Resources/Prototypes/_Arcane/Actions/flicking.yml @@ -0,0 +1,12 @@ +- type: entity + parent: BaseToggleAction + id: ActionToggleFlicking + name: Flicking Tongue + description: Start or stop flicking your tongue. + components: + - type: Action + icon: { sprite: _Arcane/Mobs/Customization/forked_tongue.rsi, state: tongue_off } + iconOn: { sprite: _Arcane/Mobs/Customization/forked_tongue.rsi, state: tongue_on } + itemIconStyle: NoItem + useDelay: 1 + checkCanInteract: false \ No newline at end of file diff --git a/Resources/Prototypes/_Arcane/Mobs/Customization/reptilian.yml b/Resources/Prototypes/_Arcane/Mobs/Customization/reptilian.yml new file mode 100644 index 00000000000..376c087cd89 --- /dev/null +++ b/Resources/Prototypes/_Arcane/Mobs/Customization/reptilian.yml @@ -0,0 +1,17 @@ +- type: marking + id: ForkedTongue + bodyPart: Face + markingCategory: Face + speciesRestriction: [ Reptilian ] + sprites: + - sprite: _Arcane/Mobs/Customization/forked_tongue.rsi + state: no_tongue + +- type: marking + id: ForkedTongueFlicking + bodyPart: Face + markingCategory: Face + speciesRestriction: [ ] + sprites: + - sprite: _Arcane/Mobs/Customization/forked_tongue.rsi + state: forked_tongue \ No newline at end of file diff --git a/Resources/Textures/_Arcane/Mobs/Customization/forked_tongue.rsi/forked_tongue.png b/Resources/Textures/_Arcane/Mobs/Customization/forked_tongue.rsi/forked_tongue.png new file mode 100644 index 00000000000..903407cad58 Binary files /dev/null and b/Resources/Textures/_Arcane/Mobs/Customization/forked_tongue.rsi/forked_tongue.png differ diff --git a/Resources/Textures/_Arcane/Mobs/Customization/forked_tongue.rsi/meta.json b/Resources/Textures/_Arcane/Mobs/Customization/forked_tongue.rsi/meta.json new file mode 100644 index 00000000000..82969dedd38 --- /dev/null +++ b/Resources/Textures/_Arcane/Mobs/Customization/forked_tongue.rsi/meta.json @@ -0,0 +1,70 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Made by gardsnake (github/discord)", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "forked_tongue", + "directions": 4, + "delays": [ + [ + 0.250, + 0.125, + 0.125, + 0.125, + 0.125, + 0.125, + 0.125, + 0.250, + 2.250 + ], + [ + 0.250, + 0.125, + 0.125, + 0.125, + 0.125, + 0.125, + 0.125, + 0.250, + 2.250 + ], + [ + 0.250, + 0.125, + 0.125, + 0.125, + 0.125, + 0.125, + 0.125, + 0.250, + 2.250 + ], + [ + 0.250, + 0.125, + 0.125, + 0.125, + 0.125, + 0.125, + 0.125, + 0.250, + 2.250 + ] + ] + }, + { + "name": "no_tongue" + }, + { + "name": "tongue_on" + }, + { + "name": "tongue_off" + } + ] +} diff --git a/Resources/Textures/_Arcane/Mobs/Customization/forked_tongue.rsi/no_tongue.png b/Resources/Textures/_Arcane/Mobs/Customization/forked_tongue.rsi/no_tongue.png new file mode 100644 index 00000000000..7244b37f5c7 Binary files /dev/null and b/Resources/Textures/_Arcane/Mobs/Customization/forked_tongue.rsi/no_tongue.png differ diff --git a/Resources/Textures/_Arcane/Mobs/Customization/forked_tongue.rsi/tongue_off.png b/Resources/Textures/_Arcane/Mobs/Customization/forked_tongue.rsi/tongue_off.png new file mode 100644 index 00000000000..0797e19dead Binary files /dev/null and b/Resources/Textures/_Arcane/Mobs/Customization/forked_tongue.rsi/tongue_off.png differ diff --git a/Resources/Textures/_Arcane/Mobs/Customization/forked_tongue.rsi/tongue_on.png b/Resources/Textures/_Arcane/Mobs/Customization/forked_tongue.rsi/tongue_on.png new file mode 100644 index 00000000000..9f9cc91d67b Binary files /dev/null and b/Resources/Textures/_Arcane/Mobs/Customization/forked_tongue.rsi/tongue_on.png differ