diff --git a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs index c4ea66887df..3b2abd2a573 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", + "Quirks", }; // 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..5eb55907102 --- /dev/null +++ b/Content.Server/_CS/Traits/Abilities/AphrodesiacBiteSystem.cs @@ -0,0 +1,59 @@ +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 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!; + + 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; + + 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 new file mode 100644 index 00000000000..b0d86f1678f --- /dev/null +++ b/Content.Shared/_CS/Traits/Abilities/AphrodesiacBiteComponent.cs @@ -0,0 +1,37 @@ +using Content.Shared.Actions; +using Content.Shared.Consent; +using Robust.Shared.Audio; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared._CS.Traits.Abilities; + +[RegisterComponent, NetworkedComponent] +public sealed partial class AphrodesiacBiteComponent : Component +{ + [DataField] + public EntProtoId Action = "ActionAphrodesiacBite"; + + [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"); + + [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 new file mode 100644 index 00000000000..a9083d184d1 --- /dev/null +++ b/Resources/Locale/en-US/_CS/traits.ftl @@ -0,0 +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/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..1eb0d6fa1ac --- /dev/null +++ b/Resources/Prototypes/_CS/Traits/Abilities/aphrodesiac_bite.yml @@ -0,0 +1,23 @@ +- 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 + event: !type:AphrodesiacBiteEvent + +- type: trait + id: AbilityAphrodesiacBite + name: trait-aphrodesiacbite-name + description: trait-aphrodesiacbite-desc + category: Quirks + 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..63a613c480c --- /dev/null +++ b/Resources/Prototypes/_CS/Traits/categories.yml @@ -0,0 +1,27 @@ +- 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/Textures/_CS/Traits/Abilities.rsi/aphrodesiac.png b/Resources/Textures/_CS/Traits/Abilities.rsi/aphrodesiac.png new file mode 100644 index 00000000000..a52165caf30 Binary files /dev/null and b/Resources/Textures/_CS/Traits/Abilities.rsi/aphrodesiac.png differ diff --git a/Resources/Textures/_CS/Traits/Abilities.rsi/meta.json b/Resources/Textures/_CS/Traits/Abilities.rsi/meta.json new file mode 100644 index 00000000000..c88f3609309 --- /dev/null +++ b/Resources/Textures/_CS/Traits/Abilities.rsi/meta.json @@ -0,0 +1,14 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "@cringe_cursed on Discord", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "aphrodesiac" + } + ] +}