mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-24 16:13:20 +00:00
<!-- Write **BELOW** The Headers and **ABOVE** The comments else it may not be viewable. --> <!-- You can view Contributing.MD for a detailed description of the pull request process. --> ## About The Pull Request Renames - `/mob/proc/notify_ghost_cloning` to `/mob/proc/notify_revival` - `/mob/dead/observer/proc/notify_cloning` to `/mob/dead/observer/proc/send_revival_notification` - `/atom/movable/screen/alert/notify_cloning` to `/atom/movable/screen/alert/revival`. I could have found a way to merge both procs together but default parameters keep me up at night. <!-- Describe The Pull Request. Please be sure every change is documented or this can delay review and even discourage maintainers from merging your PR! --> ## Why It's Good For The Game Conciseness, code that is named after a removed feature is silly. <!-- Argue for the merits of your changes and how they benefit the game, especially if they are controversial and/or far reaching. If you can't actually explain WHY what you are doing will improve the game, then it probably isn't good for the game in the first place. --> ## Changelog <!-- If your PR modifies aspects of the game that can be concretely observed by players or admins you should add a changelog. If your change does NOT meet this description, remove this section. Be sure to properly mark your PRs to prevent unnecessary GBP loss. You can read up on GBP and it's effects on PRs in the tgstation guides for contributors. Please note that maintainers freely reserve the right to remove and add tags should they deem it appropriate. You can attempt to finagle the system all you want, but it's best to shoot for clear communication right off the bat. --> nothing playerfacing <!-- Both 🆑's are required for the changelog to work! You can put your name to the right of the first 🆑 if you want to overwrite your GitHub username as author ingame. --> <!-- You can use multiple of the same prefix (they're only used for the icon ingame) and delete the unneeded ones. Despite some of the tags, changelogs should generally represent how a player might be affected by the changes rather than a summary of the PR's contents. -->
723 lines
25 KiB
Plaintext
723 lines
25 KiB
Plaintext
//backpack item
|
|
#define HALFWAYCRITDEATH ((HEALTH_THRESHOLD_CRIT + HEALTH_THRESHOLD_DEAD) * 0.5)
|
|
#define DEFIB_CAN_HURT(source) (source.combat || (source.req_defib && !source.defib.safety))
|
|
|
|
/obj/item/defibrillator
|
|
name = "defibrillator"
|
|
desc = "A device that delivers powerful shocks to detachable paddles that resuscitate incapacitated patients. \
|
|
Has a rear bracket for attachments to wall mounts and medical cyborgs."
|
|
icon = 'icons/obj/medical/defib.dmi'
|
|
icon_state = "defibunit"
|
|
inhand_icon_state = "defibunit"
|
|
lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
|
|
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
|
|
slot_flags = ITEM_SLOT_BACK
|
|
force = 5
|
|
throwforce = 6
|
|
w_class = WEIGHT_CLASS_BULKY
|
|
actions_types = list(/datum/action/item_action/toggle_paddles)
|
|
armor_type = /datum/armor/item_defibrillator
|
|
|
|
var/obj/item/shockpaddles/paddle_type = /obj/item/shockpaddles
|
|
/// If the paddles are equipped (1) or on the defib (0)
|
|
var/on = FALSE
|
|
/// If you can zap people with the defibs on harm mode
|
|
var/safety = TRUE
|
|
/// If there's a cell in the defib with enough power for a revive, blocks paddles from reviving otherwise
|
|
var/powered = FALSE
|
|
/// If the cell can be removed via screwdriver
|
|
var/cell_removable = TRUE
|
|
var/obj/item/shockpaddles/paddles
|
|
var/obj/item/stock_parts/cell/high/cell
|
|
/// If true, revive through space suits, allow for combat shocking
|
|
var/combat = FALSE
|
|
/// How long does it take to recharge
|
|
var/cooldown_duration = 5 SECONDS
|
|
/// The icon state for the paddle overlay, not applied if null
|
|
var/paddle_state = "defibunit-paddles"
|
|
/// The icon state for the powered on overlay, not applied if null
|
|
var/powered_state = "defibunit-powered"
|
|
/// The icon state for the charge bar overlay, not applied if null
|
|
var/charge_state = "defibunit-charge"
|
|
/// The icon state for the missing cell overlay, not applied if null
|
|
var/nocell_state = "defibunit-nocell"
|
|
/// The icon state for the emagged overlay, not applied if null
|
|
var/emagged_state = "defibunit-emagged"
|
|
|
|
/datum/armor/item_defibrillator
|
|
fire = 50
|
|
acid = 50
|
|
|
|
/obj/item/defibrillator/get_cell()
|
|
return cell
|
|
|
|
/obj/item/defibrillator/Initialize(mapload) //starts without a cell for rnd
|
|
. = ..()
|
|
paddles = new paddle_type(src)
|
|
update_power()
|
|
RegisterSignal(paddles, COMSIG_DEFIBRILLATOR_SUCCESS, PROC_REF(on_defib_success))
|
|
|
|
/obj/item/defibrillator/loaded/Initialize(mapload) //starts with hicap
|
|
. = ..()
|
|
cell = new(src)
|
|
update_power()
|
|
|
|
/obj/item/defibrillator/examine(mob/user)
|
|
. = ..()
|
|
if(!cell_removable)
|
|
return
|
|
if(cell)
|
|
. += span_notice("Use a screwdriver to remove the cell.")
|
|
else
|
|
. += span_warning("It has no power cell!")
|
|
|
|
/obj/item/defibrillator/fire_act(exposed_temperature, exposed_volume)
|
|
. = ..()
|
|
if(paddles?.loc == src)
|
|
paddles.fire_act(exposed_temperature, exposed_volume)
|
|
|
|
/obj/item/defibrillator/extinguish()
|
|
. = ..()
|
|
if(paddles?.loc == src)
|
|
paddles.extinguish()
|
|
|
|
/obj/item/defibrillator/proc/update_power()
|
|
if(!QDELETED(cell))
|
|
if(QDELETED(paddles) || cell.charge < paddles.revivecost)
|
|
powered = FALSE
|
|
else
|
|
powered = TRUE
|
|
else
|
|
powered = FALSE
|
|
update_appearance()
|
|
if(istype(loc, /obj/machinery/defibrillator_mount))
|
|
loc.update_appearance()
|
|
|
|
/obj/item/defibrillator/update_overlays()
|
|
. = ..()
|
|
|
|
if(!on && paddle_state)
|
|
. += paddle_state
|
|
if(powered && powered_state)
|
|
. += powered_state
|
|
if(!QDELETED(cell) && charge_state)
|
|
var/ratio = cell.charge / cell.maxcharge
|
|
ratio = CEILING(ratio*4, 1) * 25
|
|
. += "[charge_state][ratio]"
|
|
if(!cell && nocell_state)
|
|
. += "[nocell_state]"
|
|
if(!safety && emagged_state)
|
|
. += emagged_state
|
|
|
|
/obj/item/defibrillator/CheckParts(list/parts_list)
|
|
..()
|
|
cell = locate(/obj/item/stock_parts/cell) in contents
|
|
update_power()
|
|
|
|
/obj/item/defibrillator/ui_action_click()
|
|
INVOKE_ASYNC(src, PROC_REF(toggle_paddles))
|
|
|
|
//ATTACK HAND IGNORING PARENT RETURN VALUE
|
|
/obj/item/defibrillator/attack_hand(mob/user, list/modifiers)
|
|
if(loc == user)
|
|
if(slot_flags & ITEM_SLOT_BACK)
|
|
if(user.get_item_by_slot(ITEM_SLOT_BACK) == src)
|
|
ui_action_click()
|
|
else
|
|
to_chat(user, span_warning("Put the defibrillator on your back first!"))
|
|
|
|
else if(slot_flags & ITEM_SLOT_BELT)
|
|
if(user.get_item_by_slot(ITEM_SLOT_BELT) == src)
|
|
ui_action_click()
|
|
else
|
|
to_chat(user, span_warning("Strap the defibrillator's belt on first!"))
|
|
return
|
|
else if(istype(loc, /obj/machinery/defibrillator_mount))
|
|
ui_action_click() //checks for this are handled in defibrillator.mount.dm
|
|
return ..()
|
|
|
|
/obj/item/defibrillator/MouseDrop(obj/over_object)
|
|
. = ..()
|
|
if(ismob(loc))
|
|
var/mob/M = loc
|
|
if(!M.incapacitated() && istype(over_object, /atom/movable/screen/inventory/hand))
|
|
var/atom/movable/screen/inventory/hand/H = over_object
|
|
M.putItemFromInventoryInHandIfPossible(src, H.held_index)
|
|
|
|
/obj/item/defibrillator/screwdriver_act(mob/living/user, obj/item/tool)
|
|
if(!cell || !cell_removable)
|
|
return FALSE
|
|
|
|
cell.update_appearance()
|
|
cell.forceMove(get_turf(src))
|
|
balloon_alert(user, "removed [cell]")
|
|
cell = null
|
|
tool.play_tool_sound(src, 50)
|
|
update_power()
|
|
return TRUE
|
|
|
|
/obj/item/defibrillator/attackby(obj/item/W, mob/user, params)
|
|
if(W == paddles)
|
|
toggle_paddles()
|
|
else if(istype(W, /obj/item/stock_parts/cell))
|
|
var/obj/item/stock_parts/cell/C = W
|
|
if(cell)
|
|
to_chat(user, span_warning("[src] already has a cell!"))
|
|
else
|
|
if(C.maxcharge < paddles.revivecost)
|
|
to_chat(user, span_notice("[src] requires a higher capacity cell."))
|
|
return
|
|
if(!user.transferItemToLoc(W, src))
|
|
return
|
|
cell = W
|
|
to_chat(user, span_notice("You install a cell in [src]."))
|
|
update_power()
|
|
else
|
|
return ..()
|
|
|
|
/obj/item/defibrillator/emag_act(mob/user, obj/item/card/emag/emag_card)
|
|
|
|
safety = !safety
|
|
|
|
var/enabled_or_disabled = (safety ? "enabled" : "disabled")
|
|
balloon_alert(user, "safety protocols [enabled_or_disabled]")
|
|
|
|
return TRUE
|
|
|
|
/obj/item/defibrillator/emp_act(severity)
|
|
. = ..()
|
|
if(cell && !(. & EMP_PROTECT_CONTENTS))
|
|
deductcharge(1000 / severity)
|
|
if (. & EMP_PROTECT_SELF)
|
|
return
|
|
|
|
update_power()
|
|
|
|
/obj/item/defibrillator/proc/toggle_paddles()
|
|
set name = "Toggle Paddles"
|
|
set category = "Object"
|
|
on = !on
|
|
|
|
var/mob/living/carbon/user = usr
|
|
if(on)
|
|
//Detach the paddles into the user's hands
|
|
if(!usr.put_in_hands(paddles))
|
|
on = FALSE
|
|
to_chat(user, span_warning("You need a free hand to hold the paddles!"))
|
|
update_power()
|
|
return
|
|
else
|
|
//Remove from their hands and back onto the defib unit
|
|
remove_paddles(user)
|
|
|
|
update_power()
|
|
update_item_action_buttons()
|
|
|
|
|
|
/obj/item/defibrillator/equipped(mob/user, slot)
|
|
..()
|
|
if(!(slot_flags & slot))
|
|
remove_paddles(user)
|
|
update_power()
|
|
|
|
/obj/item/defibrillator/item_action_slot_check(slot, mob/user)
|
|
if(slot_flags & slot)
|
|
return TRUE
|
|
|
|
/obj/item/defibrillator/proc/remove_paddles(mob/user) //this fox the bug with the paddles when other player stole you the defib when you have the paddles equiped
|
|
if(ismob(paddles.loc))
|
|
var/mob/M = paddles.loc
|
|
M.dropItemToGround(paddles, TRUE)
|
|
return
|
|
|
|
/obj/item/defibrillator/Destroy()
|
|
if(on)
|
|
var/M = get(paddles, /mob)
|
|
remove_paddles(M)
|
|
QDEL_NULL(paddles)
|
|
QDEL_NULL(cell)
|
|
return ..()
|
|
|
|
/obj/item/defibrillator/proc/deductcharge(chrgdeductamt)
|
|
if(cell)
|
|
if(cell.charge < (paddles.revivecost+chrgdeductamt))
|
|
powered = FALSE
|
|
update_power()
|
|
if(cell.use(chrgdeductamt))
|
|
update_power()
|
|
return TRUE
|
|
else
|
|
return FALSE
|
|
|
|
/obj/item/defibrillator/proc/cooldowncheck()
|
|
addtimer(CALLBACK(src, PROC_REF(finish_charging)), cooldown_duration)
|
|
|
|
/obj/item/defibrillator/proc/finish_charging()
|
|
if(cell)
|
|
if(cell.charge >= paddles.revivecost)
|
|
visible_message(span_notice("[src] beeps: Unit ready."))
|
|
playsound(src, 'sound/machines/defib_ready.ogg', 50, FALSE)
|
|
else
|
|
visible_message(span_notice("[src] beeps: Charge depleted."))
|
|
playsound(src, 'sound/machines/defib_failed.ogg', 50, FALSE)
|
|
paddles.cooldown = FALSE
|
|
paddles.update_appearance()
|
|
update_power()
|
|
|
|
/obj/item/defibrillator/proc/on_defib_success(obj/item/shockpaddles/source)
|
|
deductcharge(source.revivecost)
|
|
source.cooldown = TRUE
|
|
cooldowncheck()
|
|
return COMPONENT_DEFIB_STOP
|
|
|
|
/obj/item/defibrillator/compact
|
|
name = "compact defibrillator"
|
|
desc = "A belt-equipped defibrillator that can be rapidly deployed."
|
|
icon_state = "defibcompact"
|
|
inhand_icon_state = null
|
|
worn_icon_state = "defibcompact"
|
|
w_class = WEIGHT_CLASS_NORMAL
|
|
slot_flags = ITEM_SLOT_BELT
|
|
paddle_state = "defibcompact-paddles"
|
|
powered_state = "defibcompact-powered"
|
|
charge_state = "defibcompact-charge"
|
|
nocell_state = "defibcompact-nocell"
|
|
emagged_state = "defibcompact-emagged"
|
|
|
|
/obj/item/defibrillator/compact/item_action_slot_check(slot, mob/user)
|
|
if(slot & user.getBeltSlot())
|
|
return TRUE
|
|
|
|
/obj/item/defibrillator/compact/loaded/Initialize(mapload)
|
|
. = ..()
|
|
cell = new(src)
|
|
update_power()
|
|
|
|
/obj/item/defibrillator/compact/combat
|
|
name = "combat defibrillator"
|
|
desc = "A belt-equipped blood-red defibrillator. Can revive through thick clothing, has an experimental self-recharging battery, and can be utilized as a weapon via applying the paddles while in a combat stance."
|
|
icon_state = "defibcombat" //needs defib inhand sprites
|
|
inhand_icon_state = null
|
|
worn_icon_state = "defibcombat"
|
|
combat = TRUE
|
|
safety = FALSE
|
|
cooldown_duration = 2.5 SECONDS
|
|
paddle_type = /obj/item/shockpaddles/syndicate
|
|
paddle_state = "defibcombat-paddles"
|
|
powered_state = null
|
|
emagged_state = null
|
|
|
|
/obj/item/defibrillator/compact/combat/loaded
|
|
cell_removable = FALSE // Don't let people just have an infinite power cell
|
|
|
|
/obj/item/defibrillator/compact/combat/loaded/Initialize(mapload)
|
|
. = ..()
|
|
cell = new /obj/item/stock_parts/cell/infinite(src)
|
|
update_power()
|
|
|
|
/obj/item/defibrillator/compact/combat/loaded/attackby(obj/item/W, mob/user, params)
|
|
if(W == paddles)
|
|
toggle_paddles()
|
|
return
|
|
|
|
/obj/item/defibrillator/compact/combat/loaded/nanotrasen
|
|
name = "elite Nanotrasen defibrillator"
|
|
desc = "A belt-equipped state-of-the-art defibrillator. Can revive through thick clothing, has an experimental self-recharging battery, and can be utilized as a weapon via applying the paddles while in a combat stance."
|
|
icon_state = "defibnt" //needs defib inhand sprites
|
|
inhand_icon_state = null
|
|
worn_icon_state = "defibnt"
|
|
paddle_type = /obj/item/shockpaddles/syndicate/nanotrasen
|
|
paddle_state = "defibnt-paddles"
|
|
|
|
//paddles
|
|
|
|
/obj/item/shockpaddles
|
|
name = "defibrillator paddles"
|
|
desc = "A pair of plastic-gripped paddles with flat metal surfaces that are used to deliver powerful electric shocks."
|
|
icon = 'icons/obj/medical/defib.dmi'
|
|
icon_state = "defibpaddles0"
|
|
inhand_icon_state = "defibpaddles0"
|
|
lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
|
|
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
|
|
|
|
force = 0
|
|
throwforce = 6
|
|
w_class = WEIGHT_CLASS_BULKY
|
|
resistance_flags = INDESTRUCTIBLE
|
|
base_icon_state = "defibpaddles"
|
|
|
|
var/revivecost = 1000
|
|
var/cooldown = FALSE
|
|
var/busy = FALSE
|
|
var/obj/item/defibrillator/defib
|
|
var/req_defib = TRUE // Whether or not the paddles require a defibrilator object
|
|
var/recharge_time = 6 SECONDS // Only applies to defibs that do not require a defibrilator. See: do_success()
|
|
var/combat = FALSE //If it penetrates armor and gives additional functionality
|
|
|
|
/obj/item/shockpaddles/Initialize(mapload)
|
|
. = ..()
|
|
AddElement(/datum/element/update_icon_updates_onmob, ITEM_SLOT_BACK)
|
|
AddComponent(/datum/component/two_handed, force_unwielded=8, force_wielded=12)
|
|
|
|
/obj/item/shockpaddles/Destroy()
|
|
defib = null
|
|
return ..()
|
|
|
|
/obj/item/shockpaddles/equipped(mob/user, slot)
|
|
. = ..()
|
|
if(!req_defib)
|
|
return
|
|
RegisterSignal(user, COMSIG_MOVABLE_MOVED, PROC_REF(check_range))
|
|
|
|
/obj/item/shockpaddles/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE)
|
|
. = ..()
|
|
check_range()
|
|
|
|
/obj/item/shockpaddles/fire_act(exposed_temperature, exposed_volume)
|
|
. = ..()
|
|
if((req_defib && defib) && loc != defib)
|
|
defib.fire_act(exposed_temperature, exposed_volume)
|
|
|
|
/obj/item/shockpaddles/proc/check_range()
|
|
SIGNAL_HANDLER
|
|
|
|
if(!req_defib || !defib)
|
|
return
|
|
if(!in_range(src,defib))
|
|
if(isliving(loc))
|
|
var/mob/living/user = loc
|
|
to_chat(user, span_warning("[defib]'s paddles overextend and come out of your hands!"))
|
|
else
|
|
visible_message(span_notice("[src] snap back into [defib]."))
|
|
snap_back()
|
|
|
|
/obj/item/shockpaddles/proc/recharge(time = 0)
|
|
if(req_defib)
|
|
return
|
|
cooldown = TRUE
|
|
update_appearance()
|
|
addtimer(CALLBACK(src, PROC_REF(finish_recharge)), time)
|
|
|
|
/obj/item/shockpaddles/proc/finish_recharge()
|
|
var/turf/current_turf = get_turf(src)
|
|
current_turf.audible_message(span_notice("[src] beeps: Unit is recharged."))
|
|
playsound(src, 'sound/machines/defib_ready.ogg', 50, FALSE)
|
|
cooldown = FALSE
|
|
update_appearance()
|
|
|
|
/obj/item/shockpaddles/Initialize(mapload)
|
|
. = ..()
|
|
ADD_TRAIT(src, TRAIT_NO_STORAGE_INSERT, TRAIT_GENERIC) //stops shockpaddles from being inserted in BoH
|
|
if(!req_defib)
|
|
return //If it doesn't need a defib, just say it exists
|
|
if (!loc || !istype(loc, /obj/item/defibrillator)) //To avoid weird issues from admin spawns
|
|
return INITIALIZE_HINT_QDEL
|
|
defib = loc
|
|
busy = FALSE
|
|
update_appearance()
|
|
|
|
/obj/item/shockpaddles/suicide_act(mob/living/user)
|
|
user.visible_message(span_danger("[user] is putting the live paddles on [user.p_their()] chest! It looks like [user.p_theyre()] trying to commit suicide!"))
|
|
if(req_defib)
|
|
defib.deductcharge(revivecost)
|
|
playsound(src, 'sound/machines/defib_zap.ogg', 50, TRUE, -1)
|
|
return OXYLOSS
|
|
|
|
/obj/item/shockpaddles/update_icon_state()
|
|
icon_state = "[base_icon_state][HAS_TRAIT(src, TRAIT_WIELDED)]"
|
|
inhand_icon_state = icon_state
|
|
if(cooldown)
|
|
icon_state = "[base_icon_state][HAS_TRAIT(src, TRAIT_WIELDED)]_cooldown"
|
|
return ..()
|
|
|
|
/obj/item/shockpaddles/dropped(mob/user)
|
|
. = ..()
|
|
if(user)
|
|
UnregisterSignal(user, COMSIG_MOVABLE_MOVED)
|
|
if(req_defib)
|
|
if(user)
|
|
to_chat(user, span_notice("The paddles snap back into the main unit."))
|
|
snap_back()
|
|
|
|
/obj/item/shockpaddles/proc/snap_back()
|
|
if(!defib)
|
|
return
|
|
defib.on = FALSE
|
|
forceMove(defib)
|
|
defib.update_power()
|
|
|
|
/obj/item/shockpaddles/attack(mob/M, mob/living/user, params)
|
|
if(busy)
|
|
return
|
|
defib?.update_power()
|
|
if(req_defib && !defib.powered)
|
|
user.visible_message(span_warning("[defib] beeps: Not enough charge!"))
|
|
playsound(src, 'sound/machines/defib_failed.ogg', 50, FALSE)
|
|
return
|
|
if(!HAS_TRAIT(src, TRAIT_WIELDED))
|
|
if(iscyborg(user))
|
|
to_chat(user, span_warning("You must activate the paddles in your active module before you can use them on someone!"))
|
|
else
|
|
to_chat(user, span_warning("You need to wield the paddles in both hands before you can use them on someone!"))
|
|
return
|
|
if(cooldown)
|
|
if(req_defib)
|
|
to_chat(user, span_warning("[defib] is recharging!"))
|
|
else
|
|
to_chat(user, span_warning("[src] are recharging!"))
|
|
return
|
|
|
|
var/list/modifiers = params2list(params)
|
|
if(LAZYACCESS(modifiers, RIGHT_CLICK))
|
|
do_disarm(M, user)
|
|
return
|
|
|
|
if(!iscarbon(M))
|
|
if(req_defib)
|
|
to_chat(user, span_warning("The instructions on [defib] don't mention how to revive that..."))
|
|
else
|
|
to_chat(user, span_warning("You aren't sure how to revive that..."))
|
|
return
|
|
var/mob/living/carbon/H = M
|
|
|
|
if(user.zone_selected != BODY_ZONE_CHEST)
|
|
to_chat(user, span_warning("You need to target your patient's chest with [src]!"))
|
|
return
|
|
|
|
if(user.combat_mode)
|
|
do_harm(H, user)
|
|
return
|
|
|
|
if(H.can_defib() == DEFIB_POSSIBLE)
|
|
H.notify_revival("Your heart is being defibrillated!")
|
|
H.grab_ghost() // Shove them back in their body.
|
|
|
|
do_help(H, user)
|
|
|
|
/// Called whenever the paddles successfuly shock something
|
|
/obj/item/shockpaddles/proc/do_success()
|
|
if(busy)
|
|
busy = FALSE
|
|
|
|
update_appearance()
|
|
if(SEND_SIGNAL(src, COMSIG_DEFIBRILLATOR_SUCCESS) & COMPONENT_DEFIB_STOP)
|
|
return
|
|
recharge(recharge_time)
|
|
|
|
/// Called whenever the paddles fail to shock something after a do_x proc
|
|
/obj/item/shockpaddles/proc/do_cancel()
|
|
if(busy)
|
|
busy = FALSE
|
|
|
|
update_appearance()
|
|
|
|
/obj/item/shockpaddles/proc/shock_pulling(dmg, mob/H)
|
|
if(isliving(H.pulledby)) //CLEAR!
|
|
var/mob/living/M = H.pulledby
|
|
if(M.electrocute_act(dmg, H))
|
|
M.visible_message(span_danger("[M] is electrocuted by [M.p_their()] contact with [H]!"))
|
|
M.emote("scream")
|
|
|
|
/obj/item/shockpaddles/proc/do_disarm(mob/living/M, mob/living/user)
|
|
if(!DEFIB_CAN_HURT(src))
|
|
return
|
|
busy = TRUE
|
|
M.visible_message(span_danger("[user] touches [M] with [src]!"), \
|
|
span_userdanger("[user] touches [M] with [src]!"))
|
|
M.adjustStaminaLoss(60)
|
|
M.Knockdown(75)
|
|
M.set_jitter_if_lower(100 SECONDS)
|
|
M.apply_status_effect(/datum/status_effect/convulsing)
|
|
playsound(src, 'sound/machines/defib_zap.ogg', 50, TRUE, -1)
|
|
if(HAS_TRAIT(M,MOB_ORGANIC))
|
|
M.emote("gasp")
|
|
log_combat(user, M, "zapped", src)
|
|
do_success()
|
|
|
|
/obj/item/shockpaddles/proc/do_harm(mob/living/carbon/H, mob/living/user)
|
|
if(!DEFIB_CAN_HURT(src))
|
|
return
|
|
user.visible_message(span_warning("[user] begins to place [src] on [H]'s chest."),
|
|
span_warning("You overcharge the paddles and begin to place them onto [H]'s chest..."))
|
|
busy = TRUE
|
|
update_appearance()
|
|
if(do_after(user, 1.5 SECONDS, H, extra_checks = CALLBACK(src, PROC_REF(is_wielded))))
|
|
user.visible_message(span_notice("[user] places [src] on [H]'s chest."),
|
|
span_warning("You place [src] on [H]'s chest and begin to charge them."))
|
|
var/turf/T = get_turf(defib)
|
|
playsound(src, 'sound/machines/defib_charge.ogg', 50, FALSE)
|
|
if(req_defib)
|
|
T.audible_message(span_warning("\The [defib] lets out an urgent beep and lets out a steadily rising hum..."))
|
|
else
|
|
user.audible_message(span_warning("[src] let out an urgent beep."))
|
|
if(do_after(user, 1.5 SECONDS, H, extra_checks = CALLBACK(src, PROC_REF(is_wielded)))) //Takes longer due to overcharging
|
|
if(!H)
|
|
do_cancel()
|
|
return
|
|
if(H && H.stat == DEAD)
|
|
to_chat(user, span_warning("[H] is dead."))
|
|
playsound(src, 'sound/machines/defib_failed.ogg', 50, FALSE)
|
|
do_cancel()
|
|
return
|
|
user.visible_message(span_boldannounce("<i>[user] shocks [H] with \the [src]!"), span_warning("You shock [H] with \the [src]!"))
|
|
playsound(src, 'sound/machines/defib_zap.ogg', 100, TRUE, -1)
|
|
playsound(src, 'sound/weapons/egloves.ogg', 100, TRUE, -1)
|
|
H.emote("scream")
|
|
shock_pulling(45, H)
|
|
if(H.can_heartattack() && !H.undergoing_cardiac_arrest())
|
|
if(!H.stat)
|
|
H.visible_message(span_warning("[H] thrashes wildly, clutching at [H.p_their()] chest!"),
|
|
span_userdanger("You feel a horrible agony in your chest!"))
|
|
H.set_heartattack(TRUE)
|
|
H.apply_damage(50, BURN, BODY_ZONE_CHEST)
|
|
log_combat(user, H, "overloaded the heart of", defib)
|
|
H.Paralyze(100)
|
|
H.set_jitter_if_lower(200 SECONDS)
|
|
do_success()
|
|
return
|
|
do_cancel()
|
|
|
|
/obj/item/shockpaddles/proc/do_help(mob/living/carbon/H, mob/living/user)
|
|
user.visible_message(span_warning("[user] begins to place [src] on [H]'s chest."), span_warning("You begin to place [src] on [H]'s chest..."))
|
|
busy = TRUE
|
|
update_appearance()
|
|
if(do_after(user, 3 SECONDS, H, extra_checks = CALLBACK(src, PROC_REF(is_wielded)))) //beginning to place the paddles on patient's chest to allow some time for people to move away to stop the process
|
|
user.visible_message(span_notice("[user] places [src] on [H]'s chest."), span_warning("You place [src] on [H]'s chest."))
|
|
playsound(src, 'sound/machines/defib_charge.ogg', 75, FALSE)
|
|
var/obj/item/organ/internal/heart = H.get_organ_by_type(/obj/item/organ/internal/heart)
|
|
if(do_after(user, 2 SECONDS, H, extra_checks = CALLBACK(src, PROC_REF(is_wielded)))) //placed on chest and short delay to shock for dramatic effect, revive time is 5sec total
|
|
if((!combat && !req_defib) || (req_defib && !defib.combat))
|
|
for(var/obj/item/clothing/C in H.get_equipped_items())
|
|
if((C.body_parts_covered & CHEST) && (C.clothing_flags & THICKMATERIAL)) //check to see if something is obscuring their chest.
|
|
user.audible_message(span_warning("[req_defib ? "[defib]" : "[src]"] buzzes: Patient's chest is obscured. Operation aborted."))
|
|
playsound(src, 'sound/machines/defib_failed.ogg', 50, FALSE)
|
|
do_cancel()
|
|
return
|
|
if(SEND_SIGNAL(H, COMSIG_DEFIBRILLATOR_PRE_HELP_ZAP, user, src) & COMPONENT_DEFIB_STOP)
|
|
do_cancel()
|
|
return
|
|
if(H.stat == DEAD)
|
|
H.visible_message(span_warning("[H]'s body convulses a bit."))
|
|
playsound(src, SFX_BODYFALL, 50, TRUE)
|
|
playsound(src, 'sound/machines/defib_zap.ogg', 75, TRUE, -1)
|
|
shock_pulling(30, H)
|
|
|
|
var/defib_result = H.can_defib()
|
|
var/fail_reason
|
|
|
|
switch (defib_result)
|
|
if (DEFIB_FAIL_SUICIDE)
|
|
fail_reason = "Recovery of patient impossible. Further attempts futile."
|
|
if (DEFIB_FAIL_NO_HEART)
|
|
fail_reason = "Patient's heart is missing."
|
|
if (DEFIB_FAIL_FAILING_HEART)
|
|
fail_reason = "Patient's heart too damaged, replace or repair and try again."
|
|
if (DEFIB_FAIL_TISSUE_DAMAGE)
|
|
fail_reason = "Tissue damage too severe, repair and try again."
|
|
if (DEFIB_FAIL_HUSK)
|
|
fail_reason = "Patient's body is a mere husk, repair and try again."
|
|
if (DEFIB_FAIL_FAILING_BRAIN)
|
|
fail_reason = "Patient's brain is too damaged, repair and try again."
|
|
if (DEFIB_FAIL_NO_INTELLIGENCE)
|
|
fail_reason = "No intelligence pattern can be detected in patient's brain. Further attempts futile."
|
|
if (DEFIB_FAIL_NO_BRAIN)
|
|
fail_reason = "Patient's brain is missing. Further attempts futile."
|
|
if (DEFIB_FAIL_BLACKLISTED)
|
|
fail_reason = "Patient has been blacklisted from revival. Further attempts futile."
|
|
|
|
if(fail_reason)
|
|
user.visible_message(span_warning("[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - [fail_reason]"))
|
|
playsound(src, 'sound/machines/defib_failed.ogg', 50, FALSE)
|
|
else
|
|
var/total_brute = H.getBruteLoss()
|
|
var/total_burn = H.getFireLoss()
|
|
|
|
var/need_mob_update = FALSE
|
|
//If the body has been fixed so that they would not be in crit when defibbed, give them oxyloss to put them back into crit
|
|
if (H.health > HALFWAYCRITDEATH)
|
|
need_mob_update += H.adjustOxyLoss(H.health - HALFWAYCRITDEATH, updating_health = FALSE)
|
|
else
|
|
var/overall_damage = total_brute + total_burn + H.getToxLoss() + H.getOxyLoss()
|
|
var/mobhealth = H.health
|
|
need_mob_update += H.adjustOxyLoss((mobhealth - HALFWAYCRITDEATH) * (H.getOxyLoss() / overall_damage), updating_health = FALSE)
|
|
need_mob_update += H.adjustToxLoss((mobhealth - HALFWAYCRITDEATH) * (H.getToxLoss() / overall_damage), updating_health = FALSE, forced = TRUE) // force tox heal for toxin lovers too
|
|
need_mob_update += H.adjustFireLoss((mobhealth - HALFWAYCRITDEATH) * (total_burn / overall_damage), updating_health = FALSE)
|
|
need_mob_update += H.adjustBruteLoss((mobhealth - HALFWAYCRITDEATH) * (total_brute / overall_damage), updating_health = FALSE)
|
|
if(need_mob_update)
|
|
H.updatehealth() // Previous "adjust" procs don't update health, so we do it manually.
|
|
user.visible_message(span_notice("[req_defib ? "[defib]" : "[src]"] pings: Resuscitation successful."))
|
|
playsound(src, 'sound/machines/defib_success.ogg', 50, FALSE)
|
|
H.set_heartattack(FALSE)
|
|
if(defib_result == DEFIB_POSSIBLE)
|
|
H.grab_ghost()
|
|
H.revive()
|
|
H.emote("gasp")
|
|
H.set_jitter_if_lower(200 SECONDS)
|
|
SEND_SIGNAL(H, COMSIG_LIVING_MINOR_SHOCK)
|
|
if(HAS_MIND_TRAIT(user, TRAIT_MORBID))
|
|
user.add_mood_event("morbid_saved_life", /datum/mood_event/morbid_saved_life)
|
|
else
|
|
user.add_mood_event("saved_life", /datum/mood_event/saved_life)
|
|
log_combat(user, H, "revived", defib)
|
|
do_success()
|
|
return
|
|
else if (!H.get_organ_by_type(/obj/item/organ/internal/heart))
|
|
user.visible_message(span_warning("[req_defib ? "[defib]" : "[src]"] buzzes: Patient's heart is missing. Operation aborted."))
|
|
playsound(src, 'sound/machines/defib_failed.ogg', 50, FALSE)
|
|
else if(H.undergoing_cardiac_arrest())
|
|
playsound(src, 'sound/machines/defib_zap.ogg', 50, TRUE, -1)
|
|
if(!(heart.organ_flags & ORGAN_FAILING))
|
|
H.set_heartattack(FALSE)
|
|
user.visible_message(span_notice("[req_defib ? "[defib]" : "[src]"] pings: Patient's heart is now beating again."))
|
|
else
|
|
user.visible_message(span_warning("[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed, heart damage detected."))
|
|
|
|
else
|
|
user.visible_message(span_warning("[req_defib ? "[defib]" : "[src]"] buzzes: Patient is not in a valid state. Operation aborted."))
|
|
playsound(src, 'sound/machines/defib_failed.ogg', 50, FALSE)
|
|
do_cancel()
|
|
|
|
/obj/item/shockpaddles/proc/is_wielded()
|
|
return HAS_TRAIT(src, TRAIT_WIELDED)
|
|
|
|
/obj/item/shockpaddles/cyborg
|
|
name = "cyborg defibrillator paddles"
|
|
icon = 'icons/obj/medical/defib.dmi'
|
|
icon_state = "defibpaddles0"
|
|
inhand_icon_state = "defibpaddles0"
|
|
req_defib = FALSE
|
|
|
|
/obj/item/shockpaddles/cyborg/attack(mob/M, mob/user)
|
|
if(iscyborg(user))
|
|
var/mob/living/silicon/robot/R = user
|
|
if(R.emagged)
|
|
combat = TRUE
|
|
else
|
|
combat = FALSE
|
|
else
|
|
combat = FALSE
|
|
|
|
. = ..()
|
|
|
|
/obj/item/shockpaddles/syndicate
|
|
name = "syndicate defibrillator paddles"
|
|
desc = "A pair of paddles used to revive deceased operatives. They possess both the ability to penetrate armor and to deliver powerful or disabling shocks offensively."
|
|
combat = TRUE
|
|
icon = 'icons/obj/medical/defib.dmi'
|
|
icon_state = "syndiepaddles0"
|
|
inhand_icon_state = "syndiepaddles0"
|
|
base_icon_state = "syndiepaddles"
|
|
|
|
/obj/item/shockpaddles/syndicate/nanotrasen
|
|
name = "elite nanotrasen defibrillator paddles"
|
|
desc = "A pair of paddles used to revive deceased ERT members. They possess both the ability to penetrate armor and to deliver powerful or disabling shocks offensively."
|
|
icon_state = "ntpaddles0"
|
|
inhand_icon_state = "ntpaddles0"
|
|
base_icon_state = "ntpaddles"
|
|
|
|
/obj/item/shockpaddles/syndicate/cyborg
|
|
req_defib = FALSE
|
|
|
|
#undef HALFWAYCRITDEATH
|
|
#undef DEFIB_CAN_HURT
|