Skip to content

Commit

Permalink
Adds Auto CPR device
Browse files Browse the repository at this point in the history
  • Loading branch information
MrMelbert committed Nov 8, 2023
1 parent 11382be commit 3a3661d
Show file tree
Hide file tree
Showing 8 changed files with 172 additions and 55 deletions.
4 changes: 2 additions & 2 deletions code/datums/elements/attack_equip.dm
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

/datum/element/attack_equip/proc/on_item_attack(obj/item/attire, mob/living/target, mob/living/user)
SIGNAL_HANDLER
if(user.combat_mode || !ishuman(target) || target == user)
if(user.combat_mode || !ishuman(target)) // NON-MODULE CHANGE : Allow self hit
return

var/mob/living/carbon/human/sharp_dresser = target
Expand Down Expand Up @@ -50,6 +50,6 @@

user.temporarilyRemoveItemFromInventory(attire)

sharp_dresser.equip_to_slot_if_possible(attire, attire.slot_flags)
sharp_dresser.equip_to_slot_if_possible(attire, attire.slot_flags, bypass_equip_delay_self = TRUE) // NON-MODULE CHANGE : Bugged

return finish_equip_mob(attire, sharp_dresser, user)
4 changes: 4 additions & 0 deletions maplestation.dme
Original file line number Diff line number Diff line change
Expand Up @@ -5631,6 +5631,10 @@
#include "maplestation_modules\code\modules\mob\living\carbon\human\human.dm"
#include "maplestation_modules\code\modules\mob\living\carbon\human\modular_sechud_icons.dm"
#include "maplestation_modules\code\modules\mob\living\carbon\human\skrell_hair.dm"
#include "maplestation_modules\code\modules\mob\living\carbon\human\heart_rework\autopulse.dm"
#include "maplestation_modules\code\modules\mob\living\carbon\human\heart_rework\cpr.dm"
#include "maplestation_modules\code\modules\mob\living\carbon\human\heart_rework\heart_attack.dm"
#include "maplestation_modules\code\modules\mob\living\carbon\human\heart_rework\heart_overrides.dm"
#include "maplestation_modules\code\modules\mob\living\carbon\human\ornithid_features\armwings.dm"
#include "maplestation_modules\code\modules\mob\living\carbon\human\ornithid_features\avian_tails.dm"
#include "maplestation_modules\code\modules\mob\living\carbon\human\ornithid_features\plumage.dm"
Expand Down
56 changes: 3 additions & 53 deletions maplestation_modules/code/datums/pain/pain_implements.dm
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@
#define FROZEN_ITEM_PAIN_MODIFIER 0.5
#define FROZEN_ITEM_TEMPERATURE_CHANGE -5

/// do_after key for using emergency blankets.
#define DOAFTER_SOURCE_BLANKET "doafter_blanket"

// Holding a beer to your busted arm, now that's classic
/obj/item/reagent_containers/cup/glass/bottle/beer/Initialize(mapload)
. = ..()
Expand Down Expand Up @@ -337,7 +334,8 @@
max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT
min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT
armor_type = /datum/armor/shock_blanket
equip_delay_self = 2 SECONDS
equip_delay_self = 3 SECONDS
equip_delay_other = 2 SECONDS
slowdown = 1.5
throwforce = 0
throw_speed = 1
Expand All @@ -350,6 +348,7 @@
name = pick("space blanket", "safety blanket")

AddElement(/datum/element/bed_tuckable, 0, 0, 0)
AddElement(/datum/element/attack_equip)

/obj/item/shock_blanket/examine(mob/user)
. = ..()
Expand All @@ -367,53 +366,6 @@
icon_state = "[initial(icon_state)]_dropped"
layer = MOB_LAYER


/obj/item/shock_blanket/pre_attack(atom/target, mob/living/user, params)
. = ..()
if(.)
return

if(ishuman(target))
try_shelter_mob(target, user)
return TRUE

/**
* Try to equip [target] with [src], done by [user].
* Basically, the ability to equip someone just by hitting them with the blanket,
* instead of needing to use the strip menu.
*
* target - the mob being hit with the blanket
* user - the mob hitting the target
*/
/obj/item/shock_blanket/proc/try_shelter_mob(mob/living/carbon/human/target, mob/living/user)
if(DOING_INTERACTION(user, DOAFTER_SOURCE_BLANKET))
return FALSE

