Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds a new changeling ability "Storage Cavity", for storing IDs and small items #3874

Merged
merged 3 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions code/__DEFINES/DNA.dm
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
#define ORGAN_SLOT_LEFT_ARM_AUG "l_arm_device" //This one ignores alphabetical order cause the arms should be together
#define ORGAN_SLOT_STOMACH "stomach"
#define ORGAN_SLOT_STOMACH_AID "stomach_aid"
#define ORGAN_SLOT_STORAGE_CAVITY "storage_cavity" // monkestation edit
#define ORGAN_SLOT_THRUSTERS "thrusters"
#define ORGAN_SLOT_TONGUE "tongue"
#define ORGAN_SLOT_VOICE "vocal_cords"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
/datum/action/changeling/storage_cavity
name = "Storage Cavity"
desc = "We evolve smaller organs, allowing us to covertly store things in their place. Opening and closing it isn't very stealthy, though."
helptext = "Can store up to 3 small items. IDs can open things from inside. Works in lesser form and while downed. Can be seen and removed via surgery."
button_icon = 'monkestation/icons/mob/actions/actions_changeling.dmi'
button_icon_state = "storage_cavity"
dna_cost = 1
req_stat = SOFT_CRIT

var/obj/item/organ/internal/storage_cavity/cavity

/datum/action/changeling/storage_cavity/Grant(mob/grant_to)
. = ..()
create_cavity()

/datum/action/changeling/storage_cavity/Remove(mob/remove_from)
. = ..()
qdel(cavity)

/datum/action/changeling/storage_cavity/sting_action(mob/living/user, mob/living/target)
..()

if(create_cavity())
to_chat(user, span_warning("Our insides squelch as we regrow our storage cavity."))

if(!cavity)
user.balloon_alert(user, "no cavity!")
return FALSE

if(!cavity.atom_storage)
qdel(cavity) // Attempt to recover.
CRASH("[user] ([user.key]) tried to use their cavity storage, but it didn't have a storage datum. This should never happen.")

if(user in cavity.atom_storage.is_using)
cavity.atom_storage.hide_contents(user)
else
cavity.atom_storage.open_storage(user)

return FALSE

/// Creates a storage cavity organ if we don't already have one. Returns whether we grew a new one.
/datum/action/changeling/storage_cavity/proc/create_cavity()
if(!iscarbon(owner) || cavity)
return FALSE
cavity = new
cavity.Insert(owner)
RegisterSignals(cavity, list(COMSIG_QDELETING, COMSIG_ORGAN_REMOVED), PROC_REF(forget_cavity))
return TRUE

/datum/action/changeling/storage_cavity/proc/forget_cavity(datum/source)
SIGNAL_HANDLER
UnregisterSignal(cavity, list(COMSIG_QDELETING, COMSIG_ORGAN_REMOVED))
cavity = null

/obj/item/organ/internal/storage_cavity
name = "rigid membrane"
desc = "An enlarged protective membrane with rigid walls. It looks like a few things might fit inside."
icon = 'monkestation/icons/obj/medical/organs/organs.dmi'
icon_state = "storage_cavity"
visual = TRUE
zone = BODY_ZONE_CHEST
slot = ORGAN_SLOT_STORAGE_CAVITY
decay_factor = 0

var/list/access = list()

/obj/item/organ/internal/storage_cavity/Initialize(mapload)
. = ..()
create_storage(storage_type = /datum/storage/storage_cavity)

/obj/item/organ/internal/storage_cavity/Insert(mob/living/carbon/receiver, special, drop_if_replaced)
. = ..()
if(!. || !owner)
return
forceMove(receiver)
RegisterSignal(receiver, COMSIG_MOB_TRIED_ACCESS, PROC_REF(on_try_access))

/obj/item/organ/internal/storage_cavity/Remove(mob/living/carbon/organ_owner, special)
. = ..()
UnregisterSignal(organ_owner, COMSIG_MOB_TRIED_ACCESS)

/obj/item/organ/internal/storage_cavity/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs)
. = ..()
if(isidcard(arrived))
var/obj/item/item = arrived
access += item.GetAccess()


/obj/item/organ/internal/storage_cavity/Exited(atom/movable/gone, direction)
. = ..()
if(isidcard(gone))
var/obj/item/item = gone
access -= item.GetAccess()

