Merge branch 'master' into upstream-25-06d

This commit is contained in:
Roxy
2025-06-30 15:10:41 -04:00
66 changed files with 1037 additions and 78 deletions

View File

@@ -16,3 +16,17 @@
#define COMSIG_OOC_ESCAPE "ooc_escape"
/// From [/datum/outfit]: (datum/outfit)
#define COMSIG_OUTFIT_EQUIP "outfit_equip"
/// From /mob/proc/equip_to_slot_if_possible()
#define COMSIG_MOB_POST_EQUIP "mob_post_equip"
/// From /mob/living/carbon/human/verb/toggle_undies()
#define COMSIG_HUMAN_TOGGLE_UNDERWEAR "human_toggle_undies"
/// From /obj/item/restraints/handcuffs/proc/apply_cuffs()
#define COMSIG_MOB_HANDCUFFED "mob_handcuffed"
/// From /datum/bodypart_overlay/simple/emote/Destroy() - Calls when an emote that applies a temporary visual effect expires
#define COMSIG_EMOTE_OVERLAY_EXPIRE "emote_overlay_exprie"
/// From /mob/living/carbon/human/proc/adjust_arousal() - Triggered by status
#define COMSIG_HUMAN_ADJUST_AROUSAL "human_adjust_arousal"
/// from /mob/living/carbon/human/verb/toggle_arousal() - Triggered by player toggle
#define COMSIG_HUMAN_TOGGLE_AROUSAL "human_toggle_arousal"
/// From /mob/living/carbon/human/verb/toggle_genitals()
#define COMSIG_HUMAN_TOGGLE_GENITALS "human_toggle_genitals"

View File

@@ -202,6 +202,7 @@ GLOBAL_LIST_INIT(WALLITEMS_INTERIOR, typecacheof(list(
/obj/structure/sign/poster/official/random,
/obj/structure/sign/poster/random,
/obj/structure/urinal,
/obj/structure/lewd_portal, //BUBBER EDIT ADDITION - Lewd Portals
)))
// Wall mounted machinery which are visually coming out of the wall.

View File

@@ -29,6 +29,8 @@
if(!referenced_bodypart)
return ..()
referenced_bodypart.remove_bodypart_overlay(src)
if(!isnull(usr))
SEND_SIGNAL(usr, COMSIG_EMOTE_OVERLAY_EXPIRE) //BUBBER EDIT ADDITION - Used for lewd portals, blush expiring breaks it
return ..()
/**

View File

@@ -158,6 +158,7 @@
cuffs = new type()
target.equip_to_slot(cuffs, ITEM_SLOT_HANDCUFFED)
SEND_SIGNAL(target, COMSIG_MOB_HANDCUFFED) //BUBBER EDIT ADDITION
if(trashtype && !dispense)
qdel(src)

View File

@@ -9,6 +9,9 @@
var/result_path
var/wall_external = FALSE // For frames that are external to the wall they are placed on, like light fixtures and cameras.
var/pixel_shift //The amount of pixels
var/multi_use = 0 //BUBBER EDIT ADDITION - User for lewd portals to allow you to place more than one
var/bypass_unpowered = FALSE //BUBBER EDIT ADDITION - Some wallframes can be placed in unpowered areas, specifically lewd portals in this case
var/bypass_floor = FALSE //BUBBER EDIT ADDITION - Some wallframes can be placed in areas without floors, specifically lewd portals in this case
/obj/item/wallframe/proc/try_build(turf/on_wall, mob/user)
if(get_dist(on_wall,user) > 1)
@@ -20,10 +23,10 @@
return
var/turf/T = get_turf(user)
var/area/A = get_area(T)
if(!isfloorturf(T))
if(!isfloorturf(T) && !bypass_floor) //BUBBER EDIT - allows for wallmounts in floorless areas
balloon_alert(user, "cannot place here!")
return
if(A.always_unpowered)
if(A.always_unpowered && !bypass_unpowered) //BUBBER EDIT - allows for wallmounts in unpowered areas
balloon_alert(user, "cannot place in this area!")
return
if(check_wall_item(T, floor_to_wall, wall_external))
@@ -53,9 +56,14 @@
if(WEST)
hanging_object.pixel_x = -pixel_shift
after_attach(hanging_object)
//BUBBER EDIT START - For lewd_portals, you can place multiple with the same frame.
if(multi_use > 1)
multi_use--
return
//BUBBER EDIT END
qdel(src)
/obj/item/wallframe/proc/after_attach(obj/attached_to)
transfer_fingerprints_to(attached_to)

View File

@@ -21,7 +21,13 @@
/turf/closed/indestructible/attackby(obj/item/attacking_item, mob/user, list/modifiers)
if(istype(attacking_item, /obj/item/poster) && Adjacent(user))
return place_poster(attacking_item, user)
//BUBBER EDIT START - Its almost certain that people are going to want to make use of lewd portals on the interlink so they can be placed on reinforced walls
if(istype(attacking_item, /obj/item/wallframe/lewd_portal) && Adjacent(user))
var/obj/item/wallframe/lewd_portal = attacking_item
if(lewd_portal.try_build(src, user))
lewd_portal.attach(src, user)
return TRUE
//BUBBER EDIT END
return ..()
/turf/closed/indestructible/oldshuttle

View File

@@ -297,27 +297,38 @@
return COMPONENT_CANCEL_ATTACK_CHAIN
/mob/living/silicon/robot/proc/ninjadrain_charge(mob/living/carbon/human/ninja, obj/item/mod/module/hacker/hacking_module)
//SKYRAT EDIT: ADDITION START
//BUBBER ADDITION BEGIN - Role selection
var/list/modelselected = list()
modelselected["Assault"] = "/obj/item/robot_model/ninja"
modelselected["Medical"] = "/obj/item/robot_model/ninja/ninja_medical"
modelselected["Saboteur"] = "/obj/item/robot_model/ninja_saboteur"
//SKYRAT EDIT: ADDITION END
//BUBBER ADDITION END - Role selection
if(!do_after(ninja, 6 SECONDS, target = src, hidden = TRUE))
return
spark_system.start()
playsound(loc, SFX_SPARKS, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
//BUBBER ADDITION BEGIN - No ai shells
if(shell)
ResetModel()
return TRUE
//BUBBER ADDITION END
to_chat(src, span_danger("UPLOAD COMPLETE. NEW CYBORG MODEL DETECTED. INSTALLING..."))
faction = list(ROLE_NINJA)
bubble_icon = "syndibot"
UnlinkSelf()
//BUBBER ADDITION BEGIN - Harder to unsubvert ninja borgs
scrambledcodes = TRUE
SetEmagged(TRUE)
//BUBBER ADDITION END
ionpulse = TRUE
laws = new /datum/ai_laws/ninja_override()
//SKYRAT EDIT CHANGE BEGIN - Role Selection
//BUBBER ADDITION BEGIN - Role Selection
//model.transform_to(pick(/obj/item/robot_model/syndicate, /obj/item/robot_model/syndicate_medical, /obj/item/robot_model/saboteur)) - SKYRAT EDIT - ORIGINAL
var/choice = input(src,"What role do you wish to become?","Select Role") in sort_list(modelselected)
var/choice = tgui_input_list(src, "What role do you wish to become?","Select Role", modelselected)
if(!choice)
choice = pick(modelselected)
model.transform_to(modelselected[choice])
//SKYRAT EDIT CHANGE END
//BUBBER ADDITION END
var/datum/antagonist/ninja/ninja_antag = ninja.mind.has_antag_datum(/datum/antagonist/ninja)

View File

@@ -234,10 +234,6 @@
show_grid = !show_grid
if("finalize")
. = TRUE
if(isobserver(user)) // Ghosts cant finalize
return
if(istype(user, /mob/living/silicon) && !Adjacent(user, src)) // Silicons cant finalize unless adjacent
return
finalize(user)
if("patronage")
. = TRUE

View File

@@ -505,6 +505,7 @@
to_chat(src, span_warning("You are unable to equip that!"))
return FALSE
equip_to_slot(W, slot, initial, redraw_mob, indirect_action = indirect_action) //This proc should not ever fail.
SEND_SIGNAL(src, COMSIG_MOB_POST_EQUIP, W, slot) //BUBBER EDIT ADDITION
return TRUE
/**

View File

@@ -1242,8 +1242,28 @@ mutant_styles: The mutant style - taur bodytype, STYLE_TESHARI, etc. // SKYRAT E
return
my_head.update_limb(is_creating = update_limb_data)
//BUBBER EDIT START - We need to account for different heights when using this proc
var/my_head_icon = my_head.get_limb_icon(dropped = FALSE, update_on = src)
add_overlay(my_head.get_limb_icon(dropped = FALSE, update_on = src))
if(mob_height != HUMAN_HEIGHT_MEDIUM)
var/string_form_index = num2text(HEAD_LAYER)
var/offset_type = GLOB.layers_to_offset[string_form_index]
if(isnull(offset_type))
if(islist(my_head_icon))
for(var/image/applied_appearance in my_head_icon)
apply_height_filters(applied_appearance)
else if(isimage(my_head_icon))
apply_height_filters(my_head_icon)
else
if(islist(my_head_icon))
for(var/image/applied_appearance in my_head_icon)
apply_height_offsets(applied_appearance, offset_type)
else if(isimage(my_head_icon))
apply_height_offsets(my_head_icon, offset_type)
add_overlay(my_head_icon)
//add_overlay(my_head.get_limb_icon(dropped = FALSE, update_on = src))
//BUBBER EDIT END
update_worn_head()
update_worn_mask()

View File

@@ -0,0 +1,6 @@
author: "pixelkitty286"
delete-after: True
changes:
- balance: "AI shells can no longer get a free ninja shell from a ninja."
- balance: "Cyborgs hacked by a ninja now set scrambled codes and are emagged."
- bugfix: "When a ninja hacks a cyborg it now uses Tgui instead of the byond selection."

View File

@@ -0,0 +1,4 @@
author: "ReturnToZender"
delete-after: True
changes:
- rscadd: "Restored the holy vetted examine text, even if it's redundant now. It can wait a bit before going to the retirement home."

View File

@@ -0,0 +1,4 @@
author: "Iamgoofball"
delete-after: True
changes:
- rscdel: "Removes pay-to-win military gloves from the loadout"

View File

@@ -197,3 +197,22 @@
shellspeed1:
- bugfix: The elevator on persistence works again. Pretty sure we had Jimmy Hoffa
stuck there or say the SOSHA folks say.
2025-06-29:
Arturlang:
- bugfix: Fixes/changes some conversion messages to make it make more sense
- bugfix: mindshielded conversion
- bugfix: SAD's should no longer nuke nitrogen lungs
Bombermansam:
- rscadd: More Mutation Toxins
- rscadd: Mutation Toxins Recipes
- rscadd: Hemophage Virus to go with the mutation toxin
- bugfix: Updates the xenomorph virus to the xenos we use rather than the old ones
Odairu:
- balance: changed point values on tellers so they run a bit earlier/later depending
on teller
SpaceCatSS13:
- rscadd: The Lustwish Portal Bore has been added, equipped with both a gloryhole
and stuck in wall mode, your lower half can now be in dorms while your head
performs your job
xPokee:
- bugfix: fixed crusader belts spawning without the storage pouch

View File

@@ -231,7 +231,9 @@
/obj/item/clothing/head/helmet/space/akula_wetsuit/attack_hand_secondary(mob/user)
..()
. = ..()
if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
return
if(!attached_hat)
return

View File

@@ -104,12 +104,3 @@
/datum/loadout_item/gloves/diamondring
name = "Diamond Ring"
item_path = /obj/item/clothing/gloves/ring/diamond
/*
* DONATOR
*/
/datum/loadout_item/gloves/military
name = "Military Gloves"
item_path = /obj/item/clothing/gloves/military
donator_only = TRUE