if(target.wear_suit)
to_chat(user, span_warning("You need to take off [target == user ? "your" : "their"] [target.wear_suit.name] first!"))
return FALSE

to_chat(user, span_notice("You begin wrapping [target == user ? "yourself" : "[target]"] with [src]..."))
visible_message(span_notice("[user] begins wrapping [target == user ? "[user.p_them()]self" : "[target]"] with [src]..."), ignored_mobs = list(user))
if(!do_after(user, (target == user ? equip_delay_self : equip_delay_other), target, interaction_key = DOAFTER_SOURCE_BLANKET))
return FALSE

do_shelter_mob(target, user)
return TRUE

/**
* Actually equip [target] with [src], done by [user].
*
* target - the mob being equipped
* user - the mob equipping the target
*/
/obj/item/shock_blanket/proc/do_shelter_mob(mob/living/carbon/human/target, mob/living/user)
if(target.equip_to_slot_if_possible(src, ITEM_SLOT_OCLOTHING, disable_warning = TRUE, bypass_equip_delay_self = TRUE))
to_chat(user, span_notice("You wrap [target == user ? "yourself" : "[target]"] with [src], helping regulate body temperature."))
user.update_held_items() // melbert todo, is this necessary?
else
to_chat(user, span_warning("You can't quite reach and fail to wrap [target == user ? "yourself" : "[target]"] with [src]."))

/obj/item/shock_blanket/equipped(mob/user, slot)
. = ..()
if(!isliving(user))
Expand Down Expand Up @@ -622,5 +574,3 @@
#undef FROZEN_ITEM_PAIN_RATE
#undef FROZEN_ITEM_PAIN_MODIFIER
#undef FROZEN_ITEM_TEMPERATURE_CHANGE

#undef DOAFTER_SOURCE_BLANKET
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
/datum/design/auto_cpr_device
name = "CPR Autopulser"
desc = "A device to automatically perform chest compressions on a patient. \
Requires a cell to power it."
id = "auto_cpr_device"
build_type = PROTOLATHE
materials = list(
/datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT,
/datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT,
/datum/material/silver = SMALL_MATERIAL_AMOUNT,
/datum/material/gold = SMALL_MATERIAL_AMOUNT,
)
build_path = /obj/item/auto_cpr
category = list(RND_CATEGORY_EQUIPMENT + RND_SUBCATEGORY_EQUIPMENT_MEDICAL)
departmental_flags = DEPARTMENT_BITFLAG_MEDICAL

/datum/techweb_node/adv_biotech
id_additions = list("auto_cpr_device")

/obj/item/auto_cpr
name = "autopulser"
desc = "A device with straps that can be worn around the chest. \
It will automatically perform chest compressions on the wearer, \
which can be useful for patients in cardiac arrest."

icon = 'maplestation_modules/icons/obj/autopulser.dmi'
worn_icon = 'maplestation_modules/icons/mob/autopulser.dmi'
lefthand_file = 'maplestation_modules/icons/mob/inhands/autopulser_lhand.dmi'
righthand_file = 'maplestation_modules/icons/mob/inhands/autopulser_rhand.dmi'
icon_state = "autopulser"

drop_sound = 'sound/items/handling/component_drop.ogg'
pickup_sound = 'sound/items/handling/component_pickup.ogg'

slot_flags = ITEM_SLOT_OCLOTHING
body_parts_covered = CHEST
resistance_flags = FIRE_PROOF

equip_delay_self = 3 SECONDS
equip_delay_other = 2 SECONDS
slowdown = 1
throw_speed = 1
throw_range = 2
custom_price = PAYCHECK_COMMAND * 10

/// Tracks how many pulses we've done on the current patient, to prevent message spam
VAR_FINAL/pulse_count = 0
/// The cell that powers this device
VAR_FINAL/obj/item/stock_parts/cell/cell
/// How much cell charge to use per pulse / compression
var/charge_per_pulse = 50

/obj/item/auto_cpr/Initialize(mapload)
. = ..()
AddElement(/datum/element/attack_equip)

