From ea62f0dbc1fdc29168a0f154b02ea5fd5f994d55 Mon Sep 17 00:00:00 2001 From: Lily Autumn Date: Mon, 23 Mar 2026 16:03:32 +1300 Subject: [PATCH 1/4] Aphrodesiac Bite Ability~! --- .../Lobby/UI/HumanoidProfileEditor.xaml.cs | 1 + .../Traits/Abilities/AphrodesiacBiteSystem.cs | 48 ++++++++++++++++++ .../Abilities/AphrodesiacBiteComponent.cs | 22 ++++++++ Resources/Locale/en-US/_CS/traits.ftl | 3 ++ Resources/Prototypes/Traits/categories.yml | 28 ---------- .../_CS/Traits/Abilities/aphrodesiac_bite.yml | 26 ++++++++++ .../Prototypes/_CS/Traits/categories.yml | 32 ++++++++++++ .../_CS/Traits/Abilities.rsi/aphrodesiac.png | Bin 0 -> 470 bytes .../_CS/Traits/Abilities.rsi/meta.json | 14 +++++ 9 files changed, 146 insertions(+), 28 deletions(-) create mode 100644 Content.Server/_CS/Traits/Abilities/AphrodesiacBiteSystem.cs create mode 100644 Content.Shared/_CS/Traits/Abilities/AphrodesiacBiteComponent.cs create mode 100644 Resources/Locale/en-US/_CS/traits.ftl create mode 100644 Resources/Prototypes/_CS/Traits/Abilities/aphrodesiac_bite.yml create mode 100644 Resources/Prototypes/_CS/Traits/categories.yml create mode 100644 Resources/Textures/_CS/Traits/Abilities.rsi/aphrodesiac.png create mode 100644 Resources/Textures/_CS/Traits/Abilities.rsi/meta.json diff --git a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs index c4ea66887df..7b80bfc4df9 100644 --- a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs +++ b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs @@ -651,6 +651,7 @@ public void RefreshTraits() "Scents", "ScentsNSFW", "BodyType", + "Abilities", }; // Create UI view from model diff --git a/Content.Server/_CS/Traits/Abilities/AphrodesiacBiteSystem.cs b/Content.Server/_CS/Traits/Abilities/AphrodesiacBiteSystem.cs new file mode 100644 index 00000000000..8380e3e2fcc --- /dev/null +++ b/Content.Server/_CS/Traits/Abilities/AphrodesiacBiteSystem.cs @@ -0,0 +1,48 @@ +using Content.Server.Body.Components; +using Content.Server.Body.Systems; +using Content.Shared._CS.Traits.Abilities; +using Content.Shared.Actions; +using Content.Shared.Chemistry.Components; +using Robust.Shared.Audio; +using Robust.Shared.Audio.Systems; + +namespace Content.Server._CS.Traits.Abilities; + +public sealed class AphrodesiacBiteSystem : EntitySystem +{ + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedActionsSystem _actions = default!; + [Dependency] private readonly BloodstreamSystem _bloodstream = default!; + private readonly SoundSpecifier _bite = new SoundPathSpecifier("/Audio/Effects/bite.ogg"); + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnBite); + SubscribeLocalEvent(OnInit); + } + + public void OnInit(EntityUid uid, AphrodesiacBiteComponent component, ComponentInit args) + { + _actions.AddAction(uid, ref component.ActionEntity, component.Action, uid); + } + + public void OnBite(AphrodesiacBiteEvent ev) + { + if (ev.Handled) + return; + + TryInject(ev.Target, ev.Performer); + } + + public void TryInject(EntityUid target, EntityUid user) + { + if (!TryComp(target, out var bloodstream)) + return; + + var solution = new Solution("Libidozenithizine", 5); + if (_bloodstream.TryAddToChemicals(target, solution, bloodstream)) + _audio.PlayPvs(_bite, user); + } +} diff --git a/Content.Shared/_CS/Traits/Abilities/AphrodesiacBiteComponent.cs b/Content.Shared/_CS/Traits/Abilities/AphrodesiacBiteComponent.cs new file mode 100644 index 00000000000..f490d99bdb1 --- /dev/null +++ b/Content.Shared/_CS/Traits/Abilities/AphrodesiacBiteComponent.cs @@ -0,0 +1,22 @@ +using Content.Shared.Actions; +using Content.Shared.DoAfter; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; + +namespace Content.Shared._CS.Traits.Abilities; + +[RegisterComponent, NetworkedComponent] +public sealed partial class AphrodesiacBiteComponent : Component +{ + [DataField] + public EntProtoId Action = "ActionAphrodesiacBite"; + + [DataField] + public EntityUid? ActionEntity; +} + +public sealed partial class AphrodesiacBiteEvent : EntityTargetActionEvent +{ + +} diff --git a/Resources/Locale/en-US/_CS/traits.ftl b/Resources/Locale/en-US/_CS/traits.ftl new file mode 100644 index 00000000000..f59ce01cfe1 --- /dev/null +++ b/Resources/Locale/en-US/_CS/traits.ftl @@ -0,0 +1,3 @@ +trait-category-abilities = Abilities +trait-aphrodesiacbite-name = Aphrodesiac Bite +trait-aphrodesiacbite-desc = You have fangs capable of injecting a powerful aphrodesiac venom. diff --git a/Resources/Prototypes/Traits/categories.yml b/Resources/Prototypes/Traits/categories.yml index 03ce7478b35..d024b3fcff3 100644 --- a/Resources/Prototypes/Traits/categories.yml +++ b/Resources/Prototypes/Traits/categories.yml @@ -10,31 +10,3 @@ - type: traitCategory id: Quirks name: trait-category-quirks - -- type: traitCategory - id: RPI - name: trait-category-rpi - -- type: traitCategory - id: Examine - name: trait-category-examine - -- type: traitCategory - id: Temperament - name: trait-category-temperament - maxTraitPoints: 2 - -- type: traitCategory - id: BodyType - name: trait-category-bodytypes - maxTraitPoints: 2 - -- type: traitCategory - id: Scents - name: trait-category-scents - maxTraitPoints: 2 - -- type: traitCategory - id: ScentsNSFW - name: trait-category-scents-nsfw - maxTraitPoints: 2 diff --git a/Resources/Prototypes/_CS/Traits/Abilities/aphrodesiac_bite.yml b/Resources/Prototypes/_CS/Traits/Abilities/aphrodesiac_bite.yml new file mode 100644 index 00000000000..04fee0d5815 --- /dev/null +++ b/Resources/Prototypes/_CS/Traits/Abilities/aphrodesiac_bite.yml @@ -0,0 +1,26 @@ +- type: entity + id: ActionAphrodesiacBite + name: Aphrodesiac Bite + description: Inject a powerful aphrodesiac venom into someone. + components: + - type: Action + icon: + sprite: _CS/Traits/Abilities.rsi + state: aphrodesiac + itemIconStyle: BigAction + useDelay: 2 + - type: TargetAction + - type: EntityTargetAction + whitelist: + components: + - SSDIndicator # weird hack but it works? + event: !type:AphrodesiacBiteEvent + +- type: trait + id: AbilityAphrodesiacBite + name: trait-aphrodesiacbite-name + description: trait-aphrodesiacbite-desc + category: Abilities + cost: 0 + components: + - type: AphrodesiacBite diff --git a/Resources/Prototypes/_CS/Traits/categories.yml b/Resources/Prototypes/_CS/Traits/categories.yml new file mode 100644 index 00000000000..89c28e1e319 --- /dev/null +++ b/Resources/Prototypes/_CS/Traits/categories.yml @@ -0,0 +1,32 @@ +- type: traitCategory + id: RPI + name: trait-category-rpi + +- type: traitCategory + id: Examine + name: trait-category-examine + +- type: traitCategory + id: Temperament + name: trait-category-temperament + maxTraitPoints: 2 + +- type: traitCategory + id: BodyType + name: trait-category-bodytypes + maxTraitPoints: 2 + +- type: traitCategory + id: Scents + name: trait-category-scents + maxTraitPoints: 2 + +- type: traitCategory + id: ScentsNSFW + name: trait-category-scents-nsfw + maxTraitPoints: 2 + +- type: traitCategory + id: Abilities + name: trait-category-abilities + maxTraitPoints: 2 diff --git a/Resources/Textures/_CS/Traits/Abilities.rsi/aphrodesiac.png b/Resources/Textures/_CS/Traits/Abilities.rsi/aphrodesiac.png new file mode 100644 index 0000000000000000000000000000000000000000..a52165caf30dafe04c386fbe0bdfe064b70b769d GIT binary patch literal 470 zcmV;{0V)28P)VvOMgqZ&Zq=nHaBIlkl^}S7 zkZe7KcmOvV5brPu?gWpZyn&LoZJDa>fk8+^78$DQ|N8a2CaIq4I~nq<4G9kRiopL? zVA0%u@+t9AwgLd}%618Mo10C5#~<~TgpL6c9zU#G*it+OPUWcz zkl2weOKJNp(Ak@+I)XY!ptWZ6S+Z=r56 Date: Mon, 23 Mar 2026 21:04:00 +1300 Subject: [PATCH 2/4] Make it arbittrary & VVable, move to Quirks --- Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs | 2 +- .../_CS/Traits/Abilities/AphrodesiacBiteSystem.cs | 8 +++++--- .../_CS/Traits/Abilities/AphrodesiacBiteComponent.cs | 10 ++++++++++ Resources/Locale/en-US/_CS/traits.ftl | 1 - .../_CS/Traits/Abilities/aphrodesiac_bite.yml | 4 ++-- Resources/Prototypes/_CS/Traits/categories.yml | 5 ----- 6 files changed, 18 insertions(+), 12 deletions(-) diff --git a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs index 7b80bfc4df9..3b2abd2a573 100644 --- a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs +++ b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs @@ -651,7 +651,7 @@ public void RefreshTraits() "Scents", "ScentsNSFW", "BodyType", - "Abilities", + "Quirks", }; // Create UI view from model diff --git a/Content.Server/_CS/Traits/Abilities/AphrodesiacBiteSystem.cs b/Content.Server/_CS/Traits/Abilities/AphrodesiacBiteSystem.cs index 8380e3e2fcc..ceec4eb68f4 100644 --- a/Content.Server/_CS/Traits/Abilities/AphrodesiacBiteSystem.cs +++ b/Content.Server/_CS/Traits/Abilities/AphrodesiacBiteSystem.cs @@ -13,7 +13,6 @@ public sealed class AphrodesiacBiteSystem : EntitySystem [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedActionsSystem _actions = default!; [Dependency] private readonly BloodstreamSystem _bloodstream = default!; - private readonly SoundSpecifier _bite = new SoundPathSpecifier("/Audio/Effects/bite.ogg"); public override void Initialize() { @@ -41,8 +40,11 @@ public void TryInject(EntityUid target, EntityUid user) if (!TryComp(target, out var bloodstream)) return; - var solution = new Solution("Libidozenithizine", 5); + if (!TryComp(user, out var bite)) + return; + + var solution = new Solution(bite.Reagent, bite.Amount); if (_bloodstream.TryAddToChemicals(target, solution, bloodstream)) - _audio.PlayPvs(_bite, user); + _audio.PlayPvs(bite.Sound, user); } } diff --git a/Content.Shared/_CS/Traits/Abilities/AphrodesiacBiteComponent.cs b/Content.Shared/_CS/Traits/Abilities/AphrodesiacBiteComponent.cs index f490d99bdb1..c5e36ee73dd 100644 --- a/Content.Shared/_CS/Traits/Abilities/AphrodesiacBiteComponent.cs +++ b/Content.Shared/_CS/Traits/Abilities/AphrodesiacBiteComponent.cs @@ -1,5 +1,6 @@ using Content.Shared.Actions; using Content.Shared.DoAfter; +using Robust.Shared.Audio; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; @@ -14,6 +15,15 @@ public sealed partial class AphrodesiacBiteComponent : Component [DataField] public EntityUid? ActionEntity; + + [DataField("reagent")] + public string Reagent = "Libidozenithizine"; + + [DataField("amount")] + public int Amount = 5; + + [DataField("sound")] + public SoundSpecifier Sound = new SoundPathSpecifier("/Audio/Effects/bite.ogg"); } public sealed partial class AphrodesiacBiteEvent : EntityTargetActionEvent diff --git a/Resources/Locale/en-US/_CS/traits.ftl b/Resources/Locale/en-US/_CS/traits.ftl index f59ce01cfe1..d9623d74e64 100644 --- a/Resources/Locale/en-US/_CS/traits.ftl +++ b/Resources/Locale/en-US/_CS/traits.ftl @@ -1,3 +1,2 @@ -trait-category-abilities = Abilities trait-aphrodesiacbite-name = Aphrodesiac Bite trait-aphrodesiacbite-desc = You have fangs capable of injecting a powerful aphrodesiac venom. diff --git a/Resources/Prototypes/_CS/Traits/Abilities/aphrodesiac_bite.yml b/Resources/Prototypes/_CS/Traits/Abilities/aphrodesiac_bite.yml index 04fee0d5815..2da027b8e02 100644 --- a/Resources/Prototypes/_CS/Traits/Abilities/aphrodesiac_bite.yml +++ b/Resources/Prototypes/_CS/Traits/Abilities/aphrodesiac_bite.yml @@ -13,14 +13,14 @@ - type: EntityTargetAction whitelist: components: - - SSDIndicator # weird hack but it works? + - Bloodstream event: !type:AphrodesiacBiteEvent - type: trait id: AbilityAphrodesiacBite name: trait-aphrodesiacbite-name description: trait-aphrodesiacbite-desc - category: Abilities + category: Quirks cost: 0 components: - type: AphrodesiacBite diff --git a/Resources/Prototypes/_CS/Traits/categories.yml b/Resources/Prototypes/_CS/Traits/categories.yml index 89c28e1e319..63a613c480c 100644 --- a/Resources/Prototypes/_CS/Traits/categories.yml +++ b/Resources/Prototypes/_CS/Traits/categories.yml @@ -25,8 +25,3 @@ id: ScentsNSFW name: trait-category-scents-nsfw maxTraitPoints: 2 - -- type: traitCategory - id: Abilities - name: trait-category-abilities - maxTraitPoints: 2 From 8ced3e8c881fe222460e5981009c8a1e5c91b2e4 Mon Sep 17 00:00:00 2001 From: Lily Autumn Date: Mon, 23 Mar 2026 21:21:42 +1300 Subject: [PATCH 3/4] Update copyright --- Resources/Textures/_CS/Traits/Abilities.rsi/meta.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Textures/_CS/Traits/Abilities.rsi/meta.json b/Resources/Textures/_CS/Traits/Abilities.rsi/meta.json index 3652d020177..c88f3609309 100644 --- a/Resources/Textures/_CS/Traits/Abilities.rsi/meta.json +++ b/Resources/Textures/_CS/Traits/Abilities.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "LilyflowerFDL / Illumos", + "copyright": "@cringe_cursed on Discord", "size": { "x": 32, "y": 32 From a895b7372284778b6f2fba29883f41726f86e6bb Mon Sep 17 00:00:00 2001 From: Lily Autumn Date: Tue, 24 Mar 2026 21:39:27 +1300 Subject: [PATCH 4/4] Add consent requirement --- .../_CS/Traits/Abilities/AphrodesiacBiteSystem.cs | 11 ++++++++++- .../_CS/Traits/Abilities/AphrodesiacBiteComponent.cs | 9 +++++++-- Resources/Locale/en-US/_CS/traits.ftl | 1 + .../_CS/Traits/Abilities/aphrodesiac_bite.yml | 3 --- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/Content.Server/_CS/Traits/Abilities/AphrodesiacBiteSystem.cs b/Content.Server/_CS/Traits/Abilities/AphrodesiacBiteSystem.cs index ceec4eb68f4..5eb55907102 100644 --- a/Content.Server/_CS/Traits/Abilities/AphrodesiacBiteSystem.cs +++ b/Content.Server/_CS/Traits/Abilities/AphrodesiacBiteSystem.cs @@ -1,16 +1,19 @@ using Content.Server.Body.Components; using Content.Server.Body.Systems; +using Content.Server.Consent; using Content.Shared._CS.Traits.Abilities; using Content.Shared.Actions; using Content.Shared.Chemistry.Components; -using Robust.Shared.Audio; +using Content.Shared.Popups; using Robust.Shared.Audio.Systems; namespace Content.Server._CS.Traits.Abilities; public sealed class AphrodesiacBiteSystem : EntitySystem { + [Dependency] private readonly ConsentSystem _consent = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedActionsSystem _actions = default!; [Dependency] private readonly BloodstreamSystem _bloodstream = default!; @@ -43,6 +46,12 @@ public void TryInject(EntityUid target, EntityUid user) if (!TryComp(user, out var bite)) return; + if (bite.RequiresConsent && !_consent.HasConsent(target, bite.ConsentToggleId)) + { + _popup.PopupEntity(Loc.GetString("aphrodesiac-no-consent", ("target", target)), user, PopupType.LargeCaution); + return; + } + var solution = new Solution(bite.Reagent, bite.Amount); if (_bloodstream.TryAddToChemicals(target, solution, bloodstream)) _audio.PlayPvs(bite.Sound, user); diff --git a/Content.Shared/_CS/Traits/Abilities/AphrodesiacBiteComponent.cs b/Content.Shared/_CS/Traits/Abilities/AphrodesiacBiteComponent.cs index c5e36ee73dd..b0d86f1678f 100644 --- a/Content.Shared/_CS/Traits/Abilities/AphrodesiacBiteComponent.cs +++ b/Content.Shared/_CS/Traits/Abilities/AphrodesiacBiteComponent.cs @@ -1,9 +1,8 @@ using Content.Shared.Actions; -using Content.Shared.DoAfter; +using Content.Shared.Consent; using Robust.Shared.Audio; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization; namespace Content.Shared._CS.Traits.Abilities; @@ -24,6 +23,12 @@ public sealed partial class AphrodesiacBiteComponent : Component [DataField("sound")] public SoundSpecifier Sound = new SoundPathSpecifier("/Audio/Effects/bite.ogg"); + + [DataField("consentRequired")] + public bool RequiresConsent = true; + + [DataField("consentPrototype")] + public ProtoId ConsentToggleId = "Aphrodisiacs"; } public sealed partial class AphrodesiacBiteEvent : EntityTargetActionEvent diff --git a/Resources/Locale/en-US/_CS/traits.ftl b/Resources/Locale/en-US/_CS/traits.ftl index d9623d74e64..a9083d184d1 100644 --- a/Resources/Locale/en-US/_CS/traits.ftl +++ b/Resources/Locale/en-US/_CS/traits.ftl @@ -1,2 +1,3 @@ trait-aphrodesiacbite-name = Aphrodesiac Bite trait-aphrodesiacbite-desc = You have fangs capable of injecting a powerful aphrodesiac venom. +aphrodesiac-no-consent = {$target} doesn't consent to that! diff --git a/Resources/Prototypes/_CS/Traits/Abilities/aphrodesiac_bite.yml b/Resources/Prototypes/_CS/Traits/Abilities/aphrodesiac_bite.yml index 2da027b8e02..1eb0d6fa1ac 100644 --- a/Resources/Prototypes/_CS/Traits/Abilities/aphrodesiac_bite.yml +++ b/Resources/Prototypes/_CS/Traits/Abilities/aphrodesiac_bite.yml @@ -11,9 +11,6 @@ useDelay: 2 - type: TargetAction - type: EntityTargetAction - whitelist: - components: - - Bloodstream event: !type:AphrodesiacBiteEvent - type: trait