View File

@@ -227,11 +227,13 @@
/obj/structure/deployable_barricade/attack_hand_secondary(mob/user, list/modifiers)
. = ..()
if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
return
if(anchored)
to_chat(usr, span_warning("It is secured to the floor, you can't turn it!"))
return FALSE
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
setDir(turn(dir, 270))
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
/*----------------------*/

View File

@@ -6,7 +6,7 @@
var/high_message = pick("You feel hyper.", "You feel like you need to go faster.", "You feel like you can run the world.")
if(SPT_PROB(2.5, seconds_per_tick))
to_chat(M, span_notice("[high_message]"))
M.add_mood_event("tweaking", /datum/mood_event/stimulant_medium, 1, name)
M.add_mood_event("tweaking", /datum/mood_event/stimulant_medium, name)
M.AdjustStun(-40 * REM * seconds_per_tick)
M.AdjustKnockdown(-40 * REM * seconds_per_tick)
M.AdjustUnconscious(-40 * REM * seconds_per_tick)

View File

@@ -544,7 +544,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/computer/cryopod, 32)
return
to_chat(user, span_notice("You tuck [occupant.name] into their pod!"))
qdel(weapon)
user.add_mood_event("tucked", /datum/mood_event/tucked_in, 1, occupant)
user.add_mood_event("tucked", /datum/mood_event/tucked_in, occupant)
tucked = TRUE
/obj/machinery/cryopod/update_icon_state()

View File

@@ -418,7 +418,7 @@
user.changeNext_move(CLICK_CD_MELEE) // To avoid spam, in some cases (sadly not all of them)
var/mob/living/living_user = user
if(istype(living_user))
living_user.add_mood_event("hug", /datum/mood_event/warmhug/rubi, 1, src)
living_user.add_mood_event("hug", /datum/mood_event/warmhug/rubi, src)
user.visible_message(span_notice("[user] hugs \the [src]."), span_notice("You hug \the [src]."))
/datum/mood_event/warmhug/rubi

View File

@@ -24,7 +24,6 @@
/obj/item/melee/cleric_mace,
/obj/item/knife,
/obj/item/melee/baton,
/obj/item/melee/baton,
/obj/item/nullrod, //holds any subset of nullrod in the sheath-storage - - -
),
canthold = list( // - - - except the second list's items (no fedora in the sheath)
@@ -39,13 +38,11 @@
/obj/item/nullrod/staff,
/obj/item/nullrod/fedora,
/obj/item/nullrod/godhand,
/obj/item/nullrod/staff,
/obj/item/nullrod/whip,
),
)
atom_storage.allow_big_nesting = TRUE // Lets the pouch work
AddElement(/datum/element/update_icon_updates_onmob)
PopulateContents()
//Overrides normal dumping code to instead dump from the pouch item inside
/datum/storage/belt/crusader/dump_content_at(atom/dest_object, mob/dumping_mob)
@@ -75,7 +72,9 @@
to_chat(user, span_notice("You fumble for [drawn_item] and it falls on the floor."))
update_appearance()
return CLICK_ACTION_SUCCESS
user.visible_message(span_notice("[user] takes [drawn_item] out of [src]."), span_notice("You take [drawn_item] out of [src]."))
user.visible_message(
span_notice("[user] takes [drawn_item] out of [src]."),
span_notice("You take [drawn_item] out of [src]."))
update_appearance()
else
to_chat(user, span_warning("[src] is empty!"))

View File

@@ -102,6 +102,7 @@
if("hide")
underwear_visibility = UNDERWEAR_HIDE_UNDIES | UNDERWEAR_HIDE_SHIRT | UNDERWEAR_HIDE_SOCKS | UNDERWEAR_HIDE_BRA
update_body()
SEND_SIGNAL(src, COMSIG_HUMAN_TOGGLE_UNDERWEAR, picked_choice)
return
/mob/living/carbon/human/revive(full_heal_flags = NONE, excess_healing = 0, force_grab_ghost = FALSE)

View File

@@ -176,3 +176,6 @@
#undef HEMOPHAGE_SPAWN_TEXT
/mob/living/carbon/human/species/hemophage //Why was this never added?
race = /datum/species/hemophage

View File

@@ -528,6 +528,7 @@
if(picked_visibility && picked_organ && (picked_organ in organs))
picked_organ.visibility_preference = gen_vis_trans[picked_visibility]
update_body()
SEND_SIGNAL(src, COMSIG_HUMAN_TOGGLE_GENITALS)
return
//Removing ERP IC verb depending on config
@@ -566,4 +567,5 @@
picked_organ.aroused = gen_arous_trans[picked_arousal]
picked_organ.update_sprite_suffix()
update_body()
SEND_SIGNAL(src, COMSIG_HUMAN_TOGGLE_AROUSAL)
return

View File

@@ -107,7 +107,7 @@
constant_dose_time += seconds_per_tick
our_guy.add_mood_event("tweaking", /datum/mood_event/stimulant_heavy/sundowner, 1, name)
our_guy.add_mood_event("tweaking", /datum/mood_event/stimulant_heavy/sundowner, name)
our_guy.adjustStaminaLoss(-10 * REM * seconds_per_tick)
our_guy.AdjustSleeping(-20 * REM * seconds_per_tick)

View File

@@ -47,7 +47,7 @@
else
user.visible_message(span_notice("[usr] brushes [human_target]'s hair!"), span_notice("You brush [human_target]'s hair."), ignored_mobs=list(human_target))
human_target.show_message(span_notice("[usr] brushes your hair!"), MSG_VISUAL)
human_target.add_mood_event("brushed", /datum/mood_event/brushed, 1, user)
human_target.add_mood_event("brushed", /datum/mood_event/brushed, user)
else if(istype(target, /mob/living/basic/pet))
if(!do_after(usr, brush_speed, target))
@@ -55,4 +55,4 @@
to_chat(user, span_notice("[target] closes [target.p_their()] eyes as you brush [target.p_them()]!"))
var/mob/living/living_user = user
if(istype(living_user))
living_user.add_mood_event("brushed", /datum/mood_event/brushed/pet, 1, target)
living_user.add_mood_event("brushed", /datum/mood_event/brushed/pet, target)

View File

@@ -6,6 +6,8 @@
var/list/datum/interaction/interactions
var/interact_last = 0
var/interact_next = 0
///Holds a reference to a relayed body if one exists
var/obj/body_relay = null
/datum/component/interactable/Initialize(...)
if(QDELETED(parent))
@@ -53,7 +55,8 @@
if(interaction.lewd && !target.client?.prefs?.read_preference(/datum/preference/toggle/erp))
return FALSE
if(!interaction.distance_allowed && !target.Adjacent(self))
return FALSE
if(!body_relay || !target.Adjacent(body_relay))
return FALSE
if(interaction.category == INTERACTION_CAT_HIDE)
return FALSE
if(self == target && interaction.usage == INTERACTION_OTHER)
@@ -98,6 +101,9 @@
data["ref_user"] = REF(user)
data["ref_self"] = REF(self)
data["self"] = self.name
if(body_relay)
if(!can_see(user, self))
data["self"] = body_relay.name
data["block_interact"] = interact_next >= world.time
data["interactions"] = categories
@@ -147,7 +153,10 @@
var/mob/living/carbon/human/user = locate(params["userref"])
if(!can_interact(GLOB.interaction_instances[interaction_id], user))
return FALSE
GLOB.interaction_instances[interaction_id].act(user, locate(params["selfref"]))
if(body_relay && !can_see(user, self))
GLOB.interaction_instances[interaction_id].act(user, locate(params["selfref"]), body_relay)
else
GLOB.interaction_instances[interaction_id].act(user, locate(params["selfref"]))
var/datum/component/interactable/interaction_component = user.GetComponent(/datum/component/interactable)
interaction_component.interact_last = world.time
interact_next = interaction_component.interact_last + INTERACTION_COOLDOWN

View File

@@ -83,7 +83,7 @@ GLOBAL_LIST_EMPTY_TYPED(interaction_instances, /datum/interaction)
CRASH("Unimplemented interaction requirement '[requirement]'")
return TRUE
/datum/interaction/proc/act(mob/living/carbon/human/user, mob/living/carbon/human/target)
/datum/interaction/proc/act(mob/living/carbon/human/user, mob/living/carbon/human/target, obj/body_relay = null)
if(!allow_act(user, target))
return
if(!message)
@@ -93,6 +93,8 @@ GLOBAL_LIST_EMPTY_TYPED(interaction_instances, /datum/interaction)
message_admins("Deprecated message handling for '[name]'. Correct format is a list with one entry. This message will only show once.")
message = list(message)
var/msg = pick(message)
if(!isnull(body_relay))
msg = replacetext(msg, "%TARGET%", "\the [body_relay.name]")
// We replace %USER% with nothing because manual_emote already prepends it.
msg = trim(replacetext(replacetext(msg, "%TARGET%", "[target]"), "%USER%", ""), INTERACTION_MAX_CHAR)
if(lewd)
@@ -101,10 +103,14 @@ GLOBAL_LIST_EMPTY_TYPED(interaction_instances, /datum/interaction)
user.manual_emote(msg)
if(user_messages.len)
var/user_msg = pick(user_messages)
if(!isnull(body_relay))
user_msg = replacetext(user_msg, "%TARGET%", "\the [body_relay.name]")
user_msg = replacetext(replacetext(user_msg, "%TARGET%", "[target]"), "%USER%", "[user]")
to_chat(user, user_msg)
if(target_messages.len)
var/target_msg = pick(target_messages)
if(!isnull(body_relay))
target_msg = replacetext(target_msg, "%USER%", "Unknown")
target_msg = replacetext(replacetext(target_msg, "%TARGET%", "[target]"), "%USER%", "[user]")
to_chat(target, target_msg)
if(sound_use)
@@ -125,6 +131,9 @@ GLOBAL_LIST_EMPTY_TYPED(interaction_instances, /datum/interaction)
target.adjust_pleasure(target_pleasure)
target.adjust_arousal(target_arousal)
target.adjust_pain(target_pain)
if(body_relay)
var/obj/lewd_portal_relay/body_portal_relay = body_relay
body_portal_relay.update_visuals()
/datum/interaction/proc/load_from_json(path)
var/fpath = path

View File