/obj/item/auto_cpr/Destroy()
QDEL_NULL(cell)
return ..()

/obj/item/auto_cpr/deconstruct(disassembled)
cell.forceMove(drop_location())
return ..()

/obj/item/auto_cpr/examine(mob/user)
. = ..()
if(isnull(cell))
. += span_notice("It has a slot for a power cell.")
else
. += span_notice("It has a power cell installed, secured by two <b>screws</b>.")

/obj/item/auto_cpr/Exited(atom/movable/gone, direction)
. = ..()
if(gone == cell)
cell = null

/obj/item/auto_cpr/screwdriver_act(mob/living/user, obj/item/tool)
if(isnull(cell))
balloon_alert(user, "no cell!")
return TOOL_ACT_SIGNAL_BLOCKING

user.put_in_hands(cell)
balloon_alert(user, "cell removed")
playsound(src, 'sound/machines/click.ogg', 50, TRUE)
tool.play_tool_sound(src, 50)
return TOOL_ACT_TOOLTYPE_SUCCESS

/obj/item/auto_cpr/attackby(obj/item/attacking_item, mob/user, params)
if(istype(attacking_item, /obj/item/stock_parts/cell))
if(!isnull(cell))
balloon_alert(user, "already has cell!")
return TRUE
if(!user.transferItemToLoc(attacking_item, src))
balloon_alert(user, "can't put cell in!")
return TRUE

cell = attacking_item
balloon_alert(user, "cell installed")
playsound(src, 'sound/machines/click.ogg', 50, TRUE)
return TRUE

return ..()

/obj/item/auto_cpr/equipped(mob/user, slot, initial)
. = ..()
if((slot & slot_flags) && ishuman(user))
START_PROCESSING(SSobj, src)

/obj/item/auto_cpr/dropped(mob/user, silent)
. = ..()
STOP_PROCESSING(SSobj, src)
pulse_count = 0

/obj/item/auto_cpr/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text, final_block_chance, damage, attack_type, damage_type)
. = ..()
if(.)
return
if(damage > 0 && (damage_type == BRUTE || damage_type == BURN))
do_sparks(2, FALSE, owner)

/obj/item/auto_cpr/process(seconds_per_tick)
if(!ishuman(loc))
return PROCESS_KILL

if(!cell?.use(charge_per_pulse))
return // Keep processing in case the cell is replaced

var/mob/living/carbon/human/wearer = loc
var/obj/item/bodypart/chest/chest = wearer.get_bodypart(BODY_ZONE_CHEST)
if(IS_ORGANIC_LIMB(chest))
var/kickstart_heart = prob(1) && wearer.undergoing_cardiac_arrest()
chest.receive_damage(brute = (kickstart_heart ? 12 : 0.5), wound_bonus = CANT_WOUND, damage_source = "automatic chest compressions")
wearer.cause_pain(BODY_ZONE_CHEST, (kickstart_heart ? 8 : 0.5))
if(kickstart_heart)
wearer.set_heartattack(FALSE)

wearer.apply_status_effect(/datum/status_effect/cpr_applied)

if(cell.charge < charge_per_pulse)
playsound(src, 'sound/machines/defib_failed.ogg', 50, vary = TRUE, frequency = 0.75)
wearer.audible_message(span_notice("[src] beeps, indicating that its power cell is empty."))

else if(pulse_count % 5 == 0)
playsound(src, 'sound/machines/defib_ready.ogg', 50, vary = TRUE, frequency = 0.75)
if(IS_ORGANIC_LIMB(chest))
wearer.visible_message(
span_warning("[src] compresses down on [wearer]'s chest!"),
span_warning("[src] compresses your chest painfully!"),
)
else
wearer.visible_message(
span_notice("[src] compresses down on [wearer]'s chest."),
span_notice("[src] compresses your chest."),
)

pulse_count++


/obj/item/auto_cpr/loaded

/obj/item/auto_cpr/loaded/Initialize(mapload)
. = ..()
cell = new /obj/item/stock_parts/cell/upgraded(src)
Binary file added maplestation_modules/icons/mob/autopulser.dmi
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added maplestation_modules/icons/obj/autopulser.dmi
Binary file not shown.

0 comments on commit 3a3661d

Please sign in to comment.