/obj/item/organ/internal/storage_cavity/proc/on_try_access(datum/source, obj/target)
SIGNAL_HANDLER
return target.check_access_list(access)

/datum/storage/storage_cavity
max_specific_storage = WEIGHT_CLASS_SMALL
max_total_storage = 6
max_slots = 3
silent = TRUE
silent_for_user = TRUE
rustle_sound = FALSE
allow_big_nesting = FALSE

var/datum/bodypart_overlay/simple/storage_cavity/overlay

/// Hack to make refresh_views not trigger opening and closing effects.
var/refreshing

/datum/storage/storage_cavity/open_storage(mob/user)
. = ..()
if(!. || refreshing)
return

var/obj/item/organ/cavity = real_location?.resolve()
if(!istype(cavity))
return

playsound(cavity, 'sound/effects/wounds/splatter.ogg', vol = 30, vary = TRUE, extrarange = SILENCED_SOUND_EXTRARANGE, ignore_walls = FALSE)

if(cavity.owner != user)
return

user.visible_message(
message = span_danger("[user]'s chest opens up, revealing a large cavity!"),
self_message = span_notice("We open our storage cavity."),
blind_message = span_hear("You hear a squelch."),
vision_distance = 2
)

apply_overlay(cavity, user)

/datum/storage/storage_cavity/hide_contents(mob/user)
. = ..()
if(isobserver(user) || refreshing)
return

var/obj/item/organ/cavity = real_location?.resolve()
if(!istype(cavity))
return

playsound(cavity, 'sound/effects/wounds/splatter.ogg', vol = 30, vary = TRUE, extrarange = SILENCED_SOUND_EXTRARANGE, ignore_walls = FALSE)

if(cavity.owner != user)
return

user.visible_message(
message = span_danger("[user]'s chest closes up!"),
self_message = span_notice("We close our storage cavity."),
blind_message = span_hear("You hear a squelch."),
vision_distance = 2
)

clear_overlay(cavity, user)

/datum/storage/storage_cavity/refresh_views()
refreshing = TRUE
. = ..()
refreshing = FALSE

/datum/storage/storage_cavity/proc/apply_overlay(obj/item/organ/cavity, mob/living/carbon/user)
RegisterSignal(cavity, COMSIG_ORGAN_REMOVED, PROC_REF(clear_overlay))
RegisterSignal(user, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
var/obj/item/bodypart/chest = user.get_bodypart(BODY_ZONE_CHEST)
overlay = new
chest.add_bodypart_overlay(overlay)
user.update_body()

/datum/storage/storage_cavity/proc/clear_overlay(obj/item/organ/cavity, mob/living/carbon/user)
SIGNAL_HANDLER
UnregisterSignal(cavity, COMSIG_ORGAN_REMOVED)
UnregisterSignal(user, COMSIG_ATOM_EXAMINE)
var/obj/item/bodypart/chest = user.get_bodypart(BODY_ZONE_CHEST)
chest.remove_bodypart_overlay(overlay)
user.update_body()
QDEL_NULL(overlay)

/datum/storage/storage_cavity/proc/on_examine(mob/living/carbon/user, mob/examiner, list/examine_text)
SIGNAL_HANDLER
examine_text += span_bolddanger("[user.p_Their()] chest has a large, open cavity!")

/datum/bodypart_overlay/simple/storage_cavity
icon = 'monkestation/icons/mob/species/misc/bodypart_overlay_simple.dmi'
icon_state = "storage_cavity"
layers = EXTERNAL_ADJACENT
Binary file not shown.
Binary file not shown.
Binary file modified monkestation/icons/obj/medical/organs/organs.dmi
Binary file not shown.
1 change: 1 addition & 0 deletions tgstation.dme
Original file line number Diff line number Diff line change
Expand Up @@ -6090,6 +6090,7 @@
#include "monkestation\code\modules\antagonists\brother\gear\_gear.dm"
#include "monkestation\code\modules\antagonists\brother\gear\misc.dm"
#include "monkestation\code\modules\antagonists\brother\gear\recipes.dm"
#include "monkestation\code\modules\antagonists\changeling\powers\storage_cavity.dm"
#include "monkestation\code\modules\antagonists\changeling\powers\teratomas.dm"
#include "monkestation\code\modules\antagonists\changeling\powers\tiny_prick.dm"
#include "monkestation\code\modules\antagonists\clock_cult\area.dm"
Expand Down
Loading