creates more deticated folders for tools and weaponry and move dm files into them (#94804)

## About The Pull Request
Hello hello the evil folder mover goat is back, i will destroy years
worth of muscle memory.

Items is an folder alot of stuff that really didn't get a spefific
location.

Items/Weaponry Contains
Shields.dm

Items/Weaponry/Melee Contains (Most of the extra dm files come from me
splitting misc/weaponry.dm into its own categories killing offf
weaponry.dm in its whole)
baseball_bat.dm, baton.dm, chainofcommand.dm, chainsaw.dm, claymore.dm,
dualsaber.dm, energy.dm
fireaxe.dm, high_frequency_blade.dm, his_grace.dm, katana.dm, knives.dm,
misc.dm, powerfist.dm, sabre.dm
soulscythe.dm, spear.dm, supermatter_sword.dm.

Items/weaponry/ranged Contains
flamethrower.dm, pnuematic_cannon.dm, throwing_star.dm

items/tools Contains
control_wand.dm, extinguisher.dm, inducer.dm, spess_knife.dm,
theft_tools.dm

items/tools/engineering Contains
airlock_painter.dm, decal_painter.dm, paintable_decals.dm, crowbar.dm,
screwdriver.dm, weldingtool.dm
wirebbrush.dm, wirecutters.dm, wrench.dm

items/tools/janitorial Contains
broom.dm, janitor_key.dm

items/tools/medical Contains
bodybag.dm, cane.dm, defib.dm, reflex_hammer.dm

boxcutter.dm is merged into knives.dm
Kitchen.dm renamed into kitchen utensils and moved into food and drinks
module
surgery_tray.dm moved into surgery module
banhammer/extendo hand moved from weaponry into toys.dm
balloon mallet moved into clown_items.dm
ectoplasm moved from weaponry.dm into soulstone.dm (since thats the
resulting residue when a shade dies, and not really a weapon)


## Why It's Good For The Game

So things are way easier to navigate

## Changelog
This commit is contained in:
Nick
2026-01-10 17:56:19 -05:00
committed by GitHub
parent 24479a2edd
commit 834245ecdb
52 changed files with 1802 additions and 1792 deletions
@@ -0,0 +1,272 @@
#define WAND_OPEN "open"
#define WAND_BOLT "bolt"
#define WAND_EMERGENCY "emergency"
#define WAND_SHOCK "shock"
#define WAND_DEPOWER "depower"
/obj/item/door_remote
icon_state = "remote"
base_icon_state = "remote"
inhand_icon_state = "electronic"
lefthand_file = 'icons/mob/inhands/items/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/items/devices_righthand.dmi'
icon = 'icons/obj/devices/remote.dmi'
name = "control wand"
desc = "A remote for controlling a set of airlocks."
w_class = WEIGHT_CLASS_TINY
drop_sound = 'sound/items/door_remote/door_remote_drop1.ogg'
pickup_sound = 'sound/items/door_remote/door_remote_pick_up1.ogg'
var/department = "civilian"
var/mode = WAND_OPEN
var/region_access = REGION_GENERAL
var/list/access_list = list()
// the trim of the owner of this remote, if applied
var/datum/id_trim/job/owner_trim = null
// areas that this remote is the exclusive owner of
var/list/area/our_domain = null
// areas specifically considered as restricted from a remote
// accessing them unless specifically allowed (vault, security, etc)
var/static/list/area/restricted_areas = list(
/area/station/command/bridge, /*so Captain's remote isn't totally useless*/
/area/station/security, /*so antag RD/HoP/QM/CMO can't easily screw up the brig doors*/
/area/station/command/vault, /*aka Vault since it's QM's special thing*/
/area/station/ai/satellite/chamber, // these are areas exclusive to RD
/area/station/ai/upload, // but sometimes mappers might misconfig their doors with our several dozen access helpers
)
COOLDOWN_DECLARE(shock_cooldown)
/// sound played when mode is switched
var/mode_switch_sound = SFX_REMOTE_MODE_SWITCH
/// sound played when an action is done
var/action_sound = SFX_REMOTE_ACTION
/obj/item/door_remote/Initialize(mapload)
. = ..()
update_icon_state()
// initialize late to make sure job accesses are fully configured
return INITIALIZE_HINT_LATELOAD
/obj/item/door_remote/LateInitialize()
access_list = SSid_access.get_region_access_list(list(region_access))
if(!isnull(owner_trim))
var/datum/id_trim/job/trim_singlet = SSid_access.trim_singletons_by_path[owner_trim]
access_list |= trim_singlet.access
/obj/item/door_remote/proc/is_my_domain(area/restricted_area)
for(var/area/dominion as anything in our_domain)
if(istype(restricted_area, dominion))
return TRUE
return FALSE
/obj/item/door_remote/emag_act(mob/user, obj/item/card/emag/emag_card)
. = ..()
if(obj_flags & EMAGGED)
return FALSE
balloon_alert(user, "restricted functions unlocked")
obj_flags |= EMAGGED
update_icon_state()
return TRUE
/obj/item/door_remote/attack_self(mob/user)
var/static/list/ops = list(WAND_OPEN = "Open Door", WAND_BOLT = "Toggle Bolts", WAND_EMERGENCY = "Toggle Emergency Access", WAND_SHOCK = "Shock Door", WAND_DEPOWER = "Depower Door")
switch(mode)
if(WAND_OPEN)
mode = WAND_BOLT
if(WAND_BOLT)
mode = WAND_EMERGENCY
if(WAND_EMERGENCY)
if(!(obj_flags & EMAGGED))
mode = WAND_OPEN
else
mode = WAND_SHOCK
if(WAND_SHOCK)
mode = WAND_DEPOWER
if(WAND_DEPOWER)
mode = WAND_OPEN
update_icon_state()
balloon_alert(user, "mode: [ops[mode]]")
if(mode_switch_sound)
playsound(src, mode_switch_sound, 50, TRUE)
/obj/item/door_remote/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
if(!istype(interacting_with, /obj/machinery/door) && !isturf(interacting_with))
return NONE
return ranged_interact_with_atom(interacting_with, user, modifiers)
/obj/item/door_remote/omni
name = "omni door remote"
desc = "This control wand can access any door on the station."
department = "omni"
region_access = REGION_ALL_STATION
our_domain = list( /area/station )
/obj/item/door_remote/captain
name = "command door remote"
desc = "A remote for controlling a set of airlocks. Despite its gaudy insignia denoting the Captain as its owner, some fine print \
indicates that its access is exclusively relegated to the Bridge and high-security command areas -- an additional byline \
specifically excludes Security from the high-security areas. Ironic."
department = "command"
region_access = REGION_COMMAND
our_domain = list(
/area/station, // still restricted by limited accesses in REGION_COMMAND
)
/obj/item/door_remote/chief_engineer
name = "engineering door remote"
desc = "A remote for controlling a set of airlocks. This one smells like burnt flesh and ozone."
department = "engi"
region_access = REGION_ENGINEERING
owner_trim = /datum/id_trim/job/chief_engineer
// doesn't need a domain because their specific high-security areas aren't on anyone else's trim but cap
/obj/item/door_remote/research_director
name = "research door remote"
desc = "A remote for controlling a set of airlocks. This one is slightly misshapen, as if squeezed by a person possessing ludicrous strength."
department = "sci"
region_access = REGION_RESEARCH
owner_trim = /datum/id_trim/job/research_director
our_domain = list(
/area/station/ai/satellite/chamber,
/area/station/ai/upload,
)
/obj/item/door_remote/head_of_security
name = "security door remote"
desc = "A remote for controlling a set of airlocks. This one smells like sweat, blood, resentment, and coffee. \
Someone appears to have tampered with the identifier."
department = "security"
region_access = REGION_SECURITY
owner_trim = /datum/id_trim/job/head_of_security
our_domain = list( /area/station/security )
/obj/item/door_remote/quartermaster
name = "cargo door remote"
desc = "Remotely controls airlocks. This remote has additional Vault access. Despite that, holding it makes you feel insecure for some reason."
department = "cargo"
region_access = REGION_SUPPLY
owner_trim = /datum/id_trim/job/quartermaster
our_domain = list( /area/station/command/vault )
/obj/item/door_remote/chief_medical_officer
name = "medical door remote"
desc = "A remote for controlling a set of airlocks. It has the overpowering odor of blood and, despite its medical insignia, \
has absolutely no accompanying odor of disinfectant."
department = "med"
region_access = REGION_MEDBAY
owner_trim = /datum/id_trim/job/chief_medical_officer
/obj/item/door_remote/head_of_personnel
name = "service door remote"
desc = "A remote for controlling a set of airlocks. This one smells like printer ink, and fills its holder with the urge \
to mysteriously vanish."
department = "civilian"
region_access = REGION_GENERAL
owner_trim = /datum/id_trim/job/head_of_personnel
/obj/item/door_remote/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
var/obj/machinery/door/door
if(action_sound)
playsound(src, action_sound, 50, TRUE)
if (istype(interacting_with, /obj/machinery/door))
door = interacting_with
if (!door.opens_with_door_remote)
return ITEM_INTERACT_BLOCKING
else
for (var/obj/machinery/door/door_on_turf in get_turf(interacting_with))
if (door_on_turf.opens_with_door_remote)
door = door_on_turf
break
if (isnull(door))
return ITEM_INTERACT_BLOCKING
if (!door.check_access_list(access_list) || !door.requiresID())
interacting_with.balloon_alert(user, "can't access!")
return ITEM_INTERACT_BLOCKING
var/area/door_area = get_area(door)
if(is_type_in_list(door_area, restricted_areas) && !is_my_domain(get_area(door)))
interacting_with.balloon_alert(user, "can't access!")
return ITEM_INTERACT_BLOCKING
var/obj/machinery/door/airlock/airlock = door
if (!door.hasPower() || (istype(airlock) && !airlock.canAIControl()))
interacting_with.balloon_alert(user, mode == WAND_OPEN ? "it won't budge!" : "nothing happens!")
return ITEM_INTERACT_BLOCKING
switch (mode)
if (WAND_OPEN)
if (door.density)
door.open()
else
door.close()
if (WAND_BOLT)
if (!istype(airlock))
interacting_with.balloon_alert(user, "only airlocks!")
return ITEM_INTERACT_BLOCKING
if (airlock.locked)
airlock.unbolt()
log_combat(user, airlock, "unbolted", src)
else
airlock.bolt()
log_combat(user, airlock, "bolted", src)
if (WAND_EMERGENCY)
if (!istype(airlock))
interacting_with.balloon_alert(user, "only airlocks!")
return ITEM_INTERACT_BLOCKING
airlock.emergency = !airlock.emergency
airlock.update_appearance(UPDATE_ICON)
if (WAND_SHOCK)
if (!istype(airlock))
interacting_with.balloon_alert(user, "only airlocks!")
return ITEM_INTERACT_BLOCKING
if (!COOLDOWN_FINISHED(src, shock_cooldown))
interacting_with.balloon_alert(user, "shock pulse resetting!")
return ITEM_INTERACT_BLOCKING
if (airlock.isElectrified())
interacting_with.balloon_alert(user, "already electrified!")
else
airlock.set_electrified(MACHINE_DEFAULT_ELECTRIFY_TIME, user)
COOLDOWN_START(src, shock_cooldown, 10 SECONDS)
if (WAND_DEPOWER)
if (!istype(airlock))
interacting_with.balloon_alert(user, "only airlocks!")
return ITEM_INTERACT_BLOCKING
// First hit disrupts main power, backup comes back in ten seconds, if you stick around you can hit backup for 60 more seconds of downtime.
if (!airlock.main_power_timer)
airlock.loseMainPower()
else if (!airlock.backup_power_time)
airlock.loseBackupPower()
return ITEM_INTERACT_SUCCESS
/obj/item/door_remote/update_icon_state()
var/icon_state_mode
if(!(obj_flags & EMAGGED))
switch(mode)
if(WAND_OPEN)
icon_state_mode = "open"
if(WAND_BOLT)
icon_state_mode = "bolt"
if(WAND_EMERGENCY)
icon_state_mode = "emergency"
else
icon_state_mode = "emergency"
icon_state = "[base_icon_state]_[department]_[icon_state_mode]"
return ..()
#undef WAND_OPEN
#undef WAND_BOLT
#undef WAND_EMERGENCY
#undef WAND_SHOCK
#undef WAND_DEPOWER
@@ -0,0 +1,394 @@
/obj/item/extinguisher
name = "fire extinguisher"
desc = "A traditional red fire extinguisher."
icon = 'icons/obj/tools.dmi'
icon_state = "fire_extinguisher0"
worn_icon_state = "fire_extinguisher"
inhand_icon_state = "fire_extinguisher"
icon_angle = 90
hitsound = 'sound/items/weapons/smash.ogg'
pickup_sound = 'sound/items/handling/gas_tank/gas_tank_pick_up.ogg'
drop_sound = 'sound/items/handling/gas_tank/gas_tank_drop.ogg'
obj_flags = CONDUCTS_ELECTRICITY
throwforce = 13
w_class = WEIGHT_CLASS_BULKY
throw_speed = 2
throw_range = 7
force = 13
demolition_mod = 1.25
custom_materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 0.9)
attack_verb_continuous = list("slams", "whacks", "bashes", "thunks", "batters", "bludgeons", "thrashes")
attack_verb_simple = list("slam", "whack", "bash", "thunk", "batter", "bludgeon", "thrash")
dog_fashion = /datum/dog_fashion/back
resistance_flags = FIRE_PROOF
interaction_flags_click = NEED_DEXTERITY|NEED_HANDS|ALLOW_RESTING
/// The max amount of water this extinguisher can hold.
var/max_water = 100
/// Does the welder extinguisher start with water.
var/starting_water = TRUE
/// Cooldown between uses.
var/last_use = 1
/// Chem we use for our extinguishing.
var/chem = /datum/reagent/water
/// Can we actually fire currently?
var/safety = TRUE
/// Can we refill this at a water tank?
var/refilling = FALSE
/// What tank we need to refill this.
var/tanktypes = list(
/obj/structure/reagent_dispensers/watertank,
/obj/structure/reagent_dispensers/plumbed,
/obj/structure/reagent_dispensers/water_cooler,
)
/// something that should be replaced with base_icon_state
var/sprite_name = "fire_extinguisher"
/// Maximum distance launched water will travel.
var/power = 5
/// By default, turfs picked from a spray are random, set to TRUE to make it always have at least one water effect per row.
var/precision = FALSE
/// Sets the cooling_temperature of the water reagent datum inside of the extinguisher when it is refilled.
var/cooling_power = 2
/// Icon state when inside a tank holder.
var/tank_holder_icon_state = "holder_extinguisher"
///The sound a fire extinguisher makes when picked up, dropped if there is liquid inside.
var/fire_extinguisher_reagent_sloshing_sound = SFX_DEFAULT_LIQUID_SLOSH
/obj/item/extinguisher/Initialize(mapload)
. = ..()
var/static/list/slapcraft_recipe_list = list(/datum/crafting_recipe/ghettojetpack)
AddElement(
/datum/element/slapcrafting,\
slapcraft_recipes = slapcraft_recipe_list,\
)
register_context()
/obj/item/extinguisher/add_context(atom/source, list/context, obj/item/held_item, mob/user)
if(held_item != src)
return
context[SCREENTIP_CONTEXT_LMB] = "Engage nozzle"
context[SCREENTIP_CONTEXT_ALT_LMB] = "Empty"
return CONTEXTUAL_SCREENTIP_SET
/obj/item/extinguisher/dropped(mob/user, silent)
. = ..()
if(fire_extinguisher_reagent_sloshing_sound && reagents.total_volume > 0)
playsound(src, fire_extinguisher_reagent_sloshing_sound, LIQUID_SLOSHING_SOUND_VOLUME, vary = TRUE, ignore_walls = FALSE)
/obj/item/extinguisher/equipped(mob/user, slot, initial = FALSE)
. = ..()
if((slot & ITEM_SLOT_HANDS) && fire_extinguisher_reagent_sloshing_sound && reagents.total_volume > 0)
playsound(src, fire_extinguisher_reagent_sloshing_sound, LIQUID_SLOSHING_SOUND_VOLUME, vary = TRUE, ignore_walls = FALSE)
// A secondary attack letting you wind up the extinguisher for a real wallopping to your targets head
/obj/item/extinguisher/attack_secondary(mob/living/victim, mob/living/user, params)
// This only makes sense for heavier extinguishers
if(w_class < WEIGHT_CLASS_BULKY)
return SECONDARY_ATTACK_CALL_NORMAL
if(issilicon(user))
return SECONDARY_ATTACK_CALL_NORMAL
if(!iscarbon(victim))
return SECONDARY_ATTACK_CALL_NORMAL
var/mob/living/carbon/wallopee = victim
var/obj/item/bodypart/head/head_to_bash = wallopee.get_bodypart(BODY_ZONE_HEAD)
if(!head_to_bash)
return SECONDARY_ATTACK_CALL_NORMAL
var/head_name = head_to_bash.name
if(fire_extinguisher_reagent_sloshing_sound && reagents.total_volume > 0)
playsound(src, fire_extinguisher_reagent_sloshing_sound, LIQUID_SLOSHING_SOUND_VOLUME, vary = TRUE, ignore_walls = FALSE)
log_combat(user, wallopee, "prepared to use a bash attack with a [src] against [wallopee]")
wallopee.visible_message(span_danger("[user] begins to raise [src] above [wallopee]'s [head_name]."), span_userdanger("[user] begins to raise [src], aiming to cave in your [head_name]!"))
if(!do_after(user, 2 SECONDS, target = wallopee))
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
wallopee.visible_message(span_danger("[user] brings [src] heavily down on [wallopee]'s [head_name]."), span_userdanger("[user] brings [src] heavily down on your [head_name]!"))
var/min_wound = head_to_bash.get_wound_threshold_of_wound_type(WOUND_BLUNT, WOUND_SEVERITY_SEVERE, return_value_if_no_wound = 30, wound_source = src)
var/max_wound = head_to_bash.get_wound_threshold_of_wound_type(WOUND_BLUNT, WOUND_SEVERITY_CRITICAL, return_value_if_no_wound = 50, wound_source = src)
wallopee.apply_damage(src.force * 3, src.damtype, head_to_bash, wound_bonus = rand(min_wound, max_wound + 10), attacking_item = src)
wallopee.emote("scream")
log_combat(user, wallopee, "used a bash attack with a [src] against [wallopee]")
user.do_attack_animation(wallopee, used_item = src)
if(fire_extinguisher_reagent_sloshing_sound && reagents.total_volume > 0)
playsound(src, fire_extinguisher_reagent_sloshing_sound, LIQUID_SLOSHING_SOUND_VOLUME, vary = TRUE, ignore_walls = FALSE)
playsound(source = src, soundin = src.hitsound, vol = src.get_clamped_volume(), vary = TRUE)
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
/obj/item/extinguisher/empty
starting_water = FALSE
/obj/item/extinguisher/mini
name = "pocket fire extinguisher"
desc = "A light and compact fibreglass-framed model fire extinguisher."
icon_state = "miniFE0"
worn_icon_state = "miniFE"
inhand_icon_state = "miniFE"
hitsound = null //it is much lighter, after all.
obj_flags = NONE //doesn't conduct electricity
throwforce = 2
w_class = WEIGHT_CLASS_SMALL
force = 3
custom_materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT* 0.5, /datum/material/glass = SMALL_MATERIAL_AMOUNT * 0.4)
max_water = 30
sprite_name = "miniFE"
dog_fashion = null
/obj/item/extinguisher/mini/empty
starting_water = FALSE
/obj/item/extinguisher/crafted
name = "Improvised cooling spray"
desc = "Spraycan turned coolant dispenser. Can be sprayed on containers to cool them. Refill using water."
icon_state = "coolant0"
worn_icon_state = "miniFE"
inhand_icon_state = "miniFE"
hitsound = null //it is much lighter, after all.
obj_flags = NONE //doesn't conduct electricity
throwforce = 1
w_class = WEIGHT_CLASS_SMALL
force = 3
custom_materials = null
max_water = 30
sprite_name = "coolant"
dog_fashion = null
cooling_power = 1.5
power = 3
/obj/item/extinguisher/crafted/attack_self(mob/user)
safety = !safety
icon_state = "[sprite_name][!safety]"
to_chat(user, "[safety ? "You remove the straw and put it on the side of the cool canister" : "You insert the straw, readying it for use"].")
/obj/item/extinguisher/proc/refill()
if(!chem)
return
create_reagents(max_water, AMOUNT_VISIBLE)
reagents.add_reagent(chem, max_water)
/obj/item/extinguisher/Initialize(mapload)
. = ..()
if(tank_holder_icon_state)
AddComponent(/datum/component/container_item/tank_holder, tank_holder_icon_state)
if(starting_water)
refill()
else if(chem)
create_reagents(max_water, AMOUNT_VISIBLE)
/obj/item/extinguisher/advanced
name = "advanced fire extinguisher"
desc = "Used to stop thermonuclear fires from spreading inside your engine."
icon_state = "foam_extinguisher0"
worn_icon_state = "foam_extinguisher"
inhand_icon_state = "foam_extinguisher"
tank_holder_icon_state = "holder_foam_extinguisher"
dog_fashion = null
throwforce = 10
w_class = WEIGHT_CLASS_NORMAL
force = 10
chem = /datum/reagent/firefighting_foam
tanktypes = list(
/obj/structure/reagent_dispensers/foamtank,
/obj/structure/reagent_dispensers/plumbed,
)
sprite_name = "foam_extinguisher"
precision = TRUE
/obj/item/extinguisher/advanced/empty
starting_water = FALSE
/obj/item/extinguisher/suicide_act(mob/living/carbon/user)
if (!safety && (reagents.total_volume >= 1))
user.visible_message(span_suicide("[user] puts the nozzle to [user.p_their()] mouth. It looks like [user.p_theyre()] trying to extinguish the spark of life!"))
interact_with_atom(user, user)
return OXYLOSS
else if (safety && (reagents.total_volume >= 1))
user.visible_message(span_warning("[user] puts the nozzle to [user.p_their()] mouth... The safety's still on!"))
return SHAME
else
user.visible_message(span_warning("[user] puts the nozzle to [user.p_their()] mouth... [src] is empty!"))
return SHAME
/obj/item/extinguisher/attack_self(mob/user)
safety = !safety
src.icon_state = "[sprite_name][!safety]"
balloon_alert(user, "safety [safety ? "on" : "off"]")
return
/obj/item/extinguisher/attack(mob/M, mob/living/user)
if(!user.combat_mode && !safety) //If we're on help intent and going to spray people, don't bash them.
return FALSE
else
return ..()
/obj/item/extinguisher/attack_atom(obj/attacked_obj, mob/living/user, list/modifiers, list/attack_modifiers)
if(AttemptRefill(attacked_obj, user))
refilling = TRUE
return FALSE
else
return ..()
/obj/item/extinguisher/examine(mob/user)
. = ..()
. += "The safety is [safety ? "on" : "off"]."
if(reagents.total_volume)
. += span_notice("Alt-click to empty it.")
/obj/item/extinguisher/proc/AttemptRefill(atom/target, mob/user)
if(is_type_in_list(target, tanktypes) && target.Adjacent(user))
if(reagents.total_volume == reagents.maximum_volume)
balloon_alert(user, "already full!")
return TRUE
// Make sure we're refilling with the proper chem.
if(!(target.reagents.has_reagent(chem, check_subtypes = TRUE)))
balloon_alert(user, "can't refill with this liquid!")
return TRUE
var/obj/structure/reagent_dispensers/W = target //will it work?
var/transferred = W.reagents.trans_to(src, max_water, transferred_by = user)
if(transferred > 0)
to_chat(user, span_notice("\The [src] has been refilled by [transferred] units."))
playsound(src.loc, 'sound/effects/refill.ogg', 50, TRUE, -6)
for(var/datum/reagent/water/R in reagents.reagent_list)
R.cooling_temperature = cooling_power
else
to_chat(user, span_warning("\The [W] is empty!"))
return TRUE
else
return FALSE
/obj/item/extinguisher/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
if(interacting_with.loc == user)
return NONE
// Always skip interaction if it's a bag or table (that's not on fire)
if(!(interacting_with.resistance_flags & ON_FIRE) && HAS_TRAIT(interacting_with, TRAIT_COMBAT_MODE_SKIP_INTERACTION))
return NONE
return ranged_interact_with_atom(interacting_with, user, modifiers)
/obj/item/extinguisher/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
if(refilling)
refilling = FALSE
return NONE
if(safety)
return NONE
if (src.reagents.total_volume < 1)
balloon_alert(user, "it's empty!")
return .
if (world.time < src.last_use + 12)
return .
src.last_use = world.time
playsound(src.loc, 'sound/effects/extinguish.ogg', 75, TRUE, -3)
var/direction = get_dir(src,interacting_with)
if(user.buckled && isobj(user.buckled) && !user.buckled.anchored)
var/obj/B = user.buckled
var/movementdirection = REVERSE_DIR(direction)
addtimer(CALLBACK(src, TYPE_PROC_REF(/obj/item/extinguisher, move_chair), B, movementdirection), 0.1 SECONDS)
else
user.newtonian_move(dir2angle(REVERSE_DIR(direction)))
//Get all the turfs that can be shot at
var/turf/T = get_turf(interacting_with)
var/turf/T1 = get_step(T,turn(direction, 90))
var/turf/T2 = get_step(T,turn(direction, -90))
var/list/the_targets = list(T,T1,T2)
if(precision)
var/turf/T3 = get_step(T1, turn(direction, 90))
var/turf/T4 = get_step(T2,turn(direction, -90))
the_targets.Add(T3,T4)
var/list/water_particles = list()
for(var/a in 1 to 5)
var/obj/effect/particle_effect/water/extinguisher/water = new /obj/effect/particle_effect/water/extinguisher(get_turf(src))
var/my_target = pick(the_targets)
water_particles[water] = my_target
// If precise, remove turf from targets so it won't be picked more than once
if(precision)
the_targets -= my_target
var/datum/reagents/water_reagents = new /datum/reagents(5)
water.reagents = water_reagents
water_reagents.my_atom = water
reagents.trans_to(water, 1, transferred_by = user)
//Make em move dat ass, hun
move_particles(water_particles)
return ITEM_INTERACT_SKIP_TO_ATTACK // You can smack while spraying
//Particle movement loop
/obj/item/extinguisher/proc/move_particles(list/particles)
var/delay = 2
// Second loop: Get all the water particles and make them move to their target
for(var/obj/effect/particle_effect/water/extinguisher/water as anything in particles)
water.move_at(particles[water], delay, power)
//Chair movement loop
/obj/item/extinguisher/proc/move_chair(obj/buckled_object, movementdirection)
var/datum/move_loop/loop = GLOB.move_manager.move(buckled_object, movementdirection, 1, timeout = 9, flags = MOVEMENT_LOOP_START_FAST, priority = MOVEMENT_ABOVE_SPACE_PRIORITY)
//This means the chair slowing down is dependant on the extinguisher existing, which is weird
//Couldn't figure out a better way though
RegisterSignal(loop, COMSIG_MOVELOOP_POSTPROCESS, PROC_REF(manage_chair_speed))
/obj/item/extinguisher/proc/manage_chair_speed(datum/move_loop/move/source)
SIGNAL_HANDLER
switch(source.lifetime)
if(4 to 5)
source.delay = 2
if(1 to 3)
source.delay = 3
/obj/item/extinguisher/click_alt(mob/user)
if(!user.is_holding(src))
to_chat(user, span_notice("You must be holding [src] in your hands to do this!"))
return CLICK_ACTION_BLOCKING
EmptyExtinguisher(user)
return CLICK_ACTION_SUCCESS
/obj/item/extinguisher/proc/EmptyExtinguisher(mob/user)
if(loc == user && reagents.total_volume)
reagents.expose(user.loc, TOUCH)
reagents.clear_reagents()
user.visible_message(span_notice("[user] empties out [src] onto the floor using the release valve."), span_info("You quietly empty out [src] using its release valve."))
// Firebot assembly
/obj/item/extinguisher/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if (!istype(tool, /obj/item/bodypart/arm/left/robot) && !istype(tool, /obj/item/bodypart/arm/right/robot))
return NONE
to_chat(user, span_notice("You add [tool] to [src]."))
qdel(tool)
var/obj/item/bot_assembly/firebot/assembly = new(drop_location())
var/held_index = user.is_holding(src)
qdel(src)
if (held_index)
user.put_in_hand(assembly, held_index)
return ITEM_INTERACT_SUCCESS
/obj/item/extinguisher/anti
name = "fire extender"
desc = "A traditional red fire extinguisher. Made in Britain... wait, what?"
chem = /datum/reagent/fuel
tanktypes = list(
/obj/structure/reagent_dispensers/fueltank,
/obj/structure/reagent_dispensers/plumbed
)
cooling_power = 0
+273
View File
@@ -0,0 +1,273 @@
/obj/item/inducer
name = "inducer"
desc = "A tool for inductively charging internal power cells and batteries."
icon = 'icons/obj/tools.dmi'
icon_state = "inducer-engi"
inhand_icon_state = "inducer-engi"
lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
force = 7
/// Multiplier that determines the speed at which this inducer works at.
var/power_transfer_multiplier = 1
/// Is the battery hatch opened
var/opened = FALSE
/// The cell for used in recharging cycles
var/obj/item/stock_parts/power_store/powerdevice = /obj/item/stock_parts/power_store/battery/high
/// Are we in the process of recharging something
var/recharging = FALSE
/obj/item/inducer/Initialize(mapload)
. = ..()
if(ispath(powerdevice))
powerdevice = new powerdevice(src)
register_context()
update_appearance(UPDATE_OVERLAYS)
/obj/item/inducer/Destroy(force)
QDEL_NULL(powerdevice)
. = ..()
/obj/item/inducer/Exited(atom/movable/gone, direction)
. = ..()
if(gone == powerdevice)
powerdevice = null
/obj/item/inducer/add_context(atom/source, list/context, obj/item/held_item, mob/user)
. = NONE
if(isnull(held_item))
if(opened && !QDELETED(powerdevice))
context[SCREENTIP_CONTEXT_LMB] = "Remove Cell"
. = CONTEXTUAL_SCREENTIP_SET
return
if(opened)
if(istype(held_item, /obj/item/stock_parts/power_store) && QDELETED(powerdevice))
context[SCREENTIP_CONTEXT_LMB] = "Insert cell"
return CONTEXTUAL_SCREENTIP_SET
if(istype(held_item, /obj/item/stack/sheet/mineral/plasma) && !QDELETED(powerdevice))
context[SCREENTIP_CONTEXT_LMB] = "Charge cell"
return CONTEXTUAL_SCREENTIP_SET
if(held_item.tool_behaviour == TOOL_SCREWDRIVER)
context[SCREENTIP_CONTEXT_LMB] = "[opened ? "Close" : "Open"] Panel"
return CONTEXTUAL_SCREENTIP_SET
/obj/item/inducer/examine(mob/living/user)
. = ..()
. += examine_hints(user)
/**
* Gives description for this inducer
* Arguments
*
* * mob/living/user - the mob we are returning the description to
*/
/obj/item/inducer/proc/examine_hints(mob/living/user)
PROTECTED_PROC(TRUE)
SHOULD_BE_PURE(TRUE)
. = list()
var/obj/item/stock_parts/power_store/our_cell = get_cell(src, user)
if(!QDELETED(our_cell))
. += span_notice("Its display shows: [display_energy(our_cell.charge)].")
if(opened)
. += span_notice("The cell can be removed with an empty hand.")
. += span_notice("Plasma sheets can be used to recharge the cell.")
else
. += span_warning("It's missing a power cell.")
. += span_notice("Its battery compartment can be [EXAMINE_HINT("screwed")] [opened ? "shut" : "open"].")
/obj/item/inducer/update_overlays()
. = ..()
if(!opened)
return
. += "inducer-[!QDELETED(powerdevice) ? "bat" : "nobat"]"
/obj/item/inducer/get_cell()
return powerdevice
/obj/item/inducer/emp_act(severity)
. = ..()
if(!QDELETED(powerdevice) && !(. & EMP_PROTECT_CONTENTS))
powerdevice.emp_act(severity)
/obj/item/inducer/screwdriver_act(mob/living/user, obj/item/tool)
. = NONE
if(!tool.use_tool(src, user, delay = 0))
return
opened = !opened
to_chat(user, span_notice("You [opened ? "open" : "close"] the battery compartment."))
update_appearance(UPDATE_OVERLAYS)
return ITEM_INTERACT_SUCCESS
/obj/item/inducer/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
. = NONE
if(user.combat_mode || !istype(tool) || tool.flags_1 & HOLOGRAM_1 || tool.item_flags & ABSTRACT)
return ITEM_INTERACT_SKIP_TO_ATTACK
if(istype(tool, /obj/item/stock_parts/power_store))
if(!opened)
balloon_alert(user, "open first!")
return ITEM_INTERACT_FAILURE
if(!QDELETED(powerdevice))
balloon_alert(user, "cell already installed!")
return ITEM_INTERACT_FAILURE
if(!user.transferItemToLoc(tool, src))
balloon_alert(user, "stuck in hand!")
return ITEM_INTERACT_FAILURE
powerdevice = tool
return ITEM_INTERACT_SUCCESS
else if(istype(tool, /obj/item/stack/sheet/mineral/plasma) && !QDELETED(powerdevice))
if(!powerdevice.used_charge())
balloon_alert(user, "fully charged!")
return ITEM_INTERACT_FAILURE
tool.use(1)
powerdevice.give(1.5 * STANDARD_CELL_CHARGE)
balloon_alert(user, "cell recharged")
return ITEM_INTERACT_SUCCESS
/obj/item/inducer/interact_with_atom(atom/movable/interacting_with, mob/living/user, list/modifiers)
. = NONE
if(HAS_TRAIT(interacting_with, TRAIT_COMBAT_MODE_SKIP_INTERACTION))
return
if(user.combat_mode || !istype(interacting_with) || interacting_with.flags_1 & HOLOGRAM_1)
return ITEM_INTERACT_SKIP_TO_ATTACK
//basic checks
if(opened)
balloon_alert(user, "close first!")
return ITEM_INTERACT_FAILURE
if(recharging || (!isturf(interacting_with) && user.loc == interacting_with))
return ITEM_INTERACT_FAILURE
if(!ISADVANCEDTOOLUSER(user))
to_chat(user, span_warning("You don't have the dexterity to use [src]!"))
return ITEM_INTERACT_FAILURE
var/obj/item/stock_parts/power_store/our_cell = get_cell(src, user)
if(QDELETED(our_cell))
balloon_alert(user, "no cell installed!")
return ITEM_INTERACT_FAILURE
if(!our_cell.charge)
balloon_alert(user, "no charge!")
return ITEM_INTERACT_FAILURE
var/obj/item/stock_parts/power_store/target_cell = interacting_with.get_cell(src, user)
if(QDELETED(target_cell))
return ITEM_INTERACT_FAILURE
if(!target_cell.used_charge())
balloon_alert(user, "fully charged!")
return ITEM_INTERACT_FAILURE
//begin recharging
recharging = TRUE
user.visible_message(span_notice("[user] starts recharging [interacting_with] with [src]."), span_notice("You start recharging [interacting_with] with [src]."))
var/done_any = FALSE
while(target_cell.used_charge())
if(!do_after(user, 1 SECONDS, target = user))
break
//transfer of charge
var/transferred = min(our_cell.charge, target_cell.used_charge(), target_cell.rating_base * target_cell.rating * power_transfer_multiplier)
if(!transferred)
break
our_cell.use(target_cell.give(transferred))
//update all appearances
our_cell.update_appearance()
target_cell.update_appearance()
interacting_with.update_appearance()
//sparks & update
do_sparks(1, FALSE, interacting_with)
done_any = TRUE
recharging = FALSE
// Only show a message if we succeeded at least once
if(done_any)
user.visible_message(span_notice("[user] recharges [interacting_with]!"), span_notice("You recharge [interacting_with]!"))
return ITEM_INTERACT_SUCCESS
/obj/item/inducer/attack_self(mob/user)
if(opened && !QDELETED(powerdevice))
user.visible_message(span_notice("[user] removes [powerdevice] from [src]!"), span_notice("You remove [powerdevice]."))
powerdevice.update_appearance()
user.put_in_hands(powerdevice)
update_appearance(UPDATE_OVERLAYS)
/obj/item/inducer/empty
powerdevice = null
opened = TRUE
/obj/item/inducer/orderable
powerdevice = /obj/item/stock_parts/power_store/battery/upgraded
opened = FALSE
/obj/item/inducer/sci
icon_state = "inducer-sci"
inhand_icon_state = "inducer-sci"
desc = "A tool for inductively charging internal power cells. This one has a science color scheme, and is less potent than its engineering counterpart."
powerdevice = null
opened = TRUE
/obj/item/inducer/syndicate
icon_state = "inducer-syndi"
inhand_icon_state = "inducer-syndi"
desc = "A tool for inductively charging internal power cells. This one has a suspicious colour scheme, and seems to be rigged to transfer charge at a much faster rate."
power_transfer_multiplier = 2 // 2x the base speed
powerdevice = /obj/item/stock_parts/power_store/battery/super
/obj/item/inducer/cyborg
name = "modular inducer"
icon = 'icons/obj/tools.dmi'
icon_state = "inducer-engi"
/obj/item/inducer/cyborg/examine_hints(mob/living/user)
. = list()
var/obj/item/stock_parts/power_store/our_cell = get_cell(src, user)
if(!QDELETED(our_cell))
. += span_notice("Its display shows: [display_energy(our_cell.charge)].")
if(opened)
. += span_notice("Plasma sheets can be used to recharge the cell.")
else
. += span_warning("It's missing a power cell.")
. += span_notice("Its battery compartment can be [EXAMINE_HINT("screwed")] [opened ? "shut" : "open"].")
/obj/item/inducer/cyborg/add_context(atom/source, list/context, obj/item/held_item, mob/user)
return NONE
/obj/item/inducer/cyborg/interact_with_atom(atom/movable/interacting_with, mob/living/user, list/modifiers)
if(iscyborg(user) && iscyborg(interacting_with))
balloon_alert(user, "can't charge this!")
return ITEM_INTERACT_FAILURE
return ..()
@@ -0,0 +1,108 @@
/// Max number of atoms a broom can sweep at once
#define BROOM_PUSH_LIMIT 20
/obj/item/pushbroom
name = "push broom"
desc = "This is my BROOMSTICK! It can be used manually or braced with two hands to sweep items as you move. It has a telescopic handle for compact storage."
icon = 'icons/obj/service/janitor.dmi'
icon_state = "broom0"
base_icon_state = "broom"
icon_angle = 135
lefthand_file = 'icons/mob/inhands/equipment/custodial_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/custodial_righthand.dmi'
force = 8
throwforce = 10
throw_speed = 3
throw_range = 7
w_class = WEIGHT_CLASS_NORMAL
attack_verb_continuous = list("sweeps", "brushes off", "bludgeons", "whacks")
attack_verb_simple = list("sweep", "brush off", "bludgeon", "whack")
resistance_flags = FLAMMABLE
/obj/item/pushbroom/Initialize(mapload)
. = ..()
AddComponent(/datum/component/jousting, damage_boost_per_tile = 1)
AddComponent(/datum/component/two_handed, \
force_unwielded = 8, \
force_wielded = 12, \
icon_wielded = "[base_icon_state]1", \
wield_callback = CALLBACK(src, PROC_REF(on_wield)), \
unwield_callback = CALLBACK(src, PROC_REF(on_unwield)), \
)
/obj/item/pushbroom/update_icon_state()
icon_state = "[base_icon_state]0"
return ..()
/**
* Handles registering the sweep proc when the broom is wielded
*
* Arguments:
* * source - The source of the on_wield proc call
* * user - The user which is wielding the broom
*/
/obj/item/pushbroom/proc/on_wield(obj/item/source, mob/user)
to_chat(user, span_notice("You brace the [src] against the ground in a firm sweeping stance."))
RegisterSignal(user, COMSIG_MOVABLE_PRE_MOVE, PROC_REF(sweep))
/**
* Handles unregistering the sweep proc when the broom is unwielded
*
* Arguments:
* * source - The source of the on_unwield proc call
* * user - The user which is unwielding the broom
*/
/obj/item/pushbroom/proc/on_unwield(obj/item/source, mob/user)
UnregisterSignal(user, COMSIG_MOVABLE_PRE_MOVE)
/obj/item/pushbroom/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
sweep(user, interacting_with)
return NONE // I guess
/**
* Attempts to push up to BROOM_PUSH_LIMIT atoms from a given location the user's faced direction
*
* Arguments:
* * user - The user of the pushbroom
* * A - The atom which is located at the location to push atoms from
*/
/obj/item/pushbroom/proc/sweep(mob/user, atom/atom)
SIGNAL_HANDLER
do_sweep(src, user, atom, user.dir)
/**
* Sweep objects in the direction we're facing towards our direction
* Arguments
* * broomer - The object being used for brooming
* * user - The person who is brooming
* * target - The object or tile that's target of a broom click or being moved into
* * sweep_dir - The directions in which we sweep objects
*/
/proc/do_sweep(obj/broomer, mob/user, atom/target, sweep_dir)
var/turf/current_item_loc = isturf(target) ? target : target.loc
if (!isturf(current_item_loc))
return
var/turf/new_item_loc = get_step(current_item_loc, sweep_dir)
var/list/items_to_sweep = list()
var/i = 1
for (var/obj/item/garbage in current_item_loc.contents)
if(garbage.anchored)
continue
items_to_sweep += garbage
i++
if(i > BROOM_PUSH_LIMIT)
break
SEND_SIGNAL(new_item_loc, COMSIG_TURF_RECEIVE_SWEEPED_ITEMS, broomer, user, items_to_sweep)
if(!length(items_to_sweep))
return
for (var/obj/item/garbage in items_to_sweep)
garbage.Move(new_item_loc, sweep_dir)
playsound(current_item_loc, 'sound/items/weapons/thudswoosh.ogg', 30, TRUE, -1)
#undef BROOM_PUSH_LIMIT
@@ -0,0 +1,87 @@
///The time limit on the keys before the access it's been given clears itself.
#define ACCESS_TIMER_LIMIT (10 MINUTES)
/obj/item/access_key
name = "access key ring"
desc = "A key ring with a beeper, allowing the keys to change shape depending on which department it has access to."
icon_state = "access_key"
inhand_icon_state = "access_key"
icon = 'icons/obj/service/janitor.dmi'
lefthand_file = 'icons/mob/inhands/items/keys_lefthand.dmi'
righthand_file = 'icons/mob/inhands/items/keys_righthand.dmi'
hitsound = 'sound/items/rattling_keys_attack.ogg'
force = 2
verb_say = "beeps" //it has a beeper
verb_ask = "questionably beeps"
verb_exclaim = "beeps loudly"
w_class = WEIGHT_CLASS_TINY
///The departmental access given to the key.
var/list/department_access
/obj/item/access_key/Initialize(mapload)
. = ..()
RegisterSignal(SSdcs, COMSIG_ON_DEPARTMENT_ACCESS, PROC_REF(department_access_given))
GLOB.janitor_devices += src
/obj/item/access_key/Destroy()
GLOB.janitor_devices -= src
return ..()
/obj/item/access_key/examine(mob/user)
. = ..()
if(department_access)
. += "It currently holds access to the [department_access] region."
/obj/item/access_key/examine_more(mob/user)
. = ..()
. += span_notice("Access can be granted through a Keycard Authentication Device.")
. += span_notice("This access is limited to one department at a time.")
/**
* Called when attempting to open an airlock.
* Checks if the keys have access, returns try_to_activate_door if it does, returns FALSE otherwise.
* Args:
* user - The person attempting to open the door
* airlock - The door we're attempting to open
*/
/obj/item/access_key/proc/attempt_open_door(mob/living/user, obj/machinery/door/airlock)
if(DOING_INTERACTION_WITH_TARGET(user, airlock))
return
user.balloon_alert_to_viewers("fumbles with keys...", "finding key...")
user.playsound_local(src, 'sound/items/rattling_keys.ogg', 25, TRUE)
if(!do_after(user, 3 SECONDS, airlock))
return FALSE
if(!department_access || !airlock.check_access_list(SSid_access.accesses_by_region[department_access]))
airlock.balloon_alert(user, "no access!")
return FALSE
return airlock.try_to_activate_door(user, access_bypass = TRUE)
/**
* Called when a keycard authenticator sends region access
* Stores it on the key to use for access, then sets a timer to clear it.
* Args:
* source - the keycard authenticator giving us the access
* region_access - the list of access we're being sent, we only take the first entry in the list as there should only have one department at a time.
*/
/obj/item/access_key/proc/department_access_given(obj/machinery/keycard_auth/source, list/region_access)
SIGNAL_HANDLER
department_access = region_access[1]
say("Access granted to [department_access] area.")
playsound(src, 'sound/machines/ding.ogg', 25, TRUE)
addtimer(CALLBACK(src, PROC_REF(clear_access)), ACCESS_TIMER_LIMIT, TIMER_UNIQUE|TIMER_OVERRIDE)
log_game("Access to the [department_access] department was given to [src] [(ismob(loc)) ? "held by [loc]" : "which is not being held"]")
investigate_log("Access to the [department_access] department was given to [src] [(ismob(loc)) ? "held by [loc]" : "which is not being held"]", INVESTIGATE_ACCESSCHANGES)
/**
* Called when a keycard authenticator runs out of time
* Clears the department access and alerts nearby people of such.
*/
/obj/item/access_key/proc/clear_access()
log_game("Access to the [department_access] department on [src] has expired.")
investigate_log("Access to the [department_access] department on [src] has expired.]", INVESTIGATE_ACCESSCHANGES)
department_access = null
say("Access revoked, time ran out.")
playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, TRUE)
#undef ACCESS_TIMER_LIMIT
@@ -0,0 +1,139 @@
/obj/item/bodybag
name = "body bag"
desc = "A folded bag designed for the storage and transportation of cadavers."
icon = 'icons/obj/medical/bodybag.dmi'
icon_state = "bodybag_folded"
w_class = WEIGHT_CLASS_SMALL
///Stored path we use for spawning a new body bag entity when unfolded.
var/unfoldedbag_path = /obj/structure/closet/body_bag
/obj/item/bodybag/attack_self(mob/user)
if(user.is_holding(src))
deploy_bodybag(user, get_turf(user))
else
deploy_bodybag(user, get_turf(src))
/obj/item/bodybag/interact_with_atom(atom/interacting_with, mob/living/user, flags)
if(isopenturf(interacting_with))
deploy_bodybag(user, interacting_with)
return ITEM_INTERACT_SUCCESS
return NONE
/obj/item/bodybag/attempt_pickup(mob/user)
// can't pick ourselves up if we are inside of the bodybag, else very weird things may happen
if(contains(user))
return TRUE
return ..()
/**
* Creates a new body bag item when unfolded, at the provided location, replacing the body bag item.
* * mob/user: User opening the body bag.
* * atom/location: the place/entity/mob where the body bag is being deployed from.
*/
/obj/item/bodybag/proc/deploy_bodybag(mob/user, atom/location)
var/obj/structure/closet/body_bag/item_bag = new unfoldedbag_path(location)
item_bag.open(user)
item_bag.add_fingerprint(user)
item_bag.foldedbag_instance = src
moveToNullspace()
return item_bag
/obj/item/bodybag/suicide_act(mob/living/user)
if(isopenturf(user.loc))
user.visible_message(span_suicide("[user] is crawling into [src]! It looks like [user.p_theyre()] trying to commit suicide!"))
var/obj/structure/closet/body_bag/R = new unfoldedbag_path(user.loc)
R.add_fingerprint(user)
qdel(src)
user.forceMove(R)
playsound(src, 'sound/items/zip/zip.ogg', 15, TRUE, -3)
return OXYLOSS
// Bluespace bodybag
/obj/item/bodybag/bluespace
name = "bluespace body bag"
desc = "A folded bluespace body bag designed for the storage and transportation of cadavers."
icon = 'icons/obj/medical/bodybag.dmi'
icon_state = "bluebodybag_folded"
unfoldedbag_path = /obj/structure/closet/body_bag/bluespace
w_class = WEIGHT_CLASS_SMALL
item_flags = NO_MAT_REDEMPTION
/obj/item/bodybag/bluespace/examine(mob/user)
. = ..()
if(contents.len)
var/s = contents.len == 1 ? "" : "s"
. += span_notice("You can make out the shape[s] of [contents.len] object[s] through the fabric.")
/obj/item/bodybag/bluespace/Destroy()
for(var/atom/movable/A in contents)
A.forceMove(get_turf(src))
if(isliving(A))
to_chat(A, span_notice("You suddenly feel the space around you torn apart! You're free!"))
return ..()
/obj/item/bodybag/bluespace/deploy_bodybag(mob/user, atom/location)
var/obj/structure/closet/body_bag/item_bag = new unfoldedbag_path(location)
for(var/atom/movable/inside in contents)
inside.forceMove(item_bag)
if(isliving(inside))
to_chat(inside, span_notice("You suddenly feel air around you! You're free!"))
item_bag.open(user)
item_bag.add_fingerprint(user)
item_bag.foldedbag_instance = src
moveToNullspace()
return item_bag
/obj/item/bodybag/bluespace/container_resist_act(mob/living/user)
if(user.incapacitated)
to_chat(user, span_warning("You can't get out while you're restrained like this!"))
return
user.changeNext_move(CLICK_CD_BREAKOUT)
user.last_special = world.time + CLICK_CD_BREAKOUT
to_chat(user, span_notice("You claw at the fabric of [src], trying to tear it open..."))
to_chat(loc, span_warning("Someone starts trying to break free of [src]!"))
if(!do_after(user, 12 SECONDS, src, timed_action_flags = (IGNORE_TARGET_LOC_CHANGE|IGNORE_HELD_ITEM)))
return
// you are still in the bag? time to go unless you KO'd, honey!
// if they escape during this time and you rebag them the timer is still clocking down and does NOT reset so they can very easily get out.
if(user.incapacitated)
to_chat(loc, span_warning("The pressure subsides. It seems that they've stopped resisting..."))
return
loc.visible_message(span_warning("[user] suddenly appears in front of [loc]!"), span_userdanger("[user] breaks free of [src]!"))
qdel(src)
/obj/item/bodybag/environmental
name = "environmental protection bag"
desc = "A folded, reinforced bag designed to protect against exoplanetary environmental storms."
icon = 'icons/obj/medical/bodybag.dmi'
icon_state = "envirobag_folded"
unfoldedbag_path = /obj/structure/closet/body_bag/environmental
w_class = WEIGHT_CLASS_NORMAL //It's reinforced and insulated, like a beefed-up sleeping bag, so it has a higher bulkiness than regular bodybag
resistance_flags = ACID_PROOF | FIRE_PROOF | FREEZE_PROOF
/obj/item/bodybag/environmental/nanotrasen
name = "elite environmental protection bag"
desc = "A folded, heavily reinforced, and insulated bag, capable of fully isolating its contents from external factors."
icon_state = "ntenvirobag_folded"
unfoldedbag_path = /obj/structure/closet/body_bag/environmental/nanotrasen
resistance_flags = ACID_PROOF | FIRE_PROOF | FREEZE_PROOF | LAVA_PROOF
/obj/item/bodybag/environmental/prisoner
name = "prisoner transport bag"
desc = "Intended for transport of prisoners through hazardous environments, this folded environmental protection bag comes with straps to keep an occupant secure."
icon = 'icons/obj/medical/bodybag.dmi'
icon_state = "prisonerenvirobag_folded"
unfoldedbag_path = /obj/structure/closet/body_bag/environmental/prisoner
/obj/item/bodybag/environmental/prisoner/pressurized
name = "pressurized prisoner transport bag"
unfoldedbag_path = /obj/structure/closet/body_bag/environmental/prisoner/pressurized
/obj/item/bodybag/environmental/prisoner/syndicate
name = "syndicate prisoner transport bag"
desc = "An alteration of Nanotrasen's environmental protection bag which has been used in several high-profile kidnappings. Designed to keep a victim unconscious, alive, and secured until they are transported to a required location."
icon = 'icons/obj/medical/bodybag.dmi'
icon_state = "syndieenvirobag_folded"
unfoldedbag_path = /obj/structure/closet/body_bag/environmental/prisoner/pressurized/syndicate
resistance_flags = ACID_PROOF | FIRE_PROOF | FREEZE_PROOF | LAVA_PROOF
@@ -0,0 +1,147 @@
/obj/item/cane
name = "cane"
desc = "A cane used by a true gentleman. Or a clown."
icon = 'icons/obj/weapons/staff.dmi'
icon_state = "cane"
inhand_icon_state = "stick"
icon_angle = 135
lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
force = 5
throwforce = 5
w_class = WEIGHT_CLASS_SMALL
custom_materials = list(/datum/material/iron= SMALL_MATERIAL_AMOUNT * 0.5)
attack_verb_continuous = list("bludgeons", "whacks", "disciplines", "thrashes")
attack_verb_simple = list("bludgeon", "whack", "discipline", "thrash")
/obj/item/cane/examine(mob/user, thats)
. = ..()
. += span_notice("This item can be used to support your weight, preventing limping from any broken bones on your legs you may have.")
/obj/item/cane/equipped(mob/living/user, slot, initial)
..()
if(!(slot & ITEM_SLOT_HANDS))
return
movement_support_add(user)
/obj/item/cane/dropped(mob/living/user, silent = FALSE)
. = ..()
movement_support_del(user)
/obj/item/cane/proc/movement_support_add(mob/living/user)
RegisterSignal(user, COMSIG_CARBON_LIMPING, PROC_REF(handle_limping))
return TRUE
/obj/item/cane/proc/movement_support_del(mob/living/user)
UnregisterSignal(user, list(COMSIG_CARBON_LIMPING))
return TRUE
/obj/item/cane/proc/handle_limping(mob/living/user)
SIGNAL_HANDLER
return COMPONENT_CANCEL_LIMP
/obj/item/cane/crutch
name = "medical crutch"
desc = "A medical crutch used by people missing a leg. Not all that useful if you're missing both of them, though."
icon = 'icons/obj/weapons/staff.dmi'
icon_state = "crutch_med"
inhand_icon_state = "crutch_med"
icon_angle = 45
lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
force = 12
throwforce = 8
w_class = WEIGHT_CLASS_BULKY
custom_materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 0.5)
attack_verb_continuous = list("bludgeons", "whacks", "thrashes")
attack_verb_simple = list("bludgeon", "whack", "thrash")
/obj/item/cane/crutch/Initialize(mapload)
. = ..()
AddElement(/datum/element/cuffable_item)
/obj/item/cane/crutch/examine(mob/user, thats)
. = ..()
// tacked on after the cane string
. += span_notice("As a crutch, it can also help lessen the slowdown incurred by missing a leg.")
/obj/item/cane/crutch/movement_support_add(mob/living/user)
. = ..()
if(!.)
return
RegisterSignal(user, COMSIG_LIVING_LIMBLESS_SLOWDOWN, PROC_REF(handle_slowdown))
user.update_usable_leg_status()
user.AddElementTrait(TRAIT_WADDLING, REF(src), /datum/element/waddling)
/obj/item/cane/crutch/movement_support_del(mob/living/user)
. = ..()
if(!.)
return
UnregisterSignal(user, list(COMSIG_LIVING_LIMBLESS_SLOWDOWN, COMSIG_CARBON_LIMPING))
user.update_usable_leg_status()
REMOVE_TRAIT(user, TRAIT_WADDLING, REF(src))
/obj/item/cane/crutch/proc/handle_slowdown(mob/living/user, limbless_slowdown, list/slowdown_mods)
SIGNAL_HANDLER
var/leg_amount = user.usable_legs
// Don't do anything if the number is equal (or higher) to the usual.
if(leg_amount >= user.default_num_legs)
return
// If we have at least one leg and it's less than the default, reduce slowdown by 60%.
if(leg_amount && (leg_amount < user.default_num_legs))
slowdown_mods += 0.4
/obj/item/cane/crutch/wood
name = "wooden crutch"
desc = "A handmade crutch. Also makes a decent bludgeon if you need it."
icon_state = "crutch_wood"
inhand_icon_state = "crutch_wood"
custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 5)
/obj/item/cane/white
name = "white cane"
desc = "Traditionally used by the blind to help them see. Folds down to be easier to transport."
icon_state = "cane_white"
inhand_icon_state = "cane_white"
icon_angle = 45
lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
force = 1
w_class = WEIGHT_CLASS_SMALL
custom_materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 6)
/obj/item/cane/white/Initialize(mapload)
. = ..()
AddElement(/datum/element/cuffable_item)
AddComponent( \
/datum/component/transforming, \
force_on = 7, \
hitsound_on = hitsound, \
w_class_on = WEIGHT_CLASS_BULKY, \
clumsy_check = FALSE, \
attack_verb_continuous_on = list("smacks", "strikes", "cracks", "beats"), \
attack_verb_simple_on = list("smack", "strike", "crack", "beat"), \
)
RegisterSignal(src, COMSIG_TRANSFORMING_ON_TRANSFORM, PROC_REF(on_transform))
/obj/item/cane/white/handle_limping(mob/living/user)
return HAS_TRAIT(src, TRAIT_TRANSFORM_ACTIVE) ? COMPONENT_CANCEL_LIMP : NONE
/*
* Signal proc for [COMSIG_TRANSFORMING_ON_TRANSFORM].
*
* Gives feedback to the user and makes it show up inhand.
*/
/obj/item/cane/white/proc/on_transform(obj/item/source, mob/user, active)
SIGNAL_HANDLER
if(user)
balloon_alert(user, active ? "extended" : "collapsed")
if(!HAS_TRAIT(src, TRAIT_BLIND_TOOL))
ADD_TRAIT(src, TRAIT_BLIND_TOOL, INNATE_TRAIT)
else
REMOVE_TRAIT(src, TRAIT_BLIND_TOOL, INNATE_TRAIT)
playsound(src, 'sound/items/weapons/batonextend.ogg', 50, TRUE)
return COMPONENT_NO_DEFAULT_MESSAGE
@@ -0,0 +1,720 @@
//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/power_store/cell/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))
AddElement(/datum/element/drag_pickup)
/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/on_craft_completion(list/components, datum/crafting_recipe/current_recipe, atom/crafter)
. = ..()
cell = locate(/obj/item/stock_parts/power_store) 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(user.get_slot_by_item(src) & slot_flags)
ui_action_click()
else
balloon_alert(user, "equip the unit 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/screwdriver_act(mob/living/user, obj/item/tool)
if(!cell || !cell_removable)
return FALSE
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/item_interaction(mob/living/user, obj/item/item, list/modifiers)
if(item == paddles)
toggle_paddles()
return NONE
if(!istype(item, /obj/item/stock_parts/power_store/cell))
return NONE
var/obj/item/stock_parts/power_store/cell/new_cell = item
if(!isnull(cell))
to_chat(user, span_warning("[src] already has a cell!"))
return ITEM_INTERACT_BLOCKING
if(new_cell.maxcharge < paddles.revivecost)
to_chat(user, span_notice("[src] requires a higher capacity cell."))
return ITEM_INTERACT_BLOCKING
if(!user.transferItemToLoc(new_cell, src))
return NONE
cell = new_cell
to_chat(user, span_notice("You install a cell in [src]."))
update_power()
return ITEM_INTERACT_SUCCESS
/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(STANDARD_CELL_CHARGE / 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/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(QDELETED(cell))
return
if(cell.charge < (paddles.revivecost + chrgdeductamt))
powered = FALSE
if(!cell.use(chrgdeductamt))
powered = FALSE
update_power()
/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/defib_ready.ogg', 50, FALSE)
else
visible_message(span_notice("[src] beeps: Charge depleted."))
playsound(src, 'sound/machines/defib/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
slot_flags = ITEM_SLOT_BELT|ITEM_SLOT_SUITSTORE|ITEM_SLOT_DEX_STORAGE
worn_icon = 'icons/mob/clothing/belt.dmi'
worn_icon_state = "defibcompact"
w_class = WEIGHT_CLASS_NORMAL
paddle_state = "defibcompact-paddles"
powered_state = "defibcompact-powered"
charge_state = "defibcompact-charge"
nocell_state = "defibcompact-nocell"
emagged_state = "defibcompact-emagged"
/obj/item/defibrillator/compact/loaded/Initialize(mapload)
. = ..()
cell = new(src)
update_power()
/obj/item/defibrillator/compact/loaded/cmo // subtype for the spy steal objective
name = "chief medical officer's compact defibrillator"
icon_state = "defibcmo"
resistance_flags = INDESTRUCTIBLE // So no cheesy getting rid of like other steal/head items
/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/power_store/cell/infinite(src)
update_power()
/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'
spawn_blacklisted = TRUE
force = 0
throwforce = 6
w_class = WEIGHT_CLASS_BULKY
resistance_flags = INDESTRUCTIBLE
base_icon_state = "defibpaddles"
var/revivecost = STANDARD_CELL_CHARGE * 0.1
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)
RegisterSignal(src, COMSIG_HUMAN_NON_STORAGE_HOTKEY, PROC_REF(on_non_storage_hotkey))
/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))
RegisterSignal(defib, 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/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/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(!req_defib)
return ..()
UnregisterSignal(defib, COMSIG_MOVABLE_MOVED)
if(user)
UnregisterSignal(user, COMSIG_MOVABLE_MOVED)
to_chat(user, span_notice("The paddles snap back into the main unit."))
snap_back()
return ..()
/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, list/modifiers, list/attack_modifiers)
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/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
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 successfully 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.adjust_stamina_loss(60)
M.Knockdown(75)
M.set_jitter_if_lower(100 SECONDS)
M.apply_status_effect(/datum/status_effect/convulsing)
playsound(src, 'sound/machines/defib/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/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/defib_failed.ogg', 50, FALSE)
do_cancel()
return
user.visible_message(span_bolddanger("<i>[user] shocks [H] with \the [src]!"), span_warning("You shock [H] with \the [src]!"))
playsound(src, 'sound/machines/defib/defib_zap.ogg', 100, TRUE, -1)
playsound(src, 'sound/items/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/defib_charge.ogg', 75, FALSE)
var/obj/item/organ/heart = H.get_organ_by_type(/obj/item/organ/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/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/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 (DEFIB_FAIL_GOLEM)
fail_reason = "Patient is constructed from inorganic materials. Further attempts futile, though manual reconstruction is possible."
if(fail_reason)
user.visible_message(span_warning("[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - [fail_reason]"))
playsound(src, 'sound/machines/defib/defib_failed.ogg', 50, FALSE)
else
var/total_brute = H.get_brute_loss()
var/total_burn = H.get_fire_loss()
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.adjust_oxy_loss(H.health - HALFWAYCRITDEATH, updating_health = FALSE)
else
var/overall_damage = total_brute + total_burn + H.get_tox_loss() + H.get_oxy_loss()
var/mobhealth = H.health
need_mob_update += H.adjust_oxy_loss((mobhealth - HALFWAYCRITDEATH) * (H.get_oxy_loss() / overall_damage), updating_health = FALSE)
need_mob_update += H.adjust_tox_loss((mobhealth - HALFWAYCRITDEATH) * (H.get_tox_loss() / overall_damage), updating_health = FALSE, forced = TRUE) // force tox heal for toxin lovers too
need_mob_update += H.adjust_fire_loss((mobhealth - HALFWAYCRITDEATH) * (total_burn / overall_damage), updating_health = FALSE)
need_mob_update += H.adjust_brute_loss((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/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/heart))
user.visible_message(span_warning("[req_defib ? "[defib]" : "[src]"] buzzes: Patient's heart is missing. Operation aborted."))
playsound(src, 'sound/machines/defib/defib_failed.ogg', 50, FALSE)
else if(H.undergoing_cardiac_arrest())
playsound(src, 'sound/machines/defib/defib_zap.ogg', 50, TRUE, -1)
if(!(heart.organ_flags & ORGAN_FAILING))
H.set_heartattack(FALSE)
do_success()
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 if(H.has_status_effect(/datum/status_effect/heart_attack))
user.visible_message(span_notice("[req_defib ? "[defib]" : "[src]"] pings: Patient's heart has stabilized, further applications may be necessary."))
SEND_SIGNAL(H, COMSIG_HEARTATTACK_DEFIB)
playsound(src, 'sound/machines/defib/defib_zap.ogg', 50, TRUE, -1)
do_success()
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/defib_failed.ogg', 50, FALSE)
do_cancel()
/obj/item/shockpaddles/proc/is_wielded()
return HAS_TRAIT(src, TRAIT_WIELDED)
/obj/item/shockpaddles/proc/on_non_storage_hotkey(datum/source, mob/living/carbon/human/user, obj/item/possible_storage)
SIGNAL_HANDLER
if(possible_storage == defib)
user.dropItemToGround(src)
return COMPONENT_STORAGE_HOTKEY_HANDLED
return NONE
/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
@@ -0,0 +1,21 @@
/obj/item/reflexhammer
name = "reflex hammer"
desc = "A small plastic headed hammer, used to test for neurological damage."
icon = 'icons/obj/weapons/hammer.dmi'
icon_state = "reflex_hammer"
icon_angle = -135
force = 1
throwforce = 1
custom_materials = list(/datum/material/iron=SMALL_MATERIAL_AMOUNT*0.75, /datum/material/plastic=SMALL_MATERIAL_AMOUNT*0.5)
w_class = WEIGHT_CLASS_TINY
attack_verb_continuous = list("tests", "jerks", "bonks", "taps")
attack_verb_simple = list("test", "jerk", "bonk", "tap")
/obj/item/reflexhammer/Initialize(mapload)
. = ..()
AddElement(/datum/element/kneejerk)
/obj/item/reflexhammer/suicide_act(mob/living/user)
user.visible_message(span_suicide("[user] is beating [user.p_them()]self to death with [src]! Unfortunately, it's not a real hammer! It looks like [user.p_theyre()] trying to commit suicide!"))
playsound(loc, 'sound/items/gavel.ogg', 50, TRUE, -1)
return STAMINALOSS | SHAME
@@ -0,0 +1,348 @@
//Items for nuke theft, supermatter theft traitor objective
// STEALING THE NUKE
//the nuke core - objective item
/obj/item/nuke_core
name = "plutonium core"
desc = "Extremely radioactive. Wear goggles."
icon = 'icons/obj/antags/syndicate_tools.dmi'
icon_state = "plutonium_core"
inhand_icon_state = "plutoniumcore"
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
var/pulse = 0
var/cooldown = 0
var/pulseicon = "plutonium_core_pulse"
/obj/item/nuke_core/Initialize(mapload)
. = ..()
START_PROCESSING(SSobj, src)
/obj/item/nuke_core/Destroy()
STOP_PROCESSING(SSobj, src)
return ..()
/obj/item/nuke_core/attackby(obj/item/nuke_core_container/container, mob/user)
if(istype(container))
container.load(src, user)
else
return ..()
/obj/item/nuke_core/process()
if(cooldown < world.time - 60)
cooldown = world.time
flick(pulseicon, src)
radiation_pulse(src, max_range = 2, threshold = RAD_EXTREME_INSULATION)
/obj/item/nuke_core/suicide_act(mob/living/user)
user.visible_message(span_suicide("[user] is rubbing [src] against [user.p_them()]self! It looks like [user.p_theyre()] trying to commit suicide!"))
return TOXLOSS
//nuke core box, for carrying the core
/obj/item/nuke_core_container
name = "nuke core container"
desc = "Solid container for radioactive objects."
icon = 'icons/obj/antags/syndicate_tools.dmi'
icon_state = "core_container_empty"
inhand_icon_state = "tile"
lefthand_file = 'icons/mob/inhands/items/tiles_lefthand.dmi'
righthand_file = 'icons/mob/inhands/items/tiles_righthand.dmi'
var/obj/item/nuke_core/core
/obj/item/nuke_core_container/Destroy()
QDEL_NULL(core)
return ..()
/obj/item/nuke_core_container/proc/load(obj/item/nuke_core/ncore, mob/user)
if(core || !istype(ncore))
return FALSE
ncore.forceMove(src)
core = ncore
icon_state = "core_container_loaded"
to_chat(user, span_warning("Container is sealing..."))
addtimer(CALLBACK(src, PROC_REF(seal)), 5 SECONDS)
return TRUE
/obj/item/nuke_core_container/proc/seal()
if(istype(core))
STOP_PROCESSING(SSobj, core)
icon_state = "core_container_sealed"
playsound(src, 'sound/items/deconstruct.ogg', 60, TRUE)
if(ismob(loc))
to_chat(loc, span_warning("[src] is permanently sealed, [core]'s radiation is contained."))
/obj/item/nuke_core_container/attackby(obj/item/nuke_core/core, mob/user)
if(istype(core))
if(!user.temporarilyRemoveItemFromInventory(core))
to_chat(user, span_warning("The [core] is stuck to your hand!"))
return
else
load(core, user)
else
return ..()
//snowflake screwdriver, works as a key to start nuke theft, traitor only
/obj/item/screwdriver/nuke
name = "screwdriver"
desc = "A screwdriver with an ultra thin tip that's carefully designed to boost screwing speed."
icon = 'icons/obj/antags/syndicate_tools.dmi'
icon_state = "screwdriver_nuke"
post_init_icon_state = null
inhand_icon_state = "screwdriver_nuke"
toolspeed = 0.5
random_color = FALSE
greyscale_config_inhand_left = null
greyscale_config_inhand_right = null
greyscale_colors = null
/obj/item/screwdriver/nuke/get_belt_overlay()
return mutable_appearance('icons/obj/clothing/belt_overlays.dmi', "screwdriver_nuke")
/obj/item/paper/guides/antag/nuke_instructions
default_raw_text = "How to break into a Nanotrasen self-destruct terminal and remove its plutonium core:<br>\
<ul>\
<li>Use a screwdriver with a very thin tip (provided) to unscrew the terminal's front panel</li>\
<li>Dislodge and remove the front panel with a crowbar</li>\
<li>Cut the inner metal plate with a welding tool</li>\
<li>Pry off the inner plate with a crowbar to expose the radioactive core</li>\
<li>Use the core container to remove the plutonium core; the container will take some time to seal</li>\
<li>???</li>\
</ul>"
/obj/item/paper/guides/antag/hdd_extraction
default_raw_text = "<h1>Source Code Theft & You - The Idiot's Guide to Crippling Nanotrasen Research & Development</h1><br>\
Rumour has it that Nanotrasen are using their R&D Servers to create something awful! They've codenamed it Project Goon, whatever that means.<br>\
This cannot be allowed to stand. Below is all our intel for stealing their source code and crippling their research efforts:<br>\
<ul>\
<li>Locate the physical R&D Server mainframes. Intel suggests these are stored in specially cooled rooms deep within their Science department.</li>\
<li>Nanotrasen is a corporation not known for subtlety in design. You should be able to identify the master server by any distinctive markings.</li>\
<li>Tools are on-site procurement. Screwdriver, crowbar and wirecutters should be all you need to do the job.<li>\
<li>The front panel screws are hidden in recesses behind stickers. Easily removed once you know they're there.</li>\
<li>You'll probably find the hard drive in secure housing. You may need to pry it loose with a crowbar, shouldn't do too much damage.</li>\
<li>Finally, carefully cut all of the hard drive's connecting wires. Don't rush this, snipping the wrong wire could wipe all data!</li>\
</ul>\
Removing this drive is guaranteed to cripple research efforts regardless of what data is contained on it.<br>\
The thing's probably hardwired. No putting it back once you've extracted it. The crew are likely to be as mad as bees if they find out!<br>\
Survive the shift and extract the hard drive safely.<br>\
Succeed and you will receive a coveted green highlight on your record for this assignment. Fail us and red's the last colour you'll ever see.<br>\
Do not disappoint us.<br>"
/obj/item/disk/computer/hdd_theft
name = "r&d server hard disk drive"
desc = "For some reason, people really seem to want to steal this. The source code on this drive is probably used for something awful!"
max_capacity = 512
w_class = WEIGHT_CLASS_NORMAL
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
sticker_icon_state = "o_nt"
// STEALING SUPERMATTER
/obj/item/paper/guides/antag/supermatter_sliver
default_raw_text = "How to safely extract a supermatter sliver:<br>\
<ul>\
<li>You must have active magnetic anchoring at all times near an active supermatter crystal.</li>\
<li>Approach an active supermatter crystal with radiation shielded personal protective equipment. DO NOT MAKE PHYSICAL CONTACT.</li>\
<li>Use a supermatter scalpel (provided) to slice off a sliver of the crystal.</li>\
<li>Use supermatter extraction tongs (also provided) to safely pick up the sliver you sliced off.</li>\
<li>Physical contact of any object with the sliver will dust the object, as well as yourself.</li>\
<li>Use the tongs to place the sliver into the provided container, which will take some time to seal.</li>\
<li>Get the hell out before the crystal delaminates.</li>\
<li>???</li>\
</ul>"
/obj/item/nuke_core/supermatter_sliver
name = "supermatter sliver"
desc = "A tiny, highly volatile sliver of a supermatter crystal. Do not handle without protection!"
icon_state = "supermatter_sliver"
inhand_icon_state = null //touching it dusts you, so no need for an inhand icon.
pulseicon = "supermatter_sliver_pulse"
layer = ABOVE_MOB_LAYER
/obj/item/nuke_core/supermatter_sliver/Initialize(mapload)
. = ..()
RegisterSignal(src, COMSIG_FISHING_ROD_CAST, PROC_REF(on_hook))
/obj/item/nuke_core/supermatter_sliver/proc/on_hook(obj/item/nuke_core/supermatter_sliver/source, obj/item/fishing_rod/rod, mob/user)
SIGNAL_HANDLER
//hook gets dusted but the rod remains intact
attackby(rod.hook, user)
return FISHING_ROD_CAST_HANDLED
/obj/item/nuke_core/supermatter_sliver/attack_tk(mob/user) // no TK dusting memes
return
/obj/item/nuke_core/supermatter_sliver/can_be_pulled(user, force) // no drag memes
return FALSE
/obj/item/nuke_core/supermatter_sliver/attackby(obj/item/W, mob/living/user, list/modifiers, list/attack_modifiers)
if(istype(W, /obj/item/hemostat/supermatter))
var/obj/item/hemostat/supermatter/tongs = W
if (tongs.sliver)
to_chat(user, span_warning("\The [tongs] is already holding a supermatter sliver!"))
return FALSE
forceMove(tongs)
tongs.sliver = src
tongs.update_appearance()
to_chat(user, span_notice("You carefully pick up [src] with [tongs]."))
else if(istype(W, /obj/item/scalpel/supermatter) || istype(W, /obj/item/nuke_core_container/supermatter/)) // we don't want it to dust
return
else
to_chat(user, span_notice("As it touches \the [src], both \the [src] and \the [W] burst into dust!"))
radiation_pulse(user, max_range = 2, threshold = RAD_EXTREME_INSULATION, chance = 40)
playsound(src, 'sound/effects/supermatter.ogg', 50, TRUE)
qdel(W)
qdel(src)
/obj/item/nuke_core/supermatter_sliver/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(!isliving(hit_atom))
return ..()
var/mob/living/victim = hit_atom
if(victim.incorporeal_move || HAS_TRAIT(victim, TRAIT_GODMODE)) //try to keep this in sync with supermatter's consume fail conditions
return ..()
var/mob/thrower = throwingdatum?.get_thrower()
if(istype(thrower))
log_combat(thrower, hit_atom, "consumed", src)
message_admins("[src] has consumed [key_name_admin(victim)] [ADMIN_JMP(src)], thrown by [key_name_admin(thrower)].")
investigate_log("has consumed [key_name(victim)], thrown by [key_name(thrower)]", INVESTIGATE_ENGINE)
else
message_admins("[src] has consumed [key_name_admin(victim)] [ADMIN_JMP(src)] via throw impact.")
investigate_log("has consumed [key_name(victim)] via throw impact.", INVESTIGATE_ENGINE)
victim.visible_message(span_danger("As [victim] is hit by [src], both flash into dust and silence fills the room..."),\
span_userdanger("You're hit by [src] and everything suddenly goes silent.\n[src] flashes into dust, and soon as you can register this, you do as well."),\
span_hear("Everything suddenly goes silent."))
victim.investigate_log("has been dusted by [src].", INVESTIGATE_DEATHS)
victim.dust()
radiation_pulse(src, max_range = 2, threshold = RAD_EXTREME_INSULATION, chance = 40)
playsound(src, 'sound/effects/supermatter.ogg', 50, TRUE)
qdel(src)
/obj/item/nuke_core/supermatter_sliver/pickup(mob/living/user)
..()
if(!isliving(user) || HAS_TRAIT(user, TRAIT_GODMODE)) //try to keep this in sync with supermatter's consume fail conditions
return FALSE
user.visible_message(span_danger("[user] reaches out and tries to pick up [src]. [user.p_their()] body starts to glow and bursts into flames before flashing into dust!"),\
span_userdanger("You reach for [src] with your hands. That was dumb."),\
span_hear("Everything suddenly goes silent."))
radiation_pulse(user, max_range = 2, threshold = RAD_EXTREME_INSULATION, chance = 40)
playsound(src, 'sound/effects/supermatter.ogg', 50, TRUE)
user.investigate_log("has been dusted by [src].", INVESTIGATE_DEATHS)
user.dust()
/obj/item/nuke_core_container/supermatter
name = "supermatter bin"
desc = "A tiny receptacle that releases an inert hyper-noblium mix upon sealing, allowing a sliver of a supermatter crystal to be safely stored."
var/obj/item/nuke_core/supermatter_sliver/sliver
/obj/item/nuke_core_container/supermatter/Destroy()
QDEL_NULL(sliver)
return ..()
/obj/item/nuke_core_container/supermatter/load(obj/item/hemostat/supermatter/T, mob/user)
if(!istype(T) || !T.sliver)
return FALSE
T.sliver.forceMove(src)
sliver = T.sliver
T.sliver = null
T.icon_state = "supermatter_tongs"
icon_state = "core_container_loaded"
to_chat(user, span_warning("Container is sealing..."))
addtimer(CALLBACK(src, PROC_REF(seal)), 5 SECONDS)
return TRUE
/obj/item/nuke_core_container/supermatter/seal()
if(istype(sliver))
STOP_PROCESSING(SSobj, sliver)
icon_state = "core_container_sealed"
playsound(src, 'sound/items/Deconstruct.ogg', 60, TRUE)
if(ismob(loc))
to_chat(loc, span_warning("[src] is permanently sealed, [sliver] is safely contained."))
/obj/item/scalpel/supermatter
name = "supermatter scalpel"
desc = "A scalpel with a fragile tip of condensed hyper-noblium gas, searingly cold to the touch, that can safely shave a sliver off a supermatter crystal."
icon = 'icons/obj/antags/syndicate_tools.dmi'
icon_state = "supermatter_scalpel"
toolspeed = 0.5
damtype = BURN
usesound = 'sound/items/weapons/bladeslice.ogg'
var/usesLeft
/obj/item/scalpel/supermatter/Initialize(mapload)
. = ..()
usesLeft = rand(2, 4)
/obj/item/hemostat/supermatter
name = "supermatter extraction tongs"
desc = "A pair of tongs made from condensed hyper-noblium gas, searingly cold to the touch, that can safely grip a supermatter sliver."
icon = 'icons/obj/antags/syndicate_tools.dmi'
icon_state = "supermatter_tongs"
lefthand_file = 'icons/mob/inhands/items_lefthand.dmi'
righthand_file = 'icons/mob/inhands/items_righthand.dmi'
inhand_icon_state = "supermatter_tongs"
toolspeed = 0.75
damtype = BURN
var/obj/item/nuke_core/supermatter_sliver/sliver
/obj/item/hemostat/supermatter/Initialize(mapload)
. = ..()
AddElement(/datum/element/update_icon_updates_onmob)
/obj/item/hemostat/supermatter/Destroy()
QDEL_NULL(sliver)
return ..()
/obj/item/hemostat/supermatter/update_icon_state()
icon_state = "supermatter_tongs[sliver ? "_loaded" : null]"
inhand_icon_state = "supermatter_tongs[sliver ? "_loaded" : null]"
return ..()
/obj/item/hemostat/supermatter/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
if(!sliver)
return ..()
if (istype(interacting_with, /obj/item/nuke_core_container/supermatter))
var/obj/item/nuke_core_container/supermatter/container = interacting_with
container.load(src, user)
return ITEM_INTERACT_SUCCESS
if(ismovable(interacting_with) && interacting_with != sliver)
Consume(interacting_with, user)
return ITEM_INTERACT_SUCCESS
return ..()
/obj/item/hemostat/supermatter/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) // no instakill supermatter javelins
if(sliver)
sliver.forceMove(loc)
visible_message(span_notice("\The [sliver] falls out of \the [src] as it hits the ground."))
sliver = null
update_appearance()
return ..()
/obj/item/hemostat/supermatter/proc/Consume(atom/movable/AM, mob/living/user)
if(ismob(AM))
if(!isliving(AM))
return
var/mob/living/victim = AM
if(victim.incorporeal_move || HAS_TRAIT(victim, TRAIT_GODMODE)) //try to keep this in sync with supermatter's consume fail conditions
return
victim.investigate_log("has been dusted by [src].", INVESTIGATE_DEATHS)
victim.dust()
message_admins("[src] has consumed [key_name_admin(victim)] [ADMIN_JMP(src)].")
investigate_log("has consumed [key_name(victim)].", INVESTIGATE_ENGINE)
else if(istype(AM, /obj/singularity))
return
else
investigate_log("has consumed [AM].", INVESTIGATE_ENGINE)
qdel(AM)
if (user)
log_combat(user, AM, "consumed", sliver, "via [src]")
user.visible_message(span_danger("As [user] touches [AM] with \the [src], both flash into dust and silence fills the room..."),\
span_userdanger("You touch [AM] with [src], and everything suddenly goes silent.\n[AM] and [sliver] flash into dust, and soon as you can register this, you do as well."),\
span_hear("Everything suddenly goes silent."))
user.investigate_log("has been dusted by [src].", INVESTIGATE_DEATHS)
user.dust()
radiation_pulse(src, max_range = 2, threshold = RAD_EXTREME_INSULATION, chance = 40)
playsound(src, 'sound/effects/supermatter.ogg', 50, TRUE)
QDEL_NULL(sliver)
update_appearance()