@@ -56,8 +56,11 @@
. += span_notice("It seems like you could use an <b>empty hand</b> to remove the magazine.")
/obj/item/gun/ballistic/automatic/smart_machine_gun/attack_hand_secondary(mob/user, list/modifiers)
if(!user.can_perform_action(src))
. = ..()
if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
return
if(!user.can_perform_action(src))
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
cover_open = !cover_open
to_chat(user, span_notice("You [cover_open ? "open" : "close"] [src]'s cover."))
playsound(src, 'sound/items/weapons/gun/l6/l6_door.ogg', 60, TRUE)

View File

@@ -101,10 +101,13 @@
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
/obj/item/clothing/neck/link_scryer/loaded/nifsoft/attack_hand_secondary(mob/user, list/modifiers)
. = ..()
if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
return
var/new_label = reject_bad_text(tgui_input_text(user, "Change the visible name", "Set Name", label, MAX_NAME_LEN))
if(!new_label)
balloon_alert(user, "invalid name!")
return
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
label = new_label
balloon_alert(user, "name set!")
update_name()

View File

@@ -21,6 +21,7 @@
tool_behaviors = list(TOOL_WIRECUTTER, TOOL_SCREWDRIVER, TOOL_WELDER) //To cut the leather and fasten/weld the sheath detailing
time = 30
category = CAT_CLOTHING
delete_contents = FALSE // Otherwise the storage pouch gets deleted when crafted.
/datum/crafting_recipe/crusader_satchel
name = "Crusader Satchel"

View File

@@ -18,6 +18,7 @@
target_genital.aroused = arousal_status
target_genital.update_sprite_suffix()
target.update_body()
SEND_SIGNAL(src, COMSIG_HUMAN_ADJUST_AROUSAL)
arousal = clamp(arousal + arous, AROUSAL_MINIMUM, AROUSAL_LIMIT)

View File

@@ -5,6 +5,7 @@
#define CLIMAX_ON_FLOOR "On the floor"
#define CLIMAX_IN_OR_ON "Climax in or on someone"
#define CLIMAX_OPEN_CONTAINER "Fill reagent container"
#define CLIMAX_PORTAL "Through the portal"
/mob/living/carbon/human
/// Used to prevent nightmare scenarios.
@@ -91,6 +92,11 @@
if(interactable_inrange_open_containers.len)
buttons += CLIMAX_OPEN_CONTAINER
// If your using a LustWish portal lets you cum through it
var/obj/structure/lewd_portal/portal = src.buckled
if(istype(portal, /obj/structure/lewd_portal))
buttons += CLIMAX_PORTAL
var/penis_climax_choice = tgui_alert(src, "Choose where to shoot your load.", "Load preference!", buttons)
var/create_cum_decal = FALSE
@@ -135,6 +141,11 @@
visible_message(span_userlove("[src] shoots [self_their] sticky load onto the floor!"), \
span_userlove("You shoot string after string of hot cum, hitting the floor!"))
else if(penis_climax_choice == CLIMAX_PORTAL)
to_chat(src, "You shoot string after string of hot cum, hitting whatever is on the other side!")
portal.relayed_body.visible_message("[portal.relayed_body] shoots its sticky load onto the floor!")
add_cum_splatter_floor(get_turf(portal.relayed_body))
else
var/target_choice = tgui_input_list(src, "Choose a person to cum in or on.", "Choose target!", interactable_inrange_humans)
if(!target_choice)
@@ -209,3 +220,4 @@
#undef CLIMAX_ON_FLOOR
#undef CLIMAX_IN_OR_ON
#undef CLIMAX_OPEN_CONTAINER
#undef CLIMAX_PORTAL

View File

@@ -83,7 +83,7 @@
// Notify the user that they're overdosing. Doesn't affect their mood.
/datum/reagent/drug/aphrodisiac/camphor/overdose_start(mob/living/carbon/human/exposed_mob)
to_chat(exposed_mob, span_userdanger("You feel like you took too much [name]!"))
exposed_mob.add_mood_event("[type]_overdose", /datum/mood_event/minor_overdose, 1, name)
exposed_mob.add_mood_event("[type]_overdose", /datum/mood_event/minor_overdose, name)
/datum/chemical_reaction/camphor
results = list(/datum/reagent/drug/aphrodisiac/camphor = 6)

View File

@@ -24,7 +24,7 @@
/datum/reagent/drug/aphrodisiac/dopamine/on_mob_add(mob/living/carbon/human/exposed_mob)
if(!(exposed_mob.client?.prefs.read_preference(/datum/preference/toggle/erp/aphro)))
return ..()
exposed_mob.add_mood_event("[type]_start", /datum/mood_event/orgasm, 1, name)
exposed_mob.add_mood_event("[type]_start", /datum/mood_event/orgasm, name)
return ..()
/datum/reagent/drug/aphrodisiac/dopamine/life_effects(mob/living/carbon/human/exposed_mob)
@@ -35,7 +35,7 @@
/datum/reagent/drug/aphrodisiac/dopamine/overdose_start(mob/living/carbon/human/exposed_mob)
. = ..()
to_chat(exposed_mob, span_purple("You feel so happy!"))
exposed_mob.add_mood_event("[type]_overdose", /datum/mood_event/overgasm, 1, name)
exposed_mob.add_mood_event("[type]_overdose", /datum/mood_event/overgasm, name)
/datum/reagent/drug/aphrodisiac/dopamine/overdose_effects(mob/living/carbon/human/exposed_mob)
if(!(exposed_mob.get_timed_status_effect_duration(/datum/status_effect/hallucination) / (2 SECONDS) < volume && prob(20)))

View File

@@ -167,7 +167,7 @@
// Notify the user that they're overdosing. Doesn't affect their mood.
/datum/reagent/drug/aphrodisiac/incubus_draft/overdose_start(mob/living/carbon/human/exposed_mob)
to_chat(exposed_mob, span_userdanger("You feel like you took too much [name]!"))
exposed_mob.add_mood_event("[type]_overdose", /datum/mood_event/minor_overdose, 1, name)
exposed_mob.add_mood_event("[type]_overdose", /datum/mood_event/minor_overdose, name)
/datum/chemical_reaction/incubus_draft
results = list(/datum/reagent/drug/aphrodisiac/incubus_draft = 8)

View File

@@ -154,7 +154,7 @@
// Notify the user that they're overdosing. Doesn't affect their mood.
/datum/reagent/drug/aphrodisiac/succubus_milk/overdose_start(mob/living/carbon/human/exposed_mob)
to_chat(exposed_mob, span_userdanger("You feel like you took too much [name]!"))
exposed_mob.add_mood_event("[type]_overdose", /datum/mood_event/minor_overdose, 1, name)
exposed_mob.add_mood_event("[type]_overdose", /datum/mood_event/minor_overdose, name)
/datum/chemical_reaction/succubus_milk
results = list(/datum/reagent/drug/aphrodisiac/succubus_milk = 8)

View File

@@ -150,6 +150,7 @@
/obj/item/storage/box/shibari_stand = 4,
/obj/item/storage/box/strippole_kit = 4,
/obj/item/storage/box/xstand_kit = 4,
/obj/item/wallframe/lewd_portal = 8,
),
),
)

View File

@@ -65,7 +65,7 @@
if(SPT_PROB(2.5, seconds_per_tick))
var/high_message = pick("You feel jittery.", "You feel like you gotta go fast.", "You feel like you need to step it up.")
to_chat(M, span_notice("[high_message]"))
M.add_mood_event("zoinked", /datum/mood_event/stimulant_heavy, 1, name)
M.add_mood_event("zoinked", /datum/mood_event/stimulant_heavy, name)
M.AdjustStun(-15 * REM * seconds_per_tick)
M.AdjustKnockdown(-15 * REM * seconds_per_tick)
M.AdjustUnconscious(-15 * REM * seconds_per_tick)
@@ -134,7 +134,7 @@
if(SPT_PROB(2.5, seconds_per_tick))
var/high_message = pick("You feel jittery.", "You feel like you gotta go fast.", "You feel like you need to step it up.")
to_chat(M, span_notice("[high_message]"))
M.add_mood_event("zoinked", /datum/mood_event/stimulant_heavy, 1, name)
M.add_mood_event("zoinked", /datum/mood_event/stimulant_heavy, name)
M.AdjustStun(-12 * REM * seconds_per_tick)
M.AdjustKnockdown(-12 * REM * seconds_per_tick)
M.AdjustUnconscious(-12 * REM * seconds_per_tick)
@@ -186,7 +186,7 @@
if(SPT_PROB(2.5, seconds_per_tick))
var/high_message = pick("You feel jittery.", "You feel like you gotta go fast.", "You feel like you need to step it up.")
to_chat(M, span_notice("[high_message]"))
M.add_mood_event("zoinked", /datum/mood_event/stimulant_heavy, 1, name)
M.add_mood_event("zoinked", /datum/mood_event/stimulant_heavy, name)
M.AdjustStun(-18 * REM * seconds_per_tick)
M.AdjustKnockdown(-18 * REM * seconds_per_tick)
M.AdjustUnconscious(-18 * REM * seconds_per_tick)

View File

@@ -109,7 +109,7 @@
var/high_message = pick("You feel euphoric.", "You feel on top of the world.")
if(SPT_PROB(2.5, seconds_per_tick))
to_chat(M, span_notice("[high_message]"))
M.add_mood_event("smacked out", /datum/mood_event/narcotic_heavy, 1, name)
M.add_mood_event("smacked out", /datum/mood_event/narcotic_heavy, name)
M.adjustBruteLoss(-0.1 * REM * seconds_per_tick, 0) //can be used as a (shitty) painkiller
M.adjustFireLoss(-0.1 * REM * seconds_per_tick, 0)
M.overlay_fullscreen("heroin_euphoria", /atom/movable/screen/fullscreen/color_vision/heroin_color)

View File

@@ -56,7 +56,7 @@
game_plane_master_controller.add_filter("weed_blur", 10, angular_blur_filter(0, 0, 0.45))
if(SPT_PROB(2.5, seconds_per_tick))
to_chat(M, span_notice("[high_message]"))
M.add_mood_event("stoned", /datum/mood_event/stoned, 1, name)
M.add_mood_event("stoned", /datum/mood_event/stoned, name)
M.throw_alert("stoned", /atom/movable/screen/alert/stoned)
M.sound_environment_override = SOUND_ENVIRONMENT_DRUGGED
M.set_dizzy_if_lower(5 * REM * seconds_per_tick * 2 SECONDS)

View File

@@ -177,16 +177,19 @@
/obj/machinery/mounted_machine_gun/attack_hand_secondary(mob/living/user, list/modifiers)
. = ..()
if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
return
if(!istype(user))
return
if(!can_interact(user))
return
if(!cover_open)
balloon_alert(user, "cover closed!")
return
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
if(!ammo_box)
return
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
remove_ammo_box(user)
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
/obj/machinery/mounted_machine_gun/attackby(obj/item/weapon, mob/user, params)
. = ..()

View File

@@ -156,6 +156,9 @@
to_chat(user, span_warning("The vitals readout is blank, the stasis unit is unoccupied!"))
/obj/machinery/stasissleeper/attack_hand_secondary(mob/user)
. = ..()
if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
return
if(occupant)
if(occupant == user)
to_chat(user, span_notice("You read the bloodstream readout on the inside of the stasis unit."))

