From 897ea73c110dc9b224ba59ffbc6c46798cf54f0b Mon Sep 17 00:00:00 2001
From: sunny <102998393+sunnyariess@users.noreply.github.com>
Date: Sun, 10 Aug 2025 04:13:03 +0200
Subject: [PATCH] Adds "Aphrodisiacal Bite" Quirk, moving "Venomous Bite" 's
ERP options. (#4269)
## About The Pull Request
Adds a new 0 Points quirk that injects people with ERP poisons, moving
them from Venomous Bite to it.
Links the Venom Milker over to this quirk.
Links Aphro-Biting to the Aphrodisiac preference, and logs its usage.
Frankly, I have no attachment over the sprite or name/descriptions, feel
free to change to something more proper.
First PR here! Tell me if I've majorly fucked something up.
## Why It's Good For The Game
I've seen people mention it was weird a Mechanical Quirk had ERP
options, especially for such a steep point cost, I agree. Separating
them fixes it, giving each quirk its own niche and usage.
## Proof Of Testing
See above, compiles.
Screenshots/Videos
## Changelog
:cl: sunnyaries
add: added a new quirk "Aphrodisiacal Bite", and moved ERP chems from
"Venomous Bite" to it.
/:cl:
---------
Co-authored-by: Roxy <75404941+TealSeer@users.noreply.github.com>
---
.../code/lewd_items/venom_milker.dm | 8 +-
.../aphrodisiacal_bite/aphrodisiacal_bite.dm | 106 +++++++++++++++++
.../aphrodisiacal_bite_quirk.dm | 111 ++++++++++++++++++
.../venomous_bite.dm | 6 +-
.../venomous_bite_quirk.dm | 50 +-------
.../mob/actions/quirks/aphrodisiacal_bite.dmi | Bin 0 -> 324 bytes
tgstation.dme | 6 +-
.../bubbers/aphrodisiacal_bite.tsx | 7 ++
8 files changed, 239 insertions(+), 55 deletions(-)
create mode 100644 modular_zubbers/code/datums/quirks/neutral_quirks/aphrodisiacal_bite/aphrodisiacal_bite.dm
create mode 100644 modular_zubbers/code/datums/quirks/neutral_quirks/aphrodisiacal_bite/aphrodisiacal_bite_quirk.dm
rename modular_zubbers/code/datums/quirks/positive_quirks/{venemous_bite => venomous_bite}/venomous_bite.dm (95%)
rename modular_zubbers/code/datums/quirks/positive_quirks/{venemous_bite => venomous_bite}/venomous_bite_quirk.dm (56%)
create mode 100644 modular_zubbers/icons/mob/actions/quirks/aphrodisiacal_bite.dmi
create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/bubbers/aphrodisiacal_bite.tsx
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/venom_milker.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/venom_milker.dm
index 9624d9f11b3..fbd063c779c 100644
--- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/venom_milker.dm
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/venom_milker.dm
@@ -14,7 +14,7 @@
/obj/item/reagent_containers/venom_milker/Initialize(mapload)
. = ..()
- var/filter_immune_string = /datum/preference/choiced/venomous_bite_venom::filter_immune_string
+ var/filter_immune_string = /datum/preference/choiced/aphrodisiacal_bite_venom::filter_immune_string
if (length(filter_immune_string))
desc += span_notice("\nThe following reagents cannot be filtered by the neutralizer: [filter_immune_string]")
@@ -52,7 +52,7 @@
* * silent = FALSE: If TRUE, will not give user any feedback.
*/
/obj/item/reagent_containers/venom_milker/proc/can_milk(mob/living/target, mob/living/user, silent = FALSE)
- var/datum/action/cooldown/mob_cooldown/venomous_bite/bite = locate() in target.actions
+ var/datum/action/cooldown/mob_cooldown/aphrodisiacal_bite/bite = locate() in target.actions
if (isnull(bite))
if (!silent)
user?.balloon_alert(user, "no fangs!")
@@ -87,7 +87,7 @@
/obj/item/reagent_containers/venom_milker/proc/siphon(mob/living/target, mob/living/user)
if (!can_milk(target, user))
return FALSE
- var/datum/action/cooldown/mob_cooldown/venomous_bite/bite = locate(/datum/action/cooldown/mob_cooldown/venomous_bite) in target.actions
+ var/datum/action/cooldown/mob_cooldown/aphrodisiacal_bite/bite = locate() in target.actions
if (isnull(bite))
return FALSE
@@ -106,7 +106,7 @@
return TRUE
/**
- * If a reagent milked from someone with the venom quirk is NOT in /datum/preference/choiced/venomous_bite_venom::milkable_venoms, it will be transformed into this
+ * If a reagent milked from someone with the venom quirk is NOT in /datum/preference/choiced/aphrodisiacal_bite_venom::milkable_venoms, it will be transformed into this
* generic chem that has no effects.
*/
/datum/reagent/generic_milked_venom
diff --git a/modular_zubbers/code/datums/quirks/neutral_quirks/aphrodisiacal_bite/aphrodisiacal_bite.dm b/modular_zubbers/code/datums/quirks/neutral_quirks/aphrodisiacal_bite/aphrodisiacal_bite.dm
new file mode 100644
index 00000000000..34c710f432a
--- /dev/null
+++ b/modular_zubbers/code/datums/quirks/neutral_quirks/aphrodisiacal_bite/aphrodisiacal_bite.dm
@@ -0,0 +1,106 @@
+/datum/action/cooldown/mob_cooldown/aphrodisiacal_bite
+ name = "Inject Aphrodisiac"
+ desc = "Sink your fangs into another and inject them with your aphrodisiac."
+
+ button_icon = 'modular_zubbers/icons/mob/actions/quirks/aphrodisiacal_bite.dmi'
+ button_icon_state = "aphrodisiac"
+
+ ranged_mousepointer = 'icons/effects/mouse_pointers/supplypod_pickturf.dmi'
+
+ check_flags = AB_CHECK_CONSCIOUS | AB_CHECK_INCAPACITATED // can use it if cuffed, enjoy
+
+ shared_cooldown = NONE
+
+ /// The reagent we will inject.
+ var/datum/reagent/reagent_typepath
+ /// How much of [reagent_typepath] we will inject.
+ var/to_inject = 0
+
+/datum/action/cooldown/mob_cooldown/aphrodisiacal_bite/New(Target, original, datum/reagent/our_reagent, quantity_override = null)
+ . = ..()
+ set_reagent(our_reagent, quantity_override)
+
+/datum/action/cooldown/mob_cooldown/aphrodisiacal_bite/proc/set_reagent(datum/reagent/new_reagent, quantity_override = null, cooldown_override = null)
+ reagent_typepath = new_reagent
+ var/list/reagent_spec = /datum/preference/choiced/aphrodisiacal_bite_venom::aphrodisiacal_bite_choice_specs[new_reagent]
+ to_inject = quantity_override || reagent_spec[1]
+ cooldown_time = cooldown_override || reagent_spec[2]
+
+/datum/action/cooldown/mob_cooldown/aphrodisiacal_bite/set_click_ability(mob/on_who)
+ . = ..()
+ if (!.)
+ return
+ owner.visible_message(span_warning("[owner] bares [owner.p_their()] fangs..."), span_notice("You bare your fangs..."))
+
+/datum/action/cooldown/mob_cooldown/aphrodisiacal_bite/Activate(atom/target_atom)
+ if (!isliving(target_atom))
+ return FALSE
+
+ if (astype(owner, /mob/living/carbon)?.is_mouth_covered())
+ owner.balloon_alert(owner, "mouth covered!")
+ return FALSE
+
+ if (!owner.Adjacent(target_atom))
+ owner.balloon_alert(owner, "too far!")
+ return FALSE
+
+ if (target_atom == owner)
+ owner.balloon_alert(owner, "can't bite yourself!")
+ return FALSE
+
+ if(!iscarbon(target_atom))
+ owner.balloon_alert(owner, "not carbon!")
+ return FALSE
+
+ if(!astype(target_atom, /mob/living/carbon).client?.prefs.read_preference(/datum/preference/toggle/erp/aphro))
+ owner.balloon_alert(owner, "not interested!")
+ log_game("[key_name(owner)] tried to bite [key_name(astype(target_atom, /mob/living/carbon))] but [key_name(astype(target_atom, /mob/living/carbon))] had aphrodisiacs disabled")
+ return FALSE
+
+ log_combat(owner, target_atom, "started to bite", null, "with venom: [reagent_typepath::name]")
+ owner.visible_message(span_warning("[owner] starts to bite [target_atom]!"), span_warning("You start to bite [target_atom]!"), ignored_mobs = target_atom)
+ to_chat(target_atom, span_userdanger("[owner] starts to bite you!"))
+ owner.balloon_alert_to_viewers("biting...")
+ if (!do_after(owner, 0.5 SECONDS, target_atom, IGNORE_HELD_ITEM))
+ return FALSE
+
+ . = ..()
+
+ if (try_bite(target_atom))
+ inject(target_atom)
+ return TRUE
+
+/// Does NOT inject reagents; represents the initial bite.
+/datum/action/cooldown/mob_cooldown/aphrodisiacal_bite/proc/try_bite(mob/living/target)
+ PRIVATE_PROC(TRUE)
+ var/target_zone = check_zone(owner.zone_selected)
+
+ var/text = "[owner] sinks [owner.p_their()] teeth into [target]'s [target.parse_zone_with_bodypart(target_zone)]!"
+ var/self_message = "You sink your teeth into [target]'s [target.parse_zone_with_bodypart(target_zone)]!"
+ var/victim_message = "[owner] sinks [owner.p_their()] teeth into your [target.parse_zone_with_bodypart(target_zone)]!"
+
+ owner.visible_message(span_warning(text), span_warning(self_message), ignored_mobs = list(target))
+ to_chat(target, span_userdanger(victim_message))
+
+ owner.do_attack_animation(target, ATTACK_EFFECT_BITE)
+ playsound(owner, 'sound/items/weapons/bite.ogg', 60, TRUE)
+ log_combat(owner, target, "successfully bit", null, "with venom: [reagent_typepath::name]")
+ return TRUE
+
+/datum/action/cooldown/mob_cooldown/aphrodisiacal_bite/proc/inject(mob/living/target)
+ if (!target.try_inject(owner, check_zone(owner.zone_selected)))
+ return FALSE
+
+ add_reagents(target.reagents)
+ return TRUE
+
+/datum/action/cooldown/mob_cooldown/aphrodisiacal_bite/proc/add_reagents(datum/reagents/target, harvesting = FALSE)
+ var/temp = astype(owner, /mob/living/carbon/human)?.coretemperature
+ var/datum/reagent/local_typepath = reagent_typepath
+ if (harvesting)
+ var/list/spec = /datum/preference/choiced/aphrodisiacal_bite_venom::aphrodisiacal_bite_choice_specs[local_typepath]
+ if (!spec[3])
+ local_typepath = /datum/reagent/generic_milked_venom
+
+ target.add_reagent(local_typepath, to_inject, reagtemp = temp)
+ return TRUE
diff --git a/modular_zubbers/code/datums/quirks/neutral_quirks/aphrodisiacal_bite/aphrodisiacal_bite_quirk.dm b/modular_zubbers/code/datums/quirks/neutral_quirks/aphrodisiacal_bite/aphrodisiacal_bite_quirk.dm
new file mode 100644
index 00000000000..ded2f3f1841
--- /dev/null
+++ b/modular_zubbers/code/datums/quirks/neutral_quirks/aphrodisiacal_bite/aphrodisiacal_bite_quirk.dm
@@ -0,0 +1,111 @@
+/datum/quirk/aphrodisiacal_bite
+ name = "Aphrodisiacal Bite"
+ desc = "You have a aphrodisiacal gland, and can bite people to inject them with an aphrodisiac of your choosing."
+ icon = FA_ICON_ICICLES
+ value = 0
+ gain_text = span_notice("You feel a aphrodisiacal gland in the back of your throat.")
+ lose_text = span_warning("Your aphrodisiacal gland vanishes.")
+ medical_record_text = "Patient possesses an aphrodisiacal gland."
+
+/datum/quirk/aphrodisiacal_bite/add(client/client_source)
+ var/datum/reagent/reagent = text2path(client_source?.prefs?.read_preference(/datum/preference/choiced/aphrodisiacal_bite_venom))
+
+ var/mob/living/carbon/human/human_holder = quirk_holder
+ var/datum/action/cooldown/mob_cooldown/aphrodisiacal_bite/action = new /datum/action/cooldown/mob_cooldown/aphrodisiacal_bite(human_holder, our_reagent = reagent)
+ action.Grant(human_holder)
+
+/datum/quirk/aphrodisiacal_bite/remove()
+ if(QDELETED(quirk_holder))
+ return ..()
+ var/datum/action/cooldown/mob_cooldown/aphrodisiacal_bite/action = locate(/datum/action/cooldown/mob_cooldown/aphrodisiacal_bite) in quirk_holder.actions
+ action.Remove()
+
+ return ..()
+
+/datum/quirk_constant_data/aphrodisiacal_bite
+ associated_typepath = /datum/quirk/aphrodisiacal_bite
+ customization_options = list(/datum/preference/choiced/aphrodisiacal_bite_venom)
+
+/datum/preference/choiced/aphrodisiacal_bite_venom
+ category = PREFERENCE_CATEGORY_MANUALLY_RENDERED
+ savefile_key = "aphrodisiacal_bite_venom"
+ savefile_identifier = PREFERENCE_CHARACTER
+ /// Format: (reagent typepath -> list(amount to inject per bite, cooldown, can be milked (venom_milker.dm)))
+ var/static/list/aphrodisiacal_bite_choice_specs = list(
+ /datum/reagent/drug/aphrodisiac/crocin = list(5, 2 SECONDS, TRUE),
+ /datum/reagent/drug/aphrodisiac/crocin/hexacrocin = list(5, 5 SECONDS, TRUE),
+ /datum/reagent/drug/aphrodisiac/succubus_milk = list(5, 2 SECONDS, TRUE),
+ /datum/reagent/drug/aphrodisiac/incubus_draft = list(5, 2 SECONDS, TRUE),
+)
+ var/static/list/milkable_venoms = generate_milkable_venom_list()
+ var/static/filter_immune_string = generate_filter_immune_string()
+
+/**
+ * Generates a static list of /datum/reagent that will not be transformed into [/datum/reagent/generic_milked_venom] upon being milked by a venom milker.
+ *
+ * Ran once, then never again.
+ */
+/proc/generate_milkable_venom_list()
+ RETURN_TYPE(/list)
+
+ var/list/milkable = list()
+
+ for (var/datum/reagent/typepath as anything in /datum/preference/choiced/aphrodisiacal_bite_venom::aphrodisiacal_bite_choice_specs)
+ var/list/spec = /datum/preference/choiced/aphrodisiacal_bite_venom::aphrodisiacal_bite_choice_specs[typepath]
+
+ if (spec[3])
+ milkable += typepath
+
+ return milkable
+
+/**
+ * Generates a static string appended to the venom milker's description, detailing which reagents available to the venom quirk will not be transformed into [/datum/reagent/generic_milked_venom].
+ *
+ * Ran once, then never again.
+ */
+/proc/generate_filter_immune_string()
+ var/filter_immune_string = ""
+ for (var/datum/reagent/typepath as anything in /datum/preference/choiced/aphrodisiacal_bite_venom::milkable_venoms)
+ if (length(filter_immune_string))
+ filter_immune_string += ", "
+ filter_immune_string += typepath::name
+ return filter_immune_string
+
+/datum/preference/choiced/aphrodisiacal_bite_venom/init_possible_values()
+ var/list/choices = list()
+ for (var/entry in aphrodisiacal_bite_choice_specs)
+ choices += "[entry]"
+
+ return choices
+
+/datum/preference/choiced/aphrodisiacal_bite_venom/create_default_value()
+ return "/datum/reagent/drug/aphrodisiac/crocin"
+
+/datum/preference/choiced/aphrodisiacal_bite_venom/compile_constant_data()
+ var/list/data = ..()
+
+ var/list/titles = list()
+
+ for (var/datum/reagent/entry as anything in aphrodisiacal_bite_choice_specs)
+ var/list/specs = aphrodisiacal_bite_choice_specs[entry]
+
+ var/inject = specs[1]
+ var/cooldown = specs[2]
+
+ var/name = entry::name
+
+ titles["[entry]"] = "[name] ([inject]u, [cooldown / 10] second cooldown)"
+
+ data[CHOICED_PREFERENCE_DISPLAY_NAMES] = titles
+
+ return data
+
+/datum/preference/choiced/aphrodisiacal_bite_venom/is_accessible(datum/preferences/preferences)
+ . = ..()
+ if (!.)
+ return FALSE
+
+ return "Aphrodisiacal Bite" in preferences.all_quirks
+
+/datum/preference/choiced/aphrodisiacal_bite_venom/apply_to_human(mob/living/carbon/human/target, value)
+ return
diff --git a/modular_zubbers/code/datums/quirks/positive_quirks/venemous_bite/venomous_bite.dm b/modular_zubbers/code/datums/quirks/positive_quirks/venomous_bite/venomous_bite.dm
similarity index 95%
rename from modular_zubbers/code/datums/quirks/positive_quirks/venemous_bite/venomous_bite.dm
rename to modular_zubbers/code/datums/quirks/positive_quirks/venomous_bite/venomous_bite.dm
index c5b38ca6749..a700816dc29 100644
--- a/modular_zubbers/code/datums/quirks/positive_quirks/venemous_bite/venomous_bite.dm
+++ b/modular_zubbers/code/datums/quirks/positive_quirks/venomous_bite/venomous_bite.dm
@@ -116,13 +116,9 @@
add_reagents(target.reagents)
return TRUE
-/datum/action/cooldown/mob_cooldown/venomous_bite/proc/add_reagents(datum/reagents/target, harvesting = FALSE)
+/datum/action/cooldown/mob_cooldown/venomous_bite/proc/add_reagents(datum/reagents/target)
var/temp = astype(owner, /mob/living/carbon/human)?.coretemperature
var/datum/reagent/local_typepath = reagent_typepath
- if (harvesting)
- var/list/spec = /datum/preference/choiced/venomous_bite_venom::venomous_bite_choice_specs[local_typepath]
- if (!spec[3])
- local_typepath = /datum/reagent/generic_milked_venom
target.add_reagent(local_typepath, to_inject, reagtemp = temp)
return TRUE
diff --git a/modular_zubbers/code/datums/quirks/positive_quirks/venemous_bite/venomous_bite_quirk.dm b/modular_zubbers/code/datums/quirks/positive_quirks/venomous_bite/venomous_bite_quirk.dm
similarity index 56%
rename from modular_zubbers/code/datums/quirks/positive_quirks/venemous_bite/venomous_bite_quirk.dm
rename to modular_zubbers/code/datums/quirks/positive_quirks/venomous_bite/venomous_bite_quirk.dm
index 55e3995df41..b6de10c8d71 100644
--- a/modular_zubbers/code/datums/quirks/positive_quirks/venemous_bite/venomous_bite_quirk.dm
+++ b/modular_zubbers/code/datums/quirks/positive_quirks/venomous_bite/venomous_bite_quirk.dm
@@ -30,53 +30,15 @@
category = PREFERENCE_CATEGORY_MANUALLY_RENDERED
savefile_key = "venomous_bite_venom"
savefile_identifier = PREFERENCE_CHARACTER
- /// Format: (reagent typepath -> list(amount to inject per bite, cooldown, can be milked (venom_milker.dm)))
+ /// Format: (reagent typepath -> list(amount to inject per bite, cooldown))
var/static/list/venomous_bite_choice_specs = list(
- /datum/reagent/toxin = list(5, 80 SECONDS, FALSE),
- /datum/reagent/toxin/venom = list(5, 180 SECONDS, FALSE),
- /datum/reagent/toxin/carpotoxin = list(5, 60 SECONDS, FALSE), // less powerful than toxin
+ /datum/reagent/toxin = list(5, 80 SECONDS),
+ /datum/reagent/toxin/venom = list(5, 180 SECONDS),
+ /datum/reagent/toxin/carpotoxin = list(5, 60 SECONDS), // less powerful than toxin
// drugs
- /datum/reagent/drug/space_drugs = list(5, 60 SECONDS, TRUE),
- /datum/reagent/toxin/mindbreaker = list(5, 60 SECONDS, FALSE),
- // erp stuff
- /datum/reagent/drug/aphrodisiac/crocin = list(5, 2 SECONDS, TRUE),
- /datum/reagent/drug/aphrodisiac/crocin/hexacrocin = list(5, 5 SECONDS, TRUE),
- /datum/reagent/drug/aphrodisiac/succubus_milk = list(5, 2 SECONDS, TRUE),
- /datum/reagent/drug/aphrodisiac/incubus_draft = list(5, 2 SECONDS, TRUE),
+ /datum/reagent/drug/space_drugs = list(5, 60 SECONDS),
+ /datum/reagent/toxin/mindbreaker = list(5, 60 SECONDS),
)
- var/static/list/milkable_venoms = generate_milkable_venom_list()
- var/static/filter_immune_string = generate_filter_immune_string()
-
-/**
- * Generates a static list of /datum/reagent that will not be transformed into [/datum/reagent/generic_milked_venom] upon being milked by a venom milker.
- *
- * Ran once, then never again.
- */
-/proc/generate_milkable_venom_list()
- RETURN_TYPE(/list)
-
- var/list/milkable = list()
-
- for (var/datum/reagent/typepath as anything in /datum/preference/choiced/venomous_bite_venom::venomous_bite_choice_specs)
- var/list/spec = /datum/preference/choiced/venomous_bite_venom::venomous_bite_choice_specs[typepath]
-
- if (spec[3])
- milkable += typepath
-
- return milkable
-
-/**
- * Generates a static string appended to the venom milker's description, detailing which reagents available to the venom quirk will not be transformed into [/datum/reagent/generic_milked_venom].
- *
- * Ran once, then never again.
- */
-/proc/generate_filter_immune_string()
- var/filter_immune_string = ""
- for (var/datum/reagent/typepath as anything in /datum/preference/choiced/venomous_bite_venom::milkable_venoms)
- if (length(filter_immune_string))
- filter_immune_string += ", "
- filter_immune_string += typepath::name
- return filter_immune_string
/datum/preference/choiced/venomous_bite_venom/init_possible_values()
var/list/choices = list()
diff --git a/modular_zubbers/icons/mob/actions/quirks/aphrodisiacal_bite.dmi b/modular_zubbers/icons/mob/actions/quirks/aphrodisiacal_bite.dmi
new file mode 100644
index 0000000000000000000000000000000000000000..2a446e1e5fed5811fcb83efff0bbb0431122804a
GIT binary patch
literal 324
zcmV-K0lWT*P)V=-0C=2JR&a84_w-Y6@%7{?OD!tS%+FJ>RWQ*r;NmRLOex6#a*U0*I5Sc+
z(=$pSoZ^zil2jm5DX}1iHkEOv#1y-YRJWzR+N~V3SlcNxca$(4F>=K
z#T=6RlOe|d0059lL_t(|ob8fJ4nRNvWTI1aJs#KNblTS7~}?R@HW
z&7>3r-f4?wv7FDX;dNP=BuoNNpcatnH!LmqY+ygVpl!il14q*fnieR5t8lFZEP#Y-
zB>eq>R|J^)1@swwH1KPI4Bg27gJBv|H}|*q!vc&18cqu!5=^*CKqSuAnD8J7KEVoF
Wt3bpNp^$h00000