View File

@@ -6,6 +6,9 @@
#define SUBTLE_ONE_TILE_TEXT "1-Tile Range"
#define SUBTLE_SAME_TILE_TEXT "Same Tile"
#define PORTAL_ONE_TILE_TEXT "Portal 1-Tile Range"
#define PORTAL_SAME_TILE_TEXT "Portal Tile"
/datum/emote/living/subtle
key = "subtle"
message = null
@@ -113,6 +116,9 @@
in_view.Remove(mob)
var/list/targets = list(SUBTLE_ONE_TILE_TEXT, SUBTLE_SAME_TILE_TEXT) + in_view
var/obj/structure/lewd_portal/portal = user?.buckled
if(istype(portal, /obj/structure/lewd_portal))
targets.Insert(1, PORTAL_ONE_TILE_TEXT, PORTAL_SAME_TILE_TEXT)
target = tgui_input_list(user, "Pick a target", "Target Selection", targets)
if(!target)
return FALSE
@@ -145,22 +151,36 @@
var/obj/effect/overlay/holo_pad_hologram/hologram = GLOB.hologram_impersonators[user]
if((get_dist(user.loc, target_mob.loc) <= subtler_range) || (hologram && get_dist(hologram.loc, target_mob.loc) <= subtler_range))
target_mob.show_message(subtler_message, alt_msg = subtler_message)
// Optional sound notification
var/datum/preferences/prefs = target_mob.client?.prefs
if(prefs && prefs.read_preference(/datum/preference/toggle/subtler_sound))
target_mob.playsound_local(get_turf(target_mob), 'sound/effects/achievement/glockenspiel_ping.ogg', 50)
subtler_sound(target_mob)
else
to_chat(user, span_warning("Your emote was unable to be sent to your target: Too far away."))
else if(istype(target, /obj/effect/overlay/holo_pad_hologram))
var/obj/effect/overlay/holo_pad_hologram/hologram = target
if(hologram.Impersonation?.client)
hologram.Impersonation.show_message(subtler_message, alt_msg = subtler_message)
// Optional sound notification
var/datum/preferences/prefs = hologram.Impersonation.client?.prefs
if(prefs && prefs.read_preference(/datum/preference/toggle/subtler_sound))
hologram.Impersonation.playsound_local(get_turf(hologram.Impersonation), 'sound/effects/achievement/glockenspiel_ping.ogg', 50)
subtler_sound(hologram.Impersonation)
else if(istype(target, /obj/lewd_portal_relay)) //Direct Message to a portal user
var/obj/lewd_portal_relay/portal_relay = target
user.show_message(subtler_message, alt_msg = subtler_message)
if(portal_relay.owner?.client)
subtler_message = span_subtler("<b>Unknown</b>[space]<i>[user.apply_message_emphasis(subtler_emote)]</i>")
portal_relay.owner.show_message(subtler_message, alt_msg = subtler_message)
subtler_sound(portal_relay.owner)
else
var/ghostless = get_hearers_in_view(target, user) - GLOB.dead_mob_list
var/ghostless
if(target == PORTAL_SAME_TILE_TEXT || target == PORTAL_ONE_TILE_TEXT)
switch(target)
if(PORTAL_ONE_TILE_TEXT)
target = SUBTLE_ONE_TILE
if(PORTAL_SAME_TILE_TEXT)
target = SUBTLE_SAME_TILE_DISTANCE
var/obj/structure/lewd_portal/portal_reference = user.buckled
var/obj/lewd_portal_relay/output_portal = portal_reference?.relayed_body
ghostless = get_hearers_in_view(target, output_portal) //Broadcast message through portal
user.show_message(subtler_message, alt_msg = subtler_message)
subtler_message = span_subtler("<b>[output_portal]</b>[space]<i>[user.apply_message_emphasis(subtler_emote)]</i>")
else
ghostless = get_hearers_in_view(target, user) - GLOB.dead_mob_list
var/obj/effect/overlay/holo_pad_hologram/hologram = GLOB.hologram_impersonators[user]
if(hologram)
@@ -173,12 +193,22 @@
for(var/mob/receiver in ghostless)
receiver.show_message(subtler_message, alt_msg = subtler_message)
// Optional sound notification
var/datum/preferences/prefs = receiver.client?.prefs
if(prefs && prefs.read_preference(/datum/preference/toggle/subtler_sound))
receiver.playsound_local(get_turf(receiver), 'sound/effects/achievement/glockenspiel_ping.ogg', 50)
subtler_sound(receiver)
for(var/obj/lewd_portal_relay/portal in ghostless) //Message portal owners caught in range
if(portal?.owner?.client && portal.owner != user)
subtler_message = span_subtler("<b>Unknown</b>[space]<i>[user.apply_message_emphasis(subtler_emote)]</i>")
portal.owner.show_message(subtler_message, alt_msg = subtler_message)
subtler_sound(portal.owner)
return TRUE
// Optional sound notification for subtler
/datum/emote/living/subtler/proc/subtler_sound(mob/hearer)
var/datum/preferences/prefs = hearer.client?.prefs
if(prefs && prefs.read_preference(/datum/preference/toggle/subtler_sound))
hearer.playsound_local(get_turf(hearer), 'sound/effects/achievement/glockenspiel_ping.ogg', 50)
/*
* VERB CODE
*/
@@ -216,3 +246,6 @@
#undef SUBTLE_ONE_TILE_TEXT
#undef SUBTLE_SAME_TILE_TEXT
#undef PORTAL_ONE_TILE_TEXT
#undef PORTAL_SAME_TILE_TEXT

View File

@@ -146,6 +146,8 @@
/obj/machinery/xenoarch/researcher/attack_hand_secondary(mob/user, list/modifiers)
. = ..()
if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
return
var/turf/src_turf = get_turf(src)
var/choice = tgui_input_list(user, "Choose which reward you would like!", "Reward Choice", list("Lavaland Chest (150)", "Anomalous Crystal (150)", "Bepis Tech (100)"))
if(!choice)

View File

@@ -1,8 +1,20 @@
/datum/quirk
/// special case logic for quirks that don't quite handle being transferred properly
var/cleanup = TRUE
/// Remove any quirks that the client's prefs do not have, and apply any new ones
/datum/controller/subsystem/processing/quirks/proc/OverrideQuirks(mob/living/user, client/applied_client)
var/list/required_quirks = applied_client.prefs.all_quirks
var/list/present_quirks = list()
for(var/datum/quirk/quirk in user.quirks)
if(!(quirk.name in required_quirks))
user.remove_quirk(quirk.type)
AssignQuirks(user, applied_client)
present_quirks[quirk.name] = TRUE
quirk.cleanup = FALSE
quirk.remove_from_current_holder(TRUE)
for(var/quirk_name in required_quirks)
var/existing_quirk = present_quirks[quirk_name]
var/quirk_type = quirks[quirk_name]
var/datum/quirk/quirk = new quirk_type
quirk.add_to_holder(user, existing_quirk, applied_client)

View File

@@ -0,0 +1,19 @@
/datum/disease/transformation/hemophage
name = "Hemophagic Viral Infection"
cure_text = "Garlic"
cures = list(/datum/reagent/consumable/garlic)
agent = "Hemophagic Viral Infection"
desc = "A gift of the night"
cure_chance = 2.5
severity = DISEASE_SEVERITY_BIOHAZARD
visibility_flags = NONE
stage1 = list("You dont feel very well")
stage2 = list("You feel cold")
stage3 = list(span_danger("Your heart skips a beat"), span_danger("You have a dull pain in your heart"))
stage4 = list(span_danger("You're hungry but normal food does not seem appetizing"))
stage5 = list(span_danger("Blood....Blood..."))
new_form = /mob/living/carbon/human/species/hemophage
infectable_biotypes = MOB_ORGANIC|MOB_UNDEAD
/datum/disease/transformation/xeno
new_form = /mob/living/carbon/alien/adult/skyrat/drone

View File

@@ -22,6 +22,12 @@
/// Are we taking damage?
var/life_support_failed = FALSE
/datum/quirk/equipping/entombed/add(client/client_source)
if(modsuit) return
modsuit = quirk_holder.get_item_by_slot(ITEM_SLOT_BACK)
if(!istype(modsuit))
modsuit = null
/datum/quirk/equipping/entombed/process(seconds_per_tick)
var/mob/living/carbon/human/human_holder = quirk_holder
if(human_holder.stat == DEAD)
@@ -33,6 +39,7 @@
// we've got no modsuit or life support and we're not on stasis. take damage ow
human_holder.adjustToxLoss(ENTOMBED_TICK_DAMAGE * seconds_per_tick, updating_health = TRUE, forced = TRUE)
human_holder.set_jitter_if_lower(10 SECONDS)
return
if (!modsuit.active)
if (!life_support_timer)
@@ -122,6 +129,8 @@
modsuit.quick_activation()
/datum/quirk/equipping/entombed/remove()
if(!cleanup)
return
var/mob/living/carbon/human/human_holder = quirk_holder
if (deploy_locked && HAS_TRAIT_FROM(human_holder, TRAIT_NODISMEMBER, QUIRK_TRAIT))
REMOVE_TRAIT(human_holder, TRAIT_NODISMEMBER, QUIRK_TRAIT)

View File

@@ -1,7 +1,10 @@
/// Looking through pillows on sofas when rightclicked
/obj/structure/chair/sofa/attack_hand_secondary(mob/user, list/modifiers)
. = ..()
if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
return
if(!ishuman(user) || !user.ckey)
return ..()
return
balloon_alert(user, "searching under pillows...")
to_chat(user, span_alert("You start scouring through the sofa's pillows...."))
if(do_after(user, 10 SECONDS, src))
@@ -16,3 +19,4 @@
new /obj/item/coin/iron(loc)
else
balloon_alert(user, "nothing")
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN

View File

@@ -41,6 +41,8 @@
/obj/structure/chalkboard/attack_hand_secondary(mob/user)
. = ..()
if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
return
to_chat(user, span_warning("You pick up the eraser and begin to clear the board."))
if(!written_text)
to_chat(user, span_warning("You pick up the eraser and give the board a few pap-paps, but it has nothing on it to erase."))

View File

@@ -0,0 +1,466 @@
#define GLORYHOLE "gloryhole"
#define WALLSTUCK "wallstuck"
#define PORTAL_SIGNAL_LIST list( \
COMSIG_MOB_POST_EQUIP, \
COMSIG_HUMAN_UNEQUIPPED_ITEM, \
COMSIG_HUMAN_TOGGLE_UNDERWEAR, \
COMSIG_MOB_HANDCUFFED, \
COMSIG_MOB_EMOTE, \
COMSIG_EMOTE_OVERLAY_EXPIRE, \
COMSIG_HUMAN_ADJUST_AROUSAL, \
COMSIG_HUMAN_TOGGLE_AROUSAL, \
COMSIG_HUMAN_TOGGLE_GENITALS \
)
/obj/structure/lewd_portal
name = "LustWish Portal"
desc = "A portal that people can partially fit through."
icon = 'modular_zubbers/icons/obj/structures/lewd_portals.dmi'
icon_state = "portal"
can_buckle = TRUE
anchored = TRUE
max_buckled_mobs = 1
buckle_lying = 0
buckle_prevents_pull = TRUE
///Human currently occupying
var/mob/living/carbon/human/current_mob = null
///Portal mode, gloryhole for crotch, wallstuck for lower body
var/portal_mode = GLORYHOLE
///The other end of the portal
var/obj/structure/lewd_portal/linked_portal
///The relayed body portion associated with this.
var/obj/lewd_portal_relay/relayed_body
///Gloryhole mode needs to make a characters penis invisible, this records the previous state
var/initial_genital_visibility
///This variable is used to get the scale of the buckled mob without touching the rotaiton, used for larger/small characters
var/datum/decompose_matrix/mob_scale_manager
///How offset someones head should be when stuck in a wall.
var/wallstuck_offset_amount = 12
/obj/structure/lewd_portal/Initialize(mapload)
LAZYINITLIST(buckled_mobs)
. = ..()
register_context()
/obj/structure/lewd_portal/Destroy()
visible_message("[src] vanishes!")
linked_portal?.linked_portal = null
if(linked_portal)
qdel(linked_portal)
return ..()
/obj/structure/lewd_portal/examine(mob/user)
. = ..()
var/inspect_mode = "gloryhole"
if(portal_mode == WALLSTUCK)
inspect_mode = "stuck in wall"
. += span_notice("Its currently in [inspect_mode] mode.")
. += span_notice("Right click to change modes.")
/obj/structure/lewd_portal/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
if(portal_mode == GLORYHOLE)
context[SCREENTIP_CONTEXT_RMB] = "Stuck In Wall Mode"
return CONTEXTUAL_SCREENTIP_SET
else
context[SCREENTIP_CONTEXT_RMB] = "Gloryhole Mode"
return CONTEXTUAL_SCREENTIP_SET
/obj/structure/lewd_portal/user_buckle_mob(mob/living/M, mob/user, check_loc)
if(!M.check_erp_prefs(/datum/preference/toggle/erp/sex_toy, user, src))
to_chat(user, span_danger("Looks like [M] doesn't want you to do that."))
return FALSE
if (!ishuman(M))
balloon_alert(user, "[M.p_they()] does not fit!")
return FALSE
if(portal_mode == GLORYHOLE)
var/mob/living/carbon/human/penis_inspection = M
var/obj/item/organ/genital/penis/penis_reference = M.get_organ_slot(ORGAN_SLOT_PENIS)
if(!penis_inspection.has_penis(REQUIRE_GENITAL_EXPOSED) || penis_reference?.visibility_preference == GENITAL_NEVER_SHOW)
balloon_alert(user, "a penis is required to operate!")
return FALSE
if (!linked_portal)
balloon_alert(user, "portal not linked!")
return FALSE
if (!isnull(linked_portal.current_mob))
balloon_alert(user, "portal already occupied!")
return FALSE
visible_message("[user] slots [M] into the [src]!")
return ..(M, user, check_loc = FALSE)
/obj/structure/lewd_portal/post_buckle_mob(mob/living/buckled_mob)
if (!ishuman(buckled_mob))
return
if(LAZYLEN(buckled_mobs))
if(ishuman(buckled_mobs[1]))
current_mob = buckled_mobs[1]
mob_scale_manager = current_mob.transform.decompose()
offset_algorithm()
if(!isnull(current_mob.dna.species))
relayed_body = new /obj/lewd_portal_relay(linked_portal.loc, current_mob, linked_portal)
relayed_body.transform = relayed_body.transform.Scale(mob_scale_manager.scale_x, mob_scale_manager.scale_y)
switch(linked_portal.dir)
if(NORTH)
relayed_body.pixel_y = 24
if(portal_mode == GLORYHOLE)
relayed_body.pixel_y += 3
if(SOUTH)
relayed_body.pixel_y = -24
relayed_body.transform = turn(relayed_body.transform, ROTATION_FLIP)
if(portal_mode == GLORYHOLE)
relayed_body.pixel_y -= 3
if(EAST)
relayed_body.pixel_x = 24
if(portal_mode == WALLSTUCK)
relayed_body.transform = turn(relayed_body.transform, ROTATION_COUNTERCLOCKWISE)
else
relayed_body.pixel_y = 7
if(WEST)
relayed_body.pixel_x = -24
if(portal_mode == WALLSTUCK)
relayed_body.transform = turn(relayed_body.transform, ROTATION_CLOCKWISE)
else
relayed_body.pixel_y = 7
relayed_body.update_visuals()
if(portal_mode == GLORYHOLE)
var/obj/item/organ/genital/penis/penis_reference = current_mob.get_organ_slot(ORGAN_SLOT_PENIS)
initial_genital_visibility = penis_reference?.visibility_preference
hide_penis()
RegisterSignals(current_mob, PORTAL_SIGNAL_LIST, PROC_REF(hide_penis))
current_mob.dir = dir
switch(dir)
if(NORTH)
current_mob.pixel_y += 24
if(SOUTH)
current_mob.pixel_y += -6
if(EAST)
current_mob.pixel_x += 12
if(WEST)
current_mob.pixel_x += -12
else
current_mob.dir = SOUTH
head_only()
RegisterSignals(current_mob, PORTAL_SIGNAL_LIST, PROC_REF(head_only))
switch(dir)
if(NORTH)
current_mob.pixel_y += wallstuck_offset_amount
if(SOUTH)
current_mob.pixel_y += -wallstuck_offset_amount
current_mob.transform = turn(current_mob.transform, ROTATION_FLIP)
if(EAST)
current_mob.pixel_x += wallstuck_offset_amount
current_mob.transform = turn(current_mob.transform, ROTATION_COUNTERCLOCKWISE)
if(WEST)
current_mob.pixel_x += -wallstuck_offset_amount
current_mob.transform = turn(current_mob.transform, ROTATION_CLOCKWISE)
else
unbuckle_all_mobs()
..()
///Algorithm that calculates what a mob head offset should be based on mob scale.
/obj/structure/lewd_portal/proc/offset_algorithm()
var/transform_scale_height = mob_scale_manager.scale_y
if(transform_scale_height <= 1)
wallstuck_offset_amount = (-30 * transform_scale_height) + 42
else
wallstuck_offset_amount = (-24 * transform_scale_height) + 36
wallstuck_offset_amount = clamp(round(wallstuck_offset_amount), 0, 18)
///Hides the buckled mob's penis
/obj/structure/lewd_portal/proc/hide_penis()
SIGNAL_HANDLER
var/obj/item/organ/genital/penis/affected_penis = current_mob.get_organ_slot(ORGAN_SLOT_PENIS) //Stolen from Strapon code, this is bad we should probably have a cleaner way
affected_penis?.visibility_preference = GENITAL_NEVER_SHOW
current_mob.update_body()
affected_penis?.visibility_preference = initial_genital_visibility //These seems weird but I need to hide the penis when people use the gloryhole while maintaining its visability so it can be interacted with.
///Removes everything besides the head for the buckled mob, used in wallstuck mode
/obj/structure/lewd_portal/proc/head_only()
SIGNAL_HANDLER
current_mob.cut_overlays()
current_mob.update_body_parts_head_only()
var/obj/item/clothing/glasses = current_mob.glasses
if(glasses)
current_mob.update_worn_glasses()
current_mob.remove_overlay(BODY_ADJ_LAYER)
current_mob.remove_overlay(BODY_LAYER)
current_mob.remove_overlay(HANDS_LAYER)
var/obj/item/bodypart/head/mob_head = current_mob.get_bodypart(BODY_ZONE_HEAD)
if(mob_head.head_flags & HEAD_EYESPRITES)
var/obj/item/organ/eyes/eye_organ = current_mob.get_organ_slot(ORGAN_SLOT_EYES)
if(eye_organ)
eye_organ.refresh(call_update = FALSE)
current_mob.overlays_standing[BODY_LAYER] += eye_organ.generate_body_overlay(current_mob)
current_mob.apply_overlay(BODY_LAYER)
/obj/structure/lewd_portal/post_unbuckle_mob(mob/living/unbuckled_mob)
UnregisterSignal(current_mob, PORTAL_SIGNAL_LIST)
visible_message("[current_mob] exits the [src]")
current_mob = null
mob_scale_manager = null
QDEL_NULL(relayed_body)
unbuckled_mob.cut_overlays()
unbuckled_mob.regenerate_icons()
var/offset_amount = 24
if(portal_mode == WALLSTUCK)
offset_amount = wallstuck_offset_amount
wallstuck_offset_amount = 12
switch(dir)
if(NORTH)
unbuckled_mob.pixel_y -= offset_amount
if(SOUTH)
if(portal_mode == WALLSTUCK)
unbuckled_mob.pixel_y += offset_amount
unbuckled_mob.transform = turn(unbuckled_mob.transform, ROTATION_FLIP)
else
unbuckled_mob.pixel_y += 6
if(EAST)
if(portal_mode == WALLSTUCK)
unbuckled_mob.pixel_x -= offset_amount
unbuckled_mob.transform = turn(unbuckled_mob.transform, ROTATION_CLOCKWISE)
else
unbuckled_mob.pixel_x -= 12
if(WEST)
if(portal_mode == WALLSTUCK)
unbuckled_mob.pixel_x += offset_amount
unbuckled_mob.transform = turn(unbuckled_mob.transform, ROTATION_COUNTERCLOCKWISE)
else
unbuckled_mob.pixel_x += 12
if(portal_mode == GLORYHOLE)
var/obj/item/organ/genital/penis/affected_penis = unbuckled_mob.get_organ_slot(ORGAN_SLOT_PENIS) //Stolen from Strapon code, this is bad we should probably have a cleaner way
affected_penis?.visibility_preference = initial_genital_visibility
initial_genital_visibility = null
unbuckled_mob.update_body()
. = ..()
/obj/structure/lewd_portal/wrench_act_secondary(mob/living/user, obj/item/weapon)
..()
weapon.play_tool_sound(src)
deconstruct(disassembled = TRUE)
return TRUE
/obj/structure/lewd_portal/attack_hand_secondary(mob/user, list/modifiers)
. = ..()
if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
return
if(isnull(linked_portal))
balloon_alert(user, "portal not linked")
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
if(!isnull(current_mob) || !isnull(linked_portal.current_mob))
balloon_alert(user, "portal occupied")
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
if(portal_mode == GLORYHOLE)
portal_mode = WALLSTUCK
linked_portal.portal_mode = WALLSTUCK
balloon_alert(user, "switched to stuck in wall mode")
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
else
portal_mode = GLORYHOLE
linked_portal.portal_mode = GLORYHOLE
balloon_alert(user, "switched to gloryhole mode")
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
/obj/item/wallframe/lewd_portal
name = "Lustwish Portal Bore"
desc = "A device utilizing bluespace technology to transpose portions of people from one space to another."
icon = 'modular_zubbers/icons/obj/structures/lewd_portals.dmi'
icon_state = "device"
result_path = /obj/structure/lewd_portal
pixel_shift = 32
multi_use = 2
bypass_unpowered = TRUE
bypass_floor = TRUE
///The mode portals created by this device will be in
var/creation_mode = GLORYHOLE
///The previous portal placed by the bore, recorded so that they can be linked.
var/obj/structure/lewd_portal/previous_portal
/obj/item/wallframe/lewd_portal/examine(mob/user)
. = ..()
var/inspect_mode = "gloryhole"
if(creation_mode == WALLSTUCK)
inspect_mode = "stuck in wall"
. += span_notice("Its currently in [inspect_mode] mode.")
. += span_notice("Use in hand to change modes.")
/obj/item/wallframe/lewd_portal/after_attach(obj/attached_to)
var/obj/structure/lewd_portal/portal_result = attached_to
portal_result.portal_mode = creation_mode
if(!previous_portal)
previous_portal = portal_result
else
portal_result.linked_portal = previous_portal
previous_portal.linked_portal = portal_result
previous_portal = null
. = ..()
/obj/item/wallframe/lewd_portal/attack_self(mob/user)
if(previous_portal)
balloon_alert(user, "portals must match")
return
if(creation_mode == GLORYHOLE)
creation_mode = WALLSTUCK
balloon_alert(user, "switched to stuck in wall mode")
else
creation_mode = GLORYHOLE
balloon_alert(user, "switched to gloryhole mode")
/obj/lewd_portal_relay
name = "portal relay"
desc = "Someone's behind hanging out from a portal."
anchored = TRUE
layer = ABOVE_MOB_LAYER
///Mob that this relay is connected to
var/mob/living/carbon/human/owner
///Portal that spawns this relay
var/obj/structure/lewd_portal/owning_portal
///What mode this portal is in
var/portal_mode = GLORYHOLE
/obj/lewd_portal_relay/Initialize(mapload, mob/living/carbon/human/owner_ref, obj/structure/lewd_portal/owning_portal_reference)
. = ..()
appearance_flags |= KEEP_TOGETHER
if(!owner_ref || !owning_portal_reference)
return INITIALIZE_HINT_QDEL
owning_portal = owning_portal_reference
portal_mode = owning_portal.portal_mode
owner = owner_ref
if(portal_mode == GLORYHOLE)
var/obj/item/organ/genital/penis/penis_reference = owner.get_organ_slot(ORGAN_SLOT_PENIS)
var/penis_type = penis_reference.genital_name
name = LOWER_TEXT("[penis_type] penis")
desc = "Someone's penis hanging out from a portal."
dir = SOUTH
if (owning_portal.dir == EAST || owning_portal.dir == WEST)
dir = REVERSE_DIR(owning_portal.dir)
else
dir = NORTH
var/species_name
if(owner.dna?.species?.lore_protected || !owner.dna?.features["custom_species"])
species_name = owner.dna.species.name
else
species_name = owner.dna.features["custom_species"]
name = LOWER_TEXT("[species_name] behind")
RegisterSignals(owner, PORTAL_SIGNAL_LIST, PROC_REF(update_visuals))
become_hearing_sensitive(ROUNDSTART_TRAIT)
var/datum/component/interactable/interact_component = owner.GetComponent(/datum/component/interactable)
interact_component?.body_relay = src
/obj/lewd_portal_relay/Destroy(force)
if(!isnull(owner))
UnregisterSignal(owner, PORTAL_SIGNAL_LIST)
var/datum/component/interactable/interact_component = owner.GetComponent(/datum/component/interactable)
owner = null
owning_portal = null
interact_component?.body_relay = null
visible_message("[src] vanishes into the portal!")
lose_hearing_sensitivity(ROUNDSTART_TRAIT)
return ..()
/obj/lewd_portal_relay/examine(mob/user)
. = ..()
for(var/genital in GLOB.possible_genitals)
if(genital == ORGAN_SLOT_BREASTS)
continue
if(owner.dna.species.mutant_bodyparts[genital])
var/datum/sprite_accessory/genital/gential_sprite = SSaccessories.sprite_accessories[genital][owner.dna.species.mutant_bodyparts[genital][MUTANT_INDEX_NAME]]
if(gential_sprite)
if(!(gential_sprite.is_hidden(owner)))
. += "<span class='notice'>It has exposed genitals... <a href='byond://?src=[REF(src)];lookup_info=genitals'>\[Look closer...\]</a></span>"
break
/obj/lewd_portal_relay/Topic(href, href_list)
. = ..()
if(href_list["lookup_info"])
if(href_list["lookup_info"] == "genitals")
var/list/line = list()
for(var/genital in GLOB.possible_genitals)
if(!owner.dna.species.mutant_bodyparts[genital] || genital == ORGAN_SLOT_BREASTS)
continue
var/datum/sprite_accessory/genital/gential_sprite = SSaccessories.sprite_accessories[genital][owner.dna.species.mutant_bodyparts[genital][MUTANT_INDEX_NAME]]
if(!gential_sprite)
continue
if(gential_sprite.is_hidden(owner))
continue
var/obj/item/organ/genital/organ = owner.get_organ_slot(gential_sprite.associated_organ_slot)
if(!organ)
continue
line += organ.get_description_string(gential_sprite)
if(length(line))
to_chat(usr, span_notice("[jointext(line, "\n")]"))
/obj/lewd_portal_relay/proc/update_visuals()
SIGNAL_HANDLER
if(portal_mode == GLORYHOLE)
penis_only()
else
lower_body_only()
/obj/lewd_portal_relay/proc/penis_only()
cut_overlays()
var/obj/item/organ/genital/penis/penis_reference = owner.get_organ_slot(ORGAN_SLOT_PENIS)
var/mutable_appearance/penis_image = penis_reference.bodypart_overlay.get_overlay(EXTERNAL_FRONT)
add_overlay(penis_image)
/obj/lewd_portal_relay/proc/lower_body_only()
owner.dna.species.handle_body(owner) //Suboptimal way for doing this but I couldn't figure out another way to maintain underwear when dropping items
cut_overlays()
for(var/limb in list(BODY_ZONE_R_LEG, BODY_ZONE_L_LEG, BODY_ZONE_CHEST))
var/obj/item/bodypart/limb_object = owner.get_bodypart(limb)
if(istype(limb_object))
var/limb_icon_list = limb_object.get_limb_icon()
if(limb_object == owner.get_bodypart(BODY_ZONE_CHEST))
limb_icon_list = torso_only(limb_icon_list)
add_overlay(limb_icon_list)
if(owner.shoes)
add_overlay(owner.overlays_standing[SHOES_LAYER])
if(owner.w_uniform)
var/image/uniform_overlay = image(owner.overlays_standing[UNIFORM_LAYER])
uniform_overlay.add_filter("upper_body_removal", 1, list("type" = "alpha", "icon" = icon('modular_zubbers/icons/obj/structures/lewd_portals.dmi', "mask")))
add_overlay(uniform_overlay)
var/list/body_layer_overlays = list()
for(var/image/body_layer_overlay in owner.overlays_standing[BODY_LAYER])
var/image/new_body_layer_overlay = image(body_layer_overlay)
new_body_layer_overlay.add_filter("upper_body_removal", 1, list("type" = "alpha", "icon" = icon('modular_zubbers/icons/obj/structures/lewd_portals.dmi', "mask")))
body_layer_overlays += new_body_layer_overlay
add_overlay(body_layer_overlays)
/obj/lewd_portal_relay/proc/torso_only(limb_icon_list)
var/list/new_limb_icon_list = list()//There may be special cases where body overlays should not pass through portals, such as moth wings, this is used to removed them
for(var/image/limb_icon in limb_icon_list)
if(compare_organ_icon(ORGAN_SLOT_EXTERNAL_WINGS, limb_icon.icon)) //Moth wings are attached to the upper back so shouldn't be portalled, their weird sprite size also messes with rotations
continue
var/limb_icon_layer = limb_icon.layer * -1
if(limb_icon_layer != BODY_BEHIND_LAYER && limb_icon_layer != BODY_FRONT_LAYER || compare_organ_icon(ORGAN_SLOT_BREASTS, limb_icon.icon)) //Tails need to be portaled
limb_icon.add_filter("upper_body_removal", 1, list("type" = "alpha", "icon" = icon('modular_zubbers/icons/obj/structures/lewd_portals.dmi', "mask")))
new_limb_icon_list += limb_icon
return new_limb_icon_list
/obj/lewd_portal_relay/proc/compare_organ_icon(organ_slot, icon_to_compare)
var/obj/item/organ/organ_ref = owner?.get_organ_slot(organ_slot)
var/datum/bodypart_overlay/mutant/overlay_ref = organ_ref?.bodypart_overlay
var/datum/sprite_accessory/accessory_ref = overlay_ref?.sprite_datum
return accessory_ref?.icon == icon_to_compare
/obj/lewd_portal_relay/attack_hand_secondary(mob/living/user)
if(!user.can_perform_action(src, NEED_DEXTERITY|NEED_HANDS|ALLOW_RESTING))
return ..()
if(portal_mode == GLORYHOLE)
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
if(dir == NORTH)
dir = SOUTH
else
dir = NORTH
to_chat(user, span_info("You flip \the [name] over."))
to_chat(owner, span_info("You feel your behind flip over."))
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
/obj/lewd_portal_relay/click_ctrl_shift(mob/user)
owner.click_ctrl_shift(user)
#undef GLORYHOLE
#undef WALLSTUCK
#undef PORTAL_SIGNAL_LIST

View File

@@ -135,7 +135,7 @@
pay_cost(TEMP_GHOULIZE_COST - bloodcost)
log_combat(owner, target, "tremere revived", addition="Revived their ghoul using dominate")
return FALSE
if(!bloodsuckerdatum_power.make_ghoul(target) )
if(!bloodsuckerdatum_power.make_ghoul(target))
owner.balloon_alert(owner, "not a valid target for ghouling!.")
return

View File

@@ -187,6 +187,7 @@
unbuckle_mob(buckled_carbons)
else
user_unbuckle_mob(buckled_carbons, user)
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
/**
* Attempts to buckle target into the ghoulrack
@@ -331,8 +332,8 @@
return
// Convert to Ghoul!
bloodsuckerdatum.AdjustBloodVolume(-TORTURE_CONVERSION_COST)
remove_loyalties(target)
if(bloodsuckerdatum.make_ghoul(target))
remove_loyalties(target)
SEND_SIGNAL(bloodsuckerdatum, COMSIG_BLOODSUCKER_MADE_GHOUL, user, target)
/obj/structure/bloodsucker/ghoulrack/proc/do_torture(mob/living/user, mob/living/carbon/target, mult = 1)
@@ -378,7 +379,7 @@
target.apply_damages(brute = torture_dmg_brute, burn = torture_dmg_burn, def_zone = selected_bodypart.body_zone)
return TRUE
/// Offer them the oppertunity to join now.
/// Offer them the opportunity to join now.
/obj/structure/bloodsucker/ghoulrack/proc/do_disloyalty(mob/living/user, mob/living/target)
if(disloyalty_offered)
return FALSE
@@ -399,8 +400,13 @@
switch(alert_response)
if("Accept")
disloyalty_confirm = TRUE
target.visible_message(
span_notice("[target] gives in to [user]'s offer of servitude!"),
span_userdanger("You give in to [user]'s offer of servitude!"))
else
target.balloon_alert_to_viewers("stares defiantly", "refused ghouling!")
target.visible_message(
span_danger("[target] stares defiantly at [user], refusing to give in!"),
span_danger("You stare defiantly at [user], refusing to give in!"))
disloyalty_offered = FALSE
return TRUE

View File

@@ -155,7 +155,7 @@
return
owner.current.visible_message(
span_deconversion_message("[owner.current]'s eyes dart feverishly from side to side, and then stop. [owner.current.p_they(TRUE)] seem[owner.current.p_s()] calm, \
span_deconversion_message("[owner.current]'s eyes dart feverishly from side to side, and then stop. [owner.current.p_They(TRUE)] seem[owner.current.p_s()] to calm, \
like [owner.current.p_they()] [owner.current.p_have()] regained some lost part of [owner.current.p_them()]self."), \
span_deconversion_message("With a snap, you are no longer enslaved to [master.owner]! You breathe in heavily, having regained your free will."))
owner.current.playsound_local(null, 'sound/effects/magic/mutate.ogg', 100, FALSE, pressure_affected = FALSE)

View File

@@ -62,6 +62,8 @@
/obj/machinery/computer/nanite_cloud_controller/attack_hand_secondary(mob/user, list/modifiers)
. = ..()
if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
return
if(disk && user.can_perform_action(src, !issilicon(user)))
to_chat(user, span_notice("You take out [disk] from [src]."))
eject(user)

View File

@@ -92,6 +92,8 @@
/obj/machinery/nanite_program_hub/attack_hand_secondary(mob/user, list/modifiers)
. = ..()
if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
return
if(disk && user.can_perform_action(src, !issilicon(user)))
to_chat(user, span_notice("You take out [disk] from [src]."))
eject(user)

View File

@@ -58,6 +58,8 @@
/obj/machinery/nanite_programmer/attack_hand_secondary(mob/user, list/modifiers)
. = ..()
if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
return
if(disk && user.can_perform_action(src, !issilicon(user)))
to_chat(user, span_notice("You take out [disk] from [src]."))
eject(user)

View File

@@ -0,0 +1,134 @@
/datum/chemical_reaction/mutationtoxin/abductor
results = list(/datum/reagent/mutationtoxin/abductor = 1)
required_reagents = list(/datum/reagent/liquid_dark_matter = 1, /datum/reagent/consumable/ethanol/abduction_fruit = 3, /datum/reagent/medicine/psicodine = 2, /datum/reagent/mutationtoxin = 1)
required_temp = 320
/datum/chemical_reaction/mutationtoxin/akula
results = list(/datum/reagent/mutationtoxin/akula = 1)
required_reagents = list(/datum/reagent/consumable/ethanol/salt_and_swell = 3, /datum/reagent/toxin/carpotoxin = 2, /datum/reagent/ammonia = 2, /datum/reagent/mutationtoxin = 1)
required_temp = 300
/datum/chemical_reaction/mutationtoxin/android
results = list(/datum/reagent/mutationtoxin/android = 1)
required_reagents = list(/datum/reagent/consumable/ethanol/oil_drum = 3, /datum/reagent/medicine/nanite_slurry = 2, /datum/reagent/consumable/ethanol/synthanol/robottears = 2, /datum/reagent/mutationtoxin = 1)
required_temp = 320
/datum/chemical_reaction/mutationtoxin/ash
results = list(/datum/reagent/mutationtoxin/ash = 1)
required_reagents = list(/datum/reagent/consumable/ethanol/coldscales = 3, /datum/reagent/consumable/ethanol/lizardwine = 3, /datum/reagent/ash = 2, /datum/reagent/mutationtoxin = 1)
required_temp = 400
/datum/chemical_reaction/mutationtoxin/dwarf
results = list(/datum/reagent/mutationtoxin/dwarf = 1)
required_reagents = list(/datum/reagent/consumable/ethanol/manly_dorf = 3, /datum/reagent/consumable/ethanol/bacchus_blessing = 2, /datum/reagent/consumable/ethanol/golden_grog = 2, /datum/reagent/mutationtoxin = 1)
required_temp = 320
/datum/chemical_reaction/mutationtoxin/felinid
results = list(/datum/reagent/mutationtoxin/felinid = 1)
required_reagents = list(/datum/reagent/toxin/mindbreaker = 5, /datum/reagent/consumable/ethanol/frisky_kitty = 3, /datum/reagent/drug/aphrodisiac/crocin = 2, /datum/reagent/mutationtoxin = 1)
required_temp = 320
/datum/chemical_reaction/mutationtoxin/felinid/primitive
results = list(/datum/reagent/mutationtoxin/felinid/primitive = 1)
required_reagents = list(/datum/reagent/consumable/frostoil = 5, /datum/reagent/consumable/ethanol/frisky_kitty = 3, /datum/reagent/drug/aphrodisiac/crocin = 2, /datum/reagent/mutationtoxin = 1)
required_temp = 270
/datum/chemical_reaction/mutationtoxin/fly
results = list(/datum/reagent/mutationtoxin/fly = 1)
required_reagents = list(/datum/reagent/consumable/ethanol/bug_zapper = 3, /datum/reagent/yuck = 3, /datum/reagent/toxin/bad_food = 3, /datum/reagent/mutationtoxin = 1)
required_temp = 320
/datum/chemical_reaction/mutationtoxin/ghoul
results = list(/datum/reagent/mutationtoxin/ghoul = 1)
required_reagents = list(/datum/reagent/consumable/ethanol/velvet_kiss = 3, /datum/reagent/medicine/strange_reagent = 2, /datum/reagent/toxin/ghoulpowder = 2, /datum/reagent/mutationtoxin = 1)
required_temp = 320
/datum/chemical_reaction/mutationtoxin/golem
results = list(/datum/reagent/mutationtoxin/golem = 1)
required_reagents = list(/datum/reagent/consumable/ethanol/laval_spit = 3, /datum/reagent/drug/space_drugs = 2, /datum/reagent/smart_foaming_agent = 2, /datum/reagent/mutationtoxin = 1)
required_temp = 320
/datum/chemical_reaction/mutationtoxin/hemophage
results = list(/datum/reagent/mutationtoxin/hemophage = 1)
required_reagents = list(/datum/reagent/consumable/ethanol/velvet_kiss = 3, /datum/reagent/blood = 2, /datum/reagent/consumable/ethanol/wine = 2, /datum/reagent/mutationtoxin = 1)
required_temp = 320
/datum/chemical_reaction/mutationtoxin/jelly
results = list(/datum/reagent/mutationtoxin/jelly = 1)
required_reagents = list(/datum/reagent/consumable/ethanol/jell_wyrm = 3, /datum/reagent/medicine/regen_jelly = 3, /datum/reagent/medicine/c2/synthflesh = 2, /datum/reagent/mutationtoxin = 1)
required_temp = 320
/datum/chemical_reaction/mutationtoxin/lizard
results = list(/datum/reagent/mutationtoxin/lizard = 1)
required_reagents = list(/datum/reagent/consumable/ethanol/coldscales = 3, /datum/reagent/consumable/ethanol/lizardwine = 3, /datum/reagent/consumable/mushroom_tea = 2, /datum/reagent/mutationtoxin = 1)
required_temp = 350
/datum/chemical_reaction/mutationtoxin/monkey
results = list(/datum/reagent/mutationtoxin/monkey = 1)
required_reagents = list(/datum/reagent/consumable/funky_monkey = 3, /datum/reagent/medicine/coagulant/banana_peel = 2, /datum/reagent/potassium = 2, /datum/reagent/mutationtoxin = 1)
required_temp = 320
/datum/chemical_reaction/mutationtoxin/moth
results = list(/datum/reagent/mutationtoxin/moth = 1)
required_reagents = list(/datum/reagent/consumable/moth_milk = 3, /datum/reagent/medicine/mannitol = 2, /datum/reagent/medicine/oculine = 2, /datum/reagent/mutationtoxin = 1)
required_temp = 320
/datum/chemical_reaction/mutationtoxin/plasma
results = list(/datum/reagent/mutationtoxin/plasma = 1)
required_reagents = list(/datum/reagent/medicine/cryoxadone = 2, /datum/reagent/stable_plasma = 2, /datum/reagent/bone_dust = 2, /datum/reagent/mutationtoxin = 1)
required_temp = 180
/datum/chemical_reaction/mutationtoxin/pod
results = list(/datum/reagent/mutationtoxin/pod = 1)
required_reagents = list(/datum/reagent/cellulose = 20, /datum/reagent/consumable/ethanol/mush_crush = 3, /datum/reagent/plantnutriment/robustharvestnutriment = 3, /datum/reagent/mutationtoxin = 1)
required_temp = 320
/datum/chemical_reaction/mutationtoxin/skeleton
results = list(/datum/reagent/mutationtoxin/skeleton = 1)
required_reagents = list(/datum/reagent/consumable/ethanol/hollow_bone = 3, /datum/reagent/liquid_dark_matter = 5, /datum/reagent/medicine/strange_reagent = 5, /datum/reagent/consumable/milk = 30, /datum/reagent/bone_dust = 2, /datum/reagent/water/hollowwater = 5, /datum/reagent/mutationtoxin = 1)
required_temp = 400
/datum/chemical_reaction/mutationtoxin/skrell
results = list(/datum/reagent/mutationtoxin/skrell = 1)
required_reagents = list(/datum/reagent/consumable/ethanol/tropical_storm = 3, /datum/reagent/toxin/carpotoxin = 2, /datum/reagent/ammonia = 2, /datum/reagent/mutationtoxin = 1)
required_temp = 320
/datum/chemical_reaction/mutationtoxin/shadow
results = list(/datum/reagent/mutationtoxin/shadow = 1)
required_reagents = list(/datum/reagent/liquid_dark_matter = 5, /datum/reagent/consumable/ethanol/singulo = 2, /datum/reagent/smoke_powder = 2, /datum/reagent/mutationtoxin = 1)
required_temp = 180
/datum/chemical_reaction/mutationtoxin/snail
results = list(/datum/reagent/snail = 1)
required_reagents = list(/datum/reagent/consumable/cucumberlemonade = 3, /datum/reagent/toxin/slimejelly = 2, /datum/reagent/lube = 2, /datum/reagent/mutationtoxin = 1)
required_temp = 320
/datum/chemical_reaction/mutationtoxin/tajaran
results = list(/datum/reagent/mutationtoxin/tajaran = 1)
required_reagents = list(/datum/reagent/consumable/ethanol/frisky_kitty = 3, /datum/reagent/consumable/frostoil = 2, /datum/reagent/consumable/ice = 2, /datum/reagent/mutationtoxin = 1)
required_temp = 270
/datum/chemical_reaction/mutationtoxin/teshari
results = list(/datum/reagent/mutationtoxin/teshari = 1)
required_reagents = list(/datum/reagent/consumable/ethanol/antifreeze = 3, /datum/reagent/drug/maint/sludge = 2, /datum/reagent/lube = 2, /datum/reagent/mutationtoxin = 1)
required_temp = 320
/datum/chemical_reaction/mutationtoxin/vox
results = list(/datum/reagent/mutationtoxin/vox = 1)
required_reagents = list(/datum/reagent/consumable/ethanol/toxins_special= 3, /datum/reagent/medicine/nanite_slurry = 2, /datum/reagent/nitrogen = 2, /datum/reagent/mutationtoxin = 1)
required_temp = 320
/datum/chemical_reaction/mutationtoxin/vox_primalis
results = list(/datum/reagent/mutationtoxin/vox_primalis = 1)
required_reagents = list(/datum/reagent/consumable/hot_ice_coffee = 3, /datum/reagent/medicine/nanite_slurry = 2, /datum/reagent/nitrogen = 2, /datum/reagent/mutationtoxin = 1)
required_temp = 320
/datum/chemical_reaction/mutationtoxin/xenohybrid
results = list(/datum/reagent/mutationtoxin/xenohybrid = 1)
required_reagents = list(/datum/reagent/consumable/ethanol/neurotoxin = 3, /datum/reagent/silicon = 2, /datum/reagent/toxin/acid/fluacid = 2, /datum/reagent/mutationtoxin = 1)
required_temp = 320
/datum/chemical_reaction/mutationtoxin/zombie
results = list(/datum/reagent/mutationtoxin/zombie = 1)
required_reagents = list(/datum/reagent/liquid_dark_matter = 5, /datum/reagent/toxin/zombiepowder = 2, /datum/reagent/medicine/strange_reagent = 2, /datum/reagent/mutationtoxin = 1)
required_temp = 320

View File

@@ -0,0 +1,99 @@
/datum/reagent/mutationtoxin/akula
name = "Akula Mutation Toxin"
description = "An akula toxin."
color = "#5EFF3B" //RGB: 94, 255, 59
race = /datum/species/akula
taste_description = "fishy"
chemical_flags = REAGENT_CAN_BE_SYNTHESIZED|REAGENT_NO_RANDOM_RECIPE
/datum/reagent/mutationtoxin/dwarf
name = "Dwarf Mutation Toxin"
description = "A dwarf toxin."
color = "#5EFF3B" //RGB: 94, 255, 59
race = /datum/species/dwarf
taste_description = "earthy"
chemical_flags = REAGENT_CAN_BE_SYNTHESIZED|REAGENT_NO_RANDOM_RECIPE
/datum/reagent/mutationtoxin/felinid/primitive
name = "Ice Walker Mutation Toxin"
description = "A ice Walker toxin."
color = "#5EFF3B" //RGB: 94, 255, 59
race = /datum/species/human/felinid/primitive
taste_description = "something ancient and cold"
chemical_flags = REAGENT_CAN_BE_SYNTHESIZED|REAGENT_NO_RANDOM_RECIPE
/datum/reagent/mutationtoxin/ghoul
name = "Ghoul Mutation Toxin"
description = "A ghoul toxin."
color = "#5EFF3B" //RGB: 94, 255, 59
race = /datum/species/ghoul
taste_description = "rotting flesh"
chemical_flags = REAGENT_CAN_BE_SYNTHESIZED|REAGENT_NO_RANDOM_RECIPE
/datum/reagent/mutationtoxin/hemophage
name = "Hemophage Corruption Virus"
description = "A hemophage virus."
color = BLOOD_COLOR_RED
taste_description = "blood"
chemical_flags = REAGENT_CAN_BE_SYNTHESIZED|REAGENT_NO_RANDOM_RECIPE
/datum/reagent/mutationtoxin/hemophage/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message = TRUE, touch_protection = 0)
. = ..()
if((methods & (PATCH|INGEST|INJECT|INHALE)) || ((methods & (VAPOR|TOUCH)) && prob(min(reac_volume,100)*(1 - touch_protection))))
exposed_mob.ForceContractDisease(new /datum/disease/transformation/hemophage(), FALSE, TRUE)
/datum/reagent/mutationtoxin/monkey
name = "Monkey Mutation Toxin"
description = "A monkey toxin."
color = "#5EFF3B" //RGB: 94, 255, 59
race = /datum/species/monkey
taste_description = "bananas"
chemical_flags = REAGENT_CAN_BE_SYNTHESIZED|REAGENT_NO_RANDOM_RECIPE
/datum/reagent/mutationtoxin/skrell
name = "Skrell Mutation Toxin"
description = "A skrell toxin."
color = "#5EFF3B" //RGB: 94, 255, 59
race = /datum/species/skrell
taste_description = "squid"
chemical_flags = REAGENT_CAN_BE_SYNTHESIZED|REAGENT_NO_RANDOM_RECIPE
/datum/reagent/mutationtoxin/tajaran
name = "Tajaran Mutation Toxin"
description = "A tajaran toxin."
color = "#5EFF3B" //RGB: 94, 255, 59
race = /datum/species/tajaran
taste_description = "toxoplasmosis"
chemical_flags = REAGENT_CAN_BE_SYNTHESIZED|REAGENT_NO_RANDOM_RECIPE
/datum/reagent/mutationtoxin/teshari
name = "Teshari Mutation Toxin"
description = "A teshari toxin."
color = "#5EFF3B" //RGB: 94, 255, 59
race = /datum/species/teshari
taste_description = "fried chicken"
chemical_flags = REAGENT_CAN_BE_SYNTHESIZED|REAGENT_NO_RANDOM_RECIPE
/datum/reagent/mutationtoxin/vox
name = "Vox Mutation Toxin"
description = "A voxin."
color = "#5EFF3B" //RGB: 94, 255, 59
race = /datum/species/vox
taste_description = "skreeing with a metallic tinge"
chemical_flags = REAGENT_CAN_BE_SYNTHESIZED|REAGENT_NO_RANDOM_RECIPE
/datum/reagent/mutationtoxin/vox_primalis
name = "Vox Primalis Mutation Toxin"
description = "A vox primalis toxin."
color = "#5EFF3B" //RGB: 94, 255, 59
race = /datum/species/vox_primalis
taste_description = "screeching with a metallic tinge"
chemical_flags = REAGENT_CAN_BE_SYNTHESIZED|REAGENT_NO_RANDOM_RECIPE
/datum/reagent/mutationtoxin/xenohybrid
name = "Xenohybrid Mutation Toxin"
description = "A xenohybrid toxin."
color = "#5EFF3B" //RGB: 94, 255, 59
race = /datum/species/xeno
taste_description = "sour"
chemical_flags = REAGENT_CAN_BE_SYNTHESIZED|REAGENT_NO_RANDOM_RECIPE

View File

@@ -5,6 +5,6 @@
/datum/storyteller_data/tracks
var/threshold_mundane = 1200
var/threshold_moderate = 1800
var/threshold_major = 8000
var/threshold_major = 7000
var/threshold_crewset = 1800
var/threshold_ghostset = 7000
var/threshold_ghostset = 6000

View File

@@ -18,6 +18,6 @@
/datum/storyteller_data/tracks/fragile
threshold_mundane = 1200
threshold_moderate = 1800
threshold_major = 8000
threshold_major = 10000
threshold_crewset = 3000
threshold_ghostset = 8000

View File

@@ -17,5 +17,5 @@
/datum/storyteller_data/tracks/gamer
threshold_moderate = 1300
threshold_major = 6150
threshold_major = 4000
threshold_ghostset = 6000

View File

@@ -0,0 +1,21 @@
/mob/living/silicon/get_silicon_flavortext()
. = ..()
if(!CONFIG_GET(flag/check_vetted))
return
if(!client || !SSplayer_ranks.initialized)
return
if(SSplayer_ranks.is_vetted(client, admin_bypass = FALSE))
. += span_greenannounce("This player has been vetted as 18+ by staff.")
else
. += span_velvet("THIS PLAYER IS NOT VETTED! CONTINUE AT YOUR OWN RISK!")
/mob/living/carbon/human/examine(mob/user)
. = ..()
if(!CONFIG_GET(flag/check_vetted))
return
if(!client || !SSplayer_ranks.initialized)
return
if(SSplayer_ranks.is_vetted(client, admin_bypass = FALSE))
. += span_greenannounce("This player has been vetted as 18+ by staff.")
else
. += span_velvet("THIS PLAYER IS NOT VETTED! CONTINUE AT YOUR OWN RISK!")

Binary file not shown.

After

Width:  |  Height:  |  Size: 797 B

View File

@@ -237,6 +237,9 @@
handle_dumping(user, tool)
/obj/machinery/cauldron/attack_hand_secondary(mob/user, list/modifiers)
. = ..()
if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
return
if(user.can_perform_action(src))
if(!length(ingredients))
balloon_alert(user, "it's empty!")

View File

@@ -8889,6 +8889,7 @@
#include "modular_zubbers\code\datums\components\vore\z_undefs.dm"
#include "modular_zubbers\code\datums\computer_datums\protolathe_modifications.dm"
#include "modular_zubbers\code\datums\diseases\advance\presets.dm"
#include "modular_zubbers\code\datums\diseases\advance\transformation.dm"
#include "modular_zubbers\code\datums\elements\examine_when_worn.dm"
#include "modular_zubbers\code\datums\elements\nanite_organ.dm"
#include "modular_zubbers\code\datums\greyscale\config_types\greyscale_configs\greyscale_clothes.dm"
@@ -9048,6 +9049,7 @@
#include "modular_zubbers\code\game\objects\items\tanks\tank_types.dm"
#include "modular_zubbers\code\game\objects\structures\aliens.dm"
#include "modular_zubbers\code\game\objects\structures\chalkboard.dm"
#include "modular_zubbers\code\game\objects\structures\lewd_portals.dm"
#include "modular_zubbers\code\game\objects\structures\ore_vent.dm"
#include "modular_zubbers\code\game\objects\structures\spirit_board.dm"
#include "modular_zubbers\code\game\objects\structures\tables_racks.dm"
@@ -9548,6 +9550,8 @@
#include "modular_zubbers\code\modules\ratqueen\regalrat.dm"
#include "modular_zubbers\code\modules\reagents\drink_reagents.dm"
#include "modular_zubbers\code\modules\reagents\chemistry\reagents\food_reagents.dm"
#include "modular_zubbers\code\modules\reagents\chemistry\reagents\other_reagents.dm"
#include "modular_zubbers\code\modules\reagents\chemistry\reagents\Recipes\others.dm"
#include "modular_zubbers\code\modules\reagents\reagent_containers\condiment.dm"
#include "modular_zubbers\code\modules\reagents\reagent_containers\medigel.dm"
#include "modular_zubbers\code\modules\reagents\reagent_containers\cups\bottle.dm"
@@ -9688,6 +9692,7 @@
#include "modular_zubbers\code\modules\vending\multisec.dm"
#include "modular_zubbers\code\modules\vending\vending.dm"
#include "modular_zubbers\code\modules\vending\wardrobe.dm"
#include "modular_zubbers\code\modules\vetted\examine.dm"
#include "modular_zubbers\code\modules\vetted\vetted.dm"
#include "modular_zubbers\code\modules\vetted\overrides\erp_preferences.dm"
#include "modular_zubbers\code\modules\voting\_votes.dm"