diff --git a/code/__DEFINES/admin.dm b/code/__DEFINES/admin.dm
index dbf9ea3f..7e546218 100644
--- a/code/__DEFINES/admin.dm
+++ b/code/__DEFINES/admin.dm
@@ -74,6 +74,9 @@
#define ADMIN_PUNISHMENT_MAZING "Puzzle"
#define ADMIN_PUNISHMENT_PIE "Cream Pie"
#define ADMIN_PUNISHMENT_TABLETIDESTATIONWIDE "Tabletide Stationwide"
+#define ADMIN_PUNISHMENT_FAKEBWOINK "Fake Bwoink"
+#define ADMIN_PUNISHMENT_NUGGET "Nugget"
+#define ADMIN_PUNISHMENT_BREADIFY ":b:read"
#define AHELP_ACTIVE 1
#define AHELP_CLOSED 2
diff --git a/code/__DEFINES/tools.dm b/code/__DEFINES/tools.dm
index f37116f0..00e08129 100644
--- a/code/__DEFINES/tools.dm
+++ b/code/__DEFINES/tools.dm
@@ -1,15 +1,21 @@
-// Tool types
-#define TOOL_CROWBAR "crowbar"
-#define TOOL_MULTITOOL "multitool"
-#define TOOL_SCREWDRIVER "screwdriver"
-#define TOOL_WIRECUTTER "wirecutter"
-#define TOOL_WRENCH "wrench"
-#define TOOL_WELDER "welder"
-#define TOOL_ANALYZER "analyzer"
-#define TOOL_MINING "mining"
-#define TOOL_SHOVEL "shovel"
-
-
-// If delay between the start and the end of tool operation is less than MIN_TOOL_SOUND_DELAY,
-// tool sound is only played when op is started. If not, it's played twice.
-#define MIN_TOOL_SOUND_DELAY 20
+// Tool types
+#define TOOL_CROWBAR "crowbar"
+#define TOOL_MULTITOOL "multitool"
+#define TOOL_SCREWDRIVER "screwdriver"
+#define TOOL_WIRECUTTER "wirecutter"
+#define TOOL_WRENCH "wrench"
+#define TOOL_WELDER "welder"
+#define TOOL_ANALYZER "analyzer"
+#define TOOL_MINING "mining"
+#define TOOL_SHOVEL "shovel"
+#define TOOL_RETRACTOR "retractor"
+#define TOOL_HEMOSTAT "hemostat"
+#define TOOL_CAUTERY "cautery"
+#define TOOL_DRILL "drill"
+#define TOOL_SCALPEL "scalpel"
+#define TOOL_SAW "saw"
+
+
+// If delay between the start and the end of tool operation is less than MIN_TOOL_SOUND_DELAY,
+// tool sound is only played when op is started. If not, it's played twice.
+#define MIN_TOOL_SOUND_DELAY 20
diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm
index bdc6f796..aaa5ece4 100644
--- a/code/__HELPERS/icons.dm
+++ b/code/__HELPERS/icons.dm
@@ -1201,3 +1201,61 @@ GLOBAL_LIST_INIT(freon_color_matrix, list("#2E5E69", "#60A2A8", "#A1AFB1", rgb(0
var/icon/I = getFlatIcon(thing)
return icon2html(I, target)
+
+GLOBAL_LIST_EMPTY(transformation_animation_objects)
+
+
+/*
+ * Creates animation that turns current icon into result appearance from top down.
+ *
+ * result_appearance - End result appearance/atom/image
+ * time - Animation duration
+ * transform_overlay - Appearance/atom/image of effect that moves along the animation - should be horizonatally centered
+ * reset_after - If FALSE, filters won't be reset and helper vis_objects will not be removed after animation duration expires. Cleanup must be handled by the caller!
+ */
+/atom/movable/proc/transformation_animation(result_appearance,time = 3 SECONDS,transform_overlay,reset_after=TRUE)
+ var/list/transformation_objects = GLOB.transformation_animation_objects[src] || list()
+ //Disappearing part
+ var/top_part_filter = filter(type="alpha",icon=icon('icons/effects/alphacolors.dmi',"white"),y=0)
+ filters += top_part_filter
+ var/filter_index = length(filters)
+ animate(filters[filter_index],y=-32,time=time)
+ //Appearing part
+ var/obj/effect/overlay/appearing_part = new
+ appearing_part.appearance = result_appearance
+ appearing_part.appearance_flags |= KEEP_TOGETHER | KEEP_APART
+ appearing_part.vis_flags = VIS_INHERIT_ID
+ appearing_part.filters = filter(type="alpha",icon=icon('icons/effects/alphacolors.dmi',"white"),y=0,flags=MASK_INVERSE)
+ animate(appearing_part.filters[1],y=-32,time=time)
+ transformation_objects += appearing_part
+ //Transform effect thing - todo make appearance passed in
+ if(transform_overlay)
+ var/obj/transform_effect = new
+ transform_effect.appearance = transform_overlay
+ transform_effect.vis_flags = VIS_INHERIT_ID
+ transform_effect.pixel_y = 16
+ transform_effect.alpha = 255
+ transformation_objects += transform_effect
+ animate(transform_effect,pixel_y=-16,time=time)
+ animate(alpha=0)
+
+ GLOB.transformation_animation_objects[src] = transformation_objects
+ for(var/A in transformation_objects)
+ vis_contents += A
+ if(reset_after)
+ addtimer(CALLBACK(src,.proc/_reset_transformation_animation,filter_index),time)
+
+/*
+ * Resets filters and removes transformation animations helper objects from vis contents.
+*/
+/atom/movable/proc/_reset_transformation_animation(filter_index)
+ var/list/transformation_objects = GLOB.transformation_animation_objects[src]
+ for(var/A in transformation_objects)
+ vis_contents -= A
+ qdel(A)
+ transformation_objects.Cut()
+ GLOB.transformation_animation_objects -= src
+ if(filters && length(filters) >= filter_index)
+ filters -= filters[filter_index]
+ //else
+ // filters = null
diff --git a/code/datums/traits/negative.dm b/code/datums/traits/negative.dm
index 23f88169..74d6ae0f 100644
--- a/code/datums/traits/negative.dm
+++ b/code/datums/traits/negative.dm
@@ -384,10 +384,14 @@
medical_record_text = "Patient has an extreme or irrational fear and aversion to an undefined stimuli."
var/datum/brain_trauma/mild/phobia/phobia
-/datum/quirk/phobia/add()
+/datum/quirk/phobia/post_add()
var/mob/living/carbon/human/H = quirk_holder
phobia = new
- H.gain_trauma(phobia, TRAUMA_RESILIENCE_SURGERY)
+ H.gain_trauma(phobia, TRAUMA_RESILIENCE_ABSOLUTE)
+
+/datum/quirk/phobia/remove()
+ var/mob/living/carbon/human/H = quirk_holder
+ H?.cure_trauma_type(phobia, TRAUMA_RESILIENCE_ABSOLUTE)
/datum/quirk/mute
name = "Mute"
diff --git a/code/game/mecha/equipment/tools/mining_tools.dm b/code/game/mecha/equipment/tools/mining_tools.dm
index 94440eb2..4044951c 100644
--- a/code/game/mecha/equipment/tools/mining_tools.dm
+++ b/code/game/mecha/equipment/tools/mining_tools.dm
@@ -13,6 +13,8 @@
energy_drain = 10
force = 15
harmful = TRUE
+ tool_behaviour = TOOL_DRILL
+ toolspeed = 0.9
var/drill_delay = 7
var/drill_level = DRILL_BASIC
@@ -141,6 +143,7 @@
drill_delay = 4
drill_level = DRILL_HARDENED
force = 15
+ toolspeed = 0.7
/obj/item/mecha_parts/mecha_equipment/mining_scanner
diff --git a/code/game/mecha/equipment/tools/work_tools.dm b/code/game/mecha/equipment/tools/work_tools.dm
index c89fe14e..ac194cc2 100644
--- a/code/game/mecha/equipment/tools/work_tools.dm
+++ b/code/game/mecha/equipment/tools/work_tools.dm
@@ -11,6 +11,8 @@
var/dam_force = 20
var/obj/mecha/working/ripley/cargo_holder
harmful = TRUE
+ tool_behaviour = TOOL_RETRACTOR
+ toolspeed = 0.8
/obj/item/mecha_parts/mecha_equipment/hydraulic_clamp/can_attach(obj/mecha/working/ripley/M as obj)
if(..())
diff --git a/code/game/objects/items/holy_weapons.dm b/code/game/objects/items/holy_weapons.dm
index 01b20d42..93da4a30 100644
--- a/code/game/objects/items/holy_weapons.dm
+++ b/code/game/objects/items/holy_weapons.dm
@@ -371,6 +371,8 @@
slot_flags = ITEM_SLOT_BELT
attack_verb = list("sawed", "torn", "cut", "chopped", "diced")
hitsound = 'sound/weapons/chainsawhit.ogg'
+ tool_behaviour = TOOL_SAW
+ toolspeed = 1.5 //slower than a real saw
/obj/item/nullrod/claymore/glowing
icon_state = "swordon"
@@ -526,7 +528,8 @@
slot_flags = ITEM_SLOT_BELT
attack_verb = list("sawed", "torn", "cut", "chopped", "diced")
hitsound = 'sound/weapons/chainsawhit.ogg'
-
+ tool_behaviour = TOOL_SAW
+ toolspeed = 0.5
/obj/item/nullrod/hammmer
icon_state = "hammeron"
@@ -553,6 +556,8 @@
attack_verb = list("sawed", "torn", "cut", "chopped", "diced")
hitsound = 'sound/weapons/chainsawhit.ogg'
total_mass = TOTAL_MASS_HAND_REPLACEMENT
+ tool_behaviour = TOOL_SAW
+ toolspeed = 2
/obj/item/nullrod/chainsaw/Initialize()
. = ..()
diff --git a/code/game/objects/items/melee/energy.dm b/code/game/objects/items/melee/energy.dm
index 2004dd6e..57b9973a 100644
--- a/code/game/objects/items/melee/energy.dm
+++ b/code/game/objects/items/melee/energy.dm
@@ -1,233 +1,235 @@
-/obj/item/melee/transforming/energy
- hitsound_on = 'sound/weapons/blade1.ogg'
- heat = 3500
- max_integrity = 200
- armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 30)
- resistance_flags = FIRE_PROOF
- var/brightness_on = 3
- total_mass = 0.4 //Survival flashlights typically weigh around 5 ounces.
-
-
-/obj/item/melee/transforming/energy/Initialize()
- . = ..()
- total_mass_on = (total_mass_on ? total_mass_on : (w_class_on * 0.75))
- if(active)
- set_light(brightness_on)
- START_PROCESSING(SSobj, src)
-
-/obj/item/melee/transforming/energy/Destroy()
- STOP_PROCESSING(SSobj, src)
- return ..()
-
-/obj/item/melee/transforming/energy/suicide_act(mob/user)
- if(!active)
- transform_weapon(user, TRUE)
- user.visible_message("[user] is [pick("slitting [user.p_their()] stomach open with", "falling on")] [src]! It looks like [user.p_theyre()] trying to commit seppuku!")
- return (BRUTELOSS|FIRELOSS)
-
-/obj/item/melee/transforming/energy/add_blood_DNA(list/blood_dna)
- return FALSE
-
-/obj/item/melee/transforming/energy/is_sharp()
- return active * sharpness
-
-/obj/item/melee/transforming/energy/process()
- open_flame()
-
-/obj/item/melee/transforming/energy/transform_weapon(mob/living/user, supress_message_text)
- . = ..()
- if(.)
- if(active)
- if(item_color)
- icon_state = "sword[item_color]"
- START_PROCESSING(SSobj, src)
- set_light(brightness_on)
- else
- STOP_PROCESSING(SSobj, src)
- set_light(0)
-
-/obj/item/melee/transforming/energy/is_hot()
- return active * heat
-
-/obj/item/melee/transforming/energy/ignition_effect(atom/A, mob/user)
- if(!active)
- return ""
-
- var/in_mouth = ""
- if(iscarbon(user))
- var/mob/living/carbon/C = user
- if(C.wear_mask)
- in_mouth = ", barely missing [C.p_their()] nose"
- . = "[user] swings [user.p_their()] [name][in_mouth]. [user.p_they(TRUE)] light[user.p_s()] [user.p_their()] [A.name] in the process."
- playsound(loc, hitsound, get_clamped_volume(), 1, -1)
- add_fingerprint(user)
-
-/obj/item/melee/transforming/energy/axe
- name = "energy axe"
- desc = "An energized battle axe."
- icon_state = "axe0"
- lefthand_file = 'icons/mob/inhands/weapons/axes_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/weapons/axes_righthand.dmi'
- force = 40
- force_on = 150
- throwforce = 25
- throwforce_on = 30
- hitsound = 'sound/weapons/bladeslice.ogg'
- throw_speed = 3
- throw_range = 5
- w_class = WEIGHT_CLASS_NORMAL
- w_class_on = WEIGHT_CLASS_HUGE
- flags_1 = CONDUCT_1
- armour_penetration = 100
- attack_verb_off = list("attacked", "chopped", "cleaved", "torn", "cut")
- attack_verb_on = list()
- light_color = "#40ceff"
- total_mass = null
-
-/obj/item/melee/transforming/energy/axe/suicide_act(mob/user)
- user.visible_message("[user] swings [src] towards [user.p_their()] head! It looks like [user.p_theyre()] trying to commit suicide!")
- return (BRUTELOSS|FIRELOSS)
-
-/obj/item/melee/transforming/energy/sword
- name = "energy sword"
- desc = "May the force be within you."
- icon_state = "sword0"
- lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
- force = 3
- throwforce = 5
- hitsound = "swing_hit" //it starts deactivated
- attack_verb_off = list("tapped", "poked")
- throw_speed = 3
- throw_range = 5
- sharpness = IS_SHARP
- embedding = list("embed_chance" = 75, "embedded_impact_pain_multiplier" = 10)
- armour_penetration = 35
- block_chance = 50
-
-/obj/item/melee/transforming/energy/sword/transform_weapon(mob/living/user, supress_message_text)
- . = ..()
- if(. && active && item_color)
- icon_state = "sword[item_color]"
-
-/obj/item/melee/transforming/energy/sword/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
- if(active)
- return ..()
- return 0
-
-/obj/item/melee/transforming/energy/sword/cyborg
- item_color = "red"
- var/hitcost = 50
-
-/obj/item/melee/transforming/energy/sword/cyborg/attack(mob/M, var/mob/living/silicon/robot/R)
- if(R.cell)
- var/obj/item/stock_parts/cell/C = R.cell
- if(active && !(C.use(hitcost)))
- attack_self(R)
- to_chat(R, "It's out of charge!")
- return
- return ..()
-
-/obj/item/melee/transforming/energy/sword/cyborg/saw //Used by medical Syndicate cyborgs
- name = "energy saw"
- desc = "For heavy duty cutting. It has a carbon-fiber blade in addition to a toggleable hard-light edge to dramatically increase sharpness."
- force_on = 30
- force = 18 //About as much as a spear
- hitsound = 'sound/weapons/circsawhit.ogg'
- icon = 'icons/obj/surgery.dmi'
- icon_state = "esaw_0"
- icon_state_on = "esaw_1"
- item_color = null //stops icon from breaking when turned on.
- hitcost = 75 //Costs more than a standard cyborg esword
- w_class = WEIGHT_CLASS_NORMAL
- sharpness = IS_SHARP
- light_color = "#40ceff"
-
-/obj/item/melee/transforming/energy/sword/cyborg/saw/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
- return 0
-
-/obj/item/melee/transforming/energy/sword/saber
- var/list/possible_colors = list("red" = LIGHT_COLOR_RED, "blue" = LIGHT_COLOR_LIGHT_CYAN, "green" = LIGHT_COLOR_GREEN, "purple" = LIGHT_COLOR_LAVENDER)
- var/hacked = FALSE
-
-/obj/item/melee/transforming/energy/sword/saber/Initialize(mapload)
- . = ..()
- if(LAZYLEN(possible_colors))
- var/set_color = pick(possible_colors)
- item_color = set_color
- light_color = possible_colors[set_color]
-
-/obj/item/melee/transforming/energy/sword/saber/process()
- . = ..()
- if(hacked)
- var/set_color = pick(possible_colors)
- light_color = possible_colors[set_color]
- update_light()
-
-/obj/item/melee/transforming/energy/sword/saber/red
- possible_colors = list("red" = LIGHT_COLOR_RED)
-
-/obj/item/melee/transforming/energy/sword/saber/blue
- possible_colors = list("blue" = LIGHT_COLOR_LIGHT_CYAN)
-
-/obj/item/melee/transforming/energy/sword/saber/green
- possible_colors = list("green" = LIGHT_COLOR_GREEN)
-
-/obj/item/melee/transforming/energy/sword/saber/purple
- possible_colors = list("purple" = LIGHT_COLOR_LAVENDER)
-
-/obj/item/melee/transforming/energy/sword/saber/attackby(obj/item/W, mob/living/user, params)
- if(istype(W, /obj/item/multitool))
- if(!hacked)
- hacked = TRUE
- item_color = "rainbow"
- to_chat(user, "RNBW_ENGAGE")
-
- if(active)
- icon_state = "swordrainbow"
- user.update_inv_hands()
- else
- to_chat(user, "It's already fabulous!")
- else
- return ..()
-
-/obj/item/melee/transforming/energy/sword/pirate
- name = "energy cutlass"
- desc = "Arrrr matey."
- icon_state = "cutlass0"
- lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
- icon_state_on = "cutlass1"
- light_color = "#ff0000"
-
-/obj/item/melee/transforming/energy/blade
- name = "energy blade"
- desc = "A concentrated beam of energy in the shape of a blade. Very stylish... and lethal."
- icon_state = "blade"
- lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
- force = 30 //Normal attacks deal esword damage
- hitsound = 'sound/weapons/blade1.ogg'
- active = 1
- throwforce = 1 //Throwing or dropping the item deletes it.
- throw_speed = 3
- throw_range = 1
- w_class = WEIGHT_CLASS_BULKY//So you can't hide it in your pocket or some such.
- var/datum/effect_system/spark_spread/spark_system
- sharpness = IS_SHARP
-
-//Most of the other special functions are handled in their own files. aka special snowflake code so kewl
-/obj/item/melee/transforming/energy/blade/Initialize()
- . = ..()
- spark_system = new /datum/effect_system/spark_spread()
- spark_system.set_up(5, 0, src)
- spark_system.attach(src)
-
-/obj/item/melee/transforming/energy/blade/transform_weapon(mob/living/user, supress_message_text)
- return
-
-/obj/item/melee/transforming/energy/blade/hardlight
- name = "hardlight blade"
- desc = "An extremely sharp blade made out of hard light. Packs quite a punch."
- icon_state = "lightblade"
- item_state = "lightblade"
+/obj/item/melee/transforming/energy
+ hitsound_on = 'sound/weapons/blade1.ogg'
+ heat = 3500
+ max_integrity = 200
+ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 30)
+ resistance_flags = FIRE_PROOF
+ var/brightness_on = 3
+ total_mass = 0.4 //Survival flashlights typically weigh around 5 ounces.
+
+
+/obj/item/melee/transforming/energy/Initialize()
+ . = ..()
+ total_mass_on = (total_mass_on ? total_mass_on : (w_class_on * 0.75))
+ if(active)
+ set_light(brightness_on)
+ START_PROCESSING(SSobj, src)
+
+/obj/item/melee/transforming/energy/Destroy()
+ STOP_PROCESSING(SSobj, src)
+ return ..()
+
+/obj/item/melee/transforming/energy/suicide_act(mob/user)
+ if(!active)
+ transform_weapon(user, TRUE)
+ user.visible_message("[user] is [pick("slitting [user.p_their()] stomach open with", "falling on")] [src]! It looks like [user.p_theyre()] trying to commit seppuku!")
+ return (BRUTELOSS|FIRELOSS)
+
+/obj/item/melee/transforming/energy/add_blood_DNA(list/blood_dna)
+ return FALSE
+
+/obj/item/melee/transforming/energy/is_sharp()
+ return active * sharpness
+
+/obj/item/melee/transforming/energy/process()
+ open_flame()
+
+/obj/item/melee/transforming/energy/transform_weapon(mob/living/user, supress_message_text)
+ . = ..()
+ if(.)
+ if(active)
+ if(item_color)
+ icon_state = "sword[item_color]"
+ START_PROCESSING(SSobj, src)
+ set_light(brightness_on)
+ else
+ STOP_PROCESSING(SSobj, src)
+ set_light(0)
+
+/obj/item/melee/transforming/energy/is_hot()
+ return active * heat
+
+/obj/item/melee/transforming/energy/ignition_effect(atom/A, mob/user)
+ if(!active)
+ return ""
+
+ var/in_mouth = ""
+ if(iscarbon(user))
+ var/mob/living/carbon/C = user
+ if(C.wear_mask)
+ in_mouth = ", barely missing [C.p_their()] nose"
+ . = "[user] swings [user.p_their()] [name][in_mouth]. [user.p_they(TRUE)] light[user.p_s()] [user.p_their()] [A.name] in the process."
+ playsound(loc, hitsound, get_clamped_volume(), 1, -1)
+ add_fingerprint(user)
+
+/obj/item/melee/transforming/energy/axe
+ name = "energy axe"
+ desc = "An energized battle axe."
+ icon_state = "axe0"
+ lefthand_file = 'icons/mob/inhands/weapons/axes_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/weapons/axes_righthand.dmi'
+ force = 40
+ force_on = 150
+ throwforce = 25
+ throwforce_on = 30
+ hitsound = 'sound/weapons/bladeslice.ogg'
+ throw_speed = 3
+ throw_range = 5
+ w_class = WEIGHT_CLASS_NORMAL
+ w_class_on = WEIGHT_CLASS_HUGE
+ flags_1 = CONDUCT_1
+ armour_penetration = 100
+ attack_verb_off = list("attacked", "chopped", "cleaved", "torn", "cut")
+ attack_verb_on = list()
+ light_color = "#40ceff"
+ total_mass = null
+
+/obj/item/melee/transforming/energy/axe/suicide_act(mob/user)
+ user.visible_message("[user] swings [src] towards [user.p_their()] head! It looks like [user.p_theyre()] trying to commit suicide!")
+ return (BRUTELOSS|FIRELOSS)
+
+/obj/item/melee/transforming/energy/sword
+ name = "energy sword"
+ desc = "May the force be within you."
+ icon_state = "sword0"
+ lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
+ force = 3
+ throwforce = 5
+ hitsound = "swing_hit" //it starts deactivated
+ attack_verb_off = list("tapped", "poked")
+ throw_speed = 3
+ throw_range = 5
+ sharpness = IS_SHARP
+ embedding = list("embed_chance" = 75, "embedded_impact_pain_multiplier" = 10)
+ armour_penetration = 35
+ block_chance = 50
+
+/obj/item/melee/transforming/energy/sword/transform_weapon(mob/living/user, supress_message_text)
+ . = ..()
+ if(. && active && item_color)
+ icon_state = "sword[item_color]"
+
+/obj/item/melee/transforming/energy/sword/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
+ if(active)
+ return ..()
+ return 0
+
+/obj/item/melee/transforming/energy/sword/cyborg
+ item_color = "red"
+ var/hitcost = 50
+
+/obj/item/melee/transforming/energy/sword/cyborg/attack(mob/M, var/mob/living/silicon/robot/R)
+ if(R.cell)
+ var/obj/item/stock_parts/cell/C = R.cell
+ if(active && !(C.use(hitcost)))
+ attack_self(R)
+ to_chat(R, "It's out of charge!")
+ return
+ return ..()
+
+/obj/item/melee/transforming/energy/sword/cyborg/saw //Used by medical Syndicate cyborgs
+ name = "energy saw"
+ desc = "For heavy duty cutting. It has a carbon-fiber blade in addition to a toggleable hard-light edge to dramatically increase sharpness."
+ force_on = 30
+ force = 18 //About as much as a spear
+ hitsound = 'sound/weapons/circsawhit.ogg'
+ icon = 'icons/obj/surgery.dmi'
+ icon_state = "esaw_0"
+ icon_state_on = "esaw_1"
+ item_color = null //stops icon from breaking when turned on.
+ hitcost = 75 //Costs more than a standard cyborg esword
+ w_class = WEIGHT_CLASS_NORMAL
+ sharpness = IS_SHARP
+ light_color = "#40ceff"
+ tool_behaviour = TOOL_SAW
+ toolspeed = 0.7
+
+/obj/item/melee/transforming/energy/sword/cyborg/saw/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
+ return 0
+
+/obj/item/melee/transforming/energy/sword/saber
+ var/list/possible_colors = list("red" = LIGHT_COLOR_RED, "blue" = LIGHT_COLOR_LIGHT_CYAN, "green" = LIGHT_COLOR_GREEN, "purple" = LIGHT_COLOR_LAVENDER)
+ var/hacked = FALSE
+
+/obj/item/melee/transforming/energy/sword/saber/Initialize(mapload)
+ . = ..()
+ if(LAZYLEN(possible_colors))
+ var/set_color = pick(possible_colors)
+ item_color = set_color
+ light_color = possible_colors[set_color]
+
+/obj/item/melee/transforming/energy/sword/saber/process()
+ . = ..()
+ if(hacked)
+ var/set_color = pick(possible_colors)
+ light_color = possible_colors[set_color]
+ update_light()
+
+/obj/item/melee/transforming/energy/sword/saber/red
+ possible_colors = list("red" = LIGHT_COLOR_RED)
+
+/obj/item/melee/transforming/energy/sword/saber/blue
+ possible_colors = list("blue" = LIGHT_COLOR_LIGHT_CYAN)
+
+/obj/item/melee/transforming/energy/sword/saber/green
+ possible_colors = list("green" = LIGHT_COLOR_GREEN)
+
+/obj/item/melee/transforming/energy/sword/saber/purple
+ possible_colors = list("purple" = LIGHT_COLOR_LAVENDER)
+
+/obj/item/melee/transforming/energy/sword/saber/attackby(obj/item/W, mob/living/user, params)
+ if(istype(W, /obj/item/multitool))
+ if(!hacked)
+ hacked = TRUE
+ item_color = "rainbow"
+ to_chat(user, "RNBW_ENGAGE")
+
+ if(active)
+ icon_state = "swordrainbow"
+ user.update_inv_hands()
+ else
+ to_chat(user, "It's already fabulous!")
+ else
+ return ..()
+
+/obj/item/melee/transforming/energy/sword/pirate
+ name = "energy cutlass"
+ desc = "Arrrr matey."
+ icon_state = "cutlass0"
+ lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
+ icon_state_on = "cutlass1"
+ light_color = "#ff0000"
+
+/obj/item/melee/transforming/energy/blade
+ name = "energy blade"
+ desc = "A concentrated beam of energy in the shape of a blade. Very stylish... and lethal."
+ icon_state = "blade"
+ lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
+ force = 30 //Normal attacks deal esword damage
+ hitsound = 'sound/weapons/blade1.ogg'
+ active = 1
+ throwforce = 1 //Throwing or dropping the item deletes it.
+ throw_speed = 3
+ throw_range = 1
+ w_class = WEIGHT_CLASS_BULKY//So you can't hide it in your pocket or some such.
+ var/datum/effect_system/spark_spread/spark_system
+ sharpness = IS_SHARP
+
+//Most of the other special functions are handled in their own files. aka special snowflake code so kewl
+/obj/item/melee/transforming/energy/blade/Initialize()
+ . = ..()
+ spark_system = new /datum/effect_system/spark_spread()
+ spark_system.set_up(5, 0, src)
+ spark_system.attach(src)
+
+/obj/item/melee/transforming/energy/blade/transform_weapon(mob/living/user, supress_message_text)
+ return
+
+/obj/item/melee/transforming/energy/blade/hardlight
+ name = "hardlight blade"
+ desc = "An extremely sharp blade made out of hard light. Packs quite a punch."
+ icon_state = "lightblade"
+ item_state = "lightblade"
diff --git a/code/game/objects/items/storage/backpack.dm b/code/game/objects/items/storage/backpack.dm
index 95f3abbd..da00e95e 100644
--- a/code/game/objects/items/storage/backpack.dm
+++ b/code/game/objects/items/storage/backpack.dm
@@ -1,608 +1,605 @@
-/* Backpacks
- * Contains:
- * Backpack
- * Backpack Types
- * Satchel Types
- */
-
-/*
- * Backpack
- */
-
-/obj/item/storage/backpack
- name = "backpack"
- desc = "You wear this on your back and put items into it."
- icon_state = "backpack"
- item_state = "backpack"
- lefthand_file = 'icons/mob/inhands/equipment/backpack_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/equipment/backpack_righthand.dmi'
- w_class = WEIGHT_CLASS_BULKY
- slot_flags = ITEM_SLOT_BACK //ERROOOOO
- resistance_flags = NONE
- max_integrity = 300
-
-/obj/item/storage/backpack/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_combined_w_class = 21
- STR.max_w_class = WEIGHT_CLASS_NORMAL
- STR.max_items = 21
-
-/*
- * Backpack Types
- */
-
-/obj/item/storage/backpack/old/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_combined_w_class = 12
-
-/obj/item/storage/backpack/holding
- name = "bag of holding"
- desc = "A backpack that opens into a localized pocket of Blue Space."
- icon_state = "holdingpack"
- item_state = "holdingpack"
- resistance_flags = FIRE_PROOF
- item_flags = NO_MAT_REDEMPTION
- armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 60, "acid" = 50)
- component_type = /datum/component/storage/concrete/bluespace/bag_of_holding
-
-/obj/item/storage/backpack/holding/satchel
- name = "satchel of holding"
- desc = "A satchel that opens into a localized pocket of Blue Space."
- icon_state = "holdingsat"
- item_state = "holdingsat"
- species_exception = list(/datum/species/angel)
-
-/obj/item/storage/backpack/holding/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.allow_big_nesting = TRUE
- STR.max_w_class = WEIGHT_CLASS_GIGANTIC
- STR.max_combined_w_class = 35
-
-/obj/item/storage/backpack/holding/suicide_act(mob/living/user)
- user.visible_message("[user] is jumping into [src]! It looks like [user.p_theyre()] trying to commit suicide.")
- user.dropItemToGround(src, TRUE)
- user.Stun(100, ignore_canstun = TRUE)
- sleep(20)
- playsound(src, "rustle", 50, 1, -5)
- qdel(user)
- return
-
-/obj/item/storage/backpack/holding/singularity_act(current_size)
- var/dist = max((current_size - 2),1)
- explosion(src.loc,(dist),(dist*2),(dist*4))
- return
-
-/obj/item/storage/backpack/santabag
- name = "Santa's Gift Bag"
- desc = "Space Santa uses this to deliver toys to all the nice children in space in Christmas! Wow, it's pretty big!"
- icon_state = "giftbag0"
- item_state = "giftbag"
- w_class = WEIGHT_CLASS_BULKY
-
-/obj/item/storage/backpack/santabag/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_w_class = WEIGHT_CLASS_NORMAL
- STR.max_combined_w_class = 60
-
-/obj/item/storage/backpack/santabag/suicide_act(mob/user)
- user.visible_message("[user] places [src] over [user.p_their()] head and pulls it tight! It looks like [user.p_they()] [user.p_are()]n't in the Christmas spirit...")
- return (OXYLOSS)
-
-/obj/item/storage/backpack/cultpack
- name = "trophy rack"
- desc = "It's useful for both carrying extra gear and proudly declaring your insanity."
- icon_state = "cultpack"
- item_state = "backpack"
-
-/obj/item/storage/backpack/clown
- name = "Giggles von Honkerton"
- desc = "It's a backpack made by Honk! Co."
- icon_state = "clownpack"
- item_state = "clownpack"
-
-/obj/item/storage/backpack/explorer
- name = "explorer bag"
- desc = "A robust backpack for stashing your loot."
- icon_state = "explorerpack"
- item_state = "explorerpack"
-
-/obj/item/storage/backpack/mime
- name = "Parcel Parceaux"
- desc = "A silent backpack made for those silent workers. Silence Co."
- icon_state = "mimepack"
- item_state = "mimepack"
-
-/obj/item/storage/backpack/medic
- name = "medical backpack"
- desc = "It's a backpack especially designed for use in a sterile environment."
- icon_state = "medicalpack"
- item_state = "medicalpack"
-
-/obj/item/storage/backpack/security
- name = "security backpack"
- desc = "It's a very robust backpack."
- icon_state = "securitypack"
- item_state = "securitypack"
-
-/obj/item/storage/backpack/captain
- name = "captain's backpack"
- desc = "It's a special backpack made exclusively for Nanotrasen officers."
- icon_state = "captainpack"
- item_state = "captainpack"
-
-/obj/item/storage/backpack/industrial
- name = "industrial backpack"
- desc = "It's a tough backpack for the daily grind of station life."
- icon_state = "engiepack"
- item_state = "engiepack"
- resistance_flags = FIRE_PROOF
-
-/obj/item/storage/backpack/botany
- name = "botany backpack"
- desc = "It's a backpack made of all-natural fibers."
- icon_state = "botpack"
- item_state = "botpack"
-
-/obj/item/storage/backpack/chemistry
- name = "chemistry backpack"
- desc = "A backpack specially designed to repel stains and hazardous liquids."
- icon_state = "chempack"
- item_state = "chempack"
-
-/obj/item/storage/backpack/genetics
- name = "genetics backpack"
- desc = "A bag designed to be super tough, just in case someone hulks out on you."
- icon_state = "genepack"
- item_state = "genepack"
-
-/obj/item/storage/backpack/science
- name = "science backpack"
- desc = "A specially designed backpack. It's fire resistant and smells vaguely of plasma."
- icon_state = "toxpack"
- item_state = "toxpack"
-
-/obj/item/storage/backpack/virology
- name = "virology backpack"
- desc = "A backpack made of hypo-allergenic fibers. It's designed to help prevent the spread of disease. Smells like monkey."
- icon_state = "viropack"
- item_state = "viropack"
-
-/*
- * Satchel Types
- */
-
-/obj/item/storage/backpack/satchel
- name = "satchel"
- desc = "A trendy looking satchel."
- icon_state = "satchel-norm"
- species_exception = list(/datum/species/angel) //satchels can be equipped since they are on the side, not back
-
-/obj/item/storage/backpack/satchel/leather
- name = "leather satchel"
- desc = "It's a very fancy satchel made with fine leather."
- icon_state = "satchel"
-
-/obj/item/storage/backpack/satchel/leather/withwallet/PopulateContents()
- new /obj/item/storage/wallet/random(src)
-
-/obj/item/storage/backpack/satchel/eng
- name = "industrial satchel"
- desc = "A tough satchel with extra pockets."
- icon_state = "satchel-eng"
- item_state = "engiepack"
-
-/obj/item/storage/backpack/satchel/med
- name = "medical satchel"
- desc = "A sterile satchel used in medical departments."
- icon_state = "satchel-med"
- item_state = "medicalpack"
-
-/obj/item/storage/backpack/satchel/vir
- name = "virologist satchel"
- desc = "A sterile satchel with virologist colours."
- icon_state = "satchel-vir"
- item_state = "satchel-vir"
-
-/obj/item/storage/backpack/satchel/chem
- name = "chemist satchel"
- desc = "A sterile satchel with chemist colours."
- icon_state = "satchel-chem"
- item_state = "satchel-chem"
-
-/obj/item/storage/backpack/satchel/gen
- name = "geneticist satchel"
- desc = "A sterile satchel with geneticist colours."
- icon_state = "satchel-gen"
- item_state = "satchel-gen"
-
-/obj/item/storage/backpack/satchel/tox
- name = "scientist satchel"
- desc = "Useful for holding research materials."
- icon_state = "satchel-tox"
- item_state = "satchel-tox"
-
-/obj/item/storage/backpack/satchel/hyd
- name = "botanist satchel"
- desc = "A satchel made of all natural fibers."
- icon_state = "satchel-hyd"
- item_state = "satchel-hyd"
-
-/obj/item/storage/backpack/satchel/sec
- name = "security satchel"
- desc = "A robust satchel for security related needs."
- icon_state = "satchel-sec"
- item_state = "securitypack"
-
-/obj/item/storage/backpack/satchel/explorer
- name = "explorer satchel"
- desc = "A robust satchel for stashing your loot."
- icon_state = "satchel-explorer"
- item_state = "securitypack"
-
-/obj/item/storage/backpack/satchel/bone
- name = "bone satchel"
- desc = "A bone satchel fashend with watcher wings and large bones from goliath. Can be worn on the belt."
- icon = 'icons/obj/mining.dmi'
- icon_state = "goliath_saddle"
- slot_flags = ITEM_SLOT_BACK
-
-/obj/item/storage/backpack/satchel/bone/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_combined_w_class = 20
- STR.max_items = 15
-
-/obj/item/storage/backpack/satchel/cap
- name = "captain's satchel"
- desc = "An exclusive satchel for Nanotrasen officers."
- icon_state = "satchel-cap"
- item_state = "captainpack"
-
-/obj/item/storage/backpack/satchel/flat
- name = "smuggler's satchel"
- desc = "A very slim satchel that can easily fit into tight spaces."
- icon_state = "satchel-flat"
- w_class = WEIGHT_CLASS_BULKY //Can fit in backpacks itself.
- level = 1
- component_type = /datum/component/storage/concrete/secret_satchel
-
-/obj/item/storage/backpack/satchel/flat/Initialize()
- . = ..()
- SSpersistence.new_secret_satchels += src
-
-/obj/item/storage/backpack/satchel/flat/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_combined_w_class = 6
- STR.cant_hold = typecacheof(list(/obj/item/storage/backpack/satchel/flat)) //muh recursive backpacks
-
-/obj/item/storage/backpack/satchel/flat/hide(intact)
- if(intact)
- invisibility = INVISIBILITY_MAXIMUM
- anchored = TRUE //otherwise you can start pulling, cover it, and drag around an invisible backpack.
- icon_state = "[initial(icon_state)]2"
- else
- invisibility = initial(invisibility)
- anchored = FALSE
- icon_state = initial(icon_state)
-
-/obj/item/storage/backpack/satchel/flat/PopulateContents()
- new /obj/item/stack/tile/plasteel(src)
- new /obj/item/crowbar(src)
-
-/obj/item/storage/backpack/satchel/flat/Destroy()
- SSpersistence.new_secret_satchels -= src
- return ..()
-
-/obj/item/storage/backpack/satchel/flat/secret
- var/list/reward_one_of_these = list() //Intended for map editing
- var/list/reward_all_of_these = list() //use paths!
- var/revealed = FALSE
-
-/obj/item/storage/backpack/satchel/flat/secret/Initialize()
- . = ..()
-
- if(isfloorturf(loc) && !isplatingturf(loc))
- hide(1)
-
-/obj/item/storage/backpack/satchel/flat/secret/hide(intact)
- ..()
- if(!intact && !revealed)
- if(reward_one_of_these.len > 0)
- var/reward = pick(reward_one_of_these)
- new reward(src)
- for(var/R in reward_all_of_these)
- new R(src)
- revealed = TRUE
-
-/obj/item/storage/backpack/duffelbag
- name = "duffel bag"
- desc = "A large duffel bag for holding extra things."
- icon_state = "duffel"
- item_state = "duffel"
- slowdown = 1
-
-/obj/item/storage/backpack/duffelbag/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_combined_w_class = 30
-
-/obj/item/storage/backpack/duffelbag/captain
- name = "captain's duffel bag"
- desc = "A large duffel bag for holding extra captainly goods."
- icon_state = "duffel-captain"
- item_state = "duffel-captain"
-
-/obj/item/storage/backpack/duffelbag/med
- name = "medical duffel bag"
- desc = "A large duffel bag for holding extra medical supplies."
- icon_state = "duffel-med"
- item_state = "duffel-med"
-
-/obj/item/storage/backpack/duffelbag/med/surgery
- name = "surgical duffel bag"
- desc = "A large duffel bag for holding extra medical supplies - this one seems to be designed for holding surgical tools."
-
-/obj/item/storage/backpack/duffelbag/med/surgery/PopulateContents()
- new /obj/item/scalpel(src)
- new /obj/item/hemostat(src)
- new /obj/item/retractor(src)
- new /obj/item/circular_saw(src)
- new /obj/item/surgicaldrill(src)
- new /obj/item/cautery(src)
- new /obj/item/surgical_drapes(src)
- new /obj/item/clothing/mask/surgical(src)
- new /obj/item/razor(src)
- new /obj/item/reagent_containers/medspray/sterilizine(src)
-
-/obj/item/storage/backpack/duffelbag/sec
- name = "security duffel bag"
- desc = "A large duffel bag for holding extra security supplies and ammunition."
- icon_state = "duffel-sec"
- item_state = "duffel-sec"
-
-/obj/item/storage/backpack/duffelbag/sec/surgery
- name = "surgical duffel bag"
- desc = "A large duffel bag for holding extra supplies - this one has a material inlay with space for various sharp-looking tools."
-
-/obj/item/storage/backpack/duffelbag/sec/surgery/PopulateContents()
- new /obj/item/scalpel(src)
- new /obj/item/hemostat(src)
- new /obj/item/retractor(src)
- new /obj/item/circular_saw(src)
- new /obj/item/surgicaldrill(src)
- new /obj/item/cautery(src)
- new /obj/item/surgical_drapes(src)
- new /obj/item/clothing/mask/surgical(src)
- new /obj/item/reagent_containers/medspray/sterilizine(src)
-
-/obj/item/storage/backpack/duffelbag/engineering
- name = "industrial duffel bag"
- desc = "A large duffel bag for holding extra tools and supplies."
- icon_state = "duffel-eng"
- item_state = "duffel-eng"
-
-/obj/item/storage/backpack/duffelbag/durathread
- name = "durathread duffel bag"
- desc = "A lightweight duffel bag made out of durathread."
- icon_state = "duffel-durathread"
- item_state = "duffel-durathread"
- resistance_flags = FIRE_PROOF
- slowdown = 0
-
-/obj/item/storage/backpack/duffelbag/drone
- name = "drone duffel bag"
- desc = "A large duffel bag for holding tools and hats."
- icon_state = "duffel-drone"
- item_state = "duffel-drone"
- resistance_flags = FIRE_PROOF
-
-/obj/item/storage/backpack/duffelbag/drone/PopulateContents()
- new /obj/item/screwdriver(src)
- new /obj/item/wrench(src)
- new /obj/item/weldingtool(src)
- new /obj/item/crowbar(src)
- new /obj/item/stack/cable_coil/random(src)
- new /obj/item/wirecutters(src)
- new /obj/item/multitool(src)
- new /obj/item/pipe_dispenser(src)
-
-/obj/item/storage/backpack/duffelbag/clown
- name = "clown's duffel bag"
- desc = "A large duffel bag for holding lots of funny gags!"
- icon_state = "duffel-clown"
- item_state = "duffel-clown"
-
-/obj/item/storage/backpack/duffelbag/clown/cream_pie/PopulateContents()
- for(var/i in 1 to 10)
- new /obj/item/reagent_containers/food/snacks/pie/cream(src)
-
-/obj/item/storage/backpack/duffelbag/syndie
- name = "suspicious looking duffel bag"
- desc = "A large duffel bag for holding extra tactical supplies."
- icon_state = "duffel-syndie"
- item_state = "duffel-syndieammo"
- slowdown = 0
-
-/obj/item/storage/backpack/duffelbag/syndie/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.silent = TRUE
-
-/obj/item/storage/backpack/duffelbag/syndie/hitman
- desc = "A large duffel bag for holding extra things. There is a Nanotrasen logo on the back."
- icon_state = "duffel-syndieammo"
- item_state = "duffel-syndieammo"
-
-/obj/item/storage/backpack/duffelbag/syndie/hitman/PopulateContents()
- new /obj/item/clothing/under/lawyer/blacksuit(src)
- new /obj/item/clothing/accessory/waistcoat(src)
- new /obj/item/clothing/suit/toggle/lawyer/black(src)
- new /obj/item/clothing/shoes/laceup(src)
- new /obj/item/clothing/gloves/color/black(src)
- new /obj/item/clothing/glasses/sunglasses(src)
- new /obj/item/clothing/head/fedora(src)
-
-/obj/item/storage/backpack/duffelbag/syndie/med
- name = "medical duffel bag"
- desc = "A large duffel bag for holding extra tactical medical supplies."
- icon_state = "duffel-syndiemed"
- item_state = "duffel-syndiemed"
-
-/obj/item/storage/backpack/duffelbag/syndie/surgery
- name = "surgery duffel bag"
- desc = "A suspicious looking duffel bag for holding surgery tools."
- icon_state = "duffel-syndiemed"
- item_state = "duffel-syndiemed"
-
-/obj/item/storage/backpack/duffelbag/syndie/surgery/PopulateContents()
- new /obj/item/scalpel(src)
- new /obj/item/hemostat(src)
- new /obj/item/retractor(src)
- new /obj/item/circular_saw(src)
- new /obj/item/surgicaldrill(src)
- new /obj/item/cautery(src)
- new /obj/item/surgical_drapes(src)
- new /obj/item/clothing/suit/straight_jacket(src)
- new /obj/item/clothing/mask/muzzle(src)
- new /obj/item/mmi/syndie(src)
- new /obj/item/implantcase(src)
- new /obj/item/implanter(src)
- new /obj/item/reagent_containers/medspray/sterilizine(src)
-
-/obj/item/storage/backpack/duffelbag/syndie/surgery_adv
- name = "advanced surgery duffel bag"
- desc = "A large duffel bag for holding surgical tools. Bears the logo of an advanced med-tech firm."
-
-/obj/item/storage/backpack/duffelbag/syndie/surgery_adv/PopulateContents()
- new /obj/item/hemostat/adv(src)
- new /obj/item/circular_saw/adv(src)
- new /obj/item/scalpel/adv(src)
- new /obj/item/retractor/adv(src)
- new /obj/item/cautery/adv(src)
- new /obj/item/surgicaldrill/adv(src)
- new /obj/item/surgical_drapes(src)
- new /obj/item/storage/firstaid/tactical(src)
- new /obj/item/clothing/suit/straight_jacket(src)
- new /obj/item/clothing/mask/muzzle(src)
- new /obj/item/mmi/syndie(src)
- new /obj/item/implantcase(src)
- new /obj/item/implanter(src)
-
-/obj/item/storage/backpack/duffelbag/syndie/ammo
- name = "ammunition duffel bag"
- desc = "A large duffel bag for holding extra weapons ammunition and supplies."
- icon_state = "duffel-syndieammo"
- item_state = "duffel-syndieammo"
-
-/obj/item/storage/backpack/duffelbag/syndie/ammo/shotgun
- desc = "A large duffel bag, packed to the brim with Bulldog shotgun drum magazines."
-
-/obj/item/storage/backpack/duffelbag/syndie/ammo/shotgun/PopulateContents()
- for(var/i in 1 to 6)
- new /obj/item/ammo_box/magazine/m12g(src)
- new /obj/item/ammo_box/magazine/m12g/stun(src)
- new /obj/item/ammo_box/magazine/m12g/slug(src)
- new /obj/item/ammo_box/magazine/m12g/dragon(src)
-
-/obj/item/storage/backpack/duffelbag/syndie/ammo/smg
- desc = "A large duffel bag, packed to the brim with C-20r magazines."
-
-/obj/item/storage/backpack/duffelbag/syndie/ammo/smg/PopulateContents()
- for(var/i in 1 to 9)
- new /obj/item/ammo_box/magazine/smgm45(src)
-
-/obj/item/storage/backpack/duffelbag/syndie/c20rbundle
- desc = "A large duffel bag containing a C-20r, some magazines, and a cheap looking suppressor."
-
-/obj/item/storage/backpack/duffelbag/syndie/c20rbundle/PopulateContents()
- new /obj/item/ammo_box/magazine/smgm45(src)
- new /obj/item/ammo_box/magazine/smgm45(src)
- new /obj/item/gun/ballistic/automatic/c20r(src)
- new /obj/item/suppressor/specialoffer(src)
-
-/obj/item/storage/backpack/duffelbag/syndie/bulldogbundle
- desc = "A large duffel bag containing a Bulldog, some drums, and a pair of thermal imaging glasses."
-
-/obj/item/storage/backpack/duffelbag/syndie/bulldogbundle/PopulateContents()
- new /obj/item/ammo_box/magazine/m12g(src)
- new /obj/item/gun/ballistic/automatic/shotgun/bulldog(src)
- new /obj/item/ammo_box/magazine/m12g/stun(src)
- new /obj/item/clothing/glasses/thermal/syndi(src)
-
-/obj/item/storage/backpack/duffelbag/syndie/med/medicalbundle
- desc = "A large duffel bag containing a tactical medkit, a Donksoft machine gun, a big jumbo box of riot darts, and a knock-off pair of magboots."
-
-/obj/item/storage/backpack/duffelbag/syndie/med/medicalbundle/PopulateContents()
- new /obj/item/clothing/shoes/magboots/syndie(src)
- new /obj/item/storage/firstaid/tactical(src)
- new /obj/item/gun/ballistic/automatic/l6_saw/toy(src)
- new /obj/item/ammo_box/foambox/riot(src)
-
-/obj/item/storage/backpack/duffelbag/syndie/med/bioterrorbundle
- desc = "A large duffel bag containing deadly chemicals, a handheld chem sprayer, Bioterror foam grenade, a Donksoft assault rifle, box of riot grade darts, a dart pistol, and a box of syringes."
-
-/obj/item/storage/backpack/duffelbag/syndie/med/bioterrorbundle/PopulateContents()
- new /obj/item/reagent_containers/spray/chemsprayer/bioterror(src)
- new /obj/item/storage/box/syndie_kit/chemical(src)
- new /obj/item/gun/syringe/syndicate(src)
- new /obj/item/gun/ballistic/automatic/c20r/toy(src)
- new /obj/item/storage/box/syringes(src)
- new /obj/item/ammo_box/foambox/riot(src)
- new /obj/item/grenade/chem_grenade/bioterrorfoam(src)
- if(prob(5))
- new /obj/item/reagent_containers/food/snacks/pizza/pineapple(src)
-
-/obj/item/storage/backpack/duffelbag/syndie/c4/PopulateContents()
- for(var/i in 1 to 10)
- new /obj/item/grenade/plastic/c4(src)
-
-/obj/item/storage/backpack/duffelbag/syndie/x4/PopulateContents()
- for(var/i in 1 to 3)
- new /obj/item/grenade/plastic/x4(src)
-
-/obj/item/storage/backpack/duffelbag/syndie/firestarter
- desc = "A large duffel bag containing a New Russian pyro backpack sprayer, Elite hardsuit, a Stechkin APS pistol, minibomb, ammo, and other equipment."
-
-/obj/item/storage/backpack/duffelbag/syndie/firestarter/PopulateContents()
- new /obj/item/clothing/under/syndicate/soviet(src)
- new /obj/item/watertank/op(src)
- new /obj/item/clothing/suit/space/hardsuit/syndi/elite(src)
- new /obj/item/gun/ballistic/automatic/pistol/APS(src)
- new /obj/item/ammo_box/magazine/pistolm9mm(src)
- new /obj/item/ammo_box/magazine/pistolm9mm(src)
- new /obj/item/reagent_containers/food/drinks/bottle/vodka/badminka(src)
- new /obj/item/reagent_containers/syringe/stimulants(src)
- new /obj/item/grenade/syndieminibomb(src)
-
-// For ClownOps.
-/obj/item/storage/backpack/duffelbag/clown/syndie/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- slowdown = 0
- STR.silent = TRUE
-
-/obj/item/storage/backpack/duffelbag/clown/syndie/PopulateContents()
- new /obj/item/pda/clown(src)
- new /obj/item/clothing/under/rank/clown(src)
- new /obj/item/clothing/shoes/clown_shoes(src)
- new /obj/item/clothing/mask/gas/clown_hat(src)
- new /obj/item/bikehorn(src)
- new /obj/item/implanter/sad_trombone(src)
-
-obj/item/storage/backpack/duffelbag/syndie/shredderbundle
- desc = "A large duffel bag containing two CX Shredders, some magazines, an elite hardsuit, and a chest rig."
-
-/obj/item/storage/backpack/duffelbag/syndie/shredderbundle/PopulateContents()
- new /obj/item/ammo_box/magazine/flechette/shredder(src)
- new /obj/item/ammo_box/magazine/flechette/shredder(src)
- new /obj/item/ammo_box/magazine/flechette/shredder(src)
- new /obj/item/ammo_box/magazine/flechette/shredder(src)
- new /obj/item/gun/ballistic/automatic/flechette/shredder(src)
- new /obj/item/gun/ballistic/automatic/flechette/shredder(src)
- new /obj/item/storage/belt/military(src)
- new /obj/item/clothing/suit/space/hardsuit/syndi/elite(src)
+/* Backpacks
+ * Contains:
+ * Backpack
+ * Backpack Types
+ * Satchel Types
+ */
+
+/*
+ * Backpack
+ */
+
+/obj/item/storage/backpack
+ name = "backpack"
+ desc = "You wear this on your back and put items into it."
+ icon_state = "backpack"
+ item_state = "backpack"
+ lefthand_file = 'icons/mob/inhands/equipment/backpack_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/equipment/backpack_righthand.dmi'
+ w_class = WEIGHT_CLASS_BULKY
+ slot_flags = ITEM_SLOT_BACK //ERROOOOO
+ resistance_flags = NONE
+ max_integrity = 300
+
+/obj/item/storage/backpack/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_combined_w_class = 21
+ STR.max_w_class = WEIGHT_CLASS_NORMAL
+ STR.max_items = 21
+
+/*
+ * Backpack Types
+ */
+
+/obj/item/storage/backpack/old/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_combined_w_class = 12
+
+/obj/item/storage/backpack/holding
+ name = "bag of holding"
+ desc = "A backpack that opens into a localized pocket of Blue Space."
+ icon_state = "holdingpack"
+ item_state = "holdingpack"
+ resistance_flags = FIRE_PROOF
+ item_flags = NO_MAT_REDEMPTION
+ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 60, "acid" = 50)
+ component_type = /datum/component/storage/concrete/bluespace/bag_of_holding
+
+/obj/item/storage/backpack/holding/satchel
+ name = "satchel of holding"
+ desc = "A satchel that opens into a localized pocket of Blue Space."
+ icon_state = "holdingsat"
+ item_state = "holdingsat"
+ species_exception = list(/datum/species/angel)
+
+/obj/item/storage/backpack/holding/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.allow_big_nesting = TRUE
+ STR.max_w_class = WEIGHT_CLASS_GIGANTIC
+ STR.max_combined_w_class = 35
+
+/obj/item/storage/backpack/holding/suicide_act(mob/living/user)
+ user.visible_message("[user] is jumping into [src]! It looks like [user.p_theyre()] trying to commit suicide.")
+ user.dropItemToGround(src, TRUE)
+ user.Stun(100, ignore_canstun = TRUE)
+ sleep(20)
+ playsound(src, "rustle", 50, 1, -5)
+ qdel(user)
+ return
+
+/obj/item/storage/backpack/holding/singularity_act(current_size)
+ var/dist = max((current_size - 2),1)
+ explosion(src.loc,(dist),(dist*2),(dist*4))
+ return
+
+/obj/item/storage/backpack/santabag
+ name = "Santa's Gift Bag"
+ desc = "Space Santa uses this to deliver toys to all the nice children in space in Christmas! Wow, it's pretty big!"
+ icon_state = "giftbag0"
+ item_state = "giftbag"
+ w_class = WEIGHT_CLASS_BULKY
+
+/obj/item/storage/backpack/santabag/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_w_class = WEIGHT_CLASS_NORMAL
+ STR.max_combined_w_class = 60
+
+/obj/item/storage/backpack/santabag/suicide_act(mob/user)
+ user.visible_message("[user] places [src] over [user.p_their()] head and pulls it tight! It looks like [user.p_they()] [user.p_are()]n't in the Christmas spirit...")
+ return (OXYLOSS)
+
+/obj/item/storage/backpack/cultpack
+ name = "trophy rack"
+ desc = "It's useful for both carrying extra gear and proudly declaring your insanity."
+ icon_state = "cultpack"
+ item_state = "backpack"
+
+/obj/item/storage/backpack/clown
+ name = "Giggles von Honkerton"
+ desc = "It's a backpack made by Honk! Co."
+ icon_state = "clownpack"
+ item_state = "clownpack"
+
+/obj/item/storage/backpack/explorer
+ name = "explorer bag"
+ desc = "A robust backpack for stashing your loot."
+ icon_state = "explorerpack"
+ item_state = "explorerpack"
+
+/obj/item/storage/backpack/mime
+ name = "Parcel Parceaux"
+ desc = "A silent backpack made for those silent workers. Silence Co."
+ icon_state = "mimepack"
+ item_state = "mimepack"
+
+/obj/item/storage/backpack/medic
+ name = "medical backpack"
+ desc = "It's a backpack especially designed for use in a sterile environment."
+ icon_state = "medicalpack"
+ item_state = "medicalpack"
+
+/obj/item/storage/backpack/security
+ name = "security backpack"
+ desc = "It's a very robust backpack."
+ icon_state = "securitypack"
+ item_state = "securitypack"
+
+/obj/item/storage/backpack/captain
+ name = "captain's backpack"
+ desc = "It's a special backpack made exclusively for Nanotrasen officers."
+ icon_state = "captainpack"
+ item_state = "captainpack"
+
+/obj/item/storage/backpack/industrial
+ name = "industrial backpack"
+ desc = "It's a tough backpack for the daily grind of station life."
+ icon_state = "engiepack"
+ item_state = "engiepack"
+ resistance_flags = FIRE_PROOF
+
+/obj/item/storage/backpack/botany
+ name = "botany backpack"
+ desc = "It's a backpack made of all-natural fibers."
+ icon_state = "botpack"
+ item_state = "botpack"
+
+/obj/item/storage/backpack/chemistry
+ name = "chemistry backpack"
+ desc = "A backpack specially designed to repel stains and hazardous liquids."
+ icon_state = "chempack"
+ item_state = "chempack"
+
+/obj/item/storage/backpack/genetics
+ name = "genetics backpack"
+ desc = "A bag designed to be super tough, just in case someone hulks out on you."
+ icon_state = "genepack"
+ item_state = "genepack"
+
+/obj/item/storage/backpack/science
+ name = "science backpack"
+ desc = "A specially designed backpack. It's fire resistant and smells vaguely of plasma."
+ icon_state = "toxpack"
+ item_state = "toxpack"
+
+/obj/item/storage/backpack/virology
+ name = "virology backpack"
+ desc = "A backpack made of hypo-allergenic fibers. It's designed to help prevent the spread of disease. Smells like monkey."
+ icon_state = "viropack"
+ item_state = "viropack"
+
+/*
+ * Satchel Types
+ */
+
+/obj/item/storage/backpack/satchel
+ name = "satchel"
+ desc = "A trendy looking satchel."
+ icon_state = "satchel-norm"
+ species_exception = list(/datum/species/angel) //satchels can be equipped since they are on the side, not back
+
+/obj/item/storage/backpack/satchel/leather
+ name = "leather satchel"
+ desc = "It's a very fancy satchel made with fine leather."
+ icon_state = "satchel"
+
+/obj/item/storage/backpack/satchel/leather/withwallet/PopulateContents()
+ new /obj/item/storage/wallet/random(src)
+
+/obj/item/storage/backpack/satchel/eng
+ name = "industrial satchel"
+ desc = "A tough satchel with extra pockets."
+ icon_state = "satchel-eng"
+ item_state = "engiepack"
+
+/obj/item/storage/backpack/satchel/med
+ name = "medical satchel"
+ desc = "A sterile satchel used in medical departments."
+ icon_state = "satchel-med"
+ item_state = "medicalpack"
+
+/obj/item/storage/backpack/satchel/vir
+ name = "virologist satchel"
+ desc = "A sterile satchel with virologist colours."
+ icon_state = "satchel-vir"
+ item_state = "satchel-vir"
+
+/obj/item/storage/backpack/satchel/chem
+ name = "chemist satchel"
+ desc = "A sterile satchel with chemist colours."
+ icon_state = "satchel-chem"
+ item_state = "satchel-chem"
+
+/obj/item/storage/backpack/satchel/gen
+ name = "geneticist satchel"
+ desc = "A sterile satchel with geneticist colours."
+ icon_state = "satchel-gen"
+ item_state = "satchel-gen"
+
+/obj/item/storage/backpack/satchel/tox
+ name = "scientist satchel"
+ desc = "Useful for holding research materials."
+ icon_state = "satchel-tox"
+ item_state = "satchel-tox"
+
+/obj/item/storage/backpack/satchel/hyd
+ name = "botanist satchel"
+ desc = "A satchel made of all natural fibers."
+ icon_state = "satchel-hyd"
+ item_state = "satchel-hyd"
+
+/obj/item/storage/backpack/satchel/sec
+ name = "security satchel"
+ desc = "A robust satchel for security related needs."
+ icon_state = "satchel-sec"
+ item_state = "securitypack"
+
+/obj/item/storage/backpack/satchel/explorer
+ name = "explorer satchel"
+ desc = "A robust satchel for stashing your loot."
+ icon_state = "satchel-explorer"
+ item_state = "securitypack"
+
+/obj/item/storage/backpack/satchel/bone
+ name = "bone satchel"
+ desc = "A bone satchel fashend with watcher wings and large bones from goliath. Can be worn on the belt."
+ icon = 'icons/obj/mining.dmi'
+ icon_state = "goliath_saddle"
+ slot_flags = ITEM_SLOT_BACK
+
+/obj/item/storage/backpack/satchel/bone/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_combined_w_class = 20
+ STR.max_items = 15
+
+/obj/item/storage/backpack/satchel/cap
+ name = "captain's satchel"
+ desc = "An exclusive satchel for Nanotrasen officers."
+ icon_state = "satchel-cap"
+ item_state = "captainpack"
+
+/obj/item/storage/backpack/satchel/flat
+ name = "smuggler's satchel"
+ desc = "A very slim satchel that can easily fit into tight spaces."
+ icon_state = "satchel-flat"
+ w_class = WEIGHT_CLASS_BULKY //Can fit in backpacks itself.
+ level = 1
+ component_type = /datum/component/storage/concrete/secret_satchel
+
+/obj/item/storage/backpack/satchel/flat/Initialize()
+ . = ..()
+ SSpersistence.new_secret_satchels += src
+
+/obj/item/storage/backpack/satchel/flat/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_combined_w_class = 6
+ STR.cant_hold = typecacheof(list(/obj/item/storage/backpack/satchel/flat)) //muh recursive backpacks
+
+/obj/item/storage/backpack/satchel/flat/hide(intact)
+ if(intact)
+ invisibility = INVISIBILITY_MAXIMUM
+ anchored = TRUE //otherwise you can start pulling, cover it, and drag around an invisible backpack.
+ icon_state = "[initial(icon_state)]2"
+ else
+ invisibility = initial(invisibility)
+ anchored = FALSE
+ icon_state = initial(icon_state)
+
+/obj/item/storage/backpack/satchel/flat/PopulateContents()
+ new /obj/item/stack/tile/plasteel(src)
+ new /obj/item/crowbar(src)
+
+/obj/item/storage/backpack/satchel/flat/Destroy()
+ SSpersistence.new_secret_satchels -= src
+ return ..()
+
+/obj/item/storage/backpack/satchel/flat/secret
+ var/list/reward_one_of_these = list() //Intended for map editing
+ var/list/reward_all_of_these = list() //use paths!
+ var/revealed = FALSE
+
+/obj/item/storage/backpack/satchel/flat/secret/Initialize()
+ . = ..()
+
+ if(isfloorturf(loc) && !isplatingturf(loc))
+ hide(1)
+
+/obj/item/storage/backpack/satchel/flat/secret/hide(intact)
+ ..()
+ if(!intact && !revealed)
+ if(reward_one_of_these.len > 0)
+ var/reward = pick(reward_one_of_these)
+ new reward(src)
+ for(var/R in reward_all_of_these)
+ new R(src)
+ revealed = TRUE
+
+/obj/item/storage/backpack/duffelbag
+ name = "duffel bag"
+ desc = "A large duffel bag for holding extra things."
+ icon_state = "duffel"
+ item_state = "duffel"
+ slowdown = 1
+
+/obj/item/storage/backpack/duffelbag/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_combined_w_class = 30
+
+/obj/item/storage/backpack/duffelbag/captain
+ name = "captain's duffel bag"
+ desc = "A large duffel bag for holding extra captainly goods."
+ icon_state = "duffel-captain"
+ item_state = "duffel-captain"
+
+/obj/item/storage/backpack/duffelbag/med
+ name = "medical duffel bag"
+ desc = "A large duffel bag for holding extra medical supplies."
+ icon_state = "duffel-med"
+ item_state = "duffel-med"
+
+/obj/item/storage/backpack/duffelbag/med/surgery
+ name = "surgical duffel bag"
+ desc = "A large duffel bag for holding extra medical supplies - this one seems to be designed for holding surgical tools."
+
+/obj/item/storage/backpack/duffelbag/med/surgery/PopulateContents()
+ new /obj/item/scalpel(src)
+ new /obj/item/hemostat(src)
+ new /obj/item/retractor(src)
+ new /obj/item/circular_saw(src)
+ new /obj/item/surgicaldrill(src)
+ new /obj/item/cautery(src)
+ new /obj/item/surgical_drapes(src)
+ new /obj/item/clothing/mask/surgical(src)
+ new /obj/item/razor(src)
+ new /obj/item/reagent_containers/medspray/sterilizine(src)
+
+/obj/item/storage/backpack/duffelbag/sec
+ name = "security duffel bag"
+ desc = "A large duffel bag for holding extra security supplies and ammunition."
+ icon_state = "duffel-sec"
+ item_state = "duffel-sec"
+
+/obj/item/storage/backpack/duffelbag/sec/surgery
+ name = "surgical duffel bag"
+ desc = "A large duffel bag for holding extra supplies - this one has a material inlay with space for various sharp-looking tools."
+
+/obj/item/storage/backpack/duffelbag/sec/surgery/PopulateContents()
+ new /obj/item/scalpel(src)
+ new /obj/item/hemostat(src)
+ new /obj/item/retractor(src)
+ new /obj/item/circular_saw(src)
+ new /obj/item/surgicaldrill(src)
+ new /obj/item/cautery(src)
+ new /obj/item/surgical_drapes(src)
+ new /obj/item/clothing/mask/surgical(src)
+ new /obj/item/reagent_containers/medspray/sterilizine(src)
+
+/obj/item/storage/backpack/duffelbag/engineering
+ name = "industrial duffel bag"
+ desc = "A large duffel bag for holding extra tools and supplies."
+ icon_state = "duffel-eng"
+ item_state = "duffel-eng"
+
+/obj/item/storage/backpack/duffelbag/durathread
+ name = "durathread duffel bag"
+ desc = "A lightweight duffel bag made out of durathread."
+ icon_state = "duffel-durathread"
+ item_state = "duffel-durathread"
+ resistance_flags = FIRE_PROOF
+ slowdown = 0
+
+/obj/item/storage/backpack/duffelbag/drone
+ name = "drone duffel bag"
+ desc = "A large duffel bag for holding tools and hats."
+ icon_state = "duffel-drone"
+ item_state = "duffel-drone"
+ resistance_flags = FIRE_PROOF
+
+/obj/item/storage/backpack/duffelbag/drone/PopulateContents()
+ new /obj/item/screwdriver(src)
+ new /obj/item/wrench(src)
+ new /obj/item/weldingtool(src)
+ new /obj/item/crowbar(src)
+ new /obj/item/stack/cable_coil/random(src)
+ new /obj/item/wirecutters(src)
+ new /obj/item/multitool(src)
+ new /obj/item/pipe_dispenser(src)
+
+/obj/item/storage/backpack/duffelbag/clown
+ name = "clown's duffel bag"
+ desc = "A large duffel bag for holding lots of funny gags!"
+ icon_state = "duffel-clown"
+ item_state = "duffel-clown"
+
+/obj/item/storage/backpack/duffelbag/clown/cream_pie/PopulateContents()
+ for(var/i in 1 to 10)
+ new /obj/item/reagent_containers/food/snacks/pie/cream(src)
+
+/obj/item/storage/backpack/duffelbag/syndie
+ name = "suspicious looking duffel bag"
+ desc = "A large duffel bag for holding extra tactical supplies."
+ icon_state = "duffel-syndie"
+ item_state = "duffel-syndieammo"
+ slowdown = 0
+
+/obj/item/storage/backpack/duffelbag/syndie/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.silent = TRUE
+
+/obj/item/storage/backpack/duffelbag/syndie/hitman
+ desc = "A large duffel bag for holding extra things. There is a Nanotrasen logo on the back."
+ icon_state = "duffel-syndieammo"
+ item_state = "duffel-syndieammo"
+
+/obj/item/storage/backpack/duffelbag/syndie/hitman/PopulateContents()
+ new /obj/item/clothing/under/lawyer/blacksuit(src)
+ new /obj/item/clothing/accessory/waistcoat(src)
+ new /obj/item/clothing/suit/toggle/lawyer/black(src)
+ new /obj/item/clothing/shoes/laceup(src)
+ new /obj/item/clothing/gloves/color/black(src)
+ new /obj/item/clothing/glasses/sunglasses(src)
+ new /obj/item/clothing/head/fedora(src)
+
+/obj/item/storage/backpack/duffelbag/syndie/med
+ name = "medical duffel bag"
+ desc = "A large duffel bag for holding extra tactical medical supplies."
+ icon_state = "duffel-syndiemed"
+ item_state = "duffel-syndiemed"
+
+/obj/item/storage/backpack/duffelbag/syndie/surgery
+ name = "surgery duffel bag"
+ desc = "A suspicious looking duffel bag for holding surgery tools."
+ icon_state = "duffel-syndiemed"
+ item_state = "duffel-syndiemed"
+
+/obj/item/storage/backpack/duffelbag/syndie/surgery/PopulateContents()
+ new /obj/item/scalpel(src)
+ new /obj/item/hemostat(src)
+ new /obj/item/retractor(src)
+ new /obj/item/circular_saw(src)
+ new /obj/item/surgicaldrill(src)
+ new /obj/item/cautery(src)
+ new /obj/item/surgical_drapes(src)
+ new /obj/item/clothing/suit/straight_jacket(src)
+ new /obj/item/clothing/mask/muzzle(src)
+ new /obj/item/mmi/syndie(src)
+ new /obj/item/implantcase(src)
+ new /obj/item/implanter(src)
+ new /obj/item/reagent_containers/medspray/sterilizine(src)
+
+/obj/item/storage/backpack/duffelbag/syndie/surgery_adv
+ name = "advanced surgery duffel bag"
+ desc = "A large duffel bag for holding surgical tools. Bears the logo of an advanced med-tech firm."
+
+/obj/item/storage/backpack/duffelbag/syndie/surgery_adv/PopulateContents()
+ new /obj/item/scalpel/advanced(src)
+ new /obj/item/retractor/advanced(src)
+ new /obj/item/surgicaldrill/advanced(src)
+ new /obj/item/surgical_drapes(src)
+ new /obj/item/storage/firstaid/tactical(src)
+ new /obj/item/clothing/suit/straight_jacket(src)
+ new /obj/item/clothing/mask/muzzle(src)
+ new /obj/item/mmi/syndie(src)
+ new /obj/item/implantcase(src)
+ new /obj/item/implanter(src)
+
+/obj/item/storage/backpack/duffelbag/syndie/ammo
+ name = "ammunition duffel bag"
+ desc = "A large duffel bag for holding extra weapons ammunition and supplies."
+ icon_state = "duffel-syndieammo"
+ item_state = "duffel-syndieammo"
+
+/obj/item/storage/backpack/duffelbag/syndie/ammo/shotgun
+ desc = "A large duffel bag, packed to the brim with Bulldog shotgun drum magazines."
+
+/obj/item/storage/backpack/duffelbag/syndie/ammo/shotgun/PopulateContents()
+ for(var/i in 1 to 6)
+ new /obj/item/ammo_box/magazine/m12g(src)
+ new /obj/item/ammo_box/magazine/m12g/stun(src)
+ new /obj/item/ammo_box/magazine/m12g/slug(src)
+ new /obj/item/ammo_box/magazine/m12g/dragon(src)
+
+/obj/item/storage/backpack/duffelbag/syndie/ammo/smg
+ desc = "A large duffel bag, packed to the brim with C-20r magazines."
+
+/obj/item/storage/backpack/duffelbag/syndie/ammo/smg/PopulateContents()
+ for(var/i in 1 to 9)
+ new /obj/item/ammo_box/magazine/smgm45(src)
+
+/obj/item/storage/backpack/duffelbag/syndie/c20rbundle
+ desc = "A large duffel bag containing a C-20r, some magazines, and a cheap looking suppressor."
+
+/obj/item/storage/backpack/duffelbag/syndie/c20rbundle/PopulateContents()
+ new /obj/item/ammo_box/magazine/smgm45(src)
+ new /obj/item/ammo_box/magazine/smgm45(src)
+ new /obj/item/gun/ballistic/automatic/c20r(src)
+ new /obj/item/suppressor/specialoffer(src)
+
+/obj/item/storage/backpack/duffelbag/syndie/bulldogbundle
+ desc = "A large duffel bag containing a Bulldog, some drums, and a pair of thermal imaging glasses."
+
+/obj/item/storage/backpack/duffelbag/syndie/bulldogbundle/PopulateContents()
+ new /obj/item/ammo_box/magazine/m12g(src)
+ new /obj/item/gun/ballistic/automatic/shotgun/bulldog(src)
+ new /obj/item/ammo_box/magazine/m12g/stun(src)
+ new /obj/item/clothing/glasses/thermal/syndi(src)
+
+/obj/item/storage/backpack/duffelbag/syndie/med/medicalbundle
+ desc = "A large duffel bag containing a tactical medkit, a Donksoft machine gun, a big jumbo box of riot darts, and a knock-off pair of magboots."
+
+/obj/item/storage/backpack/duffelbag/syndie/med/medicalbundle/PopulateContents()
+ new /obj/item/clothing/shoes/magboots/syndie(src)
+ new /obj/item/storage/firstaid/tactical(src)
+ new /obj/item/gun/ballistic/automatic/l6_saw/toy(src)
+ new /obj/item/ammo_box/foambox/riot(src)
+
+/obj/item/storage/backpack/duffelbag/syndie/med/bioterrorbundle
+ desc = "A large duffel bag containing deadly chemicals, a handheld chem sprayer, Bioterror foam grenade, a Donksoft assault rifle, box of riot grade darts, a dart pistol, and a box of syringes."
+
+/obj/item/storage/backpack/duffelbag/syndie/med/bioterrorbundle/PopulateContents()
+ new /obj/item/reagent_containers/spray/chemsprayer/bioterror(src)
+ new /obj/item/storage/box/syndie_kit/chemical(src)
+ new /obj/item/gun/syringe/syndicate(src)
+ new /obj/item/gun/ballistic/automatic/c20r/toy(src)
+ new /obj/item/storage/box/syringes(src)
+ new /obj/item/ammo_box/foambox/riot(src)
+ new /obj/item/grenade/chem_grenade/bioterrorfoam(src)
+ if(prob(5))
+ new /obj/item/reagent_containers/food/snacks/pizza/pineapple(src)
+
+/obj/item/storage/backpack/duffelbag/syndie/c4/PopulateContents()
+ for(var/i in 1 to 10)
+ new /obj/item/grenade/plastic/c4(src)
+
+/obj/item/storage/backpack/duffelbag/syndie/x4/PopulateContents()
+ for(var/i in 1 to 3)
+ new /obj/item/grenade/plastic/x4(src)
+
+/obj/item/storage/backpack/duffelbag/syndie/firestarter
+ desc = "A large duffel bag containing a New Russian pyro backpack sprayer, Elite hardsuit, a Stechkin APS pistol, minibomb, ammo, and other equipment."
+
+/obj/item/storage/backpack/duffelbag/syndie/firestarter/PopulateContents()
+ new /obj/item/clothing/under/syndicate/soviet(src)
+ new /obj/item/watertank/op(src)
+ new /obj/item/clothing/suit/space/hardsuit/syndi/elite(src)
+ new /obj/item/gun/ballistic/automatic/pistol/APS(src)
+ new /obj/item/ammo_box/magazine/pistolm9mm(src)
+ new /obj/item/ammo_box/magazine/pistolm9mm(src)
+ new /obj/item/reagent_containers/food/drinks/bottle/vodka/badminka(src)
+ new /obj/item/reagent_containers/syringe/stimulants(src)
+ new /obj/item/grenade/syndieminibomb(src)
+
+// For ClownOps.
+/obj/item/storage/backpack/duffelbag/clown/syndie/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ slowdown = 0
+ STR.silent = TRUE
+
+/obj/item/storage/backpack/duffelbag/clown/syndie/PopulateContents()
+ new /obj/item/pda/clown(src)
+ new /obj/item/clothing/under/rank/clown(src)
+ new /obj/item/clothing/shoes/clown_shoes(src)
+ new /obj/item/clothing/mask/gas/clown_hat(src)
+ new /obj/item/bikehorn(src)
+ new /obj/item/implanter/sad_trombone(src)
+
+obj/item/storage/backpack/duffelbag/syndie/shredderbundle
+ desc = "A large duffel bag containing two CX Shredders, some magazines, an elite hardsuit, and a chest rig."
+
+/obj/item/storage/backpack/duffelbag/syndie/shredderbundle/PopulateContents()
+ new /obj/item/ammo_box/magazine/flechette/shredder(src)
+ new /obj/item/ammo_box/magazine/flechette/shredder(src)
+ new /obj/item/ammo_box/magazine/flechette/shredder(src)
+ new /obj/item/ammo_box/magazine/flechette/shredder(src)
+ new /obj/item/gun/ballistic/automatic/flechette/shredder(src)
+ new /obj/item/gun/ballistic/automatic/flechette/shredder(src)
+ new /obj/item/storage/belt/military(src)
+ new /obj/item/clothing/suit/space/hardsuit/syndi/elite(src)
diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm
index d5794174..4e13446f 100644
--- a/code/game/objects/items/storage/belt.dm
+++ b/code/game/objects/items/storage/belt.dm
@@ -1,914 +1,911 @@
-/obj/item/storage/belt
- name = "belt"
- desc = "Can hold various things."
- icon = 'icons/obj/clothing/belts.dmi'
- icon_state = "utilitybelt"
- item_state = "utility"
- lefthand_file = 'icons/mob/inhands/equipment/belt_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/equipment/belt_righthand.dmi'
- slot_flags = ITEM_SLOT_BELT
- attack_verb = list("whipped", "lashed", "disciplined")
- max_integrity = 300
- var/content_overlays = FALSE //If this is true, the belt will gain overlays based on what it's holding
- var/worn_overlays = FALSE //worn counterpart of the above.
-
-/obj/item/storage/belt/suicide_act(mob/living/carbon/user)
- user.visible_message("[user] begins belting [user.p_them()]self with \the [src]! It looks like [user.p_theyre()] trying to commit suicide!")
- return BRUTELOSS
-
-/obj/item/storage/belt/update_icon()
- cut_overlays()
- if(content_overlays)
- for(var/obj/item/I in contents)
- var/mutable_appearance/M = I.get_belt_overlay()
- add_overlay(M)
- ..()
-
-/obj/item/storage/belt/worn_overlays(isinhands, icon_file)
- . = ..()
- if(!isinhands && worn_overlays)
- for(var/obj/item/I in contents)
- . += I.get_worn_belt_overlay(icon_file)
-
-/obj/item/storage/belt/Initialize()
- . = ..()
- update_icon()
-
-/obj/item/storage/belt/utility
- name = "toolbelt" //Carn: utility belt is nicer, but it bamboozles the text parsing.
- desc = "Holds tools."
- icon_state = "utilitybelt"
- item_state = "utility"
- content_overlays = TRUE
-
-/obj/item/storage/belt/utility/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- var/static/list/can_hold = typecacheof(list(
- /obj/item/crowbar,
- /obj/item/screwdriver,
- /obj/item/weldingtool,
- /obj/item/wirecutters,
- /obj/item/wrench,
- /obj/item/multitool,
- /obj/item/flashlight,
- /obj/item/stack/cable_coil,
- /obj/item/t_scanner,
- /obj/item/analyzer,
- /obj/item/geiger_counter,
- /obj/item/extinguisher/mini,
- /obj/item/radio,
- /obj/item/clothing/gloves,
- /obj/item/holosign_creator,
- /obj/item/forcefield_projector,
- /obj/item/assembly/signaler
- ))
- STR.can_hold = can_hold
-
-/obj/item/storage/belt/utility/chief
- name = "\improper Chief Engineer's toolbelt" //"the Chief Engineer's toolbelt", because "Chief Engineer's toolbelt" is not a proper noun
- desc = "Holds tools, looks snazzy."
- icon_state = "utilitybelt_ce"
- item_state = "utility_ce"
-
-/obj/item/storage/belt/utility/chief/full/PopulateContents()
- new /obj/item/screwdriver/power(src)
- new /obj/item/crowbar/power(src)
- new /obj/item/weldingtool/experimental(src)//This can be changed if this is too much
- new /obj/item/multitool(src)
- new /obj/item/stack/cable_coil(src,30,pick("red","yellow","orange"))
- new /obj/item/extinguisher/mini(src)
- new /obj/item/analyzer(src)
- //much roomier now that we've managed to remove two tools
-
-/obj/item/storage/belt/utility/full/PopulateContents()
- new /obj/item/screwdriver(src)
- new /obj/item/wrench(src)
- new /obj/item/weldingtool(src)
- new /obj/item/crowbar(src)
- new /obj/item/wirecutters(src)
- new /obj/item/multitool(src)
- new /obj/item/stack/cable_coil(src,30,pick("red","yellow","orange"))
-
-/obj/item/storage/belt/utility/full/engi/PopulateContents()
- new /obj/item/screwdriver(src)
- new /obj/item/wrench(src)
- new /obj/item/weldingtool/largetank(src)
- new /obj/item/crowbar(src)
- new /obj/item/wirecutters(src)
- new /obj/item/multitool(src)
- new /obj/item/stack/cable_coil(src,30,pick("red","yellow","orange"))
-
-
-/obj/item/storage/belt/utility/atmostech/PopulateContents()
- new /obj/item/screwdriver(src)
- new /obj/item/wrench(src)
- new /obj/item/weldingtool(src)
- new /obj/item/crowbar(src)
- new /obj/item/wirecutters(src)
- new /obj/item/t_scanner(src)
- new /obj/item/extinguisher/mini(src)
-
-/obj/item/storage/belt/utility/servant/PopulateContents()
- new /obj/item/screwdriver/brass(src)
- new /obj/item/wirecutters/brass(src)
- new /obj/item/wrench/brass(src)
- new /obj/item/crowbar/brass(src)
- new /obj/item/weldingtool/experimental/brass(src)
- new /obj/item/multitool(src)
- new /obj/item/stack/cable_coil(src, 30, "yellow")
-
-/obj/item/storage/belt/medical
- name = "medical belt"
- desc = "Can hold various medical equipment."
- icon_state = "medicalbelt"
- item_state = "medical"
- content_overlays = TRUE
-
-/obj/item/storage/belt/medical/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_w_class = WEIGHT_CLASS_BULKY
- STR.can_hold = typecacheof(list(
- /obj/item/healthanalyzer,
- /obj/item/dnainjector,
- /obj/item/reagent_containers/dropper,
- /obj/item/reagent_containers/glass/beaker,
- /obj/item/reagent_containers/glass/bottle,
- /obj/item/reagent_containers/pill,
- /obj/item/reagent_containers/syringe,
- /obj/item/reagent_containers/medspray,
- /obj/item/lighter,
- /obj/item/storage/fancy/cigarettes,
- /obj/item/storage/pill_bottle,
- /obj/item/stack/medical,
- /obj/item/flashlight/pen,
- /obj/item/extinguisher/mini,
- /obj/item/reagent_containers/hypospray,
- /obj/item/hypospray/mkii,
- /obj/item/sensor_device,
- /obj/item/radio,
- /obj/item/clothing/gloves/,
- /obj/item/lazarus_injector,
- /obj/item/bikehorn/rubberducky,
- /obj/item/clothing/mask/surgical,
- /obj/item/clothing/mask/breath,
- /obj/item/clothing/mask/breath/medical,
- /obj/item/surgical_drapes, //for true paramedics
- /obj/item/scalpel,
- /obj/item/circular_saw,
- /obj/item/surgicaldrill,
- /obj/item/retractor,
- /obj/item/cautery,
- /obj/item/hemostat,
- /obj/item/geiger_counter,
- /obj/item/clothing/neck/stethoscope,
- /obj/item/stamp,
- /obj/item/clothing/glasses,
- /obj/item/wrench/medical,
- /obj/item/clothing/mask/muzzle,
- /obj/item/reagent_containers/blood,
- /obj/item/tank/internals/emergency_oxygen,
- /obj/item/gun/syringe/syndicate,
- /obj/item/implantcase,
- /obj/item/implant,
- /obj/item/implanter,
- /obj/item/pinpointer/crew
- ))
-
-
-/obj/item/storage/belt/medical/surgery_belt_adv
- name = "surgical supply belt"
- desc = "A specialized belt designed for holding surgical equipment. It seems to have specific pockets for each and every surgical tool you can think of."
- content_overlays = FALSE
-
-/obj/item/storage/belt/medical/surgery_belt_adv/PopulateContents()
- new /obj/item/hemostat/adv(src)
- new /obj/item/circular_saw/adv(src)
- new /obj/item/scalpel/adv(src)
- new /obj/item/retractor/adv(src)
- new /obj/item/cautery/adv(src)
- new /obj/item/surgicaldrill/adv(src)
- new /obj/item/surgical_drapes(src)
-
-/obj/item/storage/belt/security
- name = "security belt"
- desc = "Can hold security gear like handcuffs and flashes."
- icon_state = "securitybelt"
- item_state = "security"//Could likely use a better one.
- content_overlays = TRUE
-
-/obj/item/storage/belt/security/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 5
- STR.max_w_class = WEIGHT_CLASS_NORMAL
- STR.can_hold = typecacheof(list(
- /obj/item/melee/baton,
- /obj/item/melee/classic_baton,
- /obj/item/grenade,
- /obj/item/reagent_containers/spray/pepper,
- /obj/item/restraints/handcuffs,
- /obj/item/assembly/flash/handheld,
- /obj/item/clothing/glasses,
- /obj/item/ammo_casing/shotgun,
- /obj/item/ammo_box,
- /obj/item/reagent_containers/food/snacks/donut,
- /obj/item/kitchen/knife/combat,
- /obj/item/flashlight/seclite,
- /obj/item/melee/classic_baton/telescopic,
- /obj/item/radio,
- /obj/item/clothing/gloves,
- /obj/item/restraints/legcuffs/bola
- ))
-
-/obj/item/storage/belt/security/full/PopulateContents()
- new /obj/item/reagent_containers/spray/pepper(src)
- new /obj/item/restraints/handcuffs(src)
- new /obj/item/grenade/flashbang(src)
- new /obj/item/assembly/flash/handheld(src)
- new /obj/item/melee/baton/loaded(src)
- update_icon()
-
-/obj/item/storage/belt/slut
- name = "slutcurity belt"
- desc = "Holds a variety of gear for \"alternative\" peacekeeping."
- icon_state = "slutbelt"
- item_state = "slut"
-
-obj/item/storage/belt/slut/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 5
- STR.max_w_class = WEIGHT_CLASS_NORMAL
- STR.can_hold = typecacheof(list(
- /obj/item/melee/baton,
- /obj/item/melee/classic_baton,
- /obj/item/grenade,
- /obj/item/reagent_containers/spray/pepper,
- /obj/item/restraints/handcuffs,
- /obj/item/assembly/flash/handheld,
- /obj/item/clothing/glasses,
- /obj/item/reagent_containers/food/snacks/donut,
- /obj/item/flashlight/seclite,
- /obj/item/melee/classic_baton/telescopic,
- /obj/item/radio,
- /obj/item/clothing/gloves,
- /obj/item/restraints/legcuffs/bola,
- /obj/item/dildo,
- /obj/item/leash,
- /obj/item/condom,
- /obj/item/bdsm_whip,
- /obj/item/clothing/mask/gas/sechailer/slut
- ))
-
-/obj/item/storage/belt/mining
- name = "explorer's webbing"
- desc = "A versatile chest rig, cherished by miners and hunters alike."
- icon_state = "explorer1"
- item_state = "explorer1"
- w_class = WEIGHT_CLASS_BULKY
-
-/obj/item/storage/belt/mining/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 6
- STR.max_w_class = WEIGHT_CLASS_BULKY
- STR.max_combined_w_class = 20
- STR.can_hold = typecacheof(list(
- /obj/item/crowbar,
- /obj/item/screwdriver,
- /obj/item/weldingtool,
- /obj/item/wirecutters,
- /obj/item/wrench,
- /obj/item/multitool,
- /obj/item/flashlight,
- /obj/item/stack/cable_coil,
- /obj/item/analyzer,
- /obj/item/extinguisher/mini,
- /obj/item/radio,
- /obj/item/clothing/gloves,
- /obj/item/resonator,
- /obj/item/mining_scanner,
- /obj/item/pickaxe,
- /obj/item/stack/sheet/animalhide,
- /obj/item/stack/sheet/sinew,
- /obj/item/stack/sheet/bone,
- /obj/item/lighter,
- /obj/item/storage/fancy/cigarettes,
- /obj/item/reagent_containers/food/drinks/bottle,
- /obj/item/stack/medical,
- /obj/item/kitchen/knife,
- /obj/item/reagent_containers/hypospray,
- /obj/item/gps,
- /obj/item/survivalcapsule,
- /obj/item/t_scanner/adv_mining_scanner,
- /obj/item/reagent_containers/pill,
- /obj/item/storage/pill_bottle,
- /obj/item/stack/ore,
- /obj/item/reagent_containers/food/drinks,
- /obj/item/organ/regenerative_core,
- /obj/item/wormhole_jaunter,
- /obj/item/stack/marker_beacon
- ))
-
-
-/obj/item/storage/belt/mining/vendor
- contents = newlist(/obj/item/survivalcapsule)
-
-/obj/item/storage/belt/mining/alt
- icon_state = "explorer2"
- item_state = "explorer2"
-
-/obj/item/storage/belt/mining/primitive
- name = "hunter's belt"
- desc = "A versatile belt, woven from sinew."
- icon_state = "ebelt"
- item_state = "ebelt"
-
-/obj/item/storage/belt/mining/primitive/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 5
-
-/obj/item/storage/belt/soulstone
- name = "soul stone belt"
- desc = "Designed for ease of access to the shards during a fight, as to not let a single enemy spirit slip away."
- icon_state = "soulstonebelt"
- item_state = "soulstonebelt"
-
-/obj/item/storage/belt/soulstone/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 6
- STR.can_hold = typecacheof(list(
- /obj/item/soulstone
- ))
-
-/obj/item/storage/belt/soulstone/full/PopulateContents()
- for(var/i in 1 to 6)
- new /obj/item/soulstone(src)
-
-/obj/item/storage/belt/soulstone/full/chappy/PopulateContents()
- for(var/i in 1 to 6)
- new /obj/item/soulstone/anybody/chaplain(src)
-
-/obj/item/storage/belt/champion
- name = "championship belt"
- desc = "Proves to the world that you are the strongest!"
- icon_state = "championbelt"
- item_state = "champion"
- materials = list(MAT_GOLD=400)
-
-/obj/item/storage/belt/champion/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 1
- STR.can_hold = list(
- /obj/item/clothing/mask/luchador
- )
-
-/obj/item/storage/belt/military
- name = "chest rig"
- desc = "A set of tactical webbing worn by Syndicate boarding parties."
- icon_state = "militarywebbing"
- item_state = "militarywebbing"
-
-/obj/item/storage/belt/military/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_w_class = WEIGHT_CLASS_SMALL
-
-/obj/item/storage/belt/military/snack
- name = "tactical snack rig"
-
-/obj/item/storage/belt/military/snack/Initialize()
- . = ..()
- var/sponsor = pick("DonkCo", "Waffle Co.", "Roffle Co.", "Gorlax Marauders", "Tiger Cooperative")
- desc = "A set of snack-tical webbing worn by athletes of the [sponsor] VR sports division."
-
-/obj/item/storage/belt/military/snack/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 6
- STR.max_w_class = WEIGHT_CLASS_SMALL
- STR.can_hold = typecacheof(list(
- /obj/item/reagent_containers/food/snacks,
- /obj/item/reagent_containers/food/drinks
- ))
-
- var/amount = 5
- var/rig_snacks
- while(contents.len <= amount)
- rig_snacks = pick(list(
- /obj/item/reagent_containers/food/snacks/candy,
- /obj/item/reagent_containers/food/drinks/dry_ramen,
- /obj/item/reagent_containers/food/snacks/chips,
- /obj/item/reagent_containers/food/snacks/sosjerky,
- /obj/item/reagent_containers/food/snacks/syndicake,
- /obj/item/reagent_containers/food/snacks/spacetwinkie,
- /obj/item/reagent_containers/food/snacks/cheesiehonkers,
- /obj/item/reagent_containers/food/snacks/nachos,
- /obj/item/reagent_containers/food/snacks/cheesynachos,
- /obj/item/reagent_containers/food/snacks/cubannachos,
- /obj/item/reagent_containers/food/snacks/nugget,
- /obj/item/reagent_containers/food/snacks/pastatomato,
- /obj/item/reagent_containers/food/snacks/rofflewaffles,
- /obj/item/reagent_containers/food/snacks/donkpocket,
- /obj/item/reagent_containers/food/drinks/soda_cans/cola,
- /obj/item/reagent_containers/food/drinks/soda_cans/space_mountain_wind,
- /obj/item/reagent_containers/food/drinks/soda_cans/dr_gibb,
- /obj/item/reagent_containers/food/drinks/soda_cans/starkist,
- /obj/item/reagent_containers/food/drinks/soda_cans/space_up,
- /obj/item/reagent_containers/food/drinks/soda_cans/pwr_game,
- /obj/item/reagent_containers/food/drinks/soda_cans/lemon_lime,
- /obj/item/reagent_containers/food/drinks/drinkingglass/filled/nuka_cola,
- /obj/item/reagent_containers/food/drinks/drinkingglass/filled/syndicatebomb
- ))
- new rig_snacks(src)
-
-/obj/item/storage/belt/military/abductor
- name = "agent belt"
- desc = "A belt used by abductor agents."
- icon = 'icons/obj/abductor.dmi'
- icon_state = "belt"
- item_state = "security"
-
-/obj/item/storage/belt/military/abductor/full/PopulateContents()
- new /obj/item/screwdriver/abductor(src)
- new /obj/item/wrench/abductor(src)
- new /obj/item/weldingtool/abductor(src)
- new /obj/item/crowbar/abductor(src)
- new /obj/item/wirecutters/abductor(src)
- new /obj/item/multitool/abductor(src)
- new /obj/item/stack/cable_coil(src,30,"white")
-
-/obj/item/storage/belt/military/army
- name = "army belt"
- desc = "A belt used by military forces."
- icon_state = "grenadebeltold"
- item_state = "security"
-
-/obj/item/storage/belt/military/assault
- name = "assault belt"
- desc = "A tactical assault belt."
- icon_state = "assaultbelt"
- item_state = "security"
-
-/obj/item/storage/belt/military/assault/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 6
-
-/obj/item/storage/belt/durathread
- name = "durathread toolbelt"
- desc = "A toolbelt made out of durathread, it seems resistant enough to hold even big tools like an RCD, it also has higher capacity."
- icon_state = "webbing-durathread"
- item_state = "webbing-durathread"
- resistance_flags = FIRE_PROOF
-
-/obj/item/storage/belt/durathread/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 14
- STR.max_combined_w_class = 32
- STR.max_w_class = WEIGHT_CLASS_NORMAL
- STR.can_hold = typecacheof(list(
- /obj/item/ammo_box,
- /obj/item/ammo_casing/shotgun,
- /obj/item/analyzer,
- /obj/item/assembly/flash/handheld,
- /obj/item/assembly/mousetrap,
- /obj/item/assembly/signaler,
- /obj/item/bikehorn/rubberducky,
- /obj/item/cautery,
- /obj/item/circular_saw,
- /obj/item/clothing/glasses,
- /obj/item/clothing/gloves,
- /obj/item/clothing/mask/breath,
- /obj/item/clothing/mask/breath/medical,
- /obj/item/clothing/mask/muzzle,
- /obj/item/clothing/mask/surgical,
- /obj/item/clothing/neck/stethoscope,
- /obj/item/construction/rcd,
- /obj/item/crowbar,
- /obj/item/dnainjector,
- /obj/item/extinguisher/mini,
- /obj/item/flashlight,
- /obj/item/flashlight/pen,
- /obj/item/flashlight/seclite,
- /obj/item/forcefield_projector,
- /obj/item/geiger_counter,
- /obj/item/grenade,
- /obj/item/grenade/chem_grenade,
- /obj/item/grenade/chem_grenade/metalfoam,
- /obj/item/grenade/chem_grenade/smart_metal_foam,
- /obj/item/gun/syringe/syndicate,
- /obj/item/healthanalyzer,
- /obj/item/hemostat,
- /obj/item/holosign_creator,
- /obj/item/holosign_creator/atmos,
- /obj/item/holosign_creator/engineering,
- /obj/item/hypospray/mkii,
- /obj/item/implant,
- /obj/item/implantcase,
- /obj/item/implanter,
- /obj/item/key/janitor,
- /obj/item/kitchen/knife/combat,
- /obj/item/lazarus_injector,
- /obj/item/lighter,
- /obj/item/lightreplacer,
- /obj/item/melee/baton,
- /obj/item/melee/classic_baton,
- /obj/item/melee/classic_baton/telescopic,
- /obj/item/melee/flyswatter,
- /obj/item/multitool,
- /obj/item/paint/paint_remover,
- /obj/item/pinpointer/crew,
- /obj/item/pipe_dispenser,
- /obj/item/radio,
- /obj/item/rcd_ammo,
- /obj/item/reagent_containers/blood,
- /obj/item/reagent_containers/dropper,
- /obj/item/reagent_containers/food/snacks/donut,
- /obj/item/reagent_containers/glass/beaker,
- /obj/item/reagent_containers/glass/bottle,
- /obj/item/reagent_containers/hypospray,
- /obj/item/reagent_containers/medspray,
- /obj/item/reagent_containers/pill,
- /obj/item/reagent_containers/spray,
- /obj/item/reagent_containers/spray/pepper,
- /obj/item/reagent_containers/syringe,
- /obj/item/restraints/handcuffs,
- /obj/item/restraints/legcuffs/bola,
- /obj/item/retractor,
- /obj/item/scalpel,
- /obj/item/screwdriver,
- /obj/item/sensor_device,
- /obj/item/soap,
- /obj/item/stack/cable_coil,
- /obj/item/stack/medical,
- /obj/item/stack/rods,
- /obj/item/stack/tile/plasteel,
- /obj/item/stamp,
- /obj/item/storage/fancy/cigarettes,
- /obj/item/storage/pill_bottle,
- /obj/item/surgical_drapes, //for true paramedics
- /obj/item/surgicaldrill,
- /obj/item/t_scanner,
- /obj/item/tank/internals/emergency_oxygen,
- /obj/item/weldingtool,
- /obj/item/wirecutters,
- /obj/item/wrench,
- /obj/item/wrench/medical
- ))
-
-/obj/item/storage/belt/grenade
- name = "grenadier belt"
- desc = "A belt for holding grenades."
- icon_state = "grenadebeltnew"
- item_state = "security"
-
-/obj/item/storage/belt/grenade/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 30
- STR.display_numerical_stacking = TRUE
- STR.max_combined_w_class = 60
- STR.max_w_class = WEIGHT_CLASS_BULKY
- STR.can_hold = typecacheof(list(
- /obj/item/grenade,
- /obj/item/screwdriver,
- /obj/item/lighter,
- /obj/item/multitool,
- /obj/item/reagent_containers/food/drinks/bottle/molotov,
- /obj/item/grenade/plastic/c4,
- ))
-
-/obj/item/storage/belt/grenade/full/PopulateContents()
- new /obj/item/grenade/flashbang(src)
- new /obj/item/grenade/smokebomb(src)
- new /obj/item/grenade/smokebomb(src)
- new /obj/item/grenade/smokebomb(src)
- new /obj/item/grenade/smokebomb(src)
- new /obj/item/grenade/empgrenade(src)
- new /obj/item/grenade/empgrenade(src)
- new /obj/item/grenade/syndieminibomb/concussion/frag(src)
- new /obj/item/grenade/syndieminibomb/concussion/frag(src)
- new /obj/item/grenade/syndieminibomb/concussion/frag(src)
- new /obj/item/grenade/syndieminibomb/concussion/frag(src)
- new /obj/item/grenade/syndieminibomb/concussion/frag(src)
- new /obj/item/grenade/syndieminibomb/concussion/frag(src)
- new /obj/item/grenade/syndieminibomb/concussion/frag(src)
- new /obj/item/grenade/syndieminibomb/concussion/frag(src)
- new /obj/item/grenade/syndieminibomb/concussion/frag(src)
- new /obj/item/grenade/syndieminibomb/concussion/frag(src)
- new /obj/item/grenade/gluon(src)
- new /obj/item/grenade/gluon(src)
- new /obj/item/grenade/gluon(src)
- new /obj/item/grenade/gluon(src)
- new /obj/item/grenade/chem_grenade/incendiary(src)
- new /obj/item/grenade/chem_grenade/incendiary(src)
- new /obj/item/grenade/chem_grenade/facid(src)
- new /obj/item/grenade/syndieminibomb(src)
- new /obj/item/grenade/syndieminibomb(src)
- new /obj/item/screwdriver(src)
- new /obj/item/multitool(src)
-
-/obj/item/storage/belt/wands
- name = "wand belt"
- desc = "A belt designed to hold various rods of power. A veritable fanny pack of exotic magic."
- icon_state = "soulstonebelt"
- item_state = "soulstonebelt"
-
-/obj/item/storage/belt/wands/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 6
- STR.can_hold = typecacheof(list(
- /obj/item/gun/magic/wand
- ))
-
-/obj/item/storage/belt/wands/full/PopulateContents()
- new /obj/item/gun/magic/wand/death(src)
- new /obj/item/gun/magic/wand/resurrection(src)
- new /obj/item/gun/magic/wand/polymorph(src)
- new /obj/item/gun/magic/wand/teleport(src)
- new /obj/item/gun/magic/wand/door(src)
- new /obj/item/gun/magic/wand/fireball(src)
-
- for(var/obj/item/gun/magic/wand/W in contents) //All wands in this pack come in the best possible condition
- W.max_charges = initial(W.max_charges)
- W.charges = W.max_charges
-
-/obj/item/storage/belt/janitor
- name = "janibelt"
- desc = "A belt used to hold most janitorial supplies."
- icon_state = "janibelt"
- item_state = "janibelt"
-
-/obj/item/storage/belt/janitor/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 6
- STR.max_w_class = WEIGHT_CLASS_BULKY // Set to this so the light replacer can fit.
- STR.can_hold = typecacheof(list(
- /obj/item/grenade/chem_grenade,
- /obj/item/lightreplacer,
- /obj/item/flashlight,
- /obj/item/reagent_containers/glass/beaker,
- /obj/item/reagent_containers/glass/bottle,
- /obj/item/reagent_containers/spray,
- /obj/item/soap,
- /obj/item/holosign_creator,
- /obj/item/key/janitor,
- /obj/item/clothing/gloves,
- /obj/item/melee/flyswatter,
- /obj/item/paint/paint_remover,
- /obj/item/assembly/mousetrap
- ))
-
-/obj/item/storage/belt/bandolier
- name = "bandolier"
- desc = "A bandolier for holding shotgun ammunition."
- icon_state = "bandolier"
- item_state = "bandolier"
-
-/obj/item/storage/belt/bandolier/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 18
- STR.display_numerical_stacking = TRUE
- STR.can_hold = typecacheof(list(
- /obj/item/ammo_casing/shotgun
- ))
-
-/obj/item/storage/belt/bandolier/durathread
- name = "durathread bandolier"
- desc = "An double stacked bandolier made out of durathread."
- icon_state = "bandolier-durathread"
- item_state = "bandolier-durathread"
- resistance_flags = FIRE_PROOF
-
-/obj/item/storage/belt/bandolier/durathread/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 32
- STR.display_numerical_stacking = TRUE
- STR.can_hold = typecacheof(list(
- /obj/item/ammo_casing
- ))
-
-/obj/item/storage/belt/medolier
- name = "medolier"
- desc = "A medical bandolier for holding smartdarts."
- icon_state = "medolier"
- item_state = "medolier"
-
-/obj/item/storage/belt/medolier/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 15
- STR.display_numerical_stacking = FALSE
- STR.allow_quick_gather = TRUE
- STR.allow_quick_empty = TRUE
- STR.click_gather = TRUE
- STR.can_hold = typecacheof(list(
- /obj/item/reagent_containers/syringe/dart
- ))
-
-/obj/item/storage/belt/medolier/full/PopulateContents()
- for(var/i in 1 to 16)
- new /obj/item/reagent_containers/syringe/dart/(src)
-
-/obj/item/storage/belt/medolier/afterattack(obj/target, mob/user , proximity)
- if(!(istype(target, /obj/item/reagent_containers/glass/beaker)))
- return
- if(!proximity)
- return
- if(!target.reagents)
- return
-
- for(var/obj/item/reagent_containers/syringe/dart/D in contents)
- if(round(target.reagents.total_volume, 1) <= 0)
- to_chat(user, "You soak as many of the darts as you can with the contents from [target].")
- return
- if(D.mode == SYRINGE_INJECT)
- continue
-
- D.afterattack(target, user, proximity)
-
- ..()
-
-/obj/item/storage/belt/holster
- name = "shoulder holster"
- desc = "A holster to carry a handgun and ammo. WARNING: Badasses only."
- icon_state = "holster"
- item_state = "holster"
- alternate_worn_layer = UNDER_SUIT_LAYER
-
-/obj/item/storage/belt/holster/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 3
- STR.max_w_class = WEIGHT_CLASS_NORMAL
- STR.can_hold = typecacheof(list(
- /obj/item/gun/ballistic/automatic/pistol,
- /obj/item/gun/ballistic/revolver,
- /obj/item/ammo_box,
- /obj/item/toy/gun,
- /obj/item/gun/energy/e_gun/mini
- ))
-
-/obj/item/storage/belt/holster/full/PopulateContents()
- new /obj/item/gun/ballistic/revolver/detective(src)
- new /obj/item/ammo_box/c38(src)
- new /obj/item/ammo_box/c38(src)
-
-/obj/item/storage/belt/fannypack
- name = "fannypack"
- desc = "A dorky fannypack for keeping small items in."
- icon_state = "fannypack_leather"
- item_state = "fannypack_leather"
-
-/obj/item/storage/belt/fannypack/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 3
- STR.max_w_class = WEIGHT_CLASS_SMALL
-
-/obj/item/storage/belt/fannypack/black
- name = "black fannypack"
- icon_state = "fannypack_black"
- item_state = "fannypack_black"
-
-/obj/item/storage/belt/fannypack/red
- name = "red fannypack"
- icon_state = "fannypack_red"
- item_state = "fannypack_red"
-
-/obj/item/storage/belt/fannypack/purple
- name = "purple fannypack"
- icon_state = "fannypack_purple"
- item_state = "fannypack_purple"
-
-/obj/item/storage/belt/fannypack/blue
- name = "blue fannypack"
- icon_state = "fannypack_blue"
- item_state = "fannypack_blue"
-
-/obj/item/storage/belt/fannypack/orange
- name = "orange fannypack"
- icon_state = "fannypack_orange"
- item_state = "fannypack_orange"
-
-/obj/item/storage/belt/fannypack/white
- name = "white fannypack"
- icon_state = "fannypack_white"
- item_state = "fannypack_white"
-
-/obj/item/storage/belt/fannypack/green
- name = "green fannypack"
- icon_state = "fannypack_green"
- item_state = "fannypack_green"
-
-/obj/item/storage/belt/fannypack/pink
- name = "pink fannypack"
- icon_state = "fannypack_pink"
- item_state = "fannypack_pink"
-
-/obj/item/storage/belt/fannypack/cyan
- name = "cyan fannypack"
- icon_state = "fannypack_cyan"
- item_state = "fannypack_cyan"
-
-/obj/item/storage/belt/fannypack/yellow
- name = "yellow fannypack"
- icon_state = "fannypack_yellow"
- item_state = "fannypack_yellow"
-
-/obj/item/storage/belt/sabre
- name = "sabre sheath"
- desc = "An ornate sheath designed to hold an officer's blade."
- icon_state = "sheath"
- item_state = "sheath"
- w_class = WEIGHT_CLASS_BULKY
- content_overlays = TRUE
- worn_overlays = TRUE
- var/list/fitting_swords = list(/obj/item/melee/sabre, /obj/item/melee/baton/stunsword)
- var/starting_sword = /obj/item/melee/sabre
-
-/obj/item/storage/belt/sabre/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 1
- STR.rustle_sound = FALSE
- STR.max_w_class = WEIGHT_CLASS_BULKY
- STR.can_hold = typecacheof(fitting_swords)
-
-/obj/item/storage/belt/sabre/examine(mob/user)
- . = ..()
- if(length(contents))
- . += "Alt-click it to quickly draw the blade."
-
-/obj/item/storage/belt/sabre/AltClick(mob/user)
- if(!iscarbon(user) || !user.canUseTopic(src, BE_CLOSE, ismonkey(user)))
- return
- if(length(contents))
- var/obj/item/I = contents[1]
- user.visible_message("[user] takes [I] out of [src].", "You take [I] out of [src].")
- user.put_in_hands(I)
- update_icon()
- else
- to_chat(user, "[src] is empty.")
-
-/obj/item/storage/belt/sabre/update_icon()
- . = ..()
- if(isliving(loc))
- var/mob/living/L = loc
- L.regenerate_icons()
-
-/obj/item/storage/belt/sabre/PopulateContents()
- new starting_sword(src)
-
-/obj/item/storage/belt/sabre/rapier
- name = "rapier sheath"
- desc = "A black, thin sheath that looks to house only a long thin blade. Feels like its made of metal."
- icon_state = "rsheath"
- item_state = "rsheath"
- force = 5
- throwforce = 15
- block_chance = 30
- w_class = WEIGHT_CLASS_BULKY
- attack_verb = list("bashed", "slashes", "prods", "pokes")
- fitting_swords = list(/obj/item/melee/rapier)
- starting_sword = /obj/item/melee/rapier
-
-/obj/item/storage/belt/sabre/rapier/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
- if(attack_type == PROJECTILE_ATTACK)
- final_block_chance = 0 //To thin to block bullets
- return ..()
-
-/obj/item/storage/belt/botany
- name = "botany belt"
- desc = "A belt used to hold most janitorial supplies."
- icon_state = "grenadebeltold" //reusing the old grenade belt sprite, can't go wrong.
- item_state = "grenadebeltold"
-
-/obj/item/storage/belt/botany/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 8
- STR.max_w_class = WEIGHT_CLASS_NORMAL
- STR.can_hold = typecacheof(list(
- /obj/item/reagent_containers/glass/beaker,
- /obj/item/reagent_containers/glass/bottle,
- /obj/item/reagent_containers/syringe,
- /obj/item/reagent_containers/spray,
- /obj/item/disk/plantgene,
- /obj/item/seeds,
- /obj/item/shovel/spade,
- /obj/item/cultivator,
- /obj/item/hatchet,
- /obj/item/plant_analyzer
- ))
+/obj/item/storage/belt
+ name = "belt"
+ desc = "Can hold various things."
+ icon = 'icons/obj/clothing/belts.dmi'
+ icon_state = "utilitybelt"
+ item_state = "utility"
+ lefthand_file = 'icons/mob/inhands/equipment/belt_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/equipment/belt_righthand.dmi'
+ slot_flags = ITEM_SLOT_BELT
+ attack_verb = list("whipped", "lashed", "disciplined")
+ max_integrity = 300
+ var/content_overlays = FALSE //If this is true, the belt will gain overlays based on what it's holding
+ var/worn_overlays = FALSE //worn counterpart of the above.
+
+/obj/item/storage/belt/suicide_act(mob/living/carbon/user)
+ user.visible_message("[user] begins belting [user.p_them()]self with \the [src]! It looks like [user.p_theyre()] trying to commit suicide!")
+ return BRUTELOSS
+
+/obj/item/storage/belt/update_icon()
+ cut_overlays()
+ if(content_overlays)
+ for(var/obj/item/I in contents)
+ var/mutable_appearance/M = I.get_belt_overlay()
+ add_overlay(M)
+ ..()
+
+/obj/item/storage/belt/worn_overlays(isinhands, icon_file)
+ . = ..()
+ if(!isinhands && worn_overlays)
+ for(var/obj/item/I in contents)
+ . += I.get_worn_belt_overlay(icon_file)
+
+/obj/item/storage/belt/Initialize()
+ . = ..()
+ update_icon()
+
+/obj/item/storage/belt/utility
+ name = "toolbelt" //Carn: utility belt is nicer, but it bamboozles the text parsing.
+ desc = "Holds tools."
+ icon_state = "utilitybelt"
+ item_state = "utility"
+ content_overlays = TRUE
+
+/obj/item/storage/belt/utility/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ var/static/list/can_hold = typecacheof(list(
+ /obj/item/crowbar,
+ /obj/item/screwdriver,
+ /obj/item/weldingtool,
+ /obj/item/wirecutters,
+ /obj/item/wrench,
+ /obj/item/multitool,
+ /obj/item/flashlight,
+ /obj/item/stack/cable_coil,
+ /obj/item/t_scanner,
+ /obj/item/analyzer,
+ /obj/item/geiger_counter,
+ /obj/item/extinguisher/mini,
+ /obj/item/radio,
+ /obj/item/clothing/gloves,
+ /obj/item/holosign_creator,
+ /obj/item/forcefield_projector,
+ /obj/item/assembly/signaler
+ ))
+ STR.can_hold = can_hold
+
+/obj/item/storage/belt/utility/chief
+ name = "\improper Chief Engineer's toolbelt" //"the Chief Engineer's toolbelt", because "Chief Engineer's toolbelt" is not a proper noun
+ desc = "Holds tools, looks snazzy."
+ icon_state = "utilitybelt_ce"
+ item_state = "utility_ce"
+
+/obj/item/storage/belt/utility/chief/full/PopulateContents()
+ new /obj/item/screwdriver/power(src)
+ new /obj/item/crowbar/power(src)
+ new /obj/item/weldingtool/experimental(src)//This can be changed if this is too much
+ new /obj/item/multitool(src)
+ new /obj/item/stack/cable_coil(src,30,pick("red","yellow","orange"))
+ new /obj/item/extinguisher/mini(src)
+ new /obj/item/analyzer(src)
+ //much roomier now that we've managed to remove two tools
+
+/obj/item/storage/belt/utility/full/PopulateContents()
+ new /obj/item/screwdriver(src)
+ new /obj/item/wrench(src)
+ new /obj/item/weldingtool(src)
+ new /obj/item/crowbar(src)
+ new /obj/item/wirecutters(src)
+ new /obj/item/multitool(src)
+ new /obj/item/stack/cable_coil(src,30,pick("red","yellow","orange"))
+
+/obj/item/storage/belt/utility/full/engi/PopulateContents()
+ new /obj/item/screwdriver(src)
+ new /obj/item/wrench(src)
+ new /obj/item/weldingtool/largetank(src)
+ new /obj/item/crowbar(src)
+ new /obj/item/wirecutters(src)
+ new /obj/item/multitool(src)
+ new /obj/item/stack/cable_coil(src,30,pick("red","yellow","orange"))
+
+
+/obj/item/storage/belt/utility/atmostech/PopulateContents()
+ new /obj/item/screwdriver(src)
+ new /obj/item/wrench(src)
+ new /obj/item/weldingtool(src)
+ new /obj/item/crowbar(src)
+ new /obj/item/wirecutters(src)
+ new /obj/item/t_scanner(src)
+ new /obj/item/extinguisher/mini(src)
+
+/obj/item/storage/belt/utility/servant/PopulateContents()
+ new /obj/item/screwdriver/brass(src)
+ new /obj/item/wirecutters/brass(src)
+ new /obj/item/wrench/brass(src)
+ new /obj/item/crowbar/brass(src)
+ new /obj/item/weldingtool/experimental/brass(src)
+ new /obj/item/multitool(src)
+ new /obj/item/stack/cable_coil(src, 30, "yellow")
+
+/obj/item/storage/belt/medical
+ name = "medical belt"
+ desc = "Can hold various medical equipment."
+ icon_state = "medicalbelt"
+ item_state = "medical"
+ content_overlays = TRUE
+
+/obj/item/storage/belt/medical/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_w_class = WEIGHT_CLASS_BULKY
+ STR.can_hold = typecacheof(list(
+ /obj/item/healthanalyzer,
+ /obj/item/dnainjector,
+ /obj/item/reagent_containers/dropper,
+ /obj/item/reagent_containers/glass/beaker,
+ /obj/item/reagent_containers/glass/bottle,
+ /obj/item/reagent_containers/pill,
+ /obj/item/reagent_containers/syringe,
+ /obj/item/reagent_containers/medspray,
+ /obj/item/lighter,
+ /obj/item/storage/fancy/cigarettes,
+ /obj/item/storage/pill_bottle,
+ /obj/item/stack/medical,
+ /obj/item/flashlight/pen,
+ /obj/item/extinguisher/mini,
+ /obj/item/reagent_containers/hypospray,
+ /obj/item/hypospray/mkii,
+ /obj/item/sensor_device,
+ /obj/item/radio,
+ /obj/item/clothing/gloves/,
+ /obj/item/lazarus_injector,
+ /obj/item/bikehorn/rubberducky,
+ /obj/item/clothing/mask/surgical,
+ /obj/item/clothing/mask/breath,
+ /obj/item/clothing/mask/breath/medical,
+ /obj/item/surgical_drapes, //for true paramedics
+ /obj/item/scalpel,
+ /obj/item/circular_saw,
+ /obj/item/surgicaldrill,
+ /obj/item/retractor,
+ /obj/item/cautery,
+ /obj/item/hemostat,
+ /obj/item/geiger_counter,
+ /obj/item/clothing/neck/stethoscope,
+ /obj/item/stamp,
+ /obj/item/clothing/glasses,
+ /obj/item/wrench/medical,
+ /obj/item/clothing/mask/muzzle,
+ /obj/item/reagent_containers/blood,
+ /obj/item/tank/internals/emergency_oxygen,
+ /obj/item/gun/syringe/syndicate,
+ /obj/item/implantcase,
+ /obj/item/implant,
+ /obj/item/implanter,
+ /obj/item/pinpointer/crew
+ ))
+
+
+/obj/item/storage/belt/medical/surgery_belt_adv
+ name = "surgical supply belt"
+ desc = "A specialized belt designed for holding surgical equipment. It seems to have specific pockets for each and every surgical tool you can think of."
+ content_overlays = FALSE
+
+/obj/item/storage/belt/medical/surgery_belt_adv/PopulateContents()
+ new /obj/item/scalpel/advanced(src)
+ new /obj/item/retractor/advanced(src)
+ new /obj/item/surgicaldrill/advanced(src)
+ new /obj/item/surgical_drapes(src)
+
+/obj/item/storage/belt/security
+ name = "security belt"
+ desc = "Can hold security gear like handcuffs and flashes."
+ icon_state = "securitybelt"
+ item_state = "security"//Could likely use a better one.
+ content_overlays = TRUE
+
+/obj/item/storage/belt/security/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_items = 5
+ STR.max_w_class = WEIGHT_CLASS_NORMAL
+ STR.can_hold = typecacheof(list(
+ /obj/item/melee/baton,
+ /obj/item/melee/classic_baton,
+ /obj/item/grenade,
+ /obj/item/reagent_containers/spray/pepper,
+ /obj/item/restraints/handcuffs,
+ /obj/item/assembly/flash/handheld,
+ /obj/item/clothing/glasses,
+ /obj/item/ammo_casing/shotgun,
+ /obj/item/ammo_box,
+ /obj/item/reagent_containers/food/snacks/donut,
+ /obj/item/kitchen/knife/combat,
+ /obj/item/flashlight/seclite,
+ /obj/item/melee/classic_baton/telescopic,
+ /obj/item/radio,
+ /obj/item/clothing/gloves,
+ /obj/item/restraints/legcuffs/bola
+ ))
+
+/obj/item/storage/belt/security/full/PopulateContents()
+ new /obj/item/reagent_containers/spray/pepper(src)
+ new /obj/item/restraints/handcuffs(src)
+ new /obj/item/grenade/flashbang(src)
+ new /obj/item/assembly/flash/handheld(src)
+ new /obj/item/melee/baton/loaded(src)
+ update_icon()
+
+/obj/item/storage/belt/slut
+ name = "slutcurity belt"
+ desc = "Holds a variety of gear for \"alternative\" peacekeeping."
+ icon_state = "slutbelt"
+ item_state = "slut"
+
+obj/item/storage/belt/slut/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_items = 5
+ STR.max_w_class = WEIGHT_CLASS_NORMAL
+ STR.can_hold = typecacheof(list(
+ /obj/item/melee/baton,
+ /obj/item/melee/classic_baton,
+ /obj/item/grenade,
+ /obj/item/reagent_containers/spray/pepper,
+ /obj/item/restraints/handcuffs,
+ /obj/item/assembly/flash/handheld,
+ /obj/item/clothing/glasses,
+ /obj/item/reagent_containers/food/snacks/donut,
+ /obj/item/flashlight/seclite,
+ /obj/item/melee/classic_baton/telescopic,
+ /obj/item/radio,
+ /obj/item/clothing/gloves,
+ /obj/item/restraints/legcuffs/bola,
+ /obj/item/dildo,
+ /obj/item/leash,
+ /obj/item/condom,
+ /obj/item/bdsm_whip,
+ /obj/item/clothing/mask/gas/sechailer/slut
+ ))
+
+/obj/item/storage/belt/mining
+ name = "explorer's webbing"
+ desc = "A versatile chest rig, cherished by miners and hunters alike."
+ icon_state = "explorer1"
+ item_state = "explorer1"
+ w_class = WEIGHT_CLASS_BULKY
+
+/obj/item/storage/belt/mining/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_items = 6
+ STR.max_w_class = WEIGHT_CLASS_BULKY
+ STR.max_combined_w_class = 20
+ STR.can_hold = typecacheof(list(
+ /obj/item/crowbar,
+ /obj/item/screwdriver,
+ /obj/item/weldingtool,
+ /obj/item/wirecutters,
+ /obj/item/wrench,
+ /obj/item/multitool,
+ /obj/item/flashlight,
+ /obj/item/stack/cable_coil,
+ /obj/item/analyzer,
+ /obj/item/extinguisher/mini,
+ /obj/item/radio,
+ /obj/item/clothing/gloves,
+ /obj/item/resonator,
+ /obj/item/mining_scanner,
+ /obj/item/pickaxe,
+ /obj/item/stack/sheet/animalhide,
+ /obj/item/stack/sheet/sinew,
+ /obj/item/stack/sheet/bone,
+ /obj/item/lighter,
+ /obj/item/storage/fancy/cigarettes,
+ /obj/item/reagent_containers/food/drinks/bottle,
+ /obj/item/stack/medical,
+ /obj/item/kitchen/knife,
+ /obj/item/reagent_containers/hypospray,
+ /obj/item/gps,
+ /obj/item/survivalcapsule,
+ /obj/item/t_scanner/adv_mining_scanner,
+ /obj/item/reagent_containers/pill,
+ /obj/item/storage/pill_bottle,
+ /obj/item/stack/ore,
+ /obj/item/reagent_containers/food/drinks,
+ /obj/item/organ/regenerative_core,
+ /obj/item/wormhole_jaunter,
+ /obj/item/stack/marker_beacon
+ ))
+
+
+/obj/item/storage/belt/mining/vendor
+ contents = newlist(/obj/item/survivalcapsule)
+
+/obj/item/storage/belt/mining/alt
+ icon_state = "explorer2"
+ item_state = "explorer2"
+
+/obj/item/storage/belt/mining/primitive
+ name = "hunter's belt"
+ desc = "A versatile belt, woven from sinew."
+ icon_state = "ebelt"
+ item_state = "ebelt"
+
+/obj/item/storage/belt/mining/primitive/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_items = 5
+
+/obj/item/storage/belt/soulstone
+ name = "soul stone belt"
+ desc = "Designed for ease of access to the shards during a fight, as to not let a single enemy spirit slip away."
+ icon_state = "soulstonebelt"
+ item_state = "soulstonebelt"
+
+/obj/item/storage/belt/soulstone/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_items = 6
+ STR.can_hold = typecacheof(list(
+ /obj/item/soulstone
+ ))
+
+/obj/item/storage/belt/soulstone/full/PopulateContents()
+ for(var/i in 1 to 6)
+ new /obj/item/soulstone(src)
+
+/obj/item/storage/belt/soulstone/full/chappy/PopulateContents()
+ for(var/i in 1 to 6)
+ new /obj/item/soulstone/anybody/chaplain(src)
+
+/obj/item/storage/belt/champion
+ name = "championship belt"
+ desc = "Proves to the world that you are the strongest!"
+ icon_state = "championbelt"
+ item_state = "champion"
+ materials = list(MAT_GOLD=400)
+
+/obj/item/storage/belt/champion/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_items = 1
+ STR.can_hold = list(
+ /obj/item/clothing/mask/luchador
+ )
+
+/obj/item/storage/belt/military
+ name = "chest rig"
+ desc = "A set of tactical webbing worn by Syndicate boarding parties."
+ icon_state = "militarywebbing"
+ item_state = "militarywebbing"
+
+/obj/item/storage/belt/military/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_w_class = WEIGHT_CLASS_SMALL
+
+/obj/item/storage/belt/military/snack
+ name = "tactical snack rig"
+
+/obj/item/storage/belt/military/snack/Initialize()
+ . = ..()
+ var/sponsor = pick("DonkCo", "Waffle Co.", "Roffle Co.", "Gorlax Marauders", "Tiger Cooperative")
+ desc = "A set of snack-tical webbing worn by athletes of the [sponsor] VR sports division."
+
+/obj/item/storage/belt/military/snack/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_items = 6
+ STR.max_w_class = WEIGHT_CLASS_SMALL
+ STR.can_hold = typecacheof(list(
+ /obj/item/reagent_containers/food/snacks,
+ /obj/item/reagent_containers/food/drinks
+ ))
+
+ var/amount = 5
+ var/rig_snacks
+ while(contents.len <= amount)
+ rig_snacks = pick(list(
+ /obj/item/reagent_containers/food/snacks/candy,
+ /obj/item/reagent_containers/food/drinks/dry_ramen,
+ /obj/item/reagent_containers/food/snacks/chips,
+ /obj/item/reagent_containers/food/snacks/sosjerky,
+ /obj/item/reagent_containers/food/snacks/syndicake,
+ /obj/item/reagent_containers/food/snacks/spacetwinkie,
+ /obj/item/reagent_containers/food/snacks/cheesiehonkers,
+ /obj/item/reagent_containers/food/snacks/nachos,
+ /obj/item/reagent_containers/food/snacks/cheesynachos,
+ /obj/item/reagent_containers/food/snacks/cubannachos,
+ /obj/item/reagent_containers/food/snacks/nugget,
+ /obj/item/reagent_containers/food/snacks/pastatomato,
+ /obj/item/reagent_containers/food/snacks/rofflewaffles,
+ /obj/item/reagent_containers/food/snacks/donkpocket,
+ /obj/item/reagent_containers/food/drinks/soda_cans/cola,
+ /obj/item/reagent_containers/food/drinks/soda_cans/space_mountain_wind,
+ /obj/item/reagent_containers/food/drinks/soda_cans/dr_gibb,
+ /obj/item/reagent_containers/food/drinks/soda_cans/starkist,
+ /obj/item/reagent_containers/food/drinks/soda_cans/space_up,
+ /obj/item/reagent_containers/food/drinks/soda_cans/pwr_game,
+ /obj/item/reagent_containers/food/drinks/soda_cans/lemon_lime,
+ /obj/item/reagent_containers/food/drinks/drinkingglass/filled/nuka_cola,
+ /obj/item/reagent_containers/food/drinks/drinkingglass/filled/syndicatebomb
+ ))
+ new rig_snacks(src)
+
+/obj/item/storage/belt/military/abductor
+ name = "agent belt"
+ desc = "A belt used by abductor agents."
+ icon = 'icons/obj/abductor.dmi'
+ icon_state = "belt"
+ item_state = "security"
+
+/obj/item/storage/belt/military/abductor/full/PopulateContents()
+ new /obj/item/screwdriver/abductor(src)
+ new /obj/item/wrench/abductor(src)
+ new /obj/item/weldingtool/abductor(src)
+ new /obj/item/crowbar/abductor(src)
+ new /obj/item/wirecutters/abductor(src)
+ new /obj/item/multitool/abductor(src)
+ new /obj/item/stack/cable_coil(src,30,"white")
+
+/obj/item/storage/belt/military/army
+ name = "army belt"
+ desc = "A belt used by military forces."
+ icon_state = "grenadebeltold"
+ item_state = "security"
+
+/obj/item/storage/belt/military/assault
+ name = "assault belt"
+ desc = "A tactical assault belt."
+ icon_state = "assaultbelt"
+ item_state = "security"
+
+/obj/item/storage/belt/military/assault/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_items = 6
+
+/obj/item/storage/belt/durathread
+ name = "durathread toolbelt"
+ desc = "A toolbelt made out of durathread, it seems resistant enough to hold even big tools like an RCD, it also has higher capacity."
+ icon_state = "webbing-durathread"
+ item_state = "webbing-durathread"
+ resistance_flags = FIRE_PROOF
+
+/obj/item/storage/belt/durathread/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_items = 14
+ STR.max_combined_w_class = 32
+ STR.max_w_class = WEIGHT_CLASS_NORMAL
+ STR.can_hold = typecacheof(list(
+ /obj/item/ammo_box,
+ /obj/item/ammo_casing/shotgun,
+ /obj/item/analyzer,
+ /obj/item/assembly/flash/handheld,
+ /obj/item/assembly/mousetrap,
+ /obj/item/assembly/signaler,
+ /obj/item/bikehorn/rubberducky,
+ /obj/item/cautery,
+ /obj/item/circular_saw,
+ /obj/item/clothing/glasses,
+ /obj/item/clothing/gloves,
+ /obj/item/clothing/mask/breath,
+ /obj/item/clothing/mask/breath/medical,
+ /obj/item/clothing/mask/muzzle,
+ /obj/item/clothing/mask/surgical,
+ /obj/item/clothing/neck/stethoscope,
+ /obj/item/construction/rcd,
+ /obj/item/crowbar,
+ /obj/item/dnainjector,
+ /obj/item/extinguisher/mini,
+ /obj/item/flashlight,
+ /obj/item/flashlight/pen,
+ /obj/item/flashlight/seclite,
+ /obj/item/forcefield_projector,
+ /obj/item/geiger_counter,
+ /obj/item/grenade,
+ /obj/item/grenade/chem_grenade,
+ /obj/item/grenade/chem_grenade/metalfoam,
+ /obj/item/grenade/chem_grenade/smart_metal_foam,
+ /obj/item/gun/syringe/syndicate,
+ /obj/item/healthanalyzer,
+ /obj/item/hemostat,
+ /obj/item/holosign_creator,
+ /obj/item/holosign_creator/atmos,
+ /obj/item/holosign_creator/engineering,
+ /obj/item/hypospray/mkii,
+ /obj/item/implant,
+ /obj/item/implantcase,
+ /obj/item/implanter,
+ /obj/item/key/janitor,
+ /obj/item/kitchen/knife/combat,
+ /obj/item/lazarus_injector,
+ /obj/item/lighter,
+ /obj/item/lightreplacer,
+ /obj/item/melee/baton,
+ /obj/item/melee/classic_baton,
+ /obj/item/melee/classic_baton/telescopic,
+ /obj/item/melee/flyswatter,
+ /obj/item/multitool,
+ /obj/item/paint/paint_remover,
+ /obj/item/pinpointer/crew,
+ /obj/item/pipe_dispenser,
+ /obj/item/radio,
+ /obj/item/rcd_ammo,
+ /obj/item/reagent_containers/blood,
+ /obj/item/reagent_containers/dropper,
+ /obj/item/reagent_containers/food/snacks/donut,
+ /obj/item/reagent_containers/glass/beaker,
+ /obj/item/reagent_containers/glass/bottle,
+ /obj/item/reagent_containers/hypospray,
+ /obj/item/reagent_containers/medspray,
+ /obj/item/reagent_containers/pill,
+ /obj/item/reagent_containers/spray,
+ /obj/item/reagent_containers/spray/pepper,
+ /obj/item/reagent_containers/syringe,
+ /obj/item/restraints/handcuffs,
+ /obj/item/restraints/legcuffs/bola,
+ /obj/item/retractor,
+ /obj/item/scalpel,
+ /obj/item/screwdriver,
+ /obj/item/sensor_device,
+ /obj/item/soap,
+ /obj/item/stack/cable_coil,
+ /obj/item/stack/medical,
+ /obj/item/stack/rods,
+ /obj/item/stack/tile/plasteel,
+ /obj/item/stamp,
+ /obj/item/storage/fancy/cigarettes,
+ /obj/item/storage/pill_bottle,
+ /obj/item/surgical_drapes, //for true paramedics
+ /obj/item/surgicaldrill,
+ /obj/item/t_scanner,
+ /obj/item/tank/internals/emergency_oxygen,
+ /obj/item/weldingtool,
+ /obj/item/wirecutters,
+ /obj/item/wrench,
+ /obj/item/wrench/medical
+ ))
+
+/obj/item/storage/belt/grenade
+ name = "grenadier belt"
+ desc = "A belt for holding grenades."
+ icon_state = "grenadebeltnew"
+ item_state = "security"
+
+/obj/item/storage/belt/grenade/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_items = 30
+ STR.display_numerical_stacking = TRUE
+ STR.max_combined_w_class = 60
+ STR.max_w_class = WEIGHT_CLASS_BULKY
+ STR.can_hold = typecacheof(list(
+ /obj/item/grenade,
+ /obj/item/screwdriver,
+ /obj/item/lighter,
+ /obj/item/multitool,
+ /obj/item/reagent_containers/food/drinks/bottle/molotov,
+ /obj/item/grenade/plastic/c4,
+ ))
+
+/obj/item/storage/belt/grenade/full/PopulateContents()
+ new /obj/item/grenade/flashbang(src)
+ new /obj/item/grenade/smokebomb(src)
+ new /obj/item/grenade/smokebomb(src)
+ new /obj/item/grenade/smokebomb(src)
+ new /obj/item/grenade/smokebomb(src)
+ new /obj/item/grenade/empgrenade(src)
+ new /obj/item/grenade/empgrenade(src)
+ new /obj/item/grenade/syndieminibomb/concussion/frag(src)
+ new /obj/item/grenade/syndieminibomb/concussion/frag(src)
+ new /obj/item/grenade/syndieminibomb/concussion/frag(src)
+ new /obj/item/grenade/syndieminibomb/concussion/frag(src)
+ new /obj/item/grenade/syndieminibomb/concussion/frag(src)
+ new /obj/item/grenade/syndieminibomb/concussion/frag(src)
+ new /obj/item/grenade/syndieminibomb/concussion/frag(src)
+ new /obj/item/grenade/syndieminibomb/concussion/frag(src)
+ new /obj/item/grenade/syndieminibomb/concussion/frag(src)
+ new /obj/item/grenade/syndieminibomb/concussion/frag(src)
+ new /obj/item/grenade/gluon(src)
+ new /obj/item/grenade/gluon(src)
+ new /obj/item/grenade/gluon(src)
+ new /obj/item/grenade/gluon(src)
+ new /obj/item/grenade/chem_grenade/incendiary(src)
+ new /obj/item/grenade/chem_grenade/incendiary(src)
+ new /obj/item/grenade/chem_grenade/facid(src)
+ new /obj/item/grenade/syndieminibomb(src)
+ new /obj/item/grenade/syndieminibomb(src)
+ new /obj/item/screwdriver(src)
+ new /obj/item/multitool(src)
+
+/obj/item/storage/belt/wands
+ name = "wand belt"
+ desc = "A belt designed to hold various rods of power. A veritable fanny pack of exotic magic."
+ icon_state = "soulstonebelt"
+ item_state = "soulstonebelt"
+
+/obj/item/storage/belt/wands/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_items = 6
+ STR.can_hold = typecacheof(list(
+ /obj/item/gun/magic/wand
+ ))
+
+/obj/item/storage/belt/wands/full/PopulateContents()
+ new /obj/item/gun/magic/wand/death(src)
+ new /obj/item/gun/magic/wand/resurrection(src)
+ new /obj/item/gun/magic/wand/polymorph(src)
+ new /obj/item/gun/magic/wand/teleport(src)
+ new /obj/item/gun/magic/wand/door(src)
+ new /obj/item/gun/magic/wand/fireball(src)
+
+ for(var/obj/item/gun/magic/wand/W in contents) //All wands in this pack come in the best possible condition
+ W.max_charges = initial(W.max_charges)
+ W.charges = W.max_charges
+
+/obj/item/storage/belt/janitor
+ name = "janibelt"
+ desc = "A belt used to hold most janitorial supplies."
+ icon_state = "janibelt"
+ item_state = "janibelt"
+
+/obj/item/storage/belt/janitor/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_items = 6
+ STR.max_w_class = WEIGHT_CLASS_BULKY // Set to this so the light replacer can fit.
+ STR.can_hold = typecacheof(list(
+ /obj/item/grenade/chem_grenade,
+ /obj/item/lightreplacer,
+ /obj/item/flashlight,
+ /obj/item/reagent_containers/glass/beaker,
+ /obj/item/reagent_containers/glass/bottle,
+ /obj/item/reagent_containers/spray,
+ /obj/item/soap,
+ /obj/item/holosign_creator,
+ /obj/item/key/janitor,
+ /obj/item/clothing/gloves,
+ /obj/item/melee/flyswatter,
+ /obj/item/paint/paint_remover,
+ /obj/item/assembly/mousetrap
+ ))
+
+/obj/item/storage/belt/bandolier
+ name = "bandolier"
+ desc = "A bandolier for holding shotgun ammunition."
+ icon_state = "bandolier"
+ item_state = "bandolier"
+
+/obj/item/storage/belt/bandolier/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_items = 18
+ STR.display_numerical_stacking = TRUE
+ STR.can_hold = typecacheof(list(
+ /obj/item/ammo_casing/shotgun
+ ))
+
+/obj/item/storage/belt/bandolier/durathread
+ name = "durathread bandolier"
+ desc = "An double stacked bandolier made out of durathread."
+ icon_state = "bandolier-durathread"
+ item_state = "bandolier-durathread"
+ resistance_flags = FIRE_PROOF
+
+/obj/item/storage/belt/bandolier/durathread/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_items = 32
+ STR.display_numerical_stacking = TRUE
+ STR.can_hold = typecacheof(list(
+ /obj/item/ammo_casing
+ ))
+
+/obj/item/storage/belt/medolier
+ name = "medolier"
+ desc = "A medical bandolier for holding smartdarts."
+ icon_state = "medolier"
+ item_state = "medolier"
+
+/obj/item/storage/belt/medolier/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_items = 15
+ STR.display_numerical_stacking = FALSE
+ STR.allow_quick_gather = TRUE
+ STR.allow_quick_empty = TRUE
+ STR.click_gather = TRUE
+ STR.can_hold = typecacheof(list(
+ /obj/item/reagent_containers/syringe/dart
+ ))
+
+/obj/item/storage/belt/medolier/full/PopulateContents()
+ for(var/i in 1 to 16)
+ new /obj/item/reagent_containers/syringe/dart/(src)
+
+/obj/item/storage/belt/medolier/afterattack(obj/target, mob/user , proximity)
+ if(!(istype(target, /obj/item/reagent_containers/glass/beaker)))
+ return
+ if(!proximity)
+ return
+ if(!target.reagents)
+ return
+
+ for(var/obj/item/reagent_containers/syringe/dart/D in contents)
+ if(round(target.reagents.total_volume, 1) <= 0)
+ to_chat(user, "You soak as many of the darts as you can with the contents from [target].")
+ return
+ if(D.mode == SYRINGE_INJECT)
+ continue
+
+ D.afterattack(target, user, proximity)
+
+ ..()
+
+/obj/item/storage/belt/holster
+ name = "shoulder holster"
+ desc = "A holster to carry a handgun and ammo. WARNING: Badasses only."
+ icon_state = "holster"
+ item_state = "holster"
+ alternate_worn_layer = UNDER_SUIT_LAYER
+
+/obj/item/storage/belt/holster/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_items = 3
+ STR.max_w_class = WEIGHT_CLASS_NORMAL
+ STR.can_hold = typecacheof(list(
+ /obj/item/gun/ballistic/automatic/pistol,
+ /obj/item/gun/ballistic/revolver,
+ /obj/item/ammo_box,
+ /obj/item/toy/gun,
+ /obj/item/gun/energy/e_gun/mini
+ ))
+
+/obj/item/storage/belt/holster/full/PopulateContents()
+ new /obj/item/gun/ballistic/revolver/detective(src)
+ new /obj/item/ammo_box/c38(src)
+ new /obj/item/ammo_box/c38(src)
+
+/obj/item/storage/belt/fannypack
+ name = "fannypack"
+ desc = "A dorky fannypack for keeping small items in."
+ icon_state = "fannypack_leather"
+ item_state = "fannypack_leather"
+
+/obj/item/storage/belt/fannypack/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_items = 3
+ STR.max_w_class = WEIGHT_CLASS_SMALL
+
+/obj/item/storage/belt/fannypack/black
+ name = "black fannypack"
+ icon_state = "fannypack_black"
+ item_state = "fannypack_black"
+
+/obj/item/storage/belt/fannypack/red
+ name = "red fannypack"
+ icon_state = "fannypack_red"
+ item_state = "fannypack_red"
+
+/obj/item/storage/belt/fannypack/purple
+ name = "purple fannypack"
+ icon_state = "fannypack_purple"
+ item_state = "fannypack_purple"
+
+/obj/item/storage/belt/fannypack/blue
+ name = "blue fannypack"
+ icon_state = "fannypack_blue"
+ item_state = "fannypack_blue"
+
+/obj/item/storage/belt/fannypack/orange
+ name = "orange fannypack"
+ icon_state = "fannypack_orange"
+ item_state = "fannypack_orange"
+
+/obj/item/storage/belt/fannypack/white
+ name = "white fannypack"
+ icon_state = "fannypack_white"
+ item_state = "fannypack_white"
+
+/obj/item/storage/belt/fannypack/green
+ name = "green fannypack"
+ icon_state = "fannypack_green"
+ item_state = "fannypack_green"
+
+/obj/item/storage/belt/fannypack/pink
+ name = "pink fannypack"
+ icon_state = "fannypack_pink"
+ item_state = "fannypack_pink"
+
+/obj/item/storage/belt/fannypack/cyan
+ name = "cyan fannypack"
+ icon_state = "fannypack_cyan"
+ item_state = "fannypack_cyan"
+
+/obj/item/storage/belt/fannypack/yellow
+ name = "yellow fannypack"
+ icon_state = "fannypack_yellow"
+ item_state = "fannypack_yellow"
+
+/obj/item/storage/belt/sabre
+ name = "sabre sheath"
+ desc = "An ornate sheath designed to hold an officer's blade."
+ icon_state = "sheath"
+ item_state = "sheath"
+ w_class = WEIGHT_CLASS_BULKY
+ content_overlays = TRUE
+ worn_overlays = TRUE
+ var/list/fitting_swords = list(/obj/item/melee/sabre, /obj/item/melee/baton/stunsword)
+ var/starting_sword = /obj/item/melee/sabre
+
+/obj/item/storage/belt/sabre/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_items = 1
+ STR.rustle_sound = FALSE
+ STR.max_w_class = WEIGHT_CLASS_BULKY
+ STR.can_hold = typecacheof(fitting_swords)
+
+/obj/item/storage/belt/sabre/examine(mob/user)
+ . = ..()
+ if(length(contents))
+ . += "Alt-click it to quickly draw the blade."
+
+/obj/item/storage/belt/sabre/AltClick(mob/user)
+ if(!iscarbon(user) || !user.canUseTopic(src, BE_CLOSE, ismonkey(user)))
+ return
+ if(length(contents))
+ var/obj/item/I = contents[1]
+ user.visible_message("[user] takes [I] out of [src].", "You take [I] out of [src].")
+ user.put_in_hands(I)
+ update_icon()
+ else
+ to_chat(user, "[src] is empty.")
+
+/obj/item/storage/belt/sabre/update_icon()
+ . = ..()
+ if(isliving(loc))
+ var/mob/living/L = loc
+ L.regenerate_icons()
+
+/obj/item/storage/belt/sabre/PopulateContents()
+ new starting_sword(src)
+
+/obj/item/storage/belt/sabre/rapier
+ name = "rapier sheath"
+ desc = "A black, thin sheath that looks to house only a long thin blade. Feels like its made of metal."
+ icon_state = "rsheath"
+ item_state = "rsheath"
+ force = 5
+ throwforce = 15
+ block_chance = 30
+ w_class = WEIGHT_CLASS_BULKY
+ attack_verb = list("bashed", "slashes", "prods", "pokes")
+ fitting_swords = list(/obj/item/melee/rapier)
+ starting_sword = /obj/item/melee/rapier
+
+/obj/item/storage/belt/sabre/rapier/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
+ if(attack_type == PROJECTILE_ATTACK)
+ final_block_chance = 0 //To thin to block bullets
+ return ..()
+
+/obj/item/storage/belt/botany
+ name = "botany belt"
+ desc = "A belt used to hold most janitorial supplies."
+ icon_state = "grenadebeltold" //reusing the old grenade belt sprite, can't go wrong.
+ item_state = "grenadebeltold"
+
+/obj/item/storage/belt/botany/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_items = 8
+ STR.max_w_class = WEIGHT_CLASS_NORMAL
+ STR.can_hold = typecacheof(list(
+ /obj/item/reagent_containers/glass/beaker,
+ /obj/item/reagent_containers/glass/bottle,
+ /obj/item/reagent_containers/syringe,
+ /obj/item/reagent_containers/spray,
+ /obj/item/disk/plantgene,
+ /obj/item/seeds,
+ /obj/item/shovel/spade,
+ /obj/item/cultivator,
+ /obj/item/hatchet,
+ /obj/item/plant_analyzer
+ ))
diff --git a/code/game/objects/items/twohanded.dm b/code/game/objects/items/twohanded.dm
index ce99eb7d..67c3ab8e 100644
--- a/code/game/objects/items/twohanded.dm
+++ b/code/game/objects/items/twohanded.dm
@@ -591,6 +591,8 @@
sharpness = IS_SHARP
actions_types = list(/datum/action/item_action/startchainsaw)
var/on = FALSE
+ tool_behaviour = TOOL_SAW
+ toolspeed = 0.5
/obj/item/twohanded/required/chainsaw/Initialize()
. = ..()
diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm
index de413445..72d328ad 100644
--- a/code/game/objects/items/weaponry.dm
+++ b/code/game/objects/items/weaponry.dm
@@ -1,703 +1,705 @@
-/obj/item/banhammer
- desc = "A banhammer."
- name = "banhammer"
- icon = 'icons/obj/items_and_weapons.dmi'
- icon_state = "toyhammer"
- slot_flags = ITEM_SLOT_BELT
- throwforce = 0
- force = 1
- w_class = WEIGHT_CLASS_TINY
- throw_speed = 3
- throw_range = 7
- attack_verb = list("banned")
- max_integrity = 200
- armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 70)
- resistance_flags = FIRE_PROOF
-
-/obj/item/banhammer/suicide_act(mob/user)
- user.visible_message("[user] is hitting [user.p_them()]self with [src]! It looks like [user.p_theyre()] trying to ban [user.p_them()]self from life.")
- return (BRUTELOSS|FIRELOSS|TOXLOSS|OXYLOSS)
-/*
-oranges says: This is a meme relating to the english translation of the ss13 russian wiki page on lurkmore.
-mrdoombringer sez: and remember kids, if you try and PR a fix for this item's grammar, you are admitting that you are, indeed, a newfriend.
-for further reading, please see: https://github.com/tgstation/tgstation/pull/30173 and https://translate.google.com/translate?sl=auto&tl=en&js=y&prev=_t&hl=en&ie=UTF-8&u=%2F%2Flurkmore.to%2FSS13&edit-text=&act=url
-*/
-/obj/item/banhammer/attack(mob/M, mob/user)
- if(user.zone_selected == BODY_ZONE_HEAD)
- M.visible_message("[user] are stroking the head of [M] with a bangammer", "[user] are stroking the head with a bangammer", "you hear a bangammer stroking a head");
- else
- M.visible_message("[M] has been banned FOR NO REISIN by [user]", "You have been banned FOR NO REISIN by [user]", "you hear a banhammer banning someone")
- playsound(loc, 'sound/effects/adminhelp.ogg', 15) //keep it at 15% volume so people don't jump out of their skin too much
- if(user.a_intent != INTENT_HELP)
- return ..(M, user)
-
-/obj/item/sord
- name = "\improper SORD"
- desc = "This thing is so unspeakably shitty you are having a hard time even holding it."
- icon_state = "sord"
- item_state = "sord"
- lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
- slot_flags = ITEM_SLOT_BELT
- force = 2
- throwforce = 1
- w_class = WEIGHT_CLASS_NORMAL
- hitsound = 'sound/weapons/bladeslice.ogg'
- attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
-
-/obj/item/sord/suicide_act(mob/user)
- user.visible_message("[user] is trying to impale [user.p_them()]self with [src]! It might be a suicide attempt if it weren't so shitty.", \
- "You try to impale yourself with [src], but it's USELESS...")
- return SHAME
-
-/obj/item/claymore
- name = "claymore"
- desc = "What are you standing around staring at this for? Get to killing!"
- icon_state = "claymore"
- item_state = "claymore"
- lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
- hitsound = 'sound/weapons/bladeslice.ogg'
- flags_1 = CONDUCT_1
- slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_BACK
- force = 40
- throwforce = 10
- w_class = WEIGHT_CLASS_NORMAL
- attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
- block_chance = 50
- sharpness = IS_SHARP
- max_integrity = 200
- armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50)
- resistance_flags = FIRE_PROOF
- total_mass = TOTAL_MASS_MEDIEVAL_WEAPON
-
-/obj/item/claymore/Initialize()
- . = ..()
- AddComponent(/datum/component/butchering, 40, 105)
-
-/obj/item/claymore/suicide_act(mob/user)
- user.visible_message("[user] is falling on [src]! It looks like [user.p_theyre()] trying to commit suicide!")
- return(BRUTELOSS)
-
-/obj/item/claymore/highlander //ALL COMMENTS MADE REGARDING THIS SWORD MUST BE MADE IN ALL CAPS
- desc = "THERE CAN BE ONLY ONE, AND IT WILL BE YOU!!!\nActivate it in your hand to point to the nearest victim."
- flags_1 = CONDUCT_1
- item_flags = DROPDEL
- slot_flags = null
- block_chance = 0 //RNG WON'T HELP YOU NOW, PANSY
- light_range = 3
- attack_verb = list("brutalized", "eviscerated", "disemboweled", "hacked", "carved", "cleaved") //ONLY THE MOST VISCERAL ATTACK VERBS
- var/notches = 0 //HOW MANY PEOPLE HAVE BEEN SLAIN WITH THIS BLADE
- var/obj/item/disk/nuclear/nuke_disk //OUR STORED NUKE DISK
-
-/obj/item/claymore/highlander/Initialize()
- . = ..()
- ADD_TRAIT(src, TRAIT_NODROP, HIGHLANDER)
- START_PROCESSING(SSobj, src)
-
-/obj/item/claymore/highlander/Destroy()
- if(nuke_disk)
- nuke_disk.forceMove(get_turf(src))
- nuke_disk.visible_message("The nuke disk is vulnerable!")
- nuke_disk = null
- STOP_PROCESSING(SSobj, src)
- return ..()
-
-/obj/item/claymore/highlander/process()
- if(ishuman(loc))
- var/mob/living/carbon/human/H = loc
- loc.layer = LARGE_MOB_LAYER //NO HIDING BEHIND PLANTS FOR YOU, DICKWEED (HA GET IT, BECAUSE WEEDS ARE PLANTS)
- H.bleedsuppress = TRUE //AND WE WON'T BLEED OUT LIKE COWARDS
- H.adjustStaminaLoss(-50) //CIT CHANGE - AND MAY HE NEVER SUCCUMB TO EXHAUSTION
- else
- if(!(flags_1 & ADMIN_SPAWNED_1))
- qdel(src)
-
-
-/obj/item/claymore/highlander/pickup(mob/living/user)
- to_chat(user, "The power of Scotland protects you! You are shielded from all stuns and knockdowns.")
- user.add_stun_absorption("highlander", INFINITY, 1, " is protected by the power of Scotland!", "The power of Scotland absorbs the stun!", " is protected by the power of Scotland!")
- user.ignore_slowdown(HIGHLANDER)
-
-/obj/item/claymore/highlander/dropped(mob/living/user)
- user.unignore_slowdown(HIGHLANDER)
- if(!QDELETED(src))
- qdel(src) //If this ever happens, it's because you lost an arm
-
-/obj/item/claymore/highlander/examine(mob/user)
- . = ..()
- . += "It has [!notches ? "nothing" : "[notches] notches"] scratched into the blade."
- if(nuke_disk)
- . += "It's holding the nuke disk!"
-
-/obj/item/claymore/highlander/attack(mob/living/target, mob/living/user)
- . = ..()
- if(!QDELETED(target) && iscarbon(target) && target.stat == DEAD && target.mind && target.mind.special_role == "highlander")
- user.fully_heal() //STEAL THE LIFE OF OUR FALLEN FOES
- add_notch(user)
- target.visible_message("[target] crumbles to dust beneath [user]'s blows!", "As you fall, your body crumbles to dust!")
- target.dust()
-
-/obj/item/claymore/highlander/attack_self(mob/living/user)
- var/closest_victim
- var/closest_distance = 255
- for(var/mob/living/carbon/human/H in GLOB.player_list - user)
- if(H.client && H.mind.special_role == "highlander" && (!closest_victim || get_dist(user, closest_victim) < closest_distance))
- closest_victim = H
- if(!closest_victim)
- to_chat(user, "[src] thrums for a moment and falls dark. Perhaps there's nobody nearby.")
- return
- to_chat(user, "[src] thrums and points to the [dir2text(get_dir(user, closest_victim))].")
-
-/obj/item/claymore/highlander/IsReflect()
- return 1 //YOU THINK YOUR PUNY LASERS CAN STOP ME?
-
-/obj/item/claymore/highlander/proc/add_notch(mob/living/user) //DYNAMIC CLAYMORE PROGRESSION SYSTEM - THIS IS THE FUTURE
- notches++
- force++
- var/new_name = name
- switch(notches)
- if(1)
- to_chat(user, "Your first kill - hopefully one of many. You scratch a notch into [src]'s blade.")
- to_chat(user, "You feel your fallen foe's soul entering your blade, restoring your wounds!")
- new_name = "notched claymore"
- if(2)
- to_chat(user, "Another falls before you. Another soul fuses with your own. Another notch in the blade.")
- new_name = "double-notched claymore"
- add_atom_colour(rgb(255, 235, 235), ADMIN_COLOUR_PRIORITY)
- if(3)
- to_chat(user, "You're beginning to relish the thrill of battle.")
- new_name = "triple-notched claymore"
- add_atom_colour(rgb(255, 215, 215), ADMIN_COLOUR_PRIORITY)
- if(4)
- to_chat(user, "You've lost count of how many you've killed.")
- new_name = "many-notched claymore"
- add_atom_colour(rgb(255, 195, 195), ADMIN_COLOUR_PRIORITY)
- if(5)
- to_chat(user, "Five voices now echo in your mind, cheering the slaughter.")
- new_name = "battle-tested claymore"
- add_atom_colour(rgb(255, 175, 175), ADMIN_COLOUR_PRIORITY)
- if(6)
- to_chat(user, "Is this what the vikings felt like? Visions of glory fill your head as you slay your sixth foe.")
- new_name = "battle-scarred claymore"
- add_atom_colour(rgb(255, 155, 155), ADMIN_COLOUR_PRIORITY)
- if(7)
- to_chat(user, "Kill. Butcher. Conquer.")
- new_name = "vicious claymore"
- add_atom_colour(rgb(255, 135, 135), ADMIN_COLOUR_PRIORITY)
- if(8)
- to_chat(user, "IT NEVER GETS OLD. THE SCREAMING. THE BLOOD AS IT SPRAYS ACROSS YOUR FACE.")
- new_name = "bloodthirsty claymore"
- add_atom_colour(rgb(255, 115, 115), ADMIN_COLOUR_PRIORITY)
- if(9)
- to_chat(user, "ANOTHER ONE FALLS TO YOUR BLOWS. ANOTHER WEAKLING UNFIT TO LIVE.")
- new_name = "gore-stained claymore"
- add_atom_colour(rgb(255, 95, 95), ADMIN_COLOUR_PRIORITY)
- if(10)
- user.visible_message("[user]'s eyes light up with a vengeful fire!", \
- "YOU FEEL THE POWER OF VALHALLA FLOWING THROUGH YOU! THERE CAN BE ONLY ONE!!!")
- user.update_icons()
- new_name = "GORE-DRENCHED CLAYMORE OF [pick("THE WHIMSICAL SLAUGHTER", "A THOUSAND SLAUGHTERED CATTLE", "GLORY AND VALHALLA", "ANNIHILATION", "OBLITERATION")]"
- icon_state = "claymore_valhalla"
- item_state = "cultblade"
- remove_atom_colour(ADMIN_COLOUR_PRIORITY)
-
- name = new_name
- playsound(user, 'sound/items/screwdriver2.ogg', 50, 1)
-
-/obj/item/katana
- name = "katana"
- desc = "Woefully underpowered in D20."
- icon_state = "katana"
- item_state = "katana"
- lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
- flags_1 = CONDUCT_1
- slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_BACK
- force = 40
- throwforce = 10
- w_class = WEIGHT_CLASS_HUGE
- hitsound = 'sound/weapons/bladeslice.ogg'
- attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
- block_chance = 50
- sharpness = IS_SHARP
- max_integrity = 200
- armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50)
- resistance_flags = FIRE_PROOF
- total_mass = TOTAL_MASS_MEDIEVAL_WEAPON
-
-/obj/item/katana/cursed
- slot_flags = null
-
-/obj/item/katana/Initialize()
- . = ..()
- ADD_TRAIT(src, TRAIT_NODROP, CURSED_ITEM_TRAIT)
-
-/obj/item/katana/suicide_act(mob/user)
- user.visible_message("[user] is slitting [user.p_their()] stomach open with [src]! It looks like [user.p_theyre()] trying to commit seppuku!")
- playsound(src, 'sound/weapons/bladeslice.ogg', 50, 1)
- return(BRUTELOSS)
-
-/obj/item/wirerod
- name = "wired rod"
- desc = "A rod with some wire wrapped around the top. It'd be easy to attach something to the top bit."
- icon_state = "wiredrod"
- item_state = "rods"
- flags_1 = CONDUCT_1
- force = 9
- throwforce = 10
- w_class = WEIGHT_CLASS_NORMAL
- materials = list(MAT_METAL=1150, MAT_GLASS=75)
- attack_verb = list("hit", "bludgeoned", "whacked", "bonked")
-
-/obj/item/wirerod/attackby(obj/item/I, mob/user, params)
- if(istype(I, /obj/item/shard))
- var/obj/item/twohanded/spear/S = new /obj/item/twohanded/spear
-
- remove_item_from_storage(user)
- qdel(I)
- qdel(src)
-
- user.put_in_hands(S)
- to_chat(user, "You fasten the glass shard to the top of the rod with the cable.")
-
- else if(istype(I, /obj/item/assembly/igniter) && !HAS_TRAIT(I, TRAIT_NODROP))
- var/obj/item/melee/baton/cattleprod/P = new /obj/item/melee/baton/cattleprod
-
- remove_item_from_storage(user)
-
- to_chat(user, "You fasten [I] to the top of the rod with the cable.")
-
- qdel(I)
- qdel(src)
-
- user.put_in_hands(P)
- else
- return ..()
-
-
-/obj/item/throwing_star
- name = "throwing star"
- desc = "An ancient weapon still used to this day, due to its ease of lodging itself into its victim's body parts."
- icon_state = "throwingstar"
- item_state = "eshield0"
- lefthand_file = 'icons/mob/inhands/equipment/shields_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/equipment/shields_righthand.dmi'
- force = 2
- throwforce = 20 //This is never used on mobs since this has a 100% embed chance.
- throw_speed = 4
- embedding = list("embedded_pain_multiplier" = 4, "embed_chance" = 100, "embedded_fall_chance" = 0)
- w_class = WEIGHT_CLASS_SMALL
- sharpness = IS_SHARP
- materials = list(MAT_METAL=500, MAT_GLASS=500)
- resistance_flags = FIRE_PROOF
-
-
-/obj/item/switchblade
- name = "switchblade"
- icon_state = "switchblade"
- lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
- desc = "A sharp, concealable, spring-loaded knife."
- flags_1 = CONDUCT_1
- force = 3
- w_class = WEIGHT_CLASS_SMALL
- throwforce = 5
- throw_speed = 3
- throw_range = 6
- materials = list(MAT_METAL=12000)
- hitsound = 'sound/weapons/genhit.ogg'
- attack_verb = list("stubbed", "poked")
- resistance_flags = FIRE_PROOF
- var/extended = 0
-
-/obj/item/switchblade/attack_self(mob/user)
- extended = !extended
- playsound(src.loc, 'sound/weapons/batonextend.ogg', 50, 1)
- if(extended)
- force = 20
- w_class = WEIGHT_CLASS_NORMAL
- throwforce = 23
- icon_state = "switchblade_ext"
- attack_verb = list("slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
- hitsound = 'sound/weapons/bladeslice.ogg'
- sharpness = IS_SHARP
- else
- force = 3
- w_class = WEIGHT_CLASS_SMALL
- throwforce = 5
- icon_state = "switchblade"
- attack_verb = list("stubbed", "poked")
- hitsound = 'sound/weapons/genhit.ogg'
- sharpness = IS_BLUNT
-
-/obj/item/switchblade/suicide_act(mob/user)
- user.visible_message("[user] is slitting [user.p_their()] own throat with [src]! It looks like [user.p_theyre()] trying to commit suicide!")
- return (BRUTELOSS)
-
-/obj/item/phone
- name = "red phone"
- desc = "Should anything ever go wrong..."
- icon = 'icons/obj/items_and_weapons.dmi'
- icon_state = "red_phone"
- force = 3
- throwforce = 2
- throw_speed = 3
- throw_range = 4
- w_class = WEIGHT_CLASS_SMALL
- attack_verb = list("called", "rang")
- hitsound = 'sound/weapons/ring.ogg'
-
-/obj/item/phone/suicide_act(mob/user)
- if(locate(/obj/structure/chair/stool) in user.loc)
- user.visible_message("[user] begins to tie a noose with [src]'s cord! It looks like [user.p_theyre()] trying to commit suicide!")
- else
- user.visible_message("[user] is strangling [user.p_them()]self with [src]'s cord! It looks like [user.p_theyre()] trying to commit suicide!")
- return(OXYLOSS)
-
-/obj/item/cane
- name = "cane"
- desc = "A cane used by a true gentleman. Or a clown."
- icon = 'icons/obj/items_and_weapons.dmi'
- icon_state = "cane"
- item_state = "stick"
- 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
- materials = list(MAT_METAL=50)
- attack_verb = list("bludgeoned", "whacked", "disciplined", "thrashed")
-
-/obj/item/staff
- name = "wizard staff"
- desc = "Apparently a staff used by the wizard."
- icon = 'icons/obj/wizard.dmi'
- icon_state = "staff"
- lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi'
- force = 3
- throwforce = 5
- throw_speed = 2
- throw_range = 5
- w_class = WEIGHT_CLASS_SMALL
- armour_penetration = 100
- attack_verb = list("bludgeoned", "whacked", "disciplined")
- resistance_flags = FLAMMABLE
-
-/obj/item/staff/broom
- name = "broom"
- desc = "Used for sweeping, and flying into the night while cackling. Black cat not included."
- icon = 'icons/obj/wizard.dmi'
- icon_state = "broom"
- resistance_flags = FLAMMABLE
-
-/obj/item/staff/stick
- name = "stick"
- desc = "A great tool to drag someone else's drinks across the bar."
- icon = 'icons/obj/items_and_weapons.dmi'
- icon_state = "cane"
- item_state = "stick"
- lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
- force = 3
- throwforce = 5
- throw_speed = 2
- throw_range = 5
- w_class = WEIGHT_CLASS_SMALL
-
-/obj/item/ectoplasm
- name = "ectoplasm"
- desc = "Spooky."
- gender = PLURAL
- icon = 'icons/obj/wizard.dmi'
- icon_state = "ectoplasm"
-
-/obj/item/ectoplasm/suicide_act(mob/user)
- user.visible_message("[user] is inhaling [src]! It looks like [user.p_theyre()] trying to visit the astral plane!")
- return (OXYLOSS)
-
-/obj/item/mounted_chainsaw
- name = "mounted chainsaw"
- desc = "A chainsaw that has replaced your arm."
- icon_state = "chainsaw_on"
- item_state = "mounted_chainsaw"
- lefthand_file = 'icons/mob/inhands/weapons/chainsaw_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/weapons/chainsaw_righthand.dmi'
- item_flags = ABSTRACT | DROPDEL
- w_class = WEIGHT_CLASS_HUGE
- force = 24
- throwforce = 0
- throw_range = 0
- throw_speed = 0
- sharpness = IS_SHARP
- attack_verb = list("sawed", "torn", "cut", "chopped", "diced")
- hitsound = 'sound/weapons/chainsawhit.ogg'
- total_mass = TOTAL_MASS_HAND_REPLACEMENT
-
-/obj/item/mounted_chainsaw/Initialize()
- . = ..()
- ADD_TRAIT(src, TRAIT_NODROP, HAND_REPLACEMENT_TRAIT)
-
-/obj/item/mounted_chainsaw/Destroy()
- var/obj/item/bodypart/part
- new /obj/item/twohanded/required/chainsaw(get_turf(src))
- if(iscarbon(loc))
- var/mob/living/carbon/holder = loc
- var/index = holder.get_held_index_of_item(src)
- if(index)
- part = holder.hand_bodyparts[index]
- . = ..()
- if(part)
- part.drop_limb()
-
-/obj/item/statuebust
- name = "bust"
- desc = "A priceless ancient marble bust, the kind that belongs in a museum." //or you can hit people with it
- icon = 'icons/obj/statue.dmi'
- icon_state = "bust"
- force = 15
- throwforce = 10
- throw_speed = 5
- throw_range = 2
- attack_verb = list("busted")
-
-/obj/item/tailclub
- name = "tail club"
- desc = "For the beating to death of lizards with their own tails."
- icon_state = "tailclub"
- force = 14
- throwforce = 1 // why are you throwing a club do you even weapon
- throw_speed = 1
- throw_range = 1
- attack_verb = list("clubbed", "bludgeoned")
-
-/obj/item/melee/chainofcommand/tailwhip
- name = "liz o' nine tails"
- desc = "A whip fashioned from the severed tails of lizards."
- icon_state = "tailwhip"
- item_flags = NONE
-
-/obj/item/melee/chainofcommand/tailwhip/kitty
- name = "cat o' nine tails"
- desc = "A whip fashioned from the severed tails of cats."
- icon_state = "catwhip"
-
-/obj/item/melee/skateboard
- name = "skateboard"
- desc = "A skateboard. It can be placed on its wheels and ridden, or used as a strong weapon."
- icon_state = "skateboard"
- item_state = "skateboard"
- force = 12
- throwforce = 4
- w_class = WEIGHT_CLASS_NORMAL
- attack_verb = list("smacked", "whacked", "slammed", "smashed")
-
-/obj/item/melee/skateboard/attack_self(mob/user)
- new /obj/vehicle/ridden/scooter/skateboard(get_turf(user))
- qdel(src)
-
-/obj/item/melee/baseball_bat
- name = "baseball bat"
- desc = "There ain't a skull in the league that can withstand a swatter."
- icon = 'icons/obj/items_and_weapons.dmi'
- icon_state = "baseball_bat"
- item_state = "baseball_bat"
- lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
- force = 10
- throwforce = 12
- attack_verb = list("beat", "smacked")
- w_class = WEIGHT_CLASS_HUGE
- var/homerun_ready = 0
- var/homerun_able = 0
- total_mass = 2.7 //a regular wooden major league baseball bat weighs somewhere between 2 to 3.4 pounds, according to google
-
-/obj/item/melee/baseball_bat/homerun
- name = "home run bat"
- desc = "This thing looks dangerous... Dangerously good at baseball, that is."
- homerun_able = 1
-
-/obj/item/melee/baseball_bat/attack_self(mob/user)
- if(!homerun_able)
- ..()
- return
- if(homerun_ready)
- to_chat(user, "You're already ready to do a home run!")
- ..()
- return
- to_chat(user, "You begin gathering strength...")
- playsound(get_turf(src), 'sound/magic/lightning_chargeup.ogg', 65, 1)
- if(do_after(user, 90, target = src))
- to_chat(user, "You gather power! Time for a home run!")
- homerun_ready = 1
- ..()
-
-/obj/item/melee/baseball_bat/attack(mob/living/target, mob/living/user)
- . = ..()
- var/atom/throw_target = get_edge_target_turf(target, user.dir)
- if(homerun_ready)
- user.visible_message("It's a home run!")
- target.throw_at(throw_target, rand(8,10), 14, user)
- target.ex_act(EXPLODE_HEAVY)
- playsound(get_turf(src), 'sound/weapons/homerun.ogg', 100, 1)
- homerun_ready = 0
- return
- else if(!target.anchored)
- target.throw_at(throw_target, rand(1,2), 7, user)
-
-/obj/item/melee/baseball_bat/ablative
- name = "metal baseball bat"
- desc = "This bat is made of highly reflective, highly armored material."
- icon_state = "baseball_bat_metal"
- item_state = "baseball_bat_metal"
- force = 12
- throwforce = 15
-
-/obj/item/melee/baseball_bat/ablative/IsReflect()//some day this will reflect thrown items instead of lasers
- var/picksound = rand(1,2)
- var/turf = get_turf(src)
- if(picksound == 1)
- playsound(turf, 'sound/weapons/effects/batreflect1.ogg', 50, 1)
- if(picksound == 2)
- playsound(turf, 'sound/weapons/effects/batreflect2.ogg', 50, 1)
- return 1
-
-/obj/item/melee/baseball_bat/ablative/syndi
- name = "syndicate major league bat"
- desc = "A metal bat made by the syndicate for the major league team."
- force = 18 //Spear damage...
- throwforce = 30
-
-/obj/item/melee/flyswatter
- name = "flyswatter"
- desc = "Useful for killing insects of all sizes."
- icon = 'icons/obj/items_and_weapons.dmi'
- icon_state = "flyswatter"
- item_state = "flyswatter"
- lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
- force = 1
- throwforce = 1
- attack_verb = list("swatted", "smacked")
- hitsound = 'sound/effects/snap.ogg'
- w_class = WEIGHT_CLASS_SMALL
- //Things in this list will be instantly splatted. Flyman weakness is handled in the flyman species weakness proc.
- var/list/strong_against
-
-/obj/item/melee/flyswatter/Initialize()
- . = ..()
- strong_against = typecacheof(list(
- /mob/living/simple_animal/hostile/poison/bees/,
- /mob/living/simple_animal/butterfly,
- /mob/living/simple_animal/cockroach,
- /obj/item/queen_bee
- ))
-
-
-/obj/item/melee/flyswatter/afterattack(atom/target, mob/user, proximity_flag)
- . = ..()
- if(proximity_flag)
- if(is_type_in_typecache(target, strong_against))
- new /obj/effect/decal/cleanable/insectguts(target.drop_location())
- to_chat(user, "You easily splat the [target].")
- if(istype(target, /mob/living/))
- var/mob/living/bug = target
- bug.death(1)
- else
- qdel(target)
-
-/obj/item/circlegame
- name = "circled hand"
- desc = "If somebody looks at this while it's below your waist, you get to bop them."
- icon_state = "madeyoulook"
- force = 0
- throwforce = 0
- item_flags = DROPDEL | ABSTRACT
- attack_verb = list("bopped")
-
-/obj/item/slapper
- name = "slapper"
- desc = "This is how real men fight."
- icon_state = "latexballon"
- item_state = "nothing"
- force = 0
- throwforce = 0
- item_flags = DROPDEL | ABSTRACT
- attack_verb = list("slapped")
- hitsound = 'sound/effects/snap.ogg'
-
-/obj/item/slapper/attack(mob/M, mob/living/carbon/human/user)
- if(ishuman(M))
- var/mob/living/carbon/human/L = M
- if(L && L.dna && L.dna.species)
- L.dna.species.stop_wagging_tail(M)
- if(user.a_intent != INTENT_HARM && ((user.zone_selected == BODY_ZONE_PRECISE_MOUTH) || (user.zone_selected == BODY_ZONE_PRECISE_EYES) || (user.zone_selected == BODY_ZONE_HEAD)))
- user.do_attack_animation(M)
- playsound(M, 'sound/weapons/slap.ogg', 50, 1, -1)
- user.visible_message("[user] slaps [M]!",
- "You slap [M]!",\
- "You hear a slap.")
- return
- else
- ..()
-
-/obj/item/proc/can_trigger_gun(mob/living/user)
- if(!user.can_use_guns(src))
- return FALSE
- return TRUE
-
-/obj/item/extendohand
- name = "extendo-hand"
- desc = "Futuristic tech has allowed these classic spring-boxing toys to essentially act as a fully functional hand-operated hand prosthetic."
- icon = 'icons/obj/items_and_weapons.dmi'
- icon_state = "extendohand"
- item_state = "extendohand"
- lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
- force = 0
- throwforce = 5
- reach = 2
-
-/obj/item/extendohand/acme
- name = "\improper ACME Extendo-Hand"
- desc = "A novelty extendo-hand produced by the ACME corporation. Originally designed to knock out roadrunners."
-
-/obj/item/extendohand/attack(atom/M, mob/living/carbon/human/user)
- var/dist = get_dist(M, user)
- if(dist < reach)
- to_chat(user, "[M] is too close to use [src] on.")
- return
- M.attack_hand(user)
-
-/obj/item/bdsm_whip
- name = "bdsm whip"
- desc = "A less lethal version of the whip the librarian has. Still hurts, but just the way you like it."
- icon_state = "whip"
- item_state = "crop"
- lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
- slot_flags = ITEM_SLOT_BELT
- damtype = AROUSAL
- throwforce = 0
- force = 5
- w_class = WEIGHT_CLASS_NORMAL
- attack_verb = list("flogged", "whipped", "lashed", "disciplined")
- hitsound = 'sound/weapons/whip.ogg'
-
-/obj/item/bdsm_whip/ridingcrop
- name = "riding crop"
- desc = "For teaching a lesson in a more compact fashion."
- icon_state = "ridingcrop"
- force = 10
-
-/obj/item/bdsm_whip/suicide_act(mob/user)
- user.visible_message("[user] is getting just a little too kinky!")
- return (OXYLOSS)
-
-/obj/item/bdsm_whip/attack(mob/M, mob/user)
- if(user.zone_selected == BODY_ZONE_PRECISE_GROIN)
- playsound(loc, 'sound/weapons/whip.ogg', 30)
- M.visible_message("[user] has [pick(attack_verb)] [M] on the ass!")
- else
- return ..(M, user)
+/obj/item/banhammer
+ desc = "A banhammer."
+ name = "banhammer"
+ icon = 'icons/obj/items_and_weapons.dmi'
+ icon_state = "toyhammer"
+ slot_flags = ITEM_SLOT_BELT
+ throwforce = 0
+ force = 1
+ w_class = WEIGHT_CLASS_TINY
+ throw_speed = 3
+ throw_range = 7
+ attack_verb = list("banned")
+ max_integrity = 200
+ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 70)
+ resistance_flags = FIRE_PROOF
+
+/obj/item/banhammer/suicide_act(mob/user)
+ user.visible_message("[user] is hitting [user.p_them()]self with [src]! It looks like [user.p_theyre()] trying to ban [user.p_them()]self from life.")
+ return (BRUTELOSS|FIRELOSS|TOXLOSS|OXYLOSS)
+/*
+oranges says: This is a meme relating to the english translation of the ss13 russian wiki page on lurkmore.
+mrdoombringer sez: and remember kids, if you try and PR a fix for this item's grammar, you are admitting that you are, indeed, a newfriend.
+for further reading, please see: https://github.com/tgstation/tgstation/pull/30173 and https://translate.google.com/translate?sl=auto&tl=en&js=y&prev=_t&hl=en&ie=UTF-8&u=%2F%2Flurkmore.to%2FSS13&edit-text=&act=url
+*/
+/obj/item/banhammer/attack(mob/M, mob/user)
+ if(user.zone_selected == BODY_ZONE_HEAD)
+ M.visible_message("[user] are stroking the head of [M] with a bangammer", "[user] are stroking the head with a bangammer", "you hear a bangammer stroking a head");
+ else
+ M.visible_message("[M] has been banned FOR NO REISIN by [user]", "You have been banned FOR NO REISIN by [user]", "you hear a banhammer banning someone")
+ playsound(loc, 'sound/effects/adminhelp.ogg', 15) //keep it at 15% volume so people don't jump out of their skin too much
+ if(user.a_intent != INTENT_HELP)
+ return ..(M, user)
+
+/obj/item/sord
+ name = "\improper SORD"
+ desc = "This thing is so unspeakably shitty you are having a hard time even holding it."
+ icon_state = "sord"
+ item_state = "sord"
+ lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
+ slot_flags = ITEM_SLOT_BELT
+ force = 2
+ throwforce = 1
+ w_class = WEIGHT_CLASS_NORMAL
+ hitsound = 'sound/weapons/bladeslice.ogg'
+ attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
+
+/obj/item/sord/suicide_act(mob/user)
+ user.visible_message("[user] is trying to impale [user.p_them()]self with [src]! It might be a suicide attempt if it weren't so shitty.", \
+ "You try to impale yourself with [src], but it's USELESS...")
+ return SHAME
+
+/obj/item/claymore
+ name = "claymore"
+ desc = "What are you standing around staring at this for? Get to killing!"
+ icon_state = "claymore"
+ item_state = "claymore"
+ lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
+ hitsound = 'sound/weapons/bladeslice.ogg'
+ flags_1 = CONDUCT_1
+ slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_BACK
+ force = 40
+ throwforce = 10
+ w_class = WEIGHT_CLASS_NORMAL
+ attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
+ block_chance = 50
+ sharpness = IS_SHARP
+ max_integrity = 200
+ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50)
+ resistance_flags = FIRE_PROOF
+ total_mass = TOTAL_MASS_MEDIEVAL_WEAPON
+
+/obj/item/claymore/Initialize()
+ . = ..()
+ AddComponent(/datum/component/butchering, 40, 105)
+
+/obj/item/claymore/suicide_act(mob/user)
+ user.visible_message("[user] is falling on [src]! It looks like [user.p_theyre()] trying to commit suicide!")
+ return(BRUTELOSS)
+
+/obj/item/claymore/highlander //ALL COMMENTS MADE REGARDING THIS SWORD MUST BE MADE IN ALL CAPS
+ desc = "THERE CAN BE ONLY ONE, AND IT WILL BE YOU!!!\nActivate it in your hand to point to the nearest victim."
+ flags_1 = CONDUCT_1
+ item_flags = DROPDEL
+ slot_flags = null
+ block_chance = 0 //RNG WON'T HELP YOU NOW, PANSY
+ light_range = 3
+ attack_verb = list("brutalized", "eviscerated", "disemboweled", "hacked", "carved", "cleaved") //ONLY THE MOST VISCERAL ATTACK VERBS
+ var/notches = 0 //HOW MANY PEOPLE HAVE BEEN SLAIN WITH THIS BLADE
+ var/obj/item/disk/nuclear/nuke_disk //OUR STORED NUKE DISK
+
+/obj/item/claymore/highlander/Initialize()
+ . = ..()
+ ADD_TRAIT(src, TRAIT_NODROP, HIGHLANDER)
+ START_PROCESSING(SSobj, src)
+
+/obj/item/claymore/highlander/Destroy()
+ if(nuke_disk)
+ nuke_disk.forceMove(get_turf(src))
+ nuke_disk.visible_message("The nuke disk is vulnerable!")
+ nuke_disk = null
+ STOP_PROCESSING(SSobj, src)
+ return ..()
+
+/obj/item/claymore/highlander/process()
+ if(ishuman(loc))
+ var/mob/living/carbon/human/H = loc
+ loc.layer = LARGE_MOB_LAYER //NO HIDING BEHIND PLANTS FOR YOU, DICKWEED (HA GET IT, BECAUSE WEEDS ARE PLANTS)
+ H.bleedsuppress = TRUE //AND WE WON'T BLEED OUT LIKE COWARDS
+ H.adjustStaminaLoss(-50) //CIT CHANGE - AND MAY HE NEVER SUCCUMB TO EXHAUSTION
+ else
+ if(!(flags_1 & ADMIN_SPAWNED_1))
+ qdel(src)
+
+
+/obj/item/claymore/highlander/pickup(mob/living/user)
+ to_chat(user, "The power of Scotland protects you! You are shielded from all stuns and knockdowns.")
+ user.add_stun_absorption("highlander", INFINITY, 1, " is protected by the power of Scotland!", "The power of Scotland absorbs the stun!", " is protected by the power of Scotland!")
+ user.ignore_slowdown(HIGHLANDER)
+
+/obj/item/claymore/highlander/dropped(mob/living/user)
+ user.unignore_slowdown(HIGHLANDER)
+ if(!QDELETED(src))
+ qdel(src) //If this ever happens, it's because you lost an arm
+
+/obj/item/claymore/highlander/examine(mob/user)
+ . = ..()
+ . += "It has [!notches ? "nothing" : "[notches] notches"] scratched into the blade."
+ if(nuke_disk)
+ . += "It's holding the nuke disk!"
+
+/obj/item/claymore/highlander/attack(mob/living/target, mob/living/user)
+ . = ..()
+ if(!QDELETED(target) && iscarbon(target) && target.stat == DEAD && target.mind && target.mind.special_role == "highlander")
+ user.fully_heal() //STEAL THE LIFE OF OUR FALLEN FOES
+ add_notch(user)
+ target.visible_message("[target] crumbles to dust beneath [user]'s blows!", "As you fall, your body crumbles to dust!")
+ target.dust()
+
+/obj/item/claymore/highlander/attack_self(mob/living/user)
+ var/closest_victim
+ var/closest_distance = 255
+ for(var/mob/living/carbon/human/H in GLOB.player_list - user)
+ if(H.client && H.mind.special_role == "highlander" && (!closest_victim || get_dist(user, closest_victim) < closest_distance))
+ closest_victim = H
+ if(!closest_victim)
+ to_chat(user, "[src] thrums for a moment and falls dark. Perhaps there's nobody nearby.")
+ return
+ to_chat(user, "[src] thrums and points to the [dir2text(get_dir(user, closest_victim))].")
+
+/obj/item/claymore/highlander/IsReflect()
+ return 1 //YOU THINK YOUR PUNY LASERS CAN STOP ME?
+
+/obj/item/claymore/highlander/proc/add_notch(mob/living/user) //DYNAMIC CLAYMORE PROGRESSION SYSTEM - THIS IS THE FUTURE
+ notches++
+ force++
+ var/new_name = name
+ switch(notches)
+ if(1)
+ to_chat(user, "Your first kill - hopefully one of many. You scratch a notch into [src]'s blade.")
+ to_chat(user, "You feel your fallen foe's soul entering your blade, restoring your wounds!")
+ new_name = "notched claymore"
+ if(2)
+ to_chat(user, "Another falls before you. Another soul fuses with your own. Another notch in the blade.")
+ new_name = "double-notched claymore"
+ add_atom_colour(rgb(255, 235, 235), ADMIN_COLOUR_PRIORITY)
+ if(3)
+ to_chat(user, "You're beginning to relish the thrill of battle.")
+ new_name = "triple-notched claymore"
+ add_atom_colour(rgb(255, 215, 215), ADMIN_COLOUR_PRIORITY)
+ if(4)
+ to_chat(user, "You've lost count of how many you've killed.")
+ new_name = "many-notched claymore"
+ add_atom_colour(rgb(255, 195, 195), ADMIN_COLOUR_PRIORITY)
+ if(5)
+ to_chat(user, "Five voices now echo in your mind, cheering the slaughter.")
+ new_name = "battle-tested claymore"
+ add_atom_colour(rgb(255, 175, 175), ADMIN_COLOUR_PRIORITY)
+ if(6)
+ to_chat(user, "Is this what the vikings felt like? Visions of glory fill your head as you slay your sixth foe.")
+ new_name = "battle-scarred claymore"
+ add_atom_colour(rgb(255, 155, 155), ADMIN_COLOUR_PRIORITY)
+ if(7)
+ to_chat(user, "Kill. Butcher. Conquer.")
+ new_name = "vicious claymore"
+ add_atom_colour(rgb(255, 135, 135), ADMIN_COLOUR_PRIORITY)
+ if(8)
+ to_chat(user, "IT NEVER GETS OLD. THE SCREAMING. THE BLOOD AS IT SPRAYS ACROSS YOUR FACE.")
+ new_name = "bloodthirsty claymore"
+ add_atom_colour(rgb(255, 115, 115), ADMIN_COLOUR_PRIORITY)
+ if(9)
+ to_chat(user, "ANOTHER ONE FALLS TO YOUR BLOWS. ANOTHER WEAKLING UNFIT TO LIVE.")
+ new_name = "gore-stained claymore"
+ add_atom_colour(rgb(255, 95, 95), ADMIN_COLOUR_PRIORITY)
+ if(10)
+ user.visible_message("[user]'s eyes light up with a vengeful fire!", \
+ "YOU FEEL THE POWER OF VALHALLA FLOWING THROUGH YOU! THERE CAN BE ONLY ONE!!!")
+ user.update_icons()
+ new_name = "GORE-DRENCHED CLAYMORE OF [pick("THE WHIMSICAL SLAUGHTER", "A THOUSAND SLAUGHTERED CATTLE", "GLORY AND VALHALLA", "ANNIHILATION", "OBLITERATION")]"
+ icon_state = "claymore_valhalla"
+ item_state = "cultblade"
+ remove_atom_colour(ADMIN_COLOUR_PRIORITY)
+
+ name = new_name
+ playsound(user, 'sound/items/screwdriver2.ogg', 50, 1)
+
+/obj/item/katana
+ name = "katana"
+ desc = "Woefully underpowered in D20."
+ icon_state = "katana"
+ item_state = "katana"
+ lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
+ flags_1 = CONDUCT_1
+ slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_BACK
+ force = 40
+ throwforce = 10
+ w_class = WEIGHT_CLASS_HUGE
+ hitsound = 'sound/weapons/bladeslice.ogg'
+ attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
+ block_chance = 50
+ sharpness = IS_SHARP
+ max_integrity = 200
+ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50)
+ resistance_flags = FIRE_PROOF
+ total_mass = TOTAL_MASS_MEDIEVAL_WEAPON
+
+/obj/item/katana/cursed
+ slot_flags = null
+
+/obj/item/katana/Initialize()
+ . = ..()
+ ADD_TRAIT(src, TRAIT_NODROP, CURSED_ITEM_TRAIT)
+
+/obj/item/katana/suicide_act(mob/user)
+ user.visible_message("[user] is slitting [user.p_their()] stomach open with [src]! It looks like [user.p_theyre()] trying to commit seppuku!")
+ playsound(src, 'sound/weapons/bladeslice.ogg', 50, 1)
+ return(BRUTELOSS)
+
+/obj/item/wirerod
+ name = "wired rod"
+ desc = "A rod with some wire wrapped around the top. It'd be easy to attach something to the top bit."
+ icon_state = "wiredrod"
+ item_state = "rods"
+ flags_1 = CONDUCT_1
+ force = 9
+ throwforce = 10
+ w_class = WEIGHT_CLASS_NORMAL
+ materials = list(MAT_METAL=1150, MAT_GLASS=75)
+ attack_verb = list("hit", "bludgeoned", "whacked", "bonked")
+
+/obj/item/wirerod/attackby(obj/item/I, mob/user, params)
+ if(istype(I, /obj/item/shard))
+ var/obj/item/twohanded/spear/S = new /obj/item/twohanded/spear
+
+ remove_item_from_storage(user)
+ qdel(I)
+ qdel(src)
+
+ user.put_in_hands(S)
+ to_chat(user, "You fasten the glass shard to the top of the rod with the cable.")
+
+ else if(istype(I, /obj/item/assembly/igniter) && !HAS_TRAIT(I, TRAIT_NODROP))
+ var/obj/item/melee/baton/cattleprod/P = new /obj/item/melee/baton/cattleprod
+
+ remove_item_from_storage(user)
+
+ to_chat(user, "You fasten [I] to the top of the rod with the cable.")
+
+ qdel(I)
+ qdel(src)
+
+ user.put_in_hands(P)
+ else
+ return ..()
+
+
+/obj/item/throwing_star
+ name = "throwing star"
+ desc = "An ancient weapon still used to this day, due to its ease of lodging itself into its victim's body parts."
+ icon_state = "throwingstar"
+ item_state = "eshield0"
+ lefthand_file = 'icons/mob/inhands/equipment/shields_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/equipment/shields_righthand.dmi'
+ force = 2
+ throwforce = 20 //This is never used on mobs since this has a 100% embed chance.
+ throw_speed = 4
+ embedding = list("embedded_pain_multiplier" = 4, "embed_chance" = 100, "embedded_fall_chance" = 0)
+ w_class = WEIGHT_CLASS_SMALL
+ sharpness = IS_SHARP
+ materials = list(MAT_METAL=500, MAT_GLASS=500)
+ resistance_flags = FIRE_PROOF
+
+
+/obj/item/switchblade
+ name = "switchblade"
+ icon_state = "switchblade"
+ lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
+ desc = "A sharp, concealable, spring-loaded knife."
+ flags_1 = CONDUCT_1
+ force = 3
+ w_class = WEIGHT_CLASS_SMALL
+ throwforce = 5
+ throw_speed = 3
+ throw_range = 6
+ materials = list(MAT_METAL=12000)
+ hitsound = 'sound/weapons/genhit.ogg'
+ attack_verb = list("stubbed", "poked")
+ resistance_flags = FIRE_PROOF
+ var/extended = 0
+
+/obj/item/switchblade/attack_self(mob/user)
+ extended = !extended
+ playsound(src.loc, 'sound/weapons/batonextend.ogg', 50, 1)
+ if(extended)
+ force = 20
+ w_class = WEIGHT_CLASS_NORMAL
+ throwforce = 23
+ icon_state = "switchblade_ext"
+ attack_verb = list("slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
+ hitsound = 'sound/weapons/bladeslice.ogg'
+ sharpness = IS_SHARP
+ else
+ force = 3
+ w_class = WEIGHT_CLASS_SMALL
+ throwforce = 5
+ icon_state = "switchblade"
+ attack_verb = list("stubbed", "poked")
+ hitsound = 'sound/weapons/genhit.ogg'
+ sharpness = IS_BLUNT
+
+/obj/item/switchblade/suicide_act(mob/user)
+ user.visible_message("[user] is slitting [user.p_their()] own throat with [src]! It looks like [user.p_theyre()] trying to commit suicide!")
+ return (BRUTELOSS)
+
+/obj/item/phone
+ name = "red phone"
+ desc = "Should anything ever go wrong..."
+ icon = 'icons/obj/items_and_weapons.dmi'
+ icon_state = "red_phone"
+ force = 3
+ throwforce = 2
+ throw_speed = 3
+ throw_range = 4
+ w_class = WEIGHT_CLASS_SMALL
+ attack_verb = list("called", "rang")
+ hitsound = 'sound/weapons/ring.ogg'
+
+/obj/item/phone/suicide_act(mob/user)
+ if(locate(/obj/structure/chair/stool) in user.loc)
+ user.visible_message("[user] begins to tie a noose with [src]'s cord! It looks like [user.p_theyre()] trying to commit suicide!")
+ else
+ user.visible_message("[user] is strangling [user.p_them()]self with [src]'s cord! It looks like [user.p_theyre()] trying to commit suicide!")
+ return(OXYLOSS)
+
+/obj/item/cane
+ name = "cane"
+ desc = "A cane used by a true gentleman. Or a clown."
+ icon = 'icons/obj/items_and_weapons.dmi'
+ icon_state = "cane"
+ item_state = "stick"
+ 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
+ materials = list(MAT_METAL=50)
+ attack_verb = list("bludgeoned", "whacked", "disciplined", "thrashed")
+
+/obj/item/staff
+ name = "wizard staff"
+ desc = "Apparently a staff used by the wizard."
+ icon = 'icons/obj/wizard.dmi'
+ icon_state = "staff"
+ lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi'
+ force = 3
+ throwforce = 5
+ throw_speed = 2
+ throw_range = 5
+ w_class = WEIGHT_CLASS_SMALL
+ armour_penetration = 100
+ attack_verb = list("bludgeoned", "whacked", "disciplined")
+ resistance_flags = FLAMMABLE
+
+/obj/item/staff/broom
+ name = "broom"
+ desc = "Used for sweeping, and flying into the night while cackling. Black cat not included."
+ icon = 'icons/obj/wizard.dmi'
+ icon_state = "broom"
+ resistance_flags = FLAMMABLE
+
+/obj/item/staff/stick
+ name = "stick"
+ desc = "A great tool to drag someone else's drinks across the bar."
+ icon = 'icons/obj/items_and_weapons.dmi'
+ icon_state = "cane"
+ item_state = "stick"
+ lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
+ force = 3
+ throwforce = 5
+ throw_speed = 2
+ throw_range = 5
+ w_class = WEIGHT_CLASS_SMALL
+
+/obj/item/ectoplasm
+ name = "ectoplasm"
+ desc = "Spooky."
+ gender = PLURAL
+ icon = 'icons/obj/wizard.dmi'
+ icon_state = "ectoplasm"
+
+/obj/item/ectoplasm/suicide_act(mob/user)
+ user.visible_message("[user] is inhaling [src]! It looks like [user.p_theyre()] trying to visit the astral plane!")
+ return (OXYLOSS)
+
+/obj/item/mounted_chainsaw
+ name = "mounted chainsaw"
+ desc = "A chainsaw that has replaced your arm."
+ icon_state = "chainsaw_on"
+ item_state = "mounted_chainsaw"
+ lefthand_file = 'icons/mob/inhands/weapons/chainsaw_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/weapons/chainsaw_righthand.dmi'
+ item_flags = ABSTRACT | DROPDEL
+ w_class = WEIGHT_CLASS_HUGE
+ force = 24
+ throwforce = 0
+ throw_range = 0
+ throw_speed = 0
+ sharpness = IS_SHARP
+ attack_verb = list("sawed", "torn", "cut", "chopped", "diced")
+ hitsound = 'sound/weapons/chainsawhit.ogg'
+ total_mass = TOTAL_MASS_HAND_REPLACEMENT
+ tool_behaviour = TOOL_SAW
+ toolspeed = 1
+
+/obj/item/mounted_chainsaw/Initialize()
+ . = ..()
+ ADD_TRAIT(src, TRAIT_NODROP, HAND_REPLACEMENT_TRAIT)
+
+/obj/item/mounted_chainsaw/Destroy()
+ var/obj/item/bodypart/part
+ new /obj/item/twohanded/required/chainsaw(get_turf(src))
+ if(iscarbon(loc))
+ var/mob/living/carbon/holder = loc
+ var/index = holder.get_held_index_of_item(src)
+ if(index)
+ part = holder.hand_bodyparts[index]
+ . = ..()
+ if(part)
+ part.drop_limb()
+
+/obj/item/statuebust
+ name = "bust"
+ desc = "A priceless ancient marble bust, the kind that belongs in a museum." //or you can hit people with it
+ icon = 'icons/obj/statue.dmi'
+ icon_state = "bust"
+ force = 15
+ throwforce = 10
+ throw_speed = 5
+ throw_range = 2
+ attack_verb = list("busted")
+
+/obj/item/tailclub
+ name = "tail club"
+ desc = "For the beating to death of lizards with their own tails."
+ icon_state = "tailclub"
+ force = 14
+ throwforce = 1 // why are you throwing a club do you even weapon
+ throw_speed = 1
+ throw_range = 1
+ attack_verb = list("clubbed", "bludgeoned")
+
+/obj/item/melee/chainofcommand/tailwhip
+ name = "liz o' nine tails"
+ desc = "A whip fashioned from the severed tails of lizards."
+ icon_state = "tailwhip"
+ item_flags = NONE
+
+/obj/item/melee/chainofcommand/tailwhip/kitty
+ name = "cat o' nine tails"
+ desc = "A whip fashioned from the severed tails of cats."
+ icon_state = "catwhip"
+
+/obj/item/melee/skateboard
+ name = "skateboard"
+ desc = "A skateboard. It can be placed on its wheels and ridden, or used as a strong weapon."
+ icon_state = "skateboard"
+ item_state = "skateboard"
+ force = 12
+ throwforce = 4
+ w_class = WEIGHT_CLASS_NORMAL
+ attack_verb = list("smacked", "whacked", "slammed", "smashed")
+
+/obj/item/melee/skateboard/attack_self(mob/user)
+ new /obj/vehicle/ridden/scooter/skateboard(get_turf(user))
+ qdel(src)
+
+/obj/item/melee/baseball_bat
+ name = "baseball bat"
+ desc = "There ain't a skull in the league that can withstand a swatter."
+ icon = 'icons/obj/items_and_weapons.dmi'
+ icon_state = "baseball_bat"
+ item_state = "baseball_bat"
+ lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
+ force = 10
+ throwforce = 12
+ attack_verb = list("beat", "smacked")
+ w_class = WEIGHT_CLASS_HUGE
+ var/homerun_ready = 0
+ var/homerun_able = 0
+ total_mass = 2.7 //a regular wooden major league baseball bat weighs somewhere between 2 to 3.4 pounds, according to google
+
+/obj/item/melee/baseball_bat/homerun
+ name = "home run bat"
+ desc = "This thing looks dangerous... Dangerously good at baseball, that is."
+ homerun_able = 1
+
+/obj/item/melee/baseball_bat/attack_self(mob/user)
+ if(!homerun_able)
+ ..()
+ return
+ if(homerun_ready)
+ to_chat(user, "You're already ready to do a home run!")
+ ..()
+ return
+ to_chat(user, "You begin gathering strength...")
+ playsound(get_turf(src), 'sound/magic/lightning_chargeup.ogg', 65, 1)
+ if(do_after(user, 90, target = src))
+ to_chat(user, "You gather power! Time for a home run!")
+ homerun_ready = 1
+ ..()
+
+/obj/item/melee/baseball_bat/attack(mob/living/target, mob/living/user)
+ . = ..()
+ var/atom/throw_target = get_edge_target_turf(target, user.dir)
+ if(homerun_ready)
+ user.visible_message("It's a home run!")
+ target.throw_at(throw_target, rand(8,10), 14, user)
+ target.ex_act(EXPLODE_HEAVY)
+ playsound(get_turf(src), 'sound/weapons/homerun.ogg', 100, 1)
+ homerun_ready = 0
+ return
+ else if(!target.anchored)
+ target.throw_at(throw_target, rand(1,2), 7, user)
+
+/obj/item/melee/baseball_bat/ablative
+ name = "metal baseball bat"
+ desc = "This bat is made of highly reflective, highly armored material."
+ icon_state = "baseball_bat_metal"
+ item_state = "baseball_bat_metal"
+ force = 12
+ throwforce = 15
+
+/obj/item/melee/baseball_bat/ablative/IsReflect()//some day this will reflect thrown items instead of lasers
+ var/picksound = rand(1,2)
+ var/turf = get_turf(src)
+ if(picksound == 1)
+ playsound(turf, 'sound/weapons/effects/batreflect1.ogg', 50, 1)
+ if(picksound == 2)
+ playsound(turf, 'sound/weapons/effects/batreflect2.ogg', 50, 1)
+ return 1
+
+/obj/item/melee/baseball_bat/ablative/syndi
+ name = "syndicate major league bat"
+ desc = "A metal bat made by the syndicate for the major league team."
+ force = 18 //Spear damage...
+ throwforce = 30
+
+/obj/item/melee/flyswatter
+ name = "flyswatter"
+ desc = "Useful for killing insects of all sizes."
+ icon = 'icons/obj/items_and_weapons.dmi'
+ icon_state = "flyswatter"
+ item_state = "flyswatter"
+ lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
+ force = 1
+ throwforce = 1
+ attack_verb = list("swatted", "smacked")
+ hitsound = 'sound/effects/snap.ogg'
+ w_class = WEIGHT_CLASS_SMALL
+ //Things in this list will be instantly splatted. Flyman weakness is handled in the flyman species weakness proc.
+ var/list/strong_against
+
+/obj/item/melee/flyswatter/Initialize()
+ . = ..()
+ strong_against = typecacheof(list(
+ /mob/living/simple_animal/hostile/poison/bees/,
+ /mob/living/simple_animal/butterfly,
+ /mob/living/simple_animal/cockroach,
+ /obj/item/queen_bee
+ ))
+
+
+/obj/item/melee/flyswatter/afterattack(atom/target, mob/user, proximity_flag)
+ . = ..()
+ if(proximity_flag)
+ if(is_type_in_typecache(target, strong_against))
+ new /obj/effect/decal/cleanable/insectguts(target.drop_location())
+ to_chat(user, "You easily splat the [target].")
+ if(istype(target, /mob/living/))
+ var/mob/living/bug = target
+ bug.death(1)
+ else
+ qdel(target)
+
+/obj/item/circlegame
+ name = "circled hand"
+ desc = "If somebody looks at this while it's below your waist, you get to bop them."
+ icon_state = "madeyoulook"
+ force = 0
+ throwforce = 0
+ item_flags = DROPDEL | ABSTRACT
+ attack_verb = list("bopped")
+
+/obj/item/slapper
+ name = "slapper"
+ desc = "This is how real men fight."
+ icon_state = "latexballon"
+ item_state = "nothing"
+ force = 0
+ throwforce = 0
+ item_flags = DROPDEL | ABSTRACT
+ attack_verb = list("slapped")
+ hitsound = 'sound/effects/snap.ogg'
+
+/obj/item/slapper/attack(mob/M, mob/living/carbon/human/user)
+ if(ishuman(M))
+ var/mob/living/carbon/human/L = M
+ if(L && L.dna && L.dna.species)
+ L.dna.species.stop_wagging_tail(M)
+ if(user.a_intent != INTENT_HARM && ((user.zone_selected == BODY_ZONE_PRECISE_MOUTH) || (user.zone_selected == BODY_ZONE_PRECISE_EYES) || (user.zone_selected == BODY_ZONE_HEAD)))
+ user.do_attack_animation(M)
+ playsound(M, 'sound/weapons/slap.ogg', 50, 1, -1)
+ user.visible_message("[user] slaps [M]!",
+ "You slap [M]!",\
+ "You hear a slap.")
+ return
+ else
+ ..()
+
+/obj/item/proc/can_trigger_gun(mob/living/user)
+ if(!user.can_use_guns(src))
+ return FALSE
+ return TRUE
+
+/obj/item/extendohand
+ name = "extendo-hand"
+ desc = "Futuristic tech has allowed these classic spring-boxing toys to essentially act as a fully functional hand-operated hand prosthetic."
+ icon = 'icons/obj/items_and_weapons.dmi'
+ icon_state = "extendohand"
+ item_state = "extendohand"
+ lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
+ force = 0
+ throwforce = 5
+ reach = 2
+
+/obj/item/extendohand/acme
+ name = "\improper ACME Extendo-Hand"
+ desc = "A novelty extendo-hand produced by the ACME corporation. Originally designed to knock out roadrunners."
+
+/obj/item/extendohand/attack(atom/M, mob/living/carbon/human/user)
+ var/dist = get_dist(M, user)
+ if(dist < reach)
+ to_chat(user, "[M] is too close to use [src] on.")
+ return
+ M.attack_hand(user)
+
+/obj/item/bdsm_whip
+ name = "bdsm whip"
+ desc = "A less lethal version of the whip the librarian has. Still hurts, but just the way you like it."
+ icon_state = "whip"
+ item_state = "crop"
+ lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
+ slot_flags = ITEM_SLOT_BELT
+ damtype = AROUSAL
+ throwforce = 0
+ force = 5
+ w_class = WEIGHT_CLASS_NORMAL
+ attack_verb = list("flogged", "whipped", "lashed", "disciplined")
+ hitsound = 'sound/weapons/whip.ogg'
+
+/obj/item/bdsm_whip/ridingcrop
+ name = "riding crop"
+ desc = "For teaching a lesson in a more compact fashion."
+ icon_state = "ridingcrop"
+ force = 10
+
+/obj/item/bdsm_whip/suicide_act(mob/user)
+ user.visible_message("[user] is getting just a little too kinky!")
+ return (OXYLOSS)
+
+/obj/item/bdsm_whip/attack(mob/M, mob/user)
+ if(user.zone_selected == BODY_ZONE_PRECISE_GROIN)
+ playsound(loc, 'sound/weapons/whip.ogg', 30)
+ M.visible_message("[user] has [pick(attack_verb)] [M] on the ass!")
+ else
+ return ..(M, user)
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index 969556c1..f3fac8c6 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -1243,13 +1243,30 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggled Hub Visibility", "[GLOB.hub_visibility ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+/client/proc/breadify(atom/movable/target)
+ var/obj/item/reagent_containers/food/snacks/store/bread/plain/funnyBread = new(get_turf(target))
+ target.forceMove(funnyBread)
+
/client/proc/smite(mob/living/carbon/human/target as mob)
set name = "Smite"
set category = "Fun"
if(!check_rights(R_ADMIN) || !check_rights(R_FUN))
return
- var/list/punishment_list = list(ADMIN_PUNISHMENT_PIE, ADMIN_PUNISHMENT_FIREBALL, ADMIN_PUNISHMENT_CLUWNE, ADMIN_PUNISHMENT_LIGHTNING, ADMIN_PUNISHMENT_BRAINDAMAGE, ADMIN_PUNISHMENT_BSA, ADMIN_PUNISHMENT_GIB, ADMIN_PUNISHMENT_SUPPLYPOD, ADMIN_PUNISHMENT_MAZING, ADMIN_PUNISHMENT_ROD, ADMIN_PUNISHMENT_TABLETIDESTATIONWIDE)
+ var/list/punishment_list = list(ADMIN_PUNISHMENT_PIE,
+ ADMIN_PUNISHMENT_FIREBALL,
+ ADMIN_PUNISHMENT_CLUWNE,
+ ADMIN_PUNISHMENT_LIGHTNING,
+ ADMIN_PUNISHMENT_BRAINDAMAGE,
+ ADMIN_PUNISHMENT_BSA,
+ ADMIN_PUNISHMENT_GIB,
+ ADMIN_PUNISHMENT_SUPPLYPOD,
+ ADMIN_PUNISHMENT_MAZING,
+ ADMIN_PUNISHMENT_ROD,
+ ADMIN_PUNISHMENT_TABLETIDESTATIONWIDE,
+ ADMIN_PUNISHMENT_FAKEBWOINK,
+ ADMIN_PUNISHMENT_NUGGET,
+ ADMIN_PUNISHMENT_BREADIFY)
var/punishment = input("Choose a punishment", "DIVINE SMITING") as null|anything in punishment_list
@@ -1299,7 +1316,6 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
plaunch.temp_pod.explosionSize = list(0,0,0,2)
plaunch.temp_pod.effectStun = TRUE
plaunch.ui_interact(usr)
-
if(ADMIN_PUNISHMENT_MAZING)
if(!puzzle_imprison(target))
to_chat(usr,"Imprisonment failed!")
@@ -1317,6 +1333,29 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
for(var/obj/structure/table/T in A)
T.tablepush(target, target)
sleep(1)
+ if(ADMIN_PUNISHMENT_FAKEBWOINK)
+ SEND_SOUND(target, 'sound/effects/adminhelp.ogg')
+ if(ADMIN_PUNISHMENT_NUGGET)
+ if (!iscarbon(target))
+ return
+ var/mob/living/carbon/carbon_target = target
+ var/timer = 2 SECONDS
+ for (var/_limb in carbon_target.bodyparts)
+ var/obj/item/bodypart/limb = _limb
+ if (limb.body_part == HEAD || limb.body_part == CHEST)
+ continue
+ addtimer(CALLBACK(limb, /obj/item/bodypart/.proc/dismember), timer)
+ addtimer(CALLBACK(GLOBAL_PROC, .proc/playsound, carbon_target, 'sound/effects/cartoon_pop.ogg', 70), timer)
+ addtimer(CALLBACK(carbon_target, /mob/living/.proc/spin, 4, 1), timer - 0.4 SECONDS)
+ timer += 2 SECONDS
+ if(ADMIN_PUNISHMENT_BREADIFY)
+ #define BREADIFY_TIME (5 SECONDS)
+ var/mutable_appearance/bread_appearance = mutable_appearance('icons/obj/food/burgerbread.dmi', "bread")
+ var/mutable_appearance/transform_scanline = mutable_appearance('icons/effects/effects.dmi', "transform_effect")
+ target.transformation_animation(bread_appearance, time = BREADIFY_TIME, transform_overlay=transform_scanline, reset_after=TRUE)
+ addtimer(CALLBACK(GLOBAL_PROC, .proc/breadify, target), BREADIFY_TIME)
+ #undef BREADIFY_TIME
+
var/msg = "[key_name_admin(usr)] punished [key_name_admin(target)] with [punishment]."
message_admins(msg)
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index 83285599..db412292 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -868,7 +868,20 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "Breast Shape:[features["breasts_shape"]]"
dat += "Lactates:[features["breasts_producing"] == TRUE ? "Yes" : "No"]"
if(features["breasts_producing"] == TRUE)
- dat += "Produces:[features["breasts_fluid"]]"
+ dat += "Produces:"
+ switch(features["breasts_fluid"])
+ if(/datum/reagent/consumable/milk)
+ dat += "Milk"
+ if(/datum/reagent/water)
+ dat += "Water"
+ if(/datum/reagent/consumable/semen)
+ dat += "Semen"
+ if(/datum/reagent/consumable/femcum)
+ dat += "Femcum"
+ else
+ dat += "Nothing?"
+ //This else is a safeguard for errors, and if it happened, they wouldn't be able to change this pref,
+ //DO NOT REMOVE IT UNLESS YOU HAVE A GOOD IDEA
dat += ""
dat += ""
diff --git a/code/modules/food_and_drinks/food/snacks_vend.dm b/code/modules/food_and_drinks/food/snacks_vend.dm
index 7f47b238..661c9bca 100644
--- a/code/modules/food_and_drinks/food/snacks_vend.dm
+++ b/code/modules/food_and_drinks/food/snacks_vend.dm
@@ -85,11 +85,11 @@
desc = "fine print: seasoned with nanoscale mechanochemical generators. Not only does it taste good, But also self-heats when opened"
icon_state = "soyfood"
trash = /obj/item/trash/soy_food
- list_reagents = list(/datum/reagent/consumable/nutriment = 4, /datum/reagent/consumable/sugar = 2, /datum/reagent/consumable/nutriment/vitamin = 3)
- junkiness = 0
+ list_reagents = list(/datum/reagent/consumable/nutriment = 3, /datum/reagent/consumable/nutriment/vitamin = 2)
+ junkiness = 25
filling_color = "#FFD700"
tastes = list("nanomachines" = 2, "soybeans" = 5)
- foodtype = DAIRY | GRAIN
+ foodtype = JUNKFOOD | DAIRY | GRAIN
/obj/item/reagent_containers/food/snacks/syndicake
name = "syndi-cakes"
@@ -106,7 +106,7 @@
desc = "A self-heating bag of hollowed charcoal noodles with a spicy soy sauce glaze. Does contain small traces of charcoal."
icon_state = "carbonnanotube_noodles"
trash = /obj/item/trash/carbonnanotube_noodles
- list_reagents = list(/datum/reagent/consumable/nutriment = 5, /datum/reagent/medicine/charcoal = 1, /datum/reagent/consumable/nutriment/vitamin = 4)
+ list_reagents = list(/datum/reagent/consumable/nutriment = 4, /datum/reagent/medicine/charcoal = 1, /datum/reagent/consumable/nutriment/vitamin = 4)
junkiness = 0
filling_color = "#FFD700"
tastes = list("charcoal" = 1, "spiciness" = 3, "soysauce" = 3)
diff --git a/code/modules/jobs/job_types/cargo_service.dm b/code/modules/jobs/job_types/cargo_service.dm
index f9655ce9..9dec1a40 100644
--- a/code/modules/jobs/job_types/cargo_service.dm
+++ b/code/modules/jobs/job_types/cargo_service.dm
@@ -58,7 +58,7 @@ Cargo Technician
outfit = /datum/outfit/job/cargo_tech
- access = list(ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_QM, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM)
+ access = list(ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM)
minimal_access = list(ACCESS_MAINT_TUNNELS, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_MAILSORTING, ACCESS_MINERAL_STOREROOM)
/datum/outfit/job/cargo_tech
@@ -92,7 +92,7 @@ Shaft Miner
outfit = /datum/outfit/job/miner
- access = list(ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_QM, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM)
+ access = list(ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM)
minimal_access = list(ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MAILSORTING, ACCESS_MINERAL_STOREROOM)
/datum/outfit/job/miner
diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm
index 9c41f0ab..98412d76 100644
--- a/code/modules/mob/living/simple_animal/bot/bot.dm
+++ b/code/modules/mob/living/simple_animal/bot/bot.dm
@@ -1,1042 +1,1042 @@
-// AI (i.e. game AI, not the AI player) controlled bots
-/mob/living/simple_animal/bot
- icon = 'icons/mob/aibots.dmi'
- layer = MOB_LAYER
- gender = NEUTER
- mob_biotypes = list(MOB_ROBOTIC)
- light_range = 3
- light_power = 0.9
- light_color = "#CDDDFF"
- stop_automated_movement = 1
- wander = 0
- healable = 0
- damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
- atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
- maxbodytemp = INFINITY
- minbodytemp = 0
- blood_volume = 0
- has_unlimited_silicon_privilege = 1
- sentience_type = SENTIENCE_ARTIFICIAL
- status_flags = NONE //no default canpush
- verb_say = "states"
- verb_ask = "queries"
- verb_exclaim = "declares"
- verb_yell = "alarms"
- initial_language_holder = /datum/language_holder/synthetic
- bubble_icon = "machine"
- speech_span = SPAN_ROBOT
-
- faction = list("neutral", "silicon" , "turret")
-
- var/obj/machinery/bot_core/bot_core = null
- var/bot_core_type = /obj/machinery/bot_core
- var/list/users = list() //for dialog updates
- var/window_id = "bot_control"
- var/window_name = "Protobot 1.0" //Popup title
- var/window_width = 0 //0 for default size
- var/window_height = 0
- var/obj/item/paicard/paicard // Inserted pai card.
- var/allow_pai = 1 // Are we even allowed to insert a pai card.
- var/bot_name
-
- var/list/player_access = list() //Additonal access the bots gets when player controlled
- var/emagged = FALSE
- var/list/prev_access = list()
- var/on = TRUE
- var/open = FALSE//Maint panel
- var/locked = TRUE
- var/hacked = FALSE //Used to differentiate between being hacked by silicons and emagged by humans.
- var/text_hack = "" //Custom text returned to a silicon upon hacking a bot.
- var/text_dehack = "" //Text shown when resetting a bots hacked status to normal.
- var/text_dehack_fail = "" //Shown when a silicon tries to reset a bot emagged with the emag item, which cannot be reset.
- var/declare_message = "" //What the bot will display to the HUD user.
- var/frustration = 0 //Used by some bots for tracking failures to reach their target.
- var/base_speed = 2 //The speed at which the bot moves, or the number of times it moves per process() tick.
- var/turf/ai_waypoint //The end point of a bot's path, or the target location.
- var/list/path = list() //List of turfs through which a bot 'steps' to reach the waypoint, associated with the path image, if there is one.
- var/pathset = 0
- var/list/ignore_list = list() //List of unreachable targets for an ignore-list enabled bot to ignore.
- var/mode = BOT_IDLE //Standardizes the vars that indicate the bot is busy with its function.
- var/tries = 0 //Number of times the bot tried and failed to move.
- var/remote_disabled = 0 //If enabled, the AI cannot *Remotely* control a bot. It can still control it through cameras.
- var/mob/living/silicon/ai/calling_ai //Links a bot to the AI calling it.
- var/obj/item/radio/Radio //The bot's radio, for speaking to people.
- var/radio_key = null //which channels can the bot listen to
- var/radio_channel = RADIO_CHANNEL_COMMON //The bot's default radio channel
- var/auto_patrol = 0// set to make bot automatically patrol
- var/turf/patrol_target // this is turf to navigate to (location of beacon)
- var/turf/summon_target // The turf of a user summoning a bot.
- var/new_destination // pending new destination (waiting for beacon response)
- var/destination // destination description tag
- var/next_destination // the next destination in the patrol route
- var/shuffle = FALSE // If we should shuffle our adjacency checking
-
- var/blockcount = 0 //number of times retried a blocked path
- var/awaiting_beacon = 0 // count of pticks awaiting a beacon response
-
- var/nearest_beacon // the nearest beacon's tag
- var/turf/nearest_beacon_loc // the nearest beacon's location
-
- var/beacon_freq = FREQ_NAV_BEACON
- var/model = "" //The type of bot it is.
- var/bot_type = 0 //The type of bot it is, for radio control.
- var/data_hud_type = DATA_HUD_DIAGNOSTIC_BASIC //The type of data HUD the bot uses. Diagnostic by default.
- //This holds text for what the bot is mode doing, reported on the remote bot control interface.
- var/list/mode_name = list("In Pursuit","Preparing to Arrest", "Arresting", \
- "Beginning Patrol", "Patrolling", "Summoned by PDA", \
- "Cleaning", "Repairing", "Proceeding to work site", "Healing", \
- "Proceeding to AI waypoint", "Navigating to Delivery Location", "Navigating to Home", \
- "Waiting for clear path", "Calculating navigation path", "Pinging beacon network", "Unable to reach destination")
- var/datum/atom_hud/data/bot_path/path_hud = new /datum/atom_hud/data/bot_path()
- var/path_image_icon = 'icons/mob/aibots.dmi'
- var/path_image_icon_state = "path_indicator"
- var/path_image_color = "#FFFFFF"
- var/reset_access_timer_id
- var/ignorelistcleanuptimer = 1 // This ticks up every automated action, at 300 we clean the ignore list
- var/robot_arm = /obj/item/bodypart/r_arm/robot
-
- var/commissioned = FALSE // Will other (noncommissioned) bots salute this bot?
- var/can_salute = TRUE
- var/salute_delay = 60 SECONDS
-
- hud_possible = list(DIAG_STAT_HUD, DIAG_BOT_HUD, DIAG_HUD, DIAG_PATH_HUD = HUD_LIST_LIST) //Diagnostic HUD views
-
-/mob/living/simple_animal/bot/proc/get_mode()
- if(client) //Player bots do not have modes, thus the override. Also an easy way for PDA users/AI to know when a bot is a player.
- if(paicard)
- return "pAI Controlled"
- else
- return "Autonomous"
- else if(!on)
- return "Inactive"
- else if(!mode)
- return "Idle"
- else
- return "[mode_name[mode]]"
-
-/mob/living/simple_animal/bot/proc/turn_on()
- if(stat)
- return FALSE
- on = TRUE
- canmove = TRUE
- set_light(initial(light_range))
- update_icon()
- diag_hud_set_botstat()
- return TRUE
-
-/mob/living/simple_animal/bot/proc/turn_off()
- on = FALSE
- canmove = FALSE
- set_light(0)
- bot_reset() //Resets an AI's call, should it exist.
- update_icon()
-
-/mob/living/simple_animal/bot/Initialize()
- . = ..()
- GLOB.bots_list += src
- access_card = new /obj/item/card/id(src)
-//This access is so bots can be immediately set to patrol and leave Robotics, instead of having to be let out first.
- access_card.access += ACCESS_ROBOTICS
- set_custom_texts()
- Radio = new/obj/item/radio(src)
- if(radio_key)
- Radio.keyslot = new radio_key
- Radio.subspace_transmission = TRUE
- Radio.canhear_range = 0 // anything greater will have the bot broadcast the channel as if it were saying it out loud.
- Radio.recalculateChannels()
-
- bot_core = new bot_core_type(src)
-
- //Adds bot to the diagnostic HUD system
- prepare_huds()
- for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds)
- diag_hud.add_to_hud(src)
- diag_hud_set_bothealth()
- diag_hud_set_botstat()
- diag_hud_set_botmode()
-
- //If a bot has its own HUD (for player bots), provide it.
- if(data_hud_type)
- var/datum/atom_hud/datahud = GLOB.huds[data_hud_type]
- datahud.add_hud_to(src)
- if(path_hud)
- path_hud.add_to_hud(src)
- path_hud.add_hud_to(src)
-
-/mob/living/simple_animal/bot/update_canmove()
- . = ..()
- if(!on)
- . = 0
- canmove = .
-
-/mob/living/simple_animal/bot/Destroy()
- if(path_hud)
- QDEL_NULL(path_hud)
- path_hud = null
- GLOB.bots_list -= src
- if(paicard)
- ejectpai()
- qdel(Radio)
- qdel(access_card)
- qdel(bot_core)
- return ..()
-
-/mob/living/simple_animal/bot/bee_friendly()
- return TRUE
-
-/mob/living/simple_animal/bot/death(gibbed)
- . = ..()
- if(!gibbed)
- explode()
-
-/mob/living/simple_animal/bot/proc/explode()
- qdel(src)
-
-/mob/living/simple_animal/bot/emag_act(mob/user)
- if(locked) //First emag application unlocks the bot's interface. Apply a screwdriver to use the emag again.
- locked = FALSE
- emagged = 1
- to_chat(user, "You bypass [src]'s controls.")
- return
- if(!locked && open) //Bot panel is unlocked by ID or emag, and the panel is screwed open. Ready for emagging.
- emagged = 2
- remote_disabled = 1 //Manually emagging the bot locks out the AI built in panel.
- locked = TRUE //Access denied forever!
- bot_reset()
- turn_on() //The bot automatically turns on when emagged, unless recently hit with EMP.
- to_chat(src, "(#$*#$^^( OVERRIDE DETECTED")
- log_combat(user, src, "emagged")
- return
- else //Bot is unlocked, but the maint panel has not been opened with a screwdriver yet.
- to_chat(user, "You need to open maintenance panel first!")
-
-/mob/living/simple_animal/bot/examine(mob/user)
- . = ..()
- if(health < maxHealth)
- if(health > maxHealth/3)
- . += "[src]'s parts look loose."
- else
- . += "[src]'s parts look very loose!"
- else
- . += "[src] is in pristine condition."
-
-/mob/living/simple_animal/bot/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
- if(amount>0 && prob(10))
- new /obj/effect/decal/cleanable/oil(loc)
- . = ..()
-
-/mob/living/simple_animal/bot/updatehealth()
- ..()
- diag_hud_set_bothealth()
-
-/mob/living/simple_animal/bot/med_hud_set_health()
- return //we use a different hud
-
-/mob/living/simple_animal/bot/med_hud_set_status()
- return //we use a different hud
-
-/mob/living/simple_animal/bot/handle_automated_action() //Master process which handles code common across most bots.
- diag_hud_set_botmode()
-
- if (ignorelistcleanuptimer % 300 == 0) // Every 300 actions, clean up the ignore list from old junk
- for(var/ref in ignore_list)
- var/atom/referredatom = locate(ref)
- if (!referredatom || !istype(referredatom) || QDELETED(referredatom))
- ignore_list -= ref
- ignorelistcleanuptimer = 1
- else
- ignorelistcleanuptimer++
-
- if(!on || client)
- return
-
- if(!commissioned && can_salute)
- for(var/mob/living/simple_animal/bot/B in get_hearers_in_view(5, get_turf(src)))
- if(B.commissioned)
- visible_message("[src] performs an elaborate salute for [B]!")
- can_salute = FALSE
- addtimer(VARSET_CALLBACK(src, can_salute, TRUE), salute_delay)
- break
-
- switch(mode) //High-priority overrides are processed first. Bots can do nothing else while under direct command.
- if(BOT_RESPONDING) //Called by the AI.
- call_mode()
- return
- if(BOT_SUMMON) //Called by PDA
- bot_summon()
- return
- return TRUE //Successful completion. Used to prevent child process() continuing if this one is ended early.
-
-
-/mob/living/simple_animal/bot/attack_hand(mob/living/carbon/human/H)
- if(H.a_intent == INTENT_HELP)
- interact(H)
- else
- return ..()
-
-/mob/living/simple_animal/bot/attack_ai(mob/user)
- if(!topic_denied(user))
- interact(user)
- else
- to_chat(user, "[src]'s interface is not responding!")
-
-/mob/living/simple_animal/bot/interact(mob/user)
- show_controls(user)
-
-/mob/living/simple_animal/bot/attackby(obj/item/W, mob/user, params)
- if(istype(W, /obj/item/screwdriver))
- if(!locked)
- open = !open
- to_chat(user, "The maintenance panel is now [open ? "opened" : "closed"].")
- else
- to_chat(user, "The maintenance panel is locked.")
- else if(istype(W, /obj/item/card/id) || istype(W, /obj/item/pda))
- if(bot_core.allowed(user) && !open && !emagged)
- locked = !locked
- to_chat(user, "Controls are now [locked ? "locked." : "unlocked."]")
- else
- if(emagged)
- to_chat(user, "ERROR")
- if(open)
- to_chat(user, "Please close the access panel before locking it.")
- else
- to_chat(user, "Access denied.")
- else if(istype(W, /obj/item/paicard))
- insertpai(user, W)
- else if(istype(W, /obj/item/hemostat) && paicard)
- if(open)
- to_chat(user, "Close the access panel before manipulating the personality slot!")
- else
- to_chat(user, "You attempt to pull [paicard] free...")
- if(do_after(user, 30, target = src))
- if (paicard)
- user.visible_message("[user] uses [W] to pull [paicard] out of [bot_name]!","You pull [paicard] out of [bot_name] with [W].")
- ejectpai(user)
- else
- user.changeNext_move(CLICK_CD_MELEE)
- if(istype(W, /obj/item/weldingtool) && user.a_intent != INTENT_HARM)
- if(health >= maxHealth)
- to_chat(user, "[src] does not need a repair!")
- return
- if(!open)
- to_chat(user, "Unable to repair with the maintenance panel closed!")
- return
-
- if(W.use_tool(src, user, 0, volume=40))
- adjustHealth(-10)
- user.visible_message("[user] repairs [src]!","You repair [src].")
- else
- if(W.force) //if force is non-zero
- do_sparks(5, TRUE, src)
- ..()
-
-/mob/living/simple_animal/bot/bullet_act(obj/item/projectile/Proj)
- if(Proj && (Proj.damage_type == BRUTE || Proj.damage_type == BURN))
- if(prob(75) && Proj.damage > 0)
- do_sparks(5, TRUE, src)
- return ..()
-
-/mob/living/simple_animal/bot/emp_act(severity)
- . = ..()
- if(. & EMP_PROTECT_SELF)
- return
- var/was_on = on
- stat |= EMPED
- new /obj/effect/temp_visual/emp(loc)
- if(paicard)
- paicard.emp_act(severity)
- src.visible_message("[paicard] is flies out of [bot_name]!","You are forcefully ejected from [bot_name]!")
- ejectpai(0)
- if(on)
- turn_off()
- spawn(severity*300)
- stat &= ~EMPED
- if(was_on)
- turn_on()
-
-/mob/living/simple_animal/bot/proc/set_custom_texts() //Superclass for setting hack texts. Appears only if a set is not given to a bot locally.
- text_hack = "You hack [name]."
- text_dehack = "You reset [name]."
- text_dehack_fail = "You fail to reset [name]."
-
-/mob/living/simple_animal/bot/proc/speak(message,channel) //Pass a message to have the bot say() it. Pass a frequency to say it on the radio.
- if((!on) || (!message))
- return
- if(channel && Radio.channels[channel])// Use radio if we have channel key
- Radio.talk_into(src, message, channel)
- else
- say(message)
-
-/mob/living/simple_animal/bot/radio(message, message_mode, list/spans, language)
- . = ..()
- if(. != 0)
- return
-
- switch(message_mode)
- if(MODE_HEADSET)
- Radio.talk_into(src, message, , spans, language)
- return REDUCE_RANGE
-
- if(MODE_DEPARTMENT)
- Radio.talk_into(src, message, message_mode, spans, language)
- return REDUCE_RANGE
-
- if(message_mode in GLOB.radiochannels)
- Radio.talk_into(src, message, message_mode, spans, language)
- return REDUCE_RANGE
-
-/mob/living/simple_animal/bot/proc/drop_part(obj/item/drop_item, dropzone)
- var/dropped_item = new drop_item(dropzone)
- drop_item = null
-
- if(istype(dropped_item, /obj/item/stock_parts/cell))
- var/obj/item/stock_parts/cell/dropped_cell = dropped_item
- dropped_cell.charge = 0
- dropped_cell.update_icon()
-
- else if(istype(dropped_item, /obj/item/storage))
- var/obj/item/storage/S = dropped_item
- S.contents = list()
-
- else if(istype(dropped_item, /obj/item/gun/energy))
- var/obj/item/gun/energy/dropped_gun = dropped_item
- dropped_gun.cell.charge = 0
- dropped_gun.update_icon()
-
-//Generalized behavior code, override where needed!
-
-/*
-scan() will search for a given type (such as turfs, human mobs, or objects) in the bot's view range, and return a single result.
-Arguments: The object type to be searched (such as "/mob/living/carbon/human"), the old scan result to be ignored, if one exists,
-and the view range, which defaults to 7 (full screen) if an override is not passed.
-If the bot maintains an ignore list, it is also checked here.
-
-Example usage: patient = scan(/mob/living/carbon/human, oldpatient, 1)
-The proc would return a human next to the bot to be set to the patient var.
-Pass the desired type path itself, declaring a temporary var beforehand is not required.
-*/
-/mob/living/simple_animal/bot/proc/scan(scan_type, old_target, scan_range = DEFAULT_SCAN_RANGE)
- var/turf/T = get_turf(src)
- if(!T)
- return
- var/list/adjacent = T.GetAtmosAdjacentTurfs(1)
- if(shuffle) //If we were on the same tile as another bot, let's randomize our choices so we dont both go the same way
- adjacent = shuffle(adjacent)
- shuffle = FALSE
- for(var/scan in adjacent)//Let's see if there's something right next to us first!
- if(check_bot(scan)) //Is there another bot there? Then let's just skip it
- continue
- if(isturf(scan_type)) //If we're lookeing for a turf we can just run the checks directly!
- var/final_result = checkscan(scan,scan_type,old_target)
- if(final_result)
- return final_result
- else
- var/turf/turfy = scan
- for(var/deepscan in turfy.contents)//Check the contents since adjacent is turfs
- var/final_result = checkscan(deepscan,scan_type,old_target)
- if(final_result)
- return final_result
- for (var/scan in shuffle(view(scan_range, src))-adjacent) //Search for something in range!
- var/final_result = checkscan(scan,scan_type,old_target)
- if(final_result)
- return final_result
-
-/mob/living/simple_animal/bot/proc/checkscan(scan, scan_type, old_target)
- if(!istype(scan, scan_type)) //Check that the thing we found is the type we want!
- return FALSE //If not, keep searching!
- if( (REF(scan) in ignore_list) || (scan == old_target) ) //Filter for blacklisted elements, usually unreachable or previously processed oness
- return FALSE
-
- var/scan_result = process_scan(scan) //Some bots may require additional processing when a result is selected.
- if(scan_result)
- return scan_result
- else
- return FALSE //The current element failed assessment, move on to the next.
- return
-
-/mob/living/simple_animal/bot/proc/check_bot(targ)
- var/turf/T = get_turf(targ)
- if(T)
- for(var/C in T.contents)
- if(istype(C,type) && (C != src)) //Is there another bot there already? If so, let's skip it so we dont all atack on top of eachother.
- return TRUE //Let's abort if we find a bot so we dont have to keep rechecking
-
-//When the scan finds a target, run bot specific processing to select it for the next step. Empty by default.
-/mob/living/simple_animal/bot/proc/process_scan(scan_target)
- return scan_target
-
-
-/mob/living/simple_animal/bot/proc/add_to_ignore(subject)
- if(ignore_list.len < 50) //This will help keep track of them, so the bot is always trying to reach a blocked spot.
- ignore_list += REF(subject)
- else //If the list is full, insert newest, delete oldest.
- ignore_list.Cut(1,2)
- ignore_list += REF(subject)
-
-/*
-Movement proc for stepping a bot through a path generated through A-star.
-Pass a positive integer as an argument to override a bot's default speed.
-*/
-/mob/living/simple_animal/bot/proc/bot_move(dest, move_speed)
- if(!dest || !path || path.len == 0) //A-star failed or a path/destination was not set.
- set_path(null)
- return FALSE
- dest = get_turf(dest) //We must always compare turfs, so get the turf of the dest var if dest was originally something else.
- var/turf/last_node = get_turf(path[path.len]) //This is the turf at the end of the path, it should be equal to dest.
- if(get_turf(src) == dest) //We have arrived, no need to move again.
- return TRUE
- else if(dest != last_node) //The path should lead us to our given destination. If this is not true, we must stop.
- set_path(null)
- return FALSE
- var/step_count = move_speed ? move_speed : base_speed //If a value is passed into move_speed, use that instead of the default speed var.
-
- if(step_count >= 1 && tries < BOT_STEP_MAX_RETRIES)
- for(var/step_number = 0, step_number < step_count,step_number++)
- spawn(BOT_STEP_DELAY*step_number)
- bot_step(dest)
- else
- return FALSE
- return TRUE
-
-
-/mob/living/simple_animal/bot/proc/bot_step(dest) //Step,increase tries if failed
- if(!path)
- return FALSE
- if(path.len > 1)
- step_towards(src, path[1])
- if(get_turf(src) == path[1]) //Successful move
- increment_path()
- tries = 0
- else
- tries++
- return FALSE
- else if(path.len == 1)
- step_to(src, dest)
- set_path(null)
- return TRUE
-
-
-/mob/living/simple_animal/bot/proc/check_bot_access()
- if(mode != BOT_SUMMON && mode != BOT_RESPONDING)
- access_card.access = prev_access
-
-/mob/living/simple_animal/bot/proc/call_bot(caller, turf/waypoint, message=TRUE)
- bot_reset() //Reset a bot before setting it to call mode.
-
- //For giving the bot temporary all-access.
- var/obj/item/card/id/all_access = new /obj/item/card/id
- var/datum/job/captain/All = new/datum/job/captain
- all_access.access = All.get_access()
-
- set_path(get_path_to(src, waypoint, /turf/proc/Distance_cardinal, 0, 200, id=all_access))
- calling_ai = caller //Link the AI to the bot!
- ai_waypoint = waypoint
-
- if(path && path.len) //Ensures that a valid path is calculated!
- var/end_area = get_area_name(waypoint)
- if(!on)
- turn_on() //Saves the AI the hassle of having to activate a bot manually.
- access_card = all_access //Give the bot all-access while under the AI's command.
- if(client)
- reset_access_timer_id = addtimer(CALLBACK (src, .proc/bot_reset), 600, TIMER_UNIQUE|TIMER_OVERRIDE|TIMER_STOPPABLE) //if the bot is player controlled, they get the extra access for a limited time
- to_chat(src, "Priority waypoint set by [icon2html(calling_ai, src)] [caller]. Proceed to [end_area].
[path.len-1] meters to destination. You have been granted additional door access for 60 seconds.")
- if(message)
- to_chat(calling_ai, "[icon2html(src, calling_ai)] [name] called to [end_area]. [path.len-1] meters to destination.")
- pathset = 1
- mode = BOT_RESPONDING
- tries = 0
- else
- if(message)
- to_chat(calling_ai, "Failed to calculate a valid route. Ensure destination is clear of obstructions and within range.")
- calling_ai = null
- set_path(null)
-
-/mob/living/simple_animal/bot/proc/call_mode() //Handles preparing a bot for a call, as well as calling the move proc.
-//Handles the bot's movement during a call.
- var/success = bot_move(ai_waypoint, 3)
- if(!success)
- if(calling_ai)
- to_chat(calling_ai, "[icon2html(src, calling_ai)] [get_turf(src) == ai_waypoint ? "[src] successfully arrived to waypoint." : "[src] failed to reach waypoint."]")
- calling_ai = null
- bot_reset()
-
-/mob/living/simple_animal/bot/proc/bot_reset()
- if(calling_ai) //Simple notification to the AI if it called a bot. It will not know the cause or identity of the bot.
- to_chat(calling_ai, "Call command to a bot has been reset.")
- calling_ai = null
- if(reset_access_timer_id)
- deltimer(reset_access_timer_id)
- reset_access_timer_id = null
- set_path(null)
- summon_target = null
- pathset = 0
- access_card.access = prev_access
- tries = 0
- mode = BOT_IDLE
- diag_hud_set_botstat()
- diag_hud_set_botmode()
-
-
-
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-//Patrol and summon code!
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-/mob/living/simple_animal/bot/proc/bot_patrol()
- patrol_step()
- spawn(5)
- if(mode == BOT_PATROL)
- patrol_step()
- return
-
-/mob/living/simple_animal/bot/proc/start_patrol()
-
- if(tries >= BOT_STEP_MAX_RETRIES) //Bot is trapped, so stop trying to patrol.
- auto_patrol = 0
- tries = 0
- speak("Unable to start patrol.")
-
- return
-
- if(!auto_patrol) //A bot not set to patrol should not be patrolling.
- mode = BOT_IDLE
- return
-
- if(patrol_target) // has patrol target
- spawn(0)
- calc_path() // Find a route to it
- if(path.len == 0)
- patrol_target = null
- return
- mode = BOT_PATROL
- else // no patrol target, so need a new one
- speak("Engaging patrol mode.")
- find_patrol_target()
- tries++
- return
-
-// perform a single patrol step
-
-/mob/living/simple_animal/bot/proc/patrol_step()
-
- if(client) // In use by player, don't actually move.
- return
-
- if(loc == patrol_target) // reached target
- //Find the next beacon matching the target.
- if(!get_next_patrol_target())
- find_patrol_target() //If it fails, look for the nearest one instead.
- return
-
- else if(path.len > 0 && patrol_target) // valid path
- if(path[1] == loc)
- increment_path()
- return
-
-
- var/moved = bot_move(patrol_target)//step_towards(src, next) // attempt to move
- if(!moved) //Couldn't proceed the next step of the path BOT_STEP_MAX_RETRIES times
- spawn(2)
- calc_path()
- if(path.len == 0)
- find_patrol_target()
- tries = 0
-
- else // no path, so calculate new one
- mode = BOT_START_PATROL
-
-// finds the nearest beacon to self
-/mob/living/simple_animal/bot/proc/find_patrol_target()
- nearest_beacon = null
- new_destination = null
- find_nearest_beacon()
- if(nearest_beacon)
- patrol_target = nearest_beacon_loc
- destination = next_destination
- else
- auto_patrol = 0
- mode = BOT_IDLE
- speak("Disengaging patrol mode.")
-
-/mob/living/simple_animal/bot/proc/get_next_patrol_target()
- // search the beacon list for the next target in the list.
- for(var/obj/machinery/navbeacon/NB in GLOB.navbeacons["[z]"])
- if(NB.location == next_destination) //Does the Beacon location text match the destination?
- destination = new_destination //We now know the name of where we want to go.
- patrol_target = NB.loc //Get its location and set it as the target.
- next_destination = NB.codes["next_patrol"] //Also get the name of the next beacon in line.
- return TRUE
-
-/mob/living/simple_animal/bot/proc/find_nearest_beacon()
- for(var/obj/machinery/navbeacon/NB in GLOB.navbeacons["[z]"])
- var/dist = get_dist(src, NB)
- if(nearest_beacon) //Loop though the beacon net to find the true closest beacon.
- //Ignore the beacon if were are located on it.
- if(dist>1 && dist 1) //Begin the search, save this one for comparison on the next loop.
- nearest_beacon = NB.location
- nearest_beacon_loc = NB.loc
- patrol_target = nearest_beacon_loc
- destination = nearest_beacon
-
-//PDA control. Some bots, especially MULEs, may have more parameters.
-/mob/living/simple_animal/bot/proc/bot_control(command, mob/user, turf/user_turf, list/user_access = list())
- if(!on || emagged == 2 || remote_disabled) //Emagged bots do not respect anyone's authority! Bots with their remote controls off cannot get commands.
- return TRUE //ACCESS DENIED
- if(client)
- bot_control_message(command,user,user_turf,user_access)
- // process control input
- switch(command)
- if("patroloff")
- bot_reset() //HOLD IT!!
- auto_patrol = 0
-
- if("patrolon")
- auto_patrol = 1
-
- if("summon")
- bot_reset()
- summon_target = user_turf
- if(user_access.len != 0)
- access_card.access = user_access + prev_access //Adds the user's access, if any.
- mode = BOT_SUMMON
- speak("Responding.", radio_channel)
- calc_summon_path()
-
- if("ejectpai")
- ejectpairemote(user)
- return
-
-//
-/mob/living/simple_animal/bot/proc/bot_control_message(command,user,user_turf,user_access)
- switch(command)
- if("patroloff")
- to_chat(src, "STOP PATROL")
- if("patrolon")
- to_chat(src, "START PATROL")
- if("summon")
- var/area/a = get_area(user_turf)
- to_chat(src, "PRIORITY ALERT:[user] in [a.name]!")
- if("stop")
- to_chat(src, "STOP!")
-
- if("go")
- to_chat(src, "GO!")
-
- if("home")
- to_chat(src, "RETURN HOME!")
- if("ejectpai")
- return
- else
- to_chat(src, "Unidentified control sequence received:[command]")
-
-/mob/living/simple_animal/bot/proc/bot_summon() // summoned to PDA
- summon_step()
-
-// calculates a path to the current destination
-// given an optional turf to avoid
-/mob/living/simple_animal/bot/proc/calc_path(turf/avoid)
- check_bot_access()
- set_path(get_path_to(src, patrol_target, /turf/proc/Distance_cardinal, 0, 120, id=access_card, exclude=avoid))
-
-/mob/living/simple_animal/bot/proc/calc_summon_path(turf/avoid)
- check_bot_access()
- spawn()
- set_path(get_path_to(src, summon_target, /turf/proc/Distance_cardinal, 0, 150, id=access_card, exclude=avoid))
- if(!path.len) //Cannot reach target. Give up and announce the issue.
- speak("Summon command failed, destination unreachable.",radio_channel)
- bot_reset()
-
-/mob/living/simple_animal/bot/proc/summon_step()
-
- if(client) // In use by player, don't actually move.
- return
-
- if(loc == summon_target) // Arrived to summon location.
- bot_reset()
- return
-
- else if(path.len > 0 && summon_target) //Proper path acquired!
- if(path[1] == loc)
- increment_path()
- return
-
- var/moved = bot_move(summon_target, 3) // Move attempt
- if(!moved)
- spawn(2)
- calc_summon_path()
- tries = 0
-
- else // no path, so calculate new one
- calc_summon_path()
-
-/mob/living/simple_animal/bot/Bump(M as mob|obj) //Leave no door unopened!
- . = ..()
- if((istype(M, /obj/machinery/door/airlock) || istype(M, /obj/machinery/door/window)) && (!isnull(access_card)))
- var/obj/machinery/door/D = M
- if(D.check_access(access_card))
- D.open()
- frustration = 0
-
-/mob/living/simple_animal/bot/proc/show_controls(mob/M)
- users |= M
- var/dat = ""
- dat = get_controls(M)
- var/datum/browser/popup = new(M,window_id,window_name,350,600)
- popup.set_content(dat)
- popup.open(use_onclose = 0)
- onclose(M,window_id,ref=src)
- return
-
-/mob/living/simple_animal/bot/proc/update_controls()
- for(var/mob/M in users)
- show_controls(M)
-
-/mob/living/simple_animal/bot/proc/get_controls(mob/M)
- return "PROTOBOT - NOT FOR USE"
-
-/mob/living/simple_animal/bot/Topic(href, href_list)
- //No ..() to prevent strip panel showing up - Todo: make that saner
- if(href_list["close"])// HUE HUE
- if(usr in users)
- users.Remove(usr)
- return TRUE
-
- if(topic_denied(usr))
- to_chat(usr, "[src]'s interface is not responding!")
- return TRUE
- add_fingerprint(usr)
-
- if((href_list["power"]) && (bot_core.allowed(usr) || !locked))
- if(on)
- turn_off()
- else
- turn_on()
-
- switch(href_list["operation"])
- if("patrol")
- auto_patrol = !auto_patrol
- bot_reset()
- if(!issilicon(usr) && !IsAdminGhost(usr) && !(bot_core.allowed(usr) || !locked))
- return TRUE
- if("remote")
- remote_disabled = !remote_disabled
- if("hack")
- if(!issilicon(usr) && !IsAdminGhost(usr))
- var/msg = "[key_name(usr)] attempted to hack a bot with a href that shouldn't be available!"
- message_admins(msg)
- log_admin(msg)
- return TRUE
- if(emagged != 2)
- emagged = 2
- hacked = TRUE
- locked = TRUE
- to_chat(usr, "[text_hack]")
- bot_reset()
- else if(!hacked)
- to_chat(usr, "[text_dehack_fail]")
- else
- emagged = FALSE
- hacked = FALSE
- to_chat(usr, "[text_dehack]")
- bot_reset()
- if("ejectpai")
- if(paicard && (!locked || issilicon(usr) || IsAdminGhost(usr)))
- to_chat(usr, "You eject [paicard] from [bot_name]")
- ejectpai(usr)
- update_controls()
-
-/mob/living/simple_animal/bot/proc/update_icon()
- icon_state = "[initial(icon_state)][on]"
-
-// Machinery to simplify topic and access calls
-/obj/machinery/bot_core
- use_power = NO_POWER_USE
- anchored = FALSE
- var/mob/living/simple_animal/bot/owner = null
-
-/obj/machinery/bot_core/Initialize()
- . = ..()
- owner = loc
- if(!istype(owner))
- return INITIALIZE_HINT_QDEL
-
-/mob/living/simple_animal/bot/proc/topic_denied(mob/user) //Access check proc for bot topics! Remember to place in a bot's individual Topic if desired.
- if(!user.canUseTopic(src))
- return TRUE
- // 0 for access, 1 for denied.
- if(emagged == 2) //An emagged bot cannot be controlled by humans, silicons can if one hacked it.
- if(!hacked) //Manually emagged by a human - access denied to all.
- return TRUE
- else if(!issilicon(user) && !IsAdminGhost(user)) //Bot is hacked, so only silicons and admins are allowed access.
- return TRUE
- return FALSE
-
-/mob/living/simple_animal/bot/proc/hack(mob/user)
- var/hack
- if(issilicon(user) || IsAdminGhost(user)) //Allows silicons or admins to toggle the emag status of a bot.
- hack += "[emagged == 2 ? "Software compromised! Unit may exhibit dangerous or erratic behavior." : "Unit operating normally. Release safety lock?"]
"
- hack += "Harm Prevention Safety System: [emagged ? "DANGER" : "Engaged"]
"
- else if(!locked) //Humans with access can use this option to hide a bot from the AI's remote control panel and PDA control.
- hack += "Remote network control radio: [remote_disabled ? "Disconnected" : "Connected"]
"
- return hack
-
-/mob/living/simple_animal/bot/proc/showpai(mob/user)
- var/eject = ""
- if((!locked || issilicon(usr) || IsAdminGhost(usr)))
- if(paicard || allow_pai)
- eject += "Personality card status: "
- if(paicard)
- if(client)
- eject += "Active"
- else
- eject += "Inactive"
- else if(!allow_pai || key)
- eject += "Unavailable"
- else
- eject += "Not inserted"
- eject += "
"
- eject += "
"
- return eject
-
-/mob/living/simple_animal/bot/proc/insertpai(mob/user, obj/item/paicard/card)
- if(paicard)
- to_chat(user, "A [paicard] is already inserted!")
- else if(allow_pai && !key)
- if(!locked && !open)
- if(card.pai && card.pai.mind)
- if(!user.transferItemToLoc(card, src))
- return
- paicard = card
- user.visible_message("[user] inserts [card] into [src]!","You insert [card] into [src].")
- paicard.pai.mind.transfer_to(src)
- to_chat(src, "You sense your form change as you are uploaded into [src].")
- bot_name = name
- name = paicard.pai.name
- faction = user.faction.Copy()
- language_holder = paicard.pai.language_holder.copy(src)
- log_combat(user, paicard.pai, "uploaded to [bot_name],")
- return TRUE
- else
- to_chat(user, "[card] is inactive.")
- else
- to_chat(user, "The personality slot is locked.")
- else
- to_chat(user, "[src] is not compatible with [card]")
-
-/mob/living/simple_animal/bot/proc/ejectpai(mob/user = null, announce = 1)
- if(paicard)
- if(mind && paicard.pai)
- mind.transfer_to(paicard.pai)
- else if(paicard.pai)
- paicard.pai.key = key
- else
- ghostize(0) // The pAI card that just got ejected was dead.
- key = null
- paicard.forceMove(loc)
- if(user)
- log_combat(user, paicard.pai, "ejected from [src.bot_name],")
- else
- log_combat(src, paicard.pai, "ejected")
- if(announce)
- to_chat(paicard.pai, "You feel your control fade as [paicard] ejects from [bot_name].")
- paicard = null
- name = bot_name
- faction = initial(faction)
-
-/mob/living/simple_animal/bot/proc/ejectpairemote(mob/user)
- if(bot_core.allowed(user) && paicard)
- speak("Ejecting personality chip.", radio_channel)
- ejectpai(user)
-
-/mob/living/simple_animal/bot/Login()
- . = ..()
- access_card.access += player_access
- diag_hud_set_botmode()
-
-/mob/living/simple_animal/bot/Logout()
- . = ..()
- bot_reset()
-
-/mob/living/simple_animal/bot/revive(full_heal = 0, admin_revive = 0)
- if(..())
- update_icon()
- . = 1
-
-/mob/living/simple_animal/bot/ghost()
- if(stat != DEAD) // Only ghost if we're doing this while alive, the pAI probably isn't dead yet.
- ..()
- if(paicard && (!client || stat == DEAD))
- ejectpai(0)
-
-/mob/living/simple_animal/bot/sentience_act()
- faction -= "silicon"
-
-/mob/living/simple_animal/bot/proc/set_path(list/newpath)
- path = newpath ? newpath : list()
- if(!path_hud)
- return
- var/list/path_huds_watching_me = list(GLOB.huds[DATA_HUD_DIAGNOSTIC_ADVANCED])
- if(path_hud)
- path_huds_watching_me += path_hud
- for(var/V in path_huds_watching_me)
- var/datum/atom_hud/H = V
- H.remove_from_hud(src)
-
- var/list/path_images = hud_list[DIAG_PATH_HUD]
- QDEL_LIST(path_images)
- if(newpath)
- for(var/i in 1 to newpath.len)
- var/turf/T = newpath[i]
- if(T == loc) //don't bother putting an image if it's where we already exist.
- continue
- var/direction = NORTH
- if(i > 1)
- var/turf/prevT = path[i - 1]
- var/image/prevI = path[prevT]
- direction = get_dir(prevT, T)
- if(i > 2)
- var/turf/prevprevT = path[i - 2]
- var/prevDir = get_dir(prevprevT, prevT)
- var/mixDir = direction|prevDir
- if(mixDir in GLOB.diagonals)
- prevI.dir = mixDir
- if(prevDir & (NORTH|SOUTH))
- var/matrix/ntransform = matrix()
- ntransform.Turn(90)
- if((mixDir == NORTHWEST) || (mixDir == SOUTHEAST))
- ntransform.Scale(-1, 1)
- else
- ntransform.Scale(1, -1)
- prevI.transform = ntransform
- var/mutable_appearance/MA = new /mutable_appearance()
- MA.icon = path_image_icon
- MA.icon_state = path_image_icon_state
- MA.layer = ABOVE_OPEN_TURF_LAYER
- MA.plane = 0
- MA.appearance_flags = RESET_COLOR|RESET_TRANSFORM
- MA.color = path_image_color
- MA.dir = direction
- var/image/I = image(loc = T)
- I.appearance = MA
- path[T] = I
- path_images += I
-
- for(var/V in path_huds_watching_me)
- var/datum/atom_hud/H = V
- H.add_to_hud(src)
-
-
-/mob/living/simple_animal/bot/proc/increment_path()
- if(!path || !path.len)
- return
- var/image/I = path[path[1]]
- if(I)
- I.icon_state = null
- path.Cut(1, 2)
+// AI (i.e. game AI, not the AI player) controlled bots
+/mob/living/simple_animal/bot
+ icon = 'icons/mob/aibots.dmi'
+ layer = MOB_LAYER
+ gender = NEUTER
+ mob_biotypes = list(MOB_ROBOTIC)
+ light_range = 3
+ light_power = 0.9
+ light_color = "#CDDDFF"
+ stop_automated_movement = 1
+ wander = 0
+ healable = 0
+ damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
+ atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
+ maxbodytemp = INFINITY
+ minbodytemp = 0
+ blood_volume = 0
+ has_unlimited_silicon_privilege = 1
+ sentience_type = SENTIENCE_ARTIFICIAL
+ status_flags = NONE //no default canpush
+ verb_say = "states"
+ verb_ask = "queries"
+ verb_exclaim = "declares"
+ verb_yell = "alarms"
+ initial_language_holder = /datum/language_holder/synthetic
+ bubble_icon = "machine"
+ speech_span = SPAN_ROBOT
+
+ faction = list("neutral", "silicon" , "turret")
+
+ var/obj/machinery/bot_core/bot_core = null
+ var/bot_core_type = /obj/machinery/bot_core
+ var/list/users = list() //for dialog updates
+ var/window_id = "bot_control"
+ var/window_name = "Protobot 1.0" //Popup title
+ var/window_width = 0 //0 for default size
+ var/window_height = 0
+ var/obj/item/paicard/paicard // Inserted pai card.
+ var/allow_pai = 1 // Are we even allowed to insert a pai card.
+ var/bot_name
+
+ var/list/player_access = list() //Additonal access the bots gets when player controlled
+ var/emagged = FALSE
+ var/list/prev_access = list()
+ var/on = TRUE
+ var/open = FALSE//Maint panel
+ var/locked = TRUE
+ var/hacked = FALSE //Used to differentiate between being hacked by silicons and emagged by humans.
+ var/text_hack = "" //Custom text returned to a silicon upon hacking a bot.
+ var/text_dehack = "" //Text shown when resetting a bots hacked status to normal.
+ var/text_dehack_fail = "" //Shown when a silicon tries to reset a bot emagged with the emag item, which cannot be reset.
+ var/declare_message = "" //What the bot will display to the HUD user.
+ var/frustration = 0 //Used by some bots for tracking failures to reach their target.
+ var/base_speed = 2 //The speed at which the bot moves, or the number of times it moves per process() tick.
+ var/turf/ai_waypoint //The end point of a bot's path, or the target location.
+ var/list/path = list() //List of turfs through which a bot 'steps' to reach the waypoint, associated with the path image, if there is one.
+ var/pathset = 0
+ var/list/ignore_list = list() //List of unreachable targets for an ignore-list enabled bot to ignore.
+ var/mode = BOT_IDLE //Standardizes the vars that indicate the bot is busy with its function.
+ var/tries = 0 //Number of times the bot tried and failed to move.
+ var/remote_disabled = 0 //If enabled, the AI cannot *Remotely* control a bot. It can still control it through cameras.
+ var/mob/living/silicon/ai/calling_ai //Links a bot to the AI calling it.
+ var/obj/item/radio/Radio //The bot's radio, for speaking to people.
+ var/radio_key = null //which channels can the bot listen to
+ var/radio_channel = RADIO_CHANNEL_COMMON //The bot's default radio channel
+ var/auto_patrol = 0// set to make bot automatically patrol
+ var/turf/patrol_target // this is turf to navigate to (location of beacon)
+ var/turf/summon_target // The turf of a user summoning a bot.
+ var/new_destination // pending new destination (waiting for beacon response)
+ var/destination // destination description tag
+ var/next_destination // the next destination in the patrol route
+ var/shuffle = FALSE // If we should shuffle our adjacency checking
+
+ var/blockcount = 0 //number of times retried a blocked path
+ var/awaiting_beacon = 0 // count of pticks awaiting a beacon response
+
+ var/nearest_beacon // the nearest beacon's tag
+ var/turf/nearest_beacon_loc // the nearest beacon's location
+
+ var/beacon_freq = FREQ_NAV_BEACON
+ var/model = "" //The type of bot it is.
+ var/bot_type = 0 //The type of bot it is, for radio control.
+ var/data_hud_type = DATA_HUD_DIAGNOSTIC_BASIC //The type of data HUD the bot uses. Diagnostic by default.
+ //This holds text for what the bot is mode doing, reported on the remote bot control interface.
+ var/list/mode_name = list("In Pursuit","Preparing to Arrest", "Arresting", \
+ "Beginning Patrol", "Patrolling", "Summoned by PDA", \
+ "Cleaning", "Repairing", "Proceeding to work site", "Healing", \
+ "Proceeding to AI waypoint", "Navigating to Delivery Location", "Navigating to Home", \
+ "Waiting for clear path", "Calculating navigation path", "Pinging beacon network", "Unable to reach destination")
+ var/datum/atom_hud/data/bot_path/path_hud = new /datum/atom_hud/data/bot_path()
+ var/path_image_icon = 'icons/mob/aibots.dmi'
+ var/path_image_icon_state = "path_indicator"
+ var/path_image_color = "#FFFFFF"
+ var/reset_access_timer_id
+ var/ignorelistcleanuptimer = 1 // This ticks up every automated action, at 300 we clean the ignore list
+ var/robot_arm = /obj/item/bodypart/r_arm/robot
+
+ var/commissioned = FALSE // Will other (noncommissioned) bots salute this bot?
+ var/can_salute = TRUE
+ var/salute_delay = 60 SECONDS
+
+ hud_possible = list(DIAG_STAT_HUD, DIAG_BOT_HUD, DIAG_HUD, DIAG_PATH_HUD = HUD_LIST_LIST) //Diagnostic HUD views
+
+/mob/living/simple_animal/bot/proc/get_mode()
+ if(client) //Player bots do not have modes, thus the override. Also an easy way for PDA users/AI to know when a bot is a player.
+ if(paicard)
+ return "pAI Controlled"
+ else
+ return "Autonomous"
+ else if(!on)
+ return "Inactive"
+ else if(!mode)
+ return "Idle"
+ else
+ return "[mode_name[mode]]"
+
+/mob/living/simple_animal/bot/proc/turn_on()
+ if(stat)
+ return FALSE
+ on = TRUE
+ canmove = TRUE
+ set_light(initial(light_range))
+ update_icon()
+ diag_hud_set_botstat()
+ return TRUE
+
+/mob/living/simple_animal/bot/proc/turn_off()
+ on = FALSE
+ canmove = FALSE
+ set_light(0)
+ bot_reset() //Resets an AI's call, should it exist.
+ update_icon()
+
+/mob/living/simple_animal/bot/Initialize()
+ . = ..()
+ GLOB.bots_list += src
+ access_card = new /obj/item/card/id(src)
+//This access is so bots can be immediately set to patrol and leave Robotics, instead of having to be let out first.
+ access_card.access += ACCESS_ROBOTICS
+ set_custom_texts()
+ Radio = new/obj/item/radio(src)
+ if(radio_key)
+ Radio.keyslot = new radio_key
+ Radio.subspace_transmission = TRUE
+ Radio.canhear_range = 0 // anything greater will have the bot broadcast the channel as if it were saying it out loud.
+ Radio.recalculateChannels()
+
+ bot_core = new bot_core_type(src)
+
+ //Adds bot to the diagnostic HUD system
+ prepare_huds()
+ for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds)
+ diag_hud.add_to_hud(src)
+ diag_hud_set_bothealth()
+ diag_hud_set_botstat()
+ diag_hud_set_botmode()
+
+ //If a bot has its own HUD (for player bots), provide it.
+ if(data_hud_type)
+ var/datum/atom_hud/datahud = GLOB.huds[data_hud_type]
+ datahud.add_hud_to(src)
+ if(path_hud)
+ path_hud.add_to_hud(src)
+ path_hud.add_hud_to(src)
+
+/mob/living/simple_animal/bot/update_canmove()
+ . = ..()
+ if(!on)
+ . = 0
+ canmove = .
+
+/mob/living/simple_animal/bot/Destroy()
+ if(path_hud)
+ QDEL_NULL(path_hud)
+ path_hud = null
+ GLOB.bots_list -= src
+ if(paicard)
+ ejectpai()
+ qdel(Radio)
+ qdel(access_card)
+ qdel(bot_core)
+ return ..()
+
+/mob/living/simple_animal/bot/bee_friendly()
+ return TRUE
+
+/mob/living/simple_animal/bot/death(gibbed)
+ . = ..()
+ if(!gibbed)
+ explode()
+
+/mob/living/simple_animal/bot/proc/explode()
+ qdel(src)
+
+/mob/living/simple_animal/bot/emag_act(mob/user)
+ if(locked) //First emag application unlocks the bot's interface. Apply a screwdriver to use the emag again.
+ locked = FALSE
+ emagged = 1
+ to_chat(user, "You bypass [src]'s controls.")
+ return
+ if(!locked && open) //Bot panel is unlocked by ID or emag, and the panel is screwed open. Ready for emagging.
+ emagged = 2
+ remote_disabled = 1 //Manually emagging the bot locks out the AI built in panel.
+ locked = TRUE //Access denied forever!
+ bot_reset()
+ turn_on() //The bot automatically turns on when emagged, unless recently hit with EMP.
+ to_chat(src, "(#$*#$^^( OVERRIDE DETECTED")
+ log_combat(user, src, "emagged")
+ return
+ else //Bot is unlocked, but the maint panel has not been opened with a screwdriver yet.
+ to_chat(user, "You need to open maintenance panel first!")
+
+/mob/living/simple_animal/bot/examine(mob/user)
+ . = ..()
+ if(health < maxHealth)
+ if(health > maxHealth/3)
+ . += "[src]'s parts look loose."
+ else
+ . += "[src]'s parts look very loose!"
+ else
+ . += "[src] is in pristine condition."
+
+/mob/living/simple_animal/bot/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
+ if(amount>0 && prob(10))
+ new /obj/effect/decal/cleanable/oil(loc)
+ . = ..()
+
+/mob/living/simple_animal/bot/updatehealth()
+ ..()
+ diag_hud_set_bothealth()
+
+/mob/living/simple_animal/bot/med_hud_set_health()
+ return //we use a different hud
+
+/mob/living/simple_animal/bot/med_hud_set_status()
+ return //we use a different hud
+
+/mob/living/simple_animal/bot/handle_automated_action() //Master process which handles code common across most bots.
+ diag_hud_set_botmode()
+
+ if (ignorelistcleanuptimer % 300 == 0) // Every 300 actions, clean up the ignore list from old junk
+ for(var/ref in ignore_list)
+ var/atom/referredatom = locate(ref)
+ if (!referredatom || !istype(referredatom) || QDELETED(referredatom))
+ ignore_list -= ref
+ ignorelistcleanuptimer = 1
+ else
+ ignorelistcleanuptimer++
+
+ if(!on || client)
+ return
+
+ if(!commissioned && can_salute)
+ for(var/mob/living/simple_animal/bot/B in get_hearers_in_view(5, get_turf(src)))
+ if(B.commissioned)
+ visible_message("[src] performs an elaborate salute for [B]!")
+ can_salute = FALSE
+ addtimer(VARSET_CALLBACK(src, can_salute, TRUE), salute_delay)
+ break
+
+ switch(mode) //High-priority overrides are processed first. Bots can do nothing else while under direct command.
+ if(BOT_RESPONDING) //Called by the AI.
+ call_mode()
+ return
+ if(BOT_SUMMON) //Called by PDA
+ bot_summon()
+ return
+ return TRUE //Successful completion. Used to prevent child process() continuing if this one is ended early.
+
+
+/mob/living/simple_animal/bot/attack_hand(mob/living/carbon/human/H)
+ if(H.a_intent == INTENT_HELP)
+ interact(H)
+ else
+ return ..()
+
+/mob/living/simple_animal/bot/attack_ai(mob/user)
+ if(!topic_denied(user))
+ interact(user)
+ else
+ to_chat(user, "[src]'s interface is not responding!")
+
+/mob/living/simple_animal/bot/interact(mob/user)
+ show_controls(user)
+
+/mob/living/simple_animal/bot/attackby(obj/item/W, mob/user, params)
+ if(istype(W, /obj/item/screwdriver))
+ if(!locked)
+ open = !open
+ to_chat(user, "The maintenance panel is now [open ? "opened" : "closed"].")
+ else
+ to_chat(user, "The maintenance panel is locked.")
+ else if(istype(W, /obj/item/card/id) || istype(W, /obj/item/pda))
+ if(bot_core.allowed(user) && !open && !emagged)
+ locked = !locked
+ to_chat(user, "Controls are now [locked ? "locked." : "unlocked."]")
+ else
+ if(emagged)
+ to_chat(user, "ERROR")
+ if(open)
+ to_chat(user, "Please close the access panel before locking it.")
+ else
+ to_chat(user, "Access denied.")
+ else if(istype(W, /obj/item/paicard))
+ insertpai(user, W)
+ else if(W.tool_behaviour == TOOL_HEMOSTAT && paicard)
+ if(open)
+ to_chat(user, "Close the access panel before manipulating the personality slot!")
+ else
+ to_chat(user, "You attempt to pull [paicard] free...")
+ if(do_after(user, 30, target = src))
+ if (paicard)
+ user.visible_message("[user] uses [W] to pull [paicard] out of [bot_name]!","You pull [paicard] out of [bot_name] with [W].")
+ ejectpai(user)
+ else
+ user.changeNext_move(CLICK_CD_MELEE)
+ if(istype(W, /obj/item/weldingtool) && user.a_intent != INTENT_HARM)
+ if(health >= maxHealth)
+ to_chat(user, "[src] does not need a repair!")
+ return
+ if(!open)
+ to_chat(user, "Unable to repair with the maintenance panel closed!")
+ return
+
+ if(W.use_tool(src, user, 0, volume=40))
+ adjustHealth(-10)
+ user.visible_message("[user] repairs [src]!","You repair [src].")
+ else
+ if(W.force) //if force is non-zero
+ do_sparks(5, TRUE, src)
+ ..()
+
+/mob/living/simple_animal/bot/bullet_act(obj/item/projectile/Proj)
+ if(Proj && (Proj.damage_type == BRUTE || Proj.damage_type == BURN))
+ if(prob(75) && Proj.damage > 0)
+ do_sparks(5, TRUE, src)
+ return ..()
+
+/mob/living/simple_animal/bot/emp_act(severity)
+ . = ..()
+ if(. & EMP_PROTECT_SELF)
+ return
+ var/was_on = on
+ stat |= EMPED
+ new /obj/effect/temp_visual/emp(loc)
+ if(paicard)
+ paicard.emp_act(severity)
+ src.visible_message("[paicard] is flies out of [bot_name]!","You are forcefully ejected from [bot_name]!")
+ ejectpai(0)
+ if(on)
+ turn_off()
+ spawn(severity*300)
+ stat &= ~EMPED
+ if(was_on)
+ turn_on()
+
+/mob/living/simple_animal/bot/proc/set_custom_texts() //Superclass for setting hack texts. Appears only if a set is not given to a bot locally.
+ text_hack = "You hack [name]."
+ text_dehack = "You reset [name]."
+ text_dehack_fail = "You fail to reset [name]."
+
+/mob/living/simple_animal/bot/proc/speak(message,channel) //Pass a message to have the bot say() it. Pass a frequency to say it on the radio.
+ if((!on) || (!message))
+ return
+ if(channel && Radio.channels[channel])// Use radio if we have channel key
+ Radio.talk_into(src, message, channel)
+ else
+ say(message)
+
+/mob/living/simple_animal/bot/radio(message, message_mode, list/spans, language)
+ . = ..()
+ if(. != 0)
+ return
+
+ switch(message_mode)
+ if(MODE_HEADSET)
+ Radio.talk_into(src, message, , spans, language)
+ return REDUCE_RANGE
+
+ if(MODE_DEPARTMENT)
+ Radio.talk_into(src, message, message_mode, spans, language)
+ return REDUCE_RANGE
+
+ if(message_mode in GLOB.radiochannels)
+ Radio.talk_into(src, message, message_mode, spans, language)
+ return REDUCE_RANGE
+
+/mob/living/simple_animal/bot/proc/drop_part(obj/item/drop_item, dropzone)
+ var/dropped_item = new drop_item(dropzone)
+ drop_item = null
+
+ if(istype(dropped_item, /obj/item/stock_parts/cell))
+ var/obj/item/stock_parts/cell/dropped_cell = dropped_item
+ dropped_cell.charge = 0
+ dropped_cell.update_icon()
+
+ else if(istype(dropped_item, /obj/item/storage))
+ var/obj/item/storage/S = dropped_item
+ S.contents = list()
+
+ else if(istype(dropped_item, /obj/item/gun/energy))
+ var/obj/item/gun/energy/dropped_gun = dropped_item
+ dropped_gun.cell.charge = 0
+ dropped_gun.update_icon()
+
+//Generalized behavior code, override where needed!
+
+/*
+scan() will search for a given type (such as turfs, human mobs, or objects) in the bot's view range, and return a single result.
+Arguments: The object type to be searched (such as "/mob/living/carbon/human"), the old scan result to be ignored, if one exists,
+and the view range, which defaults to 7 (full screen) if an override is not passed.
+If the bot maintains an ignore list, it is also checked here.
+
+Example usage: patient = scan(/mob/living/carbon/human, oldpatient, 1)
+The proc would return a human next to the bot to be set to the patient var.
+Pass the desired type path itself, declaring a temporary var beforehand is not required.
+*/
+/mob/living/simple_animal/bot/proc/scan(scan_type, old_target, scan_range = DEFAULT_SCAN_RANGE)
+ var/turf/T = get_turf(src)
+ if(!T)
+ return
+ var/list/adjacent = T.GetAtmosAdjacentTurfs(1)
+ if(shuffle) //If we were on the same tile as another bot, let's randomize our choices so we dont both go the same way
+ adjacent = shuffle(adjacent)
+ shuffle = FALSE
+ for(var/scan in adjacent)//Let's see if there's something right next to us first!
+ if(check_bot(scan)) //Is there another bot there? Then let's just skip it
+ continue
+ if(isturf(scan_type)) //If we're lookeing for a turf we can just run the checks directly!
+ var/final_result = checkscan(scan,scan_type,old_target)
+ if(final_result)
+ return final_result
+ else
+ var/turf/turfy = scan
+ for(var/deepscan in turfy.contents)//Check the contents since adjacent is turfs
+ var/final_result = checkscan(deepscan,scan_type,old_target)
+ if(final_result)
+ return final_result
+ for (var/scan in shuffle(view(scan_range, src))-adjacent) //Search for something in range!
+ var/final_result = checkscan(scan,scan_type,old_target)
+ if(final_result)
+ return final_result
+
+/mob/living/simple_animal/bot/proc/checkscan(scan, scan_type, old_target)
+ if(!istype(scan, scan_type)) //Check that the thing we found is the type we want!
+ return FALSE //If not, keep searching!
+ if( (REF(scan) in ignore_list) || (scan == old_target) ) //Filter for blacklisted elements, usually unreachable or previously processed oness
+ return FALSE
+
+ var/scan_result = process_scan(scan) //Some bots may require additional processing when a result is selected.
+ if(scan_result)
+ return scan_result
+ else
+ return FALSE //The current element failed assessment, move on to the next.
+ return
+
+/mob/living/simple_animal/bot/proc/check_bot(targ)
+ var/turf/T = get_turf(targ)
+ if(T)
+ for(var/C in T.contents)
+ if(istype(C,type) && (C != src)) //Is there another bot there already? If so, let's skip it so we dont all atack on top of eachother.
+ return TRUE //Let's abort if we find a bot so we dont have to keep rechecking
+
+//When the scan finds a target, run bot specific processing to select it for the next step. Empty by default.
+/mob/living/simple_animal/bot/proc/process_scan(scan_target)
+ return scan_target
+
+
+/mob/living/simple_animal/bot/proc/add_to_ignore(subject)
+ if(ignore_list.len < 50) //This will help keep track of them, so the bot is always trying to reach a blocked spot.
+ ignore_list += REF(subject)
+ else //If the list is full, insert newest, delete oldest.
+ ignore_list.Cut(1,2)
+ ignore_list += REF(subject)
+
+/*
+Movement proc for stepping a bot through a path generated through A-star.
+Pass a positive integer as an argument to override a bot's default speed.
+*/
+/mob/living/simple_animal/bot/proc/bot_move(dest, move_speed)
+ if(!dest || !path || path.len == 0) //A-star failed or a path/destination was not set.
+ set_path(null)
+ return FALSE
+ dest = get_turf(dest) //We must always compare turfs, so get the turf of the dest var if dest was originally something else.
+ var/turf/last_node = get_turf(path[path.len]) //This is the turf at the end of the path, it should be equal to dest.
+ if(get_turf(src) == dest) //We have arrived, no need to move again.
+ return TRUE
+ else if(dest != last_node) //The path should lead us to our given destination. If this is not true, we must stop.
+ set_path(null)
+ return FALSE
+ var/step_count = move_speed ? move_speed : base_speed //If a value is passed into move_speed, use that instead of the default speed var.
+
+ if(step_count >= 1 && tries < BOT_STEP_MAX_RETRIES)
+ for(var/step_number = 0, step_number < step_count,step_number++)
+ spawn(BOT_STEP_DELAY*step_number)
+ bot_step(dest)
+ else
+ return FALSE
+ return TRUE
+
+
+/mob/living/simple_animal/bot/proc/bot_step(dest) //Step,increase tries if failed
+ if(!path)
+ return FALSE
+ if(path.len > 1)
+ step_towards(src, path[1])
+ if(get_turf(src) == path[1]) //Successful move
+ increment_path()
+ tries = 0
+ else
+ tries++
+ return FALSE
+ else if(path.len == 1)
+ step_to(src, dest)
+ set_path(null)
+ return TRUE
+
+
+/mob/living/simple_animal/bot/proc/check_bot_access()
+ if(mode != BOT_SUMMON && mode != BOT_RESPONDING)
+ access_card.access = prev_access
+
+/mob/living/simple_animal/bot/proc/call_bot(caller, turf/waypoint, message=TRUE)
+ bot_reset() //Reset a bot before setting it to call mode.
+
+ //For giving the bot temporary all-access.
+ var/obj/item/card/id/all_access = new /obj/item/card/id
+ var/datum/job/captain/All = new/datum/job/captain
+ all_access.access = All.get_access()
+
+ set_path(get_path_to(src, waypoint, /turf/proc/Distance_cardinal, 0, 200, id=all_access))
+ calling_ai = caller //Link the AI to the bot!
+ ai_waypoint = waypoint
+
+ if(path && path.len) //Ensures that a valid path is calculated!
+ var/end_area = get_area_name(waypoint)
+ if(!on)
+ turn_on() //Saves the AI the hassle of having to activate a bot manually.
+ access_card = all_access //Give the bot all-access while under the AI's command.
+ if(client)
+ reset_access_timer_id = addtimer(CALLBACK (src, .proc/bot_reset), 600, TIMER_UNIQUE|TIMER_OVERRIDE|TIMER_STOPPABLE) //if the bot is player controlled, they get the extra access for a limited time
+ to_chat(src, "Priority waypoint set by [icon2html(calling_ai, src)] [caller]. Proceed to [end_area].
[path.len-1] meters to destination. You have been granted additional door access for 60 seconds.")
+ if(message)
+ to_chat(calling_ai, "[icon2html(src, calling_ai)] [name] called to [end_area]. [path.len-1] meters to destination.")
+ pathset = 1
+ mode = BOT_RESPONDING
+ tries = 0
+ else
+ if(message)
+ to_chat(calling_ai, "Failed to calculate a valid route. Ensure destination is clear of obstructions and within range.")
+ calling_ai = null
+ set_path(null)
+
+/mob/living/simple_animal/bot/proc/call_mode() //Handles preparing a bot for a call, as well as calling the move proc.
+//Handles the bot's movement during a call.
+ var/success = bot_move(ai_waypoint, 3)
+ if(!success)
+ if(calling_ai)
+ to_chat(calling_ai, "[icon2html(src, calling_ai)] [get_turf(src) == ai_waypoint ? "[src] successfully arrived to waypoint." : "[src] failed to reach waypoint."]")
+ calling_ai = null
+ bot_reset()
+
+/mob/living/simple_animal/bot/proc/bot_reset()
+ if(calling_ai) //Simple notification to the AI if it called a bot. It will not know the cause or identity of the bot.
+ to_chat(calling_ai, "Call command to a bot has been reset.")
+ calling_ai = null
+ if(reset_access_timer_id)
+ deltimer(reset_access_timer_id)
+ reset_access_timer_id = null
+ set_path(null)
+ summon_target = null
+ pathset = 0
+ access_card.access = prev_access
+ tries = 0
+ mode = BOT_IDLE
+ diag_hud_set_botstat()
+ diag_hud_set_botmode()
+
+
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//Patrol and summon code!
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+/mob/living/simple_animal/bot/proc/bot_patrol()
+ patrol_step()
+ spawn(5)
+ if(mode == BOT_PATROL)
+ patrol_step()
+ return
+
+/mob/living/simple_animal/bot/proc/start_patrol()
+
+ if(tries >= BOT_STEP_MAX_RETRIES) //Bot is trapped, so stop trying to patrol.
+ auto_patrol = 0
+ tries = 0
+ speak("Unable to start patrol.")
+
+ return
+
+ if(!auto_patrol) //A bot not set to patrol should not be patrolling.
+ mode = BOT_IDLE
+ return
+
+ if(patrol_target) // has patrol target
+ spawn(0)
+ calc_path() // Find a route to it
+ if(path.len == 0)
+ patrol_target = null
+ return
+ mode = BOT_PATROL
+ else // no patrol target, so need a new one
+ speak("Engaging patrol mode.")
+ find_patrol_target()
+ tries++
+ return
+
+// perform a single patrol step
+
+/mob/living/simple_animal/bot/proc/patrol_step()
+
+ if(client) // In use by player, don't actually move.
+ return
+
+ if(loc == patrol_target) // reached target
+ //Find the next beacon matching the target.
+ if(!get_next_patrol_target())
+ find_patrol_target() //If it fails, look for the nearest one instead.
+ return
+
+ else if(path.len > 0 && patrol_target) // valid path
+ if(path[1] == loc)
+ increment_path()
+ return
+
+
+ var/moved = bot_move(patrol_target)//step_towards(src, next) // attempt to move
+ if(!moved) //Couldn't proceed the next step of the path BOT_STEP_MAX_RETRIES times
+ spawn(2)
+ calc_path()
+ if(path.len == 0)
+ find_patrol_target()
+ tries = 0
+
+ else // no path, so calculate new one
+ mode = BOT_START_PATROL
+
+// finds the nearest beacon to self
+/mob/living/simple_animal/bot/proc/find_patrol_target()
+ nearest_beacon = null
+ new_destination = null
+ find_nearest_beacon()
+ if(nearest_beacon)
+ patrol_target = nearest_beacon_loc
+ destination = next_destination
+ else
+ auto_patrol = 0
+ mode = BOT_IDLE
+ speak("Disengaging patrol mode.")
+
+/mob/living/simple_animal/bot/proc/get_next_patrol_target()
+ // search the beacon list for the next target in the list.
+ for(var/obj/machinery/navbeacon/NB in GLOB.navbeacons["[z]"])
+ if(NB.location == next_destination) //Does the Beacon location text match the destination?
+ destination = new_destination //We now know the name of where we want to go.
+ patrol_target = NB.loc //Get its location and set it as the target.
+ next_destination = NB.codes["next_patrol"] //Also get the name of the next beacon in line.
+ return TRUE
+
+/mob/living/simple_animal/bot/proc/find_nearest_beacon()
+ for(var/obj/machinery/navbeacon/NB in GLOB.navbeacons["[z]"])
+ var/dist = get_dist(src, NB)
+ if(nearest_beacon) //Loop though the beacon net to find the true closest beacon.
+ //Ignore the beacon if were are located on it.
+ if(dist>1 && dist 1) //Begin the search, save this one for comparison on the next loop.
+ nearest_beacon = NB.location
+ nearest_beacon_loc = NB.loc
+ patrol_target = nearest_beacon_loc
+ destination = nearest_beacon
+
+//PDA control. Some bots, especially MULEs, may have more parameters.
+/mob/living/simple_animal/bot/proc/bot_control(command, mob/user, turf/user_turf, list/user_access = list())
+ if(!on || emagged == 2 || remote_disabled) //Emagged bots do not respect anyone's authority! Bots with their remote controls off cannot get commands.
+ return TRUE //ACCESS DENIED
+ if(client)
+ bot_control_message(command,user,user_turf,user_access)
+ // process control input
+ switch(command)
+ if("patroloff")
+ bot_reset() //HOLD IT!!
+ auto_patrol = 0
+
+ if("patrolon")
+ auto_patrol = 1
+
+ if("summon")
+ bot_reset()
+ summon_target = user_turf
+ if(user_access.len != 0)
+ access_card.access = user_access + prev_access //Adds the user's access, if any.
+ mode = BOT_SUMMON
+ speak("Responding.", radio_channel)
+ calc_summon_path()
+
+ if("ejectpai")
+ ejectpairemote(user)
+ return
+
+//
+/mob/living/simple_animal/bot/proc/bot_control_message(command,user,user_turf,user_access)
+ switch(command)
+ if("patroloff")
+ to_chat(src, "STOP PATROL")
+ if("patrolon")
+ to_chat(src, "START PATROL")
+ if("summon")
+ var/area/a = get_area(user_turf)
+ to_chat(src, "PRIORITY ALERT:[user] in [a.name]!")
+ if("stop")
+ to_chat(src, "STOP!")
+
+ if("go")
+ to_chat(src, "GO!")
+
+ if("home")
+ to_chat(src, "RETURN HOME!")
+ if("ejectpai")
+ return
+ else
+ to_chat(src, "Unidentified control sequence received:[command]")
+
+/mob/living/simple_animal/bot/proc/bot_summon() // summoned to PDA
+ summon_step()
+
+// calculates a path to the current destination
+// given an optional turf to avoid
+/mob/living/simple_animal/bot/proc/calc_path(turf/avoid)
+ check_bot_access()
+ set_path(get_path_to(src, patrol_target, /turf/proc/Distance_cardinal, 0, 120, id=access_card, exclude=avoid))
+
+/mob/living/simple_animal/bot/proc/calc_summon_path(turf/avoid)
+ check_bot_access()
+ spawn()
+ set_path(get_path_to(src, summon_target, /turf/proc/Distance_cardinal, 0, 150, id=access_card, exclude=avoid))
+ if(!path.len) //Cannot reach target. Give up and announce the issue.
+ speak("Summon command failed, destination unreachable.",radio_channel)
+ bot_reset()
+
+/mob/living/simple_animal/bot/proc/summon_step()
+
+ if(client) // In use by player, don't actually move.
+ return
+
+ if(loc == summon_target) // Arrived to summon location.
+ bot_reset()
+ return
+
+ else if(path.len > 0 && summon_target) //Proper path acquired!
+ if(path[1] == loc)
+ increment_path()
+ return
+
+ var/moved = bot_move(summon_target, 3) // Move attempt
+ if(!moved)
+ spawn(2)
+ calc_summon_path()
+ tries = 0
+
+ else // no path, so calculate new one
+ calc_summon_path()
+
+/mob/living/simple_animal/bot/Bump(M as mob|obj) //Leave no door unopened!
+ . = ..()
+ if((istype(M, /obj/machinery/door/airlock) || istype(M, /obj/machinery/door/window)) && (!isnull(access_card)))
+ var/obj/machinery/door/D = M
+ if(D.check_access(access_card))
+ D.open()
+ frustration = 0
+
+/mob/living/simple_animal/bot/proc/show_controls(mob/M)
+ users |= M
+ var/dat = ""
+ dat = get_controls(M)
+ var/datum/browser/popup = new(M,window_id,window_name,350,600)
+ popup.set_content(dat)
+ popup.open(use_onclose = 0)
+ onclose(M,window_id,ref=src)
+ return
+
+/mob/living/simple_animal/bot/proc/update_controls()
+ for(var/mob/M in users)
+ show_controls(M)
+
+/mob/living/simple_animal/bot/proc/get_controls(mob/M)
+ return "PROTOBOT - NOT FOR USE"
+
+/mob/living/simple_animal/bot/Topic(href, href_list)
+ //No ..() to prevent strip panel showing up - Todo: make that saner
+ if(href_list["close"])// HUE HUE
+ if(usr in users)
+ users.Remove(usr)
+ return TRUE
+
+ if(topic_denied(usr))
+ to_chat(usr, "[src]'s interface is not responding!")
+ return TRUE
+ add_fingerprint(usr)
+
+ if((href_list["power"]) && (bot_core.allowed(usr) || !locked))
+ if(on)
+ turn_off()
+ else
+ turn_on()
+
+ switch(href_list["operation"])
+ if("patrol")
+ auto_patrol = !auto_patrol
+ bot_reset()
+ if(!issilicon(usr) && !IsAdminGhost(usr) && !(bot_core.allowed(usr) || !locked))
+ return TRUE
+ if("remote")
+ remote_disabled = !remote_disabled
+ if("hack")
+ if(!issilicon(usr) && !IsAdminGhost(usr))
+ var/msg = "[key_name(usr)] attempted to hack a bot with a href that shouldn't be available!"
+ message_admins(msg)
+ log_admin(msg)
+ return TRUE
+ if(emagged != 2)
+ emagged = 2
+ hacked = TRUE
+ locked = TRUE
+ to_chat(usr, "[text_hack]")
+ bot_reset()
+ else if(!hacked)
+ to_chat(usr, "[text_dehack_fail]")
+ else
+ emagged = FALSE
+ hacked = FALSE
+ to_chat(usr, "[text_dehack]")
+ bot_reset()
+ if("ejectpai")
+ if(paicard && (!locked || issilicon(usr) || IsAdminGhost(usr)))
+ to_chat(usr, "You eject [paicard] from [bot_name]")
+ ejectpai(usr)
+ update_controls()
+
+/mob/living/simple_animal/bot/proc/update_icon()
+ icon_state = "[initial(icon_state)][on]"
+
+// Machinery to simplify topic and access calls
+/obj/machinery/bot_core
+ use_power = NO_POWER_USE
+ anchored = FALSE
+ var/mob/living/simple_animal/bot/owner = null
+
+/obj/machinery/bot_core/Initialize()
+ . = ..()
+ owner = loc
+ if(!istype(owner))
+ return INITIALIZE_HINT_QDEL
+
+/mob/living/simple_animal/bot/proc/topic_denied(mob/user) //Access check proc for bot topics! Remember to place in a bot's individual Topic if desired.
+ if(!user.canUseTopic(src))
+ return TRUE
+ // 0 for access, 1 for denied.
+ if(emagged == 2) //An emagged bot cannot be controlled by humans, silicons can if one hacked it.
+ if(!hacked) //Manually emagged by a human - access denied to all.
+ return TRUE
+ else if(!issilicon(user) && !IsAdminGhost(user)) //Bot is hacked, so only silicons and admins are allowed access.
+ return TRUE
+ return FALSE
+
+/mob/living/simple_animal/bot/proc/hack(mob/user)
+ var/hack
+ if(issilicon(user) || IsAdminGhost(user)) //Allows silicons or admins to toggle the emag status of a bot.
+ hack += "[emagged == 2 ? "Software compromised! Unit may exhibit dangerous or erratic behavior." : "Unit operating normally. Release safety lock?"]
"
+ hack += "Harm Prevention Safety System: [emagged ? "DANGER" : "Engaged"]
"
+ else if(!locked) //Humans with access can use this option to hide a bot from the AI's remote control panel and PDA control.
+ hack += "Remote network control radio: [remote_disabled ? "Disconnected" : "Connected"]
"
+ return hack
+
+/mob/living/simple_animal/bot/proc/showpai(mob/user)
+ var/eject = ""
+ if((!locked || issilicon(usr) || IsAdminGhost(usr)))
+ if(paicard || allow_pai)
+ eject += "Personality card status: "
+ if(paicard)
+ if(client)
+ eject += "Active"
+ else
+ eject += "Inactive"
+ else if(!allow_pai || key)
+ eject += "Unavailable"
+ else
+ eject += "Not inserted"
+ eject += "
"
+ eject += "
"
+ return eject
+
+/mob/living/simple_animal/bot/proc/insertpai(mob/user, obj/item/paicard/card)
+ if(paicard)
+ to_chat(user, "A [paicard] is already inserted!")
+ else if(allow_pai && !key)
+ if(!locked && !open)
+ if(card.pai && card.pai.mind)
+ if(!user.transferItemToLoc(card, src))
+ return
+ paicard = card
+ user.visible_message("[user] inserts [card] into [src]!","You insert [card] into [src].")
+ paicard.pai.mind.transfer_to(src)
+ to_chat(src, "You sense your form change as you are uploaded into [src].")
+ bot_name = name
+ name = paicard.pai.name
+ faction = user.faction.Copy()
+ language_holder = paicard.pai.language_holder.copy(src)
+ log_combat(user, paicard.pai, "uploaded to [bot_name],")
+ return TRUE
+ else
+ to_chat(user, "[card] is inactive.")
+ else
+ to_chat(user, "The personality slot is locked.")
+ else
+ to_chat(user, "[src] is not compatible with [card]")
+
+/mob/living/simple_animal/bot/proc/ejectpai(mob/user = null, announce = 1)
+ if(paicard)
+ if(mind && paicard.pai)
+ mind.transfer_to(paicard.pai)
+ else if(paicard.pai)
+ paicard.pai.key = key
+ else
+ ghostize(0) // The pAI card that just got ejected was dead.
+ key = null
+ paicard.forceMove(loc)
+ if(user)
+ log_combat(user, paicard.pai, "ejected from [src.bot_name],")
+ else
+ log_combat(src, paicard.pai, "ejected")
+ if(announce)
+ to_chat(paicard.pai, "You feel your control fade as [paicard] ejects from [bot_name].")
+ paicard = null
+ name = bot_name
+ faction = initial(faction)
+
+/mob/living/simple_animal/bot/proc/ejectpairemote(mob/user)
+ if(bot_core.allowed(user) && paicard)
+ speak("Ejecting personality chip.", radio_channel)
+ ejectpai(user)
+
+/mob/living/simple_animal/bot/Login()
+ . = ..()
+ access_card.access += player_access
+ diag_hud_set_botmode()
+
+/mob/living/simple_animal/bot/Logout()
+ . = ..()
+ bot_reset()
+
+/mob/living/simple_animal/bot/revive(full_heal = 0, admin_revive = 0)
+ if(..())
+ update_icon()
+ . = 1
+
+/mob/living/simple_animal/bot/ghost()
+ if(stat != DEAD) // Only ghost if we're doing this while alive, the pAI probably isn't dead yet.
+ ..()
+ if(paicard && (!client || stat == DEAD))
+ ejectpai(0)
+
+/mob/living/simple_animal/bot/sentience_act()
+ faction -= "silicon"
+
+/mob/living/simple_animal/bot/proc/set_path(list/newpath)
+ path = newpath ? newpath : list()
+ if(!path_hud)
+ return
+ var/list/path_huds_watching_me = list(GLOB.huds[DATA_HUD_DIAGNOSTIC_ADVANCED])
+ if(path_hud)
+ path_huds_watching_me += path_hud
+ for(var/V in path_huds_watching_me)
+ var/datum/atom_hud/H = V
+ H.remove_from_hud(src)
+
+ var/list/path_images = hud_list[DIAG_PATH_HUD]
+ QDEL_LIST(path_images)
+ if(newpath)
+ for(var/i in 1 to newpath.len)
+ var/turf/T = newpath[i]
+ if(T == loc) //don't bother putting an image if it's where we already exist.
+ continue
+ var/direction = NORTH
+ if(i > 1)
+ var/turf/prevT = path[i - 1]
+ var/image/prevI = path[prevT]
+ direction = get_dir(prevT, T)
+ if(i > 2)
+ var/turf/prevprevT = path[i - 2]
+ var/prevDir = get_dir(prevprevT, prevT)
+ var/mixDir = direction|prevDir
+ if(mixDir in GLOB.diagonals)
+ prevI.dir = mixDir
+ if(prevDir & (NORTH|SOUTH))
+ var/matrix/ntransform = matrix()
+ ntransform.Turn(90)
+ if((mixDir == NORTHWEST) || (mixDir == SOUTHEAST))
+ ntransform.Scale(-1, 1)
+ else
+ ntransform.Scale(1, -1)
+ prevI.transform = ntransform
+ var/mutable_appearance/MA = new /mutable_appearance()
+ MA.icon = path_image_icon
+ MA.icon_state = path_image_icon_state
+ MA.layer = ABOVE_OPEN_TURF_LAYER
+ MA.plane = 0
+ MA.appearance_flags = RESET_COLOR|RESET_TRANSFORM
+ MA.color = path_image_color
+ MA.dir = direction
+ var/image/I = image(loc = T)
+ I.appearance = MA
+ path[T] = I
+ path_images += I
+
+ for(var/V in path_huds_watching_me)
+ var/datum/atom_hud/H = V
+ H.add_to_hud(src)
+
+
+/mob/living/simple_animal/bot/proc/increment_path()
+ if(!path || !path.len)
+ return
+ var/image/I = path[path[1]]
+ if(I)
+ I.icon_state = null
+ path.Cut(1, 2)
diff --git a/code/modules/projectiles/guns/ballistic/revolver.dm b/code/modules/projectiles/guns/ballistic/revolver.dm
index 3c0d2800..aa3d0dab 100644
--- a/code/modules/projectiles/guns/ballistic/revolver.dm
+++ b/code/modules/projectiles/guns/ballistic/revolver.dm
@@ -277,12 +277,12 @@
..()
if(istype(A, /obj/item/ammo_box) || istype(A, /obj/item/ammo_casing))
chamber_round()
+ if(A.tool_behaviour == TOOL_SAW || istype(A, /obj/item/gun/energy/plasmacutter))
+ sawoff(user)
if(istype(A, /obj/item/melee/transforming/energy))
var/obj/item/melee/transforming/energy/W = A
if(W.active)
sawoff(user)
- if(istype(A, /obj/item/circular_saw) || istype(A, /obj/item/gun/energy/plasmacutter))
- sawoff(user)
/obj/item/gun/ballistic/revolver/doublebarrel/attack_self(mob/living/user)
var/num_unloaded = 0
diff --git a/code/modules/projectiles/guns/ballistic/shotgun.dm b/code/modules/projectiles/guns/ballistic/shotgun.dm
index ff51adfa..6ca47af1 100644
--- a/code/modules/projectiles/guns/ballistic/shotgun.dm
+++ b/code/modules/projectiles/guns/ballistic/shotgun.dm
@@ -91,7 +91,7 @@
/obj/item/gun/ballistic/shotgun/riot/attackby(obj/item/A, mob/user, params)
..()
- if(istype(A, /obj/item/circular_saw) || istype(A, /obj/item/gun/energy/plasmacutter))
+ if(A.tool_behaviour == TOOL_SAW || istype(A, /obj/item/gun/energy/plasmacutter))
sawoff(user)
if(istype(A, /obj/item/melee/transforming/energy))
var/obj/item/melee/transforming/energy/W = A
diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm
index 9cdabf34..e76ac3cf 100644
--- a/code/modules/research/designs/medical_designs.dm
+++ b/code/modules/research/designs/medical_designs.dm
@@ -609,36 +609,6 @@
/////////////////////
//Adv Surgery Tools//
/////////////////////
-/datum/design/laserscalpel
- name = "Laser Scalpel"
- desc = "A laser scalpel used for precise cutting."
- id = "laserscalpel"
- build_path = /obj/item/scalpel/advanced
- build_type = PROTOLATHE
- materials = list(MAT_METAL = 6000, MAT_GLASS = 1500, MAT_SILVER = 2000, MAT_GOLD = 1500, MAT_DIAMOND = 200, MAT_TITANIUM = 4000)
- category = list("Medical Designs")
- departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE
-
-/datum/design/mechanicalpinches
- name = "Mechanical Pinches"
- desc = "These pinches can be either used as retractor or hemostat."
- id = "mechanicalpinches"
- build_path = /obj/item/retractor/advanced
- build_type = PROTOLATHE
- materials = list(MAT_METAL = 12000, MAT_GLASS = 4000, MAT_SILVER = 4000, MAT_TITANIUM = 5000)
- category = list("Medical Designs")
- departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE
-
-/datum/design/searingtool
- name = "Searing Tool"
- desc = "Used to mend tissue togheter."
- id = "searingtool"
- build_path = /obj/item/cautery/advanced
- build_type = PROTOLATHE
- materials = list(MAT_METAL = 4000, MAT_GLASS = 2000, MAT_PLASMA = 2000, MAT_URANIUM = 3000, MAT_TITANIUM = 3000)
- category = list("Medical Designs")
- departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE
-
/datum/design/drapes
name = "Plastic Drapes"
desc = "A large surgery drape made of plastic."
@@ -649,6 +619,35 @@
category = list("Medical Designs")
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE
+/datum/design/retractor_adv
+ name = "Advanced Retractor"
+ desc = "An almagation of rods and gears, able to function as both a surgical clamp and retractor. "
+ id = "retractor_adv"
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL = 500, MAT_GLASS = 500, MAT_SILVER = 1500, MAT_GOLD = 1000)
+ build_path = /obj/item/retractor/advanced
+ category = list("Medical Designs")
+ departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE
+
+/datum/design/surgicaldrill_adv
+ name = "Surgical Laser Drill"
+ desc = "It projects a high power laser used for medical applications."
+ id = "surgicaldrill_adv"
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL = 2500, MAT_GLASS = 2500, MAT_SILVER = 6000, MAT_GOLD = 5500, MAT_DIAMOND = 3500)
+ build_path = /obj/item/surgicaldrill/advanced
+ category = list("Medical Designs")
+ departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE
+
+/datum/design/scalpel_adv
+ name = "Laser Scalpel"
+ desc = "An advanced scalpel which uses laser technology to cut."
+ id = "scalpel_adv"
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL = 1500, MAT_GLASS = 1500, MAT_SILVER = 4000, MAT_GOLD = 2500)
+ build_path = /obj/item/scalpel/advanced
+ category = list("Medical Designs")
+ departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE
/////////////////////////////////////////
//////////Alien Surgery Tools////////////
diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm
index 87f1e4cb..d2e374c3 100644
--- a/code/modules/research/techweb/all_nodes.dm
+++ b/code/modules/research/techweb/all_nodes.dm
@@ -116,7 +116,7 @@
display_name = "Advanced Surgery Tools"
description = "Refined and improved redesigns for the run-of-the-mill medical utensils."
prereq_ids = list("adv_biotech", "adv_surgery")
- design_ids = list("drapes", "laserscalpel", "mechanicalpinches", "searingtool" )
+ design_ids = list("drapes", "retractor_adv", "surgicaldrill_adv", "scalpel_adv")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
export_price = 5000
diff --git a/code/modules/surgery/advanced/bioware/experimental_dissection..dm b/code/modules/surgery/advanced/bioware/experimental_dissection..dm
index 6266480b..5dac3e74 100644
--- a/code/modules/surgery/advanced/bioware/experimental_dissection..dm
+++ b/code/modules/surgery/advanced/bioware/experimental_dissection..dm
@@ -20,7 +20,7 @@
/datum/surgery_step/dissection
name = "dissection"
- implements = list(/obj/item/scalpel = 60, /obj/item/kitchen/knife = 30, /obj/item/shard = 15)
+ implements = list(TOOL_SCALPEL = 60, /obj/item/kitchen/knife = 30, /obj/item/shard = 15)
time = 125
/datum/surgery_step/dissection/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
diff --git a/code/modules/surgery/advanced/brainwashing.dm b/code/modules/surgery/advanced/brainwashing.dm
index 8596323e..60d53324 100644
--- a/code/modules/surgery/advanced/brainwashing.dm
+++ b/code/modules/surgery/advanced/brainwashing.dm
@@ -12,7 +12,6 @@
/datum/surgery_step/clamp_bleeders,
/datum/surgery_step/brainwash,
/datum/surgery_step/close)
-
target_mobtypes = list(/mob/living/carbon/human)
possible_locs = list(BODY_ZONE_HEAD)
@@ -25,7 +24,7 @@
return TRUE
/datum/surgery_step/brainwash
name = "brainwash"
- implements = list(/obj/item/hemostat = 85, TOOL_WIRECUTTER = 50, /obj/item/stack/packageWrap = 35, /obj/item/stack/cable_coil = 15)
+ implements = list(TOOL_HEMOSTAT = 85, TOOL_WIRECUTTER = 50, /obj/item/stack/packageWrap = 35, /obj/item/stack/cable_coil = 15)
time = 200
var/objective
/datum/surgery_step/brainwash/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
diff --git a/code/modules/surgery/advanced/lobotomy.dm b/code/modules/surgery/advanced/lobotomy.dm
index 0f516a39..5667a213 100644
--- a/code/modules/surgery/advanced/lobotomy.dm
+++ b/code/modules/surgery/advanced/lobotomy.dm
@@ -21,7 +21,7 @@
return TRUE
/datum/surgery_step/lobotomize
name = "perform lobotomy"
- implements = list(/obj/item/scalpel = 85, /obj/item/melee/transforming/energy/sword = 55, /obj/item/kitchen/knife = 35,
+ implements = list(TOOL_SCALPEL = 85, /obj/item/melee/transforming/energy/sword = 55, /obj/item/kitchen/knife = 35,
/obj/item/shard = 25, /obj/item = 20)
time = 100
/datum/surgery_step/lobotomize/tool_check(mob/user, obj/item/tool)
diff --git a/code/modules/surgery/advanced/necrotic_revival.dm b/code/modules/surgery/advanced/necrotic_revival.dm
index 6c8dc2f1..d95d0a3a 100644
--- a/code/modules/surgery/advanced/necrotic_revival.dm
+++ b/code/modules/surgery/advanced/necrotic_revival.dm
@@ -16,7 +16,7 @@
/datum/surgery_step/bionecrosis
name = "start bionecrosis"
- implements = list(/obj/item/hemostat = 100, TOOL_SCREWDRIVER = 35, /obj/item/pen = 15)
+ implements = list(TOOL_HEMOSTAT = 100, TOOL_SCREWDRIVER = 35, /obj/item/pen = 15)
implements = list(/obj/item/reagent_containers/syringe = 100, /obj/item/pen = 30)
time = 50
chems_needed = list(/datum/reagent/toxin/zombiepowder, /datum/reagent/medicine/rezadone)
diff --git a/code/modules/surgery/advanced/pacification.dm b/code/modules/surgery/advanced/pacification.dm
index 93864fc1..866c8584 100644
--- a/code/modules/surgery/advanced/pacification.dm
+++ b/code/modules/surgery/advanced/pacification.dm
@@ -18,7 +18,7 @@
return FALSE
/datum/surgery_step/pacify
name = "rewire brain"
- implements = list(/obj/item/hemostat = 100, TOOL_SCREWDRIVER = 35, /obj/item/pen = 15)
+ implements = list(TOOL_HEMOSTAT = 100, TOOL_SCREWDRIVER = 35, /obj/item/pen = 15)
time = 40
/datum/surgery_step/pacify/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
@@ -38,4 +38,4 @@
"[user] screws up, causing brain damage!",
"[user] completes the surgery on [target]'s brain.")
target.gain_trauma_type(BRAIN_TRAUMA_SEVERE, TRAUMA_RESILIENCE_LOBOTOMY)
- return FALSE
+ return FALSE
diff --git a/code/modules/surgery/advanced/toxichealing.dm b/code/modules/surgery/advanced/toxichealing.dm
index 30dfffd1..bb28a0fb 100644
--- a/code/modules/surgery/advanced/toxichealing.dm
+++ b/code/modules/surgery/advanced/toxichealing.dm
@@ -17,7 +17,7 @@
/datum/surgery_step/toxichealing
name = "rejuvenate body"
- implements = list(/obj/item/hemostat = 100, TOOL_SCREWDRIVER = 35, /obj/item/pen = 15)
+ implements = list(TOOL_HEMOSTAT = 100, TOOL_SCREWDRIVER = 35, /obj/item/pen = 15)
repeatable = TRUE
time = 25
diff --git a/code/modules/surgery/advanced/viral_bonding.dm b/code/modules/surgery/advanced/viral_bonding.dm
index 45dbb4de..6f33789a 100644
--- a/code/modules/surgery/advanced/viral_bonding.dm
+++ b/code/modules/surgery/advanced/viral_bonding.dm
@@ -18,7 +18,7 @@
return TRUE
/datum/surgery_step/viral_bond
name = "viral bond"
- implements = list(/obj/item/cautery = 100, TOOL_WELDER = 50, /obj/item = 30) // 30% success with any hot item.
+ implements = list(TOOL_CAUTERY = 100, TOOL_WELDER = 50, /obj/item = 30) // 30% success with any hot item.
time = 100
chems_needed = list(/datum/reagent/medicine/spaceacillin, /datum/reagent/consumable/virus_food,/datum/reagent/toxin/formaldehyde)
diff --git a/code/modules/surgery/amputation.dm b/code/modules/surgery/amputation.dm
index d491a3f7..5c775321 100644
--- a/code/modules/surgery/amputation.dm
+++ b/code/modules/surgery/amputation.dm
@@ -6,7 +6,7 @@
requires_bodypart_type = 0
/datum/surgery_step/sever_limb
name = "sever limb"
- implements = list(/obj/item/scalpel = 100, /obj/item/circular_saw = 100, /obj/item/melee/transforming/energy/sword/cyborg/saw = 100, /obj/item/melee/arm_blade = 80, /obj/item/twohanded/required/chainsaw = 80, /obj/item/mounted_chainsaw = 80, /obj/item/twohanded/fireaxe = 50, /obj/item/hatchet = 40, /obj/item/kitchen/knife/butcher = 25)
+ implements = list(TOOL_SCALPEL = 100, TOOL_SAW = 100, /obj/item/melee/transforming/energy/sword/cyborg/saw = 100, /obj/item/melee/arm_blade = 80, /obj/item/twohanded/required/chainsaw = 80, /obj/item/mounted_chainsaw = 80, /obj/item/twohanded/fireaxe = 50, /obj/item/hatchet = 40, /obj/item/kitchen/knife/butcher = 25)
time = 64
/datum/surgery_step/sever_limb/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
diff --git a/code/modules/surgery/brain_surgery.dm b/code/modules/surgery/brain_surgery.dm
index 3218e7b3..d86f5731 100644
--- a/code/modules/surgery/brain_surgery.dm
+++ b/code/modules/surgery/brain_surgery.dm
@@ -13,7 +13,7 @@
requires_bodypart_type = 0
/datum/surgery_step/fix_brain
name = "fix brain"
- implements = list(/obj/item/hemostat = 85, TOOL_SCREWDRIVER = 35, /obj/item/pen = 15) //don't worry, pouring some alcohol on their open brain will get that chance to 100
+ implements = list(TOOL_HEMOSTAT = 85, TOOL_SCREWDRIVER = 35, /obj/item/pen = 15) //don't worry, pouring some alcohol on their open brain will get that chance to 100
time = 120 //long and complicated
/datum/surgery/brain_surgery/can_start(mob/user, mob/living/carbon/target)
var/obj/item/organ/brain/B = target.getorganslot(ORGAN_SLOT_BRAIN)
diff --git a/code/modules/surgery/core_removal.dm b/code/modules/surgery/core_removal.dm
index 06aa66a1..3f36a8bb 100644
--- a/code/modules/surgery/core_removal.dm
+++ b/code/modules/surgery/core_removal.dm
@@ -13,7 +13,7 @@
//extract brain
/datum/surgery_step/extract_core
name = "extract core"
- implements = list(/obj/item/hemostat = 100, TOOL_CROWBAR = 100)
+ implements = list(TOOL_HEMOSTAT = 100, TOOL_CROWBAR = 100)
time = 16
/datum/surgery_step/extract_core/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
diff --git a/code/modules/surgery/coronary_bypass.dm b/code/modules/surgery/coronary_bypass.dm
index a98f419a..c6cc457a 100644
--- a/code/modules/surgery/coronary_bypass.dm
+++ b/code/modules/surgery/coronary_bypass.dm
@@ -48,7 +48,7 @@
//grafts a coronary bypass onto the individual's heart, success chance is 90% base again
/datum/surgery_step/coronary_bypass
name = "graft coronary bypass"
- implements = list(/obj/item/hemostat = 90, TOOL_WIRECUTTER = 35, /obj/item/stack/packageWrap = 15, /obj/item/stack/cable_coil = 5)
+ implements = list(TOOL_HEMOSTAT = 90, TOOL_WIRECUTTER = 35, /obj/item/stack/packageWrap = 15, /obj/item/stack/cable_coil = 5)
time = 90
/datum/surgery_step/coronary_bypass/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
@@ -74,4 +74,4 @@
"[user] screws up, causing blood to spurt out of [H]'s chest profusely!")
H.adjustOrganLoss(ORGAN_SLOT_HEART, 20)
H.bleed_rate += 30
- return FALSE
\ No newline at end of file
+ return FALSE
diff --git a/code/modules/surgery/embalming.dm b/code/modules/surgery/embalming.dm
index 329b13bc..b64c886c 100644
--- a/code/modules/surgery/embalming.dm
+++ b/code/modules/surgery/embalming.dm
@@ -11,7 +11,7 @@
/datum/surgery_step/embalming
name = "embalming body"
- implements = list(/obj/item/hemostat = 100)
+ implements = list(TOOL_HEMOSTAT = 100, TOOL_SCREWDRIVER = 35)
time = 10
chems_needed = list(/datum/reagent/drying_agent, /datum/reagent/space_cleaner/sterilizine)
require_all_chems = FALSE
@@ -22,9 +22,9 @@
/datum/surgery_step/embalming/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
user.visible_message("[user] embalms [target]'s body.", "You succeed in embalming [target]'s body.")
ADD_TRAIT(target, TRAIT_HUSK, MAGIC_TRAIT) //Husk's prevent body smell
- return FALSE
+ return TRUE
/datum/surgery_step/embalming/failure(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
user.visible_message("[user] screws up!", "You screwed up!")
ADD_TRAIT(target, TRAIT_NOCLONE, MAGIC_TRAIT) //That body is ruined, but still gives miasma
- return FALSE
\ No newline at end of file
+ return FALSE
diff --git a/code/modules/surgery/eye_surgery.dm b/code/modules/surgery/eye_surgery.dm
index 45f24070..f7a06af3 100644
--- a/code/modules/surgery/eye_surgery.dm
+++ b/code/modules/surgery/eye_surgery.dm
@@ -7,7 +7,7 @@
//fix eyes
/datum/surgery_step/fix_eyes
name = "fix eyes"
- implements = list(/obj/item/hemostat = 100, TOOL_SCREWDRIVER = 45, /obj/item/pen = 25)
+ implements = list(TOOL_HEMOSTAT = 100, TOOL_SCREWDRIVER = 45, /obj/item/pen = 25)
time = 64
/datum/surgery/eye_surgery/can_start(mob/user, mob/living/carbon/target)
var/obj/item/organ/eyes/E = target.getorganslot(ORGAN_SLOT_EYES)
diff --git a/code/modules/surgery/implant_removal.dm b/code/modules/surgery/implant_removal.dm
index e9634026..3eb05c91 100644
--- a/code/modules/surgery/implant_removal.dm
+++ b/code/modules/surgery/implant_removal.dm
@@ -6,7 +6,7 @@
//extract implant
/datum/surgery_step/extract_implant
name = "extract implant"
- implements = list(/obj/item/hemostat = 100, TOOL_CROWBAR = 65)
+ implements = list(TOOL_HEMOSTAT = 100, TOOL_CROWBAR = 65)
time = 64
var/obj/item/implant/I = null
/datum/surgery_step/extract_implant/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
diff --git a/code/modules/surgery/limb_augmentation.dm b/code/modules/surgery/limb_augmentation.dm
index 136e1093..b1d8990b 100644
--- a/code/modules/surgery/limb_augmentation.dm
+++ b/code/modules/surgery/limb_augmentation.dm
@@ -2,7 +2,7 @@
//SURGERY STEPS
/datum/surgery_step/replace
name = "sever muscles"
- implements = list(/obj/item/scalpel = 100, TOOL_WIRECUTTER = 55)
+ implements = list(TOOL_SCALPEL = 100, TOOL_WIRECUTTER = 55)
time = 32
diff --git a/code/modules/surgery/lipoplasty.dm b/code/modules/surgery/lipoplasty.dm
index 6414f471..b99668dd 100644
--- a/code/modules/surgery/lipoplasty.dm
+++ b/code/modules/surgery/lipoplasty.dm
@@ -1,56 +1,56 @@
-/datum/surgery/lipoplasty
- name = "Lipoplasty"
- steps = list(/datum/surgery_step/incise, /datum/surgery_step/clamp_bleeders, /datum/surgery_step/cut_fat, /datum/surgery_step/remove_fat, /datum/surgery_step/close)
- possible_locs = list(BODY_ZONE_CHEST)
-/datum/surgery/lipoplasty/can_start(mob/user, mob/living/carbon/target)
- if(HAS_TRAIT(target, TRAIT_FAT))
- return 1
- return 0
-//cut fat
-/datum/surgery_step/cut_fat
- name = "cut excess fat"
- implements = list(/obj/item/circular_saw = 100, /obj/item/melee/transforming/energy/sword/cyborg/saw = 100, /obj/item/hatchet = 35, /obj/item/kitchen/knife/butcher = 25)
- time = 64
-
-/datum/surgery_step/cut_fat/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
- display_results(user, target, "You begin to cut away [target]'s excess fat...",
- "[user] begins to cut away [target]'s excess fat.",
- "[user] begins to cut [target]'s [target_zone] with [tool].")
-
-/datum/surgery_step/cut_fat/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
- display_results(user, target, "You cut [target]'s excess fat loose.",
- "[user] cuts [target]'s excess fat loose!",
- "[user] finishes the cut on [target]'s [target_zone].")
- return 1
-
-//remove fat
-/datum/surgery_step/remove_fat
- name = "remove loose fat"
- implements = list(/obj/item/retractor = 100, TOOL_SCREWDRIVER = 45, TOOL_WIRECUTTER = 35)
- time = 32
-
-/datum/surgery_step/remove_fat/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
- display_results(user, target, "You begin to extract [target]'s loose fat...",
- "[user] begins to extract [target]'s loose fat!",
- "[user] begins to extract something from [target]'s [target_zone].")
-
-/datum/surgery_step/remove_fat/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
- display_results(user, target, "You extract [target]'s fat.",
- "[user] extracts [target]'s fat!",
- "[user] extracts [target]'s fat!")
- target.overeatduration = 0 //patient is unfatted
- var/removednutriment = target.nutrition
- target.nutrition = NUTRITION_LEVEL_WELL_FED
- removednutriment -= 450 //whatever was removed goes into the meat
- var/mob/living/carbon/human/H = target
- var/typeofmeat = /obj/item/reagent_containers/food/snacks/meat/slab/human
- if(H.dna && H.dna.species)
- typeofmeat = H.dna.species.meat
- var/obj/item/reagent_containers/food/snacks/meat/slab/human/newmeat = new typeofmeat
- newmeat.name = "fatty meat"
- newmeat.desc = "Extremely fatty tissue taken from a patient."
- newmeat.subjectname = H.real_name
- newmeat.subjectjob = H.job
- newmeat.reagents.add_reagent (/datum/reagent/consumable/nutriment, (removednutriment / 15)) //To balance with nutriment_factor of nutriment
- newmeat.forceMove(target.loc)
- return 1
+/datum/surgery/lipoplasty
+ name = "Lipoplasty"
+ steps = list(/datum/surgery_step/incise, /datum/surgery_step/clamp_bleeders, /datum/surgery_step/cut_fat, /datum/surgery_step/remove_fat, /datum/surgery_step/close)
+ possible_locs = list(BODY_ZONE_CHEST)
+/datum/surgery/lipoplasty/can_start(mob/user, mob/living/carbon/target)
+ if(HAS_TRAIT(target, TRAIT_FAT))
+ return 1
+ return 0
+//cut fat
+/datum/surgery_step/cut_fat
+ name = "cut excess fat"
+ implements = list(TOOL_SAW = 100, /obj/item/hatchet = 35, /obj/item/kitchen/knife/butcher = 25) //why we need a saw to cut adipose tissue is beyond me, shit's soft as fuck
+ time = 64
+
+/datum/surgery_step/cut_fat/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ display_results(user, target, "You begin to cut away [target]'s excess fat...",
+ "[user] begins to cut away [target]'s excess fat.",
+ "[user] begins to cut [target]'s [target_zone] with [tool].")
+
+/datum/surgery_step/cut_fat/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ display_results(user, target, "You cut [target]'s excess fat loose.",
+ "[user] cuts [target]'s excess fat loose!",
+ "[user] finishes the cut on [target]'s [target_zone].")
+ return 1
+
+//remove fat
+/datum/surgery_step/remove_fat
+ name = "remove loose fat"
+ implements = list(/obj/item/retractor = 100, TOOL_SCREWDRIVER = 45, TOOL_WIRECUTTER = 35)
+ time = 32
+
+/datum/surgery_step/remove_fat/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ display_results(user, target, "You begin to extract [target]'s loose fat...",
+ "[user] begins to extract [target]'s loose fat!",
+ "[user] begins to extract something from [target]'s [target_zone].")
+
+/datum/surgery_step/remove_fat/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ display_results(user, target, "You extract [target]'s fat.",
+ "[user] extracts [target]'s fat!",
+ "[user] extracts [target]'s fat!")
+ target.overeatduration = 0 //patient is unfatted
+ var/removednutriment = target.nutrition
+ target.nutrition = NUTRITION_LEVEL_WELL_FED
+ removednutriment -= 450 //whatever was removed goes into the meat
+ var/mob/living/carbon/human/H = target
+ var/typeofmeat = /obj/item/reagent_containers/food/snacks/meat/slab/human
+ if(H.dna && H.dna.species)
+ typeofmeat = H.dna.species.meat
+ var/obj/item/reagent_containers/food/snacks/meat/slab/human/newmeat = new typeofmeat
+ newmeat.name = "fatty meat"
+ newmeat.desc = "Extremely fatty tissue taken from a patient."
+ newmeat.subjectname = H.real_name
+ newmeat.subjectjob = H.job
+ newmeat.reagents.add_reagent (/datum/reagent/consumable/nutriment, (removednutriment / 15)) //To balance with nutriment_factor of nutriment
+ newmeat.forceMove(target.loc)
+ return 1
diff --git a/code/modules/surgery/lobectomy.dm b/code/modules/surgery/lobectomy.dm
index 7d8b8a53..243de3ba 100644
--- a/code/modules/surgery/lobectomy.dm
+++ b/code/modules/surgery/lobectomy.dm
@@ -15,7 +15,7 @@
//lobectomy, removes the most damaged lung lobe with a 95% base success chance
/datum/surgery_step/lobectomy
name = "excise damaged lung node"
- implements = list(/obj/item/scalpel = 95, /obj/item/melee/transforming/energy/sword = 65, /obj/item/kitchen/knife = 45,
+ implements = list(TOOL_SCALPEL = 95, /obj/item/melee/transforming/energy/sword = 65, /obj/item/kitchen/knife = 45,
/obj/item/shard = 35)
time = 42
@@ -43,4 +43,4 @@
"[user] screws up!")
H.losebreath += 4
H.adjustOrganLoss(ORGAN_SLOT_LUNGS, 10)
- return FALSE
\ No newline at end of file
+ return FALSE
diff --git a/code/modules/surgery/mechanic_steps.dm b/code/modules/surgery/mechanic_steps.dm
index 7d364d9e..23f9c167 100644
--- a/code/modules/surgery/mechanic_steps.dm
+++ b/code/modules/surgery/mechanic_steps.dm
@@ -3,7 +3,7 @@
name = "unscrew shell"
implements = list(
TOOL_SCREWDRIVER = 100,
- /obj/item/scalpel = 75, // med borgs could try to unskrew shell with scalpel
+ TOOL_SCALPEL = 75, // med borgs could try to unskrew shell with scalpel
/obj/item/kitchen/knife = 50,
/obj/item = 10) // 10% success with any sharp item.
time = 24
@@ -22,7 +22,7 @@
name = "screw shell"
implements = list(
TOOL_SCREWDRIVER = 100,
- /obj/item/scalpel = 75,
+ TOOL_SCALPELl = 75,
/obj/item/kitchen/knife = 50,
/obj/item = 10) // 10% success with any sharp item.
time = 24
@@ -41,7 +41,7 @@
name = "prepare electronics"
implements = list(
TOOL_MULTITOOL = 100,
- /obj/item/hemostat = 10) // try to reboot internal controllers via short circuit with some conductor
+ TOOL_HEMOSTAT = 10) // try to reboot internal controllers via short circuit with some conductor
time = 24
/datum/surgery_step/prepare_electronics/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
@@ -54,7 +54,7 @@
name = "unwrench bolts"
implements = list(
TOOL_WRENCH = 100,
- /obj/item/retractor = 10)
+ TOOL_RETRACTOR = 10)
time = 24
/datum/surgery_step/mechanic_unwrench/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
@@ -67,7 +67,7 @@
name = "wrench bolts"
implements = list(
TOOL_WRENCH = 100,
- /obj/item/retractor = 10)
+ TOOL_RETRACTOR = 10)
time = 24
/datum/surgery_step/mechanic_wrench/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
@@ -84,4 +84,4 @@
/datum/surgery_step/open_hatch/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
display_results(user, target, "You begin to open the hatch holders in [target]'s [parse_zone(target_zone)]...",
"[user] begins to open the hatch holders in [target]'s [parse_zone(target_zone)].",
- "[user] begins to open the hatch holders in [target]'s [parse_zone(target_zone)].")
+ "[user] begins to open the hatch holders in [target]'s [parse_zone(target_zone)].")
diff --git a/code/modules/surgery/organ_manipulation.dm b/code/modules/surgery/organ_manipulation.dm
index fe6139cc..699b3f7f 100644
--- a/code/modules/surgery/organ_manipulation.dm
+++ b/code/modules/surgery/organ_manipulation.dm
@@ -62,7 +62,7 @@
name = "manipulate organs"
repeatable = 1
implements = list(/obj/item/organ = 100, /obj/item/reagent_containers/food/snacks/organ = 0, /obj/item/organ_storage = 100)
- var/implements_extract = list(/obj/item/hemostat = 100, TOOL_CROWBAR = 55)
+ var/implements_extract = list(TOOL_HEMOSTAT = 100, TOOL_CROWBAR = 55)
var/current_type
var/obj/item/organ/I = null
/datum/surgery_step/manipulate_organs/New()
diff --git a/code/modules/surgery/organic_steps.dm b/code/modules/surgery/organic_steps.dm
index 0a80be6c..392244fb 100644
--- a/code/modules/surgery/organic_steps.dm
+++ b/code/modules/surgery/organic_steps.dm
@@ -1,7 +1,7 @@
//make incision
/datum/surgery_step/incise
name = "make incision"
- implements = list(/obj/item/scalpel = 100, /obj/item/melee/transforming/energy/sword = 75, /obj/item/kitchen/knife = 65,
+ implements = list(TOOL_SCALPEL = 100, /obj/item/melee/transforming/energy/sword = 75, /obj/item/kitchen/knife = 65,
/obj/item/shard = 45, /obj/item = 30) // 30% success with any sharp item.
time = 16
@@ -37,7 +37,7 @@
//clamp bleeders
/datum/surgery_step/clamp_bleeders
name = "clamp bleeders"
- implements = list(/obj/item/hemostat = 100, TOOL_WIRECUTTER = 60, /obj/item/stack/packageWrap = 35, /obj/item/stack/cable_coil = 15)
+ implements = list(TOOL_HEMOSTAT = 100, TOOL_WIRECUTTER = 60, /obj/item/stack/packageWrap = 35, /obj/item/stack/cable_coil = 15)
time = 24
/datum/surgery_step/clamp_bleeders/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
@@ -55,7 +55,7 @@
//retract skin
/datum/surgery_step/retract_skin
name = "retract skin"
- implements = list(/obj/item/retractor = 100, TOOL_SCREWDRIVER = 45, TOOL_WIRECUTTER = 35)
+ implements = list(TOOL_RETRACTOR = 100, TOOL_SCREWDRIVER = 45, TOOL_WIRECUTTER = 35)
time = 24
/datum/surgery_step/retract_skin/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
@@ -68,7 +68,7 @@
//close incision
/datum/surgery_step/close
name = "mend incision"
- implements = list(/obj/item/cautery = 100, /obj/item/gun/energy/laser = 90, TOOL_WELDER = 70,
+ implements = list(TOOL_CAUTERY = 100, /obj/item/gun/energy/laser = 90, TOOL_WELDER = 70,
/obj/item = 30) // 30% success with any hot item.
time = 24
@@ -91,9 +91,7 @@
//saw bone
/datum/surgery_step/saw
name = "saw bone"
- implements = list(/obj/item/circular_saw = 100, /obj/item/melee/transforming/energy/sword/cyborg/saw = 100,
- /obj/item/melee/arm_blade = 75, /obj/item/mounted_chainsaw = 65, /obj/item/twohanded/required/chainsaw = 50,
- /obj/item/twohanded/fireaxe = 50, /obj/item/hatchet = 35, /obj/item/kitchen/knife/butcher = 25)
+ implements = list(TOOL_SAW = 100, /obj/item/melee/arm_blade = 75, /obj/item/twohanded/fireaxe = 50, /obj/item/hatchet = 35, /obj/item/kitchen/knife/butcher = 25)
time = 54
/datum/surgery_step/saw/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
@@ -111,7 +109,7 @@
//drill bone
/datum/surgery_step/drill
name = "drill bone"
- implements = list(/obj/item/surgicaldrill = 100, /obj/item/screwdriver/power = 80, /obj/item/pickaxe/drill = 60, /obj/item/mecha_parts/mecha_equipment/drill = 60, TOOL_SCREWDRIVER = 20)
+ implements = list(TOOL_DRILL = 100, /obj/item/screwdriver/power = 80, /obj/item/pickaxe/drill = 60, TOOL_SCREWDRIVER = 20)
time = 30
/datum/surgery_step/drill/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
diff --git a/code/modules/surgery/plastic_surgery.dm b/code/modules/surgery/plastic_surgery.dm
index f24572d7..fe22ffae 100644
--- a/code/modules/surgery/plastic_surgery.dm
+++ b/code/modules/surgery/plastic_surgery.dm
@@ -1,51 +1,51 @@
-/datum/surgery/plastic_surgery
- name = "Plastic surgery"
- steps = list(/datum/surgery_step/incise, /datum/surgery_step/retract_skin, /datum/surgery_step/reshape_face, /datum/surgery_step/close)
- possible_locs = list(BODY_ZONE_HEAD)
-//reshape_face
-/datum/surgery_step/reshape_face
- name = "reshape face"
- implements = list(/obj/item/scalpel = 100, /obj/item/kitchen/knife = 50, TOOL_WIRECUTTER = 35)
- time = 64
-
-/datum/surgery_step/reshape_face/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
- display_results(user, target, "You begin to alter [target]'s appearance...",
- "[user] begins to alter [target]'s appearance.",
- "[user] begins to make an incision in [target]'s face.")
-
-/datum/surgery_step/reshape_face/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
- if(HAS_TRAIT_FROM(target, TRAIT_DISFIGURED, TRAIT_GENERIC))
- REMOVE_TRAIT(target, TRAIT_DISFIGURED, TRAIT_GENERIC)
- display_results(user, target, "You successfully restore [target]'s appearance.",
- "[user] successfully restores [target]'s appearance!",
- "[user] finishes the operation on [target]'s face.")
- else
- var/list/names = list()
- if(!isabductor(user))
- for(var/i in 1 to 10)
- names += target.dna.species.random_name(target.gender, TRUE)
- else
- for(var/_i in 1 to 9)
- names += "Subject [target.gender == MALE ? "i" : "o"]-[pick("a", "b", "c", "d", "e")]-[rand(10000, 99999)]"
- names += target.dna.species.random_name(target.gender, TRUE) //give one normal name in case they want to do regular plastic surgery
- var/chosen_name = input(user, "Choose a new name to assign.", "Plastic Surgery") as null|anything in names
- if(!chosen_name)
- return
- var/oldname = target.real_name
- target.real_name = chosen_name
- var/newname = target.real_name //something about how the code handles names required that I use this instead of target.real_name
- display_results(user, target, "You alter [oldname]'s appearance completely, [target.p_they()] is now [newname].",
- "[user] alters [oldname]'s appearance completely, [target.p_they()] is now [newname]!",
- "[user] finishes the operation on [target]'s face.")
- if(ishuman(target))
- var/mob/living/carbon/human/H = target
- H.sec_hud_set_ID()
- return 1
- return TRUE
-
-/datum/surgery_step/reshape_face/failure(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
- display_results(user, target, "You screw up, leaving [target]'s appearance disfigured!",
- "[user] screws up, disfiguring [target]'s appearance!",
- "[user] finishes the operation on [target]'s face.")
- ADD_TRAIT(target, TRAIT_DISFIGURED, TRAIT_GENERIC)
- return FALSE
+/datum/surgery/plastic_surgery
+ name = "Plastic surgery"
+ steps = list(/datum/surgery_step/incise, /datum/surgery_step/retract_skin, /datum/surgery_step/reshape_face, /datum/surgery_step/close)
+ possible_locs = list(BODY_ZONE_HEAD)
+//reshape_face
+/datum/surgery_step/reshape_face
+ name = "reshape face"
+ implements = list(TOOL_SCALPEL = 100, /obj/item/kitchen/knife = 50, TOOL_WIRECUTTER = 35)
+ time = 64
+
+/datum/surgery_step/reshape_face/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ display_results(user, target, "You begin to alter [target]'s appearance...",
+ "[user] begins to alter [target]'s appearance.",
+ "[user] begins to make an incision in [target]'s face.")
+
+/datum/surgery_step/reshape_face/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ if(HAS_TRAIT_FROM(target, TRAIT_DISFIGURED, TRAIT_GENERIC))
+ REMOVE_TRAIT(target, TRAIT_DISFIGURED, TRAIT_GENERIC)
+ display_results(user, target, "You successfully restore [target]'s appearance.",
+ "[user] successfully restores [target]'s appearance!",
+ "[user] finishes the operation on [target]'s face.")
+ else
+ var/list/names = list()
+ if(!isabductor(user))
+ for(var/i in 1 to 10)
+ names += target.dna.species.random_name(target.gender, TRUE)
+ else
+ for(var/_i in 1 to 9)
+ names += "Subject [target.gender == MALE ? "i" : "o"]-[pick("a", "b", "c", "d", "e")]-[rand(10000, 99999)]"
+ names += target.dna.species.random_name(target.gender, TRUE) //give one normal name in case they want to do regular plastic surgery
+ var/chosen_name = input(user, "Choose a new name to assign.", "Plastic Surgery") as null|anything in names
+ if(!chosen_name)
+ return
+ var/oldname = target.real_name
+ target.real_name = chosen_name
+ var/newname = target.real_name //something about how the code handles names required that I use this instead of target.real_name
+ display_results(user, target, "You alter [oldname]'s appearance completely, [target.p_they()] is now [newname].",
+ "[user] alters [oldname]'s appearance completely, [target.p_they()] is now [newname]!",
+ "[user] finishes the operation on [target]'s face.")
+ if(ishuman(target))
+ var/mob/living/carbon/human/H = target
+ H.sec_hud_set_ID()
+ return 1
+ return TRUE
+
+/datum/surgery_step/reshape_face/failure(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ display_results(user, target, "You screw up, leaving [target]'s appearance disfigured!",
+ "[user] screws up, disfiguring [target]'s appearance!",
+ "[user] finishes the operation on [target]'s face.")
+ ADD_TRAIT(target, TRAIT_DISFIGURED, TRAIT_GENERIC)
+ return FALSE
diff --git a/code/modules/surgery/tools.dm b/code/modules/surgery/tools.dm
index 0d0b361c..7e64ee62 100644
--- a/code/modules/surgery/tools.dm
+++ b/code/modules/surgery/tools.dm
@@ -1,464 +1,469 @@
-/obj/item/retractor
- name = "retractor"
- desc = "Retracts stuff."
- icon = 'icons/obj/surgery.dmi'
- icon_state = "retractor"
- materials = list(MAT_METAL=6000, MAT_GLASS=3000)
- item_flags = SURGICAL_TOOL
- flags_1 = CONDUCT_1
- w_class = WEIGHT_CLASS_TINY
-
-/obj/item/retractor/adv
- name = "Advanced Retractor"
- desc = "A high-class, premium retractor, featuring precision crafted, silver-plated hook-ends and an electrum handle."
- icon = 'icons/obj/surgery.dmi'
- icon_state = "retractor"
- materials = list(MAT_METAL=6000, MAT_GLASS=3000)
- flags_1 = CONDUCT_1
- w_class = WEIGHT_CLASS_TINY
- toolspeed = 0.65
-
-/obj/item/retractor/augment
- name = "retractor"
- desc = "Micro-mechanical manipulator for retracting stuff."
- icon = 'icons/obj/surgery.dmi'
- icon_state = "retractor"
- materials = list(MAT_METAL=6000, MAT_GLASS=3000)
- flags_1 = CONDUCT_1
- w_class = WEIGHT_CLASS_TINY
- toolspeed = 0.5
-
-/obj/item/hemostat
- name = "hemostat"
- desc = "You think you have seen this before."
- icon = 'icons/obj/surgery.dmi'
- icon_state = "hemostat"
- materials = list(MAT_METAL=5000, MAT_GLASS=2500)
- item_flags = SURGICAL_TOOL
- flags_1 = CONDUCT_1
- w_class = WEIGHT_CLASS_TINY
- attack_verb = list("attacked", "pinched")
-
-/obj/item/hemostat/adv
- name = "Advanced Hemostat"
- desc = "An exceptionally fine pair of arterial forceps. These appear to be plated in electrum and feel soft to the touch."
- icon = 'icons/obj/surgery.dmi'
- icon_state = "hemostat"
- materials = list(MAT_METAL=5000, MAT_GLASS=2500)
- flags_1 = CONDUCT_1
- w_class = WEIGHT_CLASS_TINY
- toolspeed = 0.65
- attack_verb = list("attacked", "pinched")
-
-/obj/item/hemostat/augment
- name = "hemostat"
- desc = "Tiny servos power a pair of pincers to stop bleeding."
- icon = 'icons/obj/surgery.dmi'
- icon_state = "hemostat"
- materials = list(MAT_METAL=5000, MAT_GLASS=2500)
- flags_1 = CONDUCT_1
- w_class = WEIGHT_CLASS_TINY
- toolspeed = 0.5
- attack_verb = list("attacked", "pinched")
-
-
-/obj/item/cautery
- name = "cautery"
- desc = "This stops bleeding."
- icon = 'icons/obj/surgery.dmi'
- icon_state = "cautery"
- materials = list(MAT_METAL=2500, MAT_GLASS=750)
- item_flags = SURGICAL_TOOL
- flags_1 = CONDUCT_1
- w_class = WEIGHT_CLASS_TINY
- attack_verb = list("burnt")
-
-/obj/item/cautery/adv
- name = "Electrocautery"
- desc = "A high-tech unipolar Electrocauter. This tiny device contains an extremely powerful microbattery that uses arcs of electricity to painlessly sear wounds shut. It seems to recharge with the user's body-heat. Wow!"
- icon = 'icons/obj/surgery.dmi'
- icon_state = "cautery"
- materials = list(MAT_METAL=2500, MAT_GLASS=750)
- flags_1 = CONDUCT_1
- w_class = WEIGHT_CLASS_TINY
- toolspeed = 0.5
- attack_verb = list("burnt")
-
-/obj/item/cautery/augment
- name = "cautery"
- desc = "A heated element that cauterizes wounds."
- icon = 'icons/obj/surgery.dmi'
- icon_state = "cautery"
- materials = list(MAT_METAL=2500, MAT_GLASS=750)
- flags_1 = CONDUCT_1
- w_class = WEIGHT_CLASS_TINY
- toolspeed = 0.5
- attack_verb = list("burnt")
-
-
-/obj/item/surgicaldrill
- name = "surgical drill"
- desc = "You can drill using this item. You dig?"
- icon = 'icons/obj/surgery.dmi'
- icon_state = "drill"
- lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
- hitsound = 'sound/weapons/circsawhit.ogg'
- materials = list(MAT_METAL=10000, MAT_GLASS=6000)
- item_flags = SURGICAL_TOOL
- flags_1 = CONDUCT_1
- force = 15
- w_class = WEIGHT_CLASS_NORMAL
- attack_verb = list("drilled")
-
-/obj/item/surgicaldrill/adv
- name = "Surgical Autodrill"
- desc = "With a diamond tip and built-in depth and safety sensors, this drill alerts the user before overpenetrating a patient's skull or tooth. There also appears to be a disable switch."
- icon = 'icons/obj/surgery.dmi'
- icon_state = "drill"
- lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
- hitsound = 'sound/weapons/circsawhit.ogg'
- materials = list(MAT_METAL=10000, MAT_GLASS=6000)
- flags_1 = CONDUCT_1
- force = 13 //Damions are not ment for flesh cutting!
- w_class = WEIGHT_CLASS_NORMAL
- toolspeed = 0.65
- attack_verb = list("drilled")
- sharpness = IS_SHARP_ACCURATE // Were making them use a damion for this...
-
-/obj/item/surgicaldrill/augment
- name = "surgical drill"
- desc = "Effectively a small power drill contained within your arm, edges dulled to prevent tissue damage. May or may not pierce the heavens."
- icon = 'icons/obj/surgery.dmi'
- icon_state = "drill"
- hitsound = 'sound/weapons/circsawhit.ogg'
- materials = list(MAT_METAL=10000, MAT_GLASS=6000)
- flags_1 = CONDUCT_1
- force = 10
- w_class = WEIGHT_CLASS_SMALL
- toolspeed = 0.5
- attack_verb = list("drilled")
-
-
-/obj/item/scalpel
- name = "scalpel"
- desc = "Cut, cut, and once more cut."
- icon = 'icons/obj/surgery.dmi'
- icon_state = "scalpel"
- lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
- flags_1 = CONDUCT_1
- force = 10
- w_class = WEIGHT_CLASS_TINY
- throwforce = 5
- throw_speed = 3
- throw_range = 5
- materials = list(MAT_METAL=4000, MAT_GLASS=1000)
- item_flags = SURGICAL_TOOL
- attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
- hitsound = 'sound/weapons/bladeslice.ogg'
- sharpness = IS_SHARP_ACCURATE
-
-/obj/item/scalpel/Initialize()
- . = ..()
- AddComponent(/datum/component/butchering, 80 * toolspeed, 100, 0)
-
-/obj/item/scalpel/adv
- name = "Precision Scalpel"
- desc = "A perfectly balanced electrum scalpel with a silicon-coated edge to eliminate wear and tear."
- icon = 'icons/obj/surgery.dmi'
- icon_state = "scalpel"
- lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
- flags_1 = CONDUCT_1
- force = 8
- w_class = WEIGHT_CLASS_TINY
- throwforce = 7
- throw_speed = 3
- throw_range = 6
- materials = list(MAT_METAL=4000, MAT_GLASS=1000)
- attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
- toolspeed = 0.65
- hitsound = 'sound/weapons/bladeslice.ogg'
- sharpness = IS_SHARP_ACCURATE
-
-/obj/item/scalpel/augment
- name = "scalpel"
- desc = "Ultra-sharp blade attached directly to your bone for extra-accuracy."
- icon = 'icons/obj/surgery.dmi'
- icon_state = "scalpel"
- flags_1 = CONDUCT_1
- force = 10
- w_class = WEIGHT_CLASS_TINY
- throwforce = 5
- throw_speed = 3
- throw_range = 5
- materials = list(MAT_METAL=4000, MAT_GLASS=1000)
- attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
- toolspeed = 0.5
- hitsound = 'sound/weapons/bladeslice.ogg'
- sharpness = IS_SHARP_ACCURATE
-
-/obj/item/scalpel/suicide_act(mob/user)
- user.visible_message("[user] is slitting [user.p_their()] [pick("wrists", "throat", "stomach")] with [src]! It looks like [user.p_theyre()] trying to commit suicide!")
- return (BRUTELOSS)
-
-
-/obj/item/circular_saw
- name = "circular saw"
- desc = "For heavy duty cutting."
- icon = 'icons/obj/surgery.dmi'
- icon_state = "saw"
- lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
- hitsound = 'sound/weapons/circsawhit.ogg'
- throwhitsound = 'sound/weapons/pierce.ogg'
- item_flags = SURGICAL_TOOL
- flags_1 = CONDUCT_1
- force = 15
- w_class = WEIGHT_CLASS_NORMAL
- throwforce = 9
- throw_speed = 2
- throw_range = 5
- materials = list(MAT_METAL=10000, MAT_GLASS=6000)
- attack_verb = list("attacked", "slashed", "sawed", "cut")
- sharpness = IS_SHARP
-
-/obj/item/circular_saw/Initialize()
- . = ..()
- AddComponent(/datum/component/butchering, 40 * toolspeed, 100, 5, 'sound/weapons/circsawhit.ogg') //saws are very accurate and fast at butchering
-
-/obj/item/circular_saw/adv
- name = "Diamond-Grit Circular Saw"
- desc = "For those Assistants with REALLY thick skulls."
- icon = 'icons/obj/surgery.dmi'
- icon_state = "saw"
- lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
- hitsound = 'sound/weapons/circsawhit.ogg'
- throwhitsound = 'sound/weapons/pierce.ogg'
- flags_1 = CONDUCT_1
- force = 13
- w_class = WEIGHT_CLASS_NORMAL
- throwforce = 6
- throw_speed = 1
- throw_range = 3
- materials = list(MAT_METAL=10000, MAT_GLASS=6000)
- attack_verb = list("attacked", "slashed", "sawed", "cut")
- toolspeed = 0.65
- sharpness = IS_SHARP
-
-/obj/item/circular_saw/augment
- name = "circular saw"
- desc = "A small but very fast spinning saw. Edges dulled to prevent accidental cutting inside of the surgeon."
- icon = 'icons/obj/surgery.dmi'
- icon_state = "saw"
- hitsound = 'sound/weapons/circsawhit.ogg'
- throwhitsound = 'sound/weapons/pierce.ogg'
- flags_1 = CONDUCT_1
- force = 10
- w_class = WEIGHT_CLASS_SMALL
- throwforce = 9
- throw_speed = 2
- throw_range = 5
- materials = list(MAT_METAL=10000, MAT_GLASS=6000)
- toolspeed = 0.5
- attack_verb = list("attacked", "slashed", "sawed", "cut")
- sharpness = IS_SHARP
-
-/obj/item/surgical_drapes
- name = "surgical drapes"
- desc = "Nanotrasen brand surgical drapes provide optimal safety and infection control."
- icon = 'icons/obj/surgery.dmi'
- icon_state = "surgical_drapes"
- w_class = WEIGHT_CLASS_TINY
- attack_verb = list("slapped")
-
-/obj/item/surgical_drapes/attack(mob/living/M, mob/user)
- if(!attempt_initiate_surgery(src, M, user))
- ..()
-
-/obj/item/organ_storage //allows medical cyborgs to manipulate organs without hands
- name = "organ storage bag"
- desc = "A container for holding body parts."
- icon = 'icons/obj/storage.dmi'
- icon_state = "evidenceobj"
- item_flags = SURGICAL_TOOL
-
-/obj/item/organ_storage/afterattack(obj/item/I, mob/user, proximity)
- . = ..()
- if(!proximity)
- return
- if(contents.len)
- to_chat(user, "[src] already has something inside it.")
- return
- if(!isorgan(I) && !isbodypart(I))
- to_chat(user, "[src] can only hold body parts!")
- return
-
- user.visible_message("[user] puts [I] into [src].", "You put [I] inside [src].")
- icon_state = "evidence"
- var/xx = I.pixel_x
- var/yy = I.pixel_y
- I.pixel_x = 0
- I.pixel_y = 0
- var/image/img = image("icon"=I, "layer"=FLOAT_LAYER)
- img.plane = FLOAT_PLANE
- I.pixel_x = xx
- I.pixel_y = yy
- add_overlay(img)
- add_overlay("evidence")
- desc = "An organ storage container holding [I]."
- I.forceMove(src)
- w_class = I.w_class
-
-/obj/item/organ_storage/attack_self(mob/user)
- if(contents.len)
- var/obj/item/I = contents[1]
- user.visible_message("[user] dumps [I] from [src].", "You dump [I] from [src].")
- cut_overlays()
- I.forceMove(get_turf(src))
- icon_state = "evidenceobj"
- desc = "A container for holding body parts."
- else
- to_chat(user, "[src] is empty.")
- return
-
-/obj/item/surgical_processor //allows medical cyborgs to scan and initiate advanced surgeries
- name = "\improper Surgical Processor"
- desc = "A device for scanning and initiating surgeries from a disk or operating computer."
- icon = 'icons/obj/device.dmi'
- icon_state = "spectrometer"
- item_flags = NOBLUDGEON
- var/list/advanced_surgeries = list()
-
-/obj/item/surgical_processor/afterattack(obj/item/O, mob/user, proximity)
- . = ..()
- if(!proximity)
- return
- if(istype(O, /obj/item/disk/surgery))
- to_chat(user, "You load the surgery protocol from [O] into [src].")
- var/obj/item/disk/surgery/D = O
- if(do_after(user, 10, target = O))
- advanced_surgeries |= D.surgeries
- return TRUE
- if(istype(O, /obj/machinery/computer/operating))
- to_chat(user, "You copy surgery protocols from [O] into [src].")
- var/obj/machinery/computer/operating/OC = O
- if(do_after(user, 10, target = O))
- advanced_surgeries |= OC.advanced_surgeries
- return TRUE
- return
-
-/obj/item/scalpel/advanced
- name = "laser scalpel"
- desc = "An advanced scalpel which uses laser technology to cut. It's set to scalpel mode."
- icon = 'icons/obj/surgery.dmi'
- icon_state = "scalpel_a"
- hitsound = 'sound/weapons/blade1.ogg'
- force = 16
- toolspeed = 0.7
- light_color = LIGHT_COLOR_GREEN
-
-/obj/item/scalpel/advanced/Initialize()
- . = ..()
- set_light(1)
-
-/obj/item/scalpel/advanced/attack_self(mob/user)
- playsound(get_turf(user),'sound/machines/click.ogg',50,TRUE)
- var/obj/item/circular_saw/advanced/saw = new /obj/item/circular_saw/advanced(drop_location())
- to_chat(user, "You incease the power, now it can cut bones.")
- qdel(src)
- user.put_in_active_hand(saw)
-
-/obj/item/circular_saw/advanced
- name = "laser scalpel"
- desc = "An advanced scalpel which uses laser technology to cut. It's set to saw mode."
- icon = 'icons/obj/surgery.dmi'
- icon_state = "saw_a"
- hitsound = 'sound/weapons/blade1.ogg'
- force = 17
- toolspeed = 0.7
- sharpness = IS_SHARP_ACCURATE
- light_color = LIGHT_COLOR_GREEN
-
-/obj/item/circular_saw/advanced/Initialize()
- . = ..()
- set_light(2)
-
-/obj/item/circular_saw/advanced/attack_self(mob/user)
- playsound(get_turf(user),'sound/machines/click.ogg',50,TRUE)
- var/obj/item/scalpel/advanced/scalpel = new /obj/item/scalpel/advanced(drop_location())
- to_chat(user, "You lower the power.")
- qdel(src)
- user.put_in_active_hand(scalpel)
-
-/obj/item/retractor/advanced
- name = "mechanical pinches"
- desc = "An agglomerate of rods and gears. It resembles a retractor."
- icon = 'icons/obj/surgery.dmi'
- icon_state = "retractor_a"
- toolspeed = 0.7
-
-/obj/item/retractor/advanced/attack_self(mob/user)
- playsound(get_turf(user),'sound/items/change_drill.ogg',50,TRUE)
- var/obj/item/hemostat/advanced/hemostat = new /obj/item/hemostat/advanced(drop_location())
- to_chat(user, "You set the [src] to hemostat mode.")
- qdel(src)
- user.put_in_active_hand(hemostat)
-
-/obj/item/hemostat/advanced
- name = "mechanical pinches"
- desc = "An agglomerate of rods and gears. It resembles an hemostat."
- icon = 'icons/obj/surgery.dmi'
- icon_state = "hemostat_a"
- toolspeed = 0.7
-
-/obj/item/hemostat/advanced/attack_self(mob/user)
- playsound(get_turf(user),'sound/items/change_drill.ogg',50,TRUE)
- var/obj/item/retractor/advanced/retractor = new /obj/item/retractor/advanced(drop_location())
- to_chat(user, "You set the [src] to retractor mode.")
- qdel(src)
- user.put_in_active_hand(retractor)
-
-/obj/item/surgicaldrill/advanced
- name = "searing tool"
- desc = "It projects a high power laser used for medical application. It's set to drilling mode."
- icon = 'icons/obj/surgery.dmi'
- icon_state = "surgicaldrill_a"
- hitsound = 'sound/items/welder.ogg'
- toolspeed = 0.7
- light_color = LIGHT_COLOR_RED
-
-/obj/item/surgicaldrill/advanced/Initialize()
- . = ..()
- set_light(1)
-
-/obj/item/surgicaldrill/advanced/attack_self(mob/user)
- playsound(get_turf(user),'sound/weapons/tap.ogg',50,TRUE)
- var/obj/item/cautery/advanced/cautery = new /obj/item/cautery/advanced(drop_location())
- to_chat(user, "You dilate the lenses, setting it to mending mode.")
- qdel(src)
- user.put_in_active_hand(cautery)
-
-/obj/item/cautery/advanced
- name = "searing tool"
- desc = "It projects a high power laser used for medical application. It's set to mending mode."
- icon = 'icons/obj/surgery.dmi'
- icon_state = "cautery_a"
- hitsound = 'sound/items/welder2.ogg'
- force = 15
- toolspeed = 0.7
- light_color = LIGHT_COLOR_RED
-
-/obj/item/cautery/advanced/Initialize()
- . = ..()
- set_light(1)
-
-/obj/item/cautery/advanced/attack_self(mob/user)
- playsound(get_turf(user),'sound/items/welderdeactivate.ogg',50,TRUE)
- var/obj/item/surgicaldrill/advanced/surgicaldrill = new /obj/item/surgicaldrill/advanced(drop_location())
- to_chat(user, "You focus the lensess, it is now set to drilling mode.")
- qdel(src)
- user.put_in_active_hand(surgicaldrill)
+/obj/item/retractor
+ name = "retractor"
+ desc = "Retracts stuff."
+ icon = 'icons/obj/surgery.dmi'
+ icon_state = "retractor"
+ materials = list(MAT_METAL=6000, MAT_GLASS=3000)
+ item_flags = SURGICAL_TOOL
+ flags_1 = CONDUCT_1
+ w_class = WEIGHT_CLASS_TINY
+ tool_behaviour = TOOL_RETRACTOR
+ toolspeed = 1
+
+/obj/item/retractor/advanced
+ name = "mechanical pinches"
+ desc = "An agglomerate of rods and gears."
+ icon = 'icons/obj/surgery.dmi'
+ icon_state = "retractor_a"
+ toolspeed = 0.7
+
+/obj/item/retractor/advanced/attack_self(mob/user)
+ playsound(get_turf(user), 'sound/items/change_drill.ogg', 50, TRUE)
+ if(tool_behaviour == TOOL_RETRACTOR)
+ tool_behaviour = TOOL_HEMOSTAT
+ to_chat(user, "You configure the gears of [src], they are now in hemostat mode.")
+ icon_state = "hemostat_a"
+ else
+ tool_behaviour = TOOL_RETRACTOR
+ to_chat(user, "You configure the gears of [src], they are now in retractor mode.")
+ icon_state = "retractor_a"
+
+/obj/item/retractor/advanced/examine(mob/living/user)
+ to_chat(user, "You focus the lenses of [src], it is now in mending mode.")
+ icon_state = "cautery_a"
+ else
+ tool_behaviour = TOOL_DRILL
+ to_chat(user, "You dilate the lenses of [src], it is now in drilling mode.")
+ icon_state = "surgicaldrill_a"
+
+/obj/item/surgicaldrill/advanced/examine(mob/living/user)
+ to_chat(user, "")
+
+/obj/item/surgicaldrill/augment
+ name = "surgical drill"
+ desc = "Effectively a small power drill contained within your arm, edges dulled to prevent tissue damage. May or may not pierce the heavens."
+ icon = 'icons/obj/surgery.dmi'
+ icon_state = "drill"
+ hitsound = 'sound/weapons/circsawhit.ogg'
+ materials = list(MAT_METAL=10000, MAT_GLASS=6000)
+ flags_1 = CONDUCT_1
+ force = 10
+ w_class = WEIGHT_CLASS_SMALL
+ toolspeed = 0.5
+ attack_verb = list("drilled")
+
+
+/obj/item/scalpel
+ name = "scalpel"
+ desc = "Cut, cut, and once more cut."
+ icon = 'icons/obj/surgery.dmi'
+ icon_state = "scalpel"
+ lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
+ flags_1 = CONDUCT_1
+ force = 10
+ w_class = WEIGHT_CLASS_TINY
+ throwforce = 5
+ throw_speed = 3
+ throw_range = 5
+ materials = list(MAT_METAL=4000, MAT_GLASS=1000)
+ item_flags = SURGICAL_TOOL
+ attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
+ hitsound = 'sound/weapons/bladeslice.ogg'
+ sharpness = IS_SHARP_ACCURATE
+ tool_behaviour = TOOL_SCALPEL
+ toolspeed = 1
+
+/obj/item/scalpel/Initialize()
+ . = ..()
+ AddComponent(/datum/component/butchering, 80 * toolspeed, 100, 0)
+
+/obj/item/scalpel/advanced
+ name = "laser scalpel"
+ desc = "An advanced scalpel which uses laser technology to cut."
+ icon = 'icons/obj/surgery.dmi'
+ icon_state = "scalpel_a"
+ hitsound = 'sound/weapons/blade1.ogg'
+ force = 16
+ toolspeed = 0.7
+ light_color = LIGHT_COLOR_GREEN
+ sharpness = IS_SHARP_ACCURATE
+
+/obj/item/scalpel/advanced/Initialize()
+ . = ..()
+ set_light(1)
+
+/obj/item/scalpel/advanced/attack_self(mob/user)
+ playsound(get_turf(user), 'sound/machines/click.ogg', 50, TRUE)
+ if(tool_behaviour == TOOL_SCALPEL)
+ tool_behaviour = TOOL_SAW
+ to_chat(user, "You increase the power of [src], now it can cut bones.")
+ set_light(2)
+ force += 1 //we don't want to ruin sharpened stuff
+ icon_state = "saw_a"
+ else
+ tool_behaviour = TOOL_SCALPEL
+ to_chat(user, "You lower the power of [src], it can no longer cut bones.")
+ set_light(1)
+ force -= 1
+ icon_state = "scalpel_a"
+
+/obj/item/scalpel/advanced/examine(mob/living/user)
+ to_chat(user, "")
+
+/obj/item/scalpel/augment
+ name = "scalpel"
+ desc = "Ultra-sharp blade attached directly to your bone for extra-accuracy."
+ icon = 'icons/obj/surgery.dmi'
+ icon_state = "scalpel"
+ flags_1 = CONDUCT_1
+ force = 10
+ w_class = WEIGHT_CLASS_TINY
+ throwforce = 5
+ throw_speed = 3
+ throw_range = 5
+ materials = list(MAT_METAL=4000, MAT_GLASS=1000)
+ attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
+ toolspeed = 0.5
+ hitsound = 'sound/weapons/bladeslice.ogg'
+ sharpness = IS_SHARP_ACCURATE
+
+/obj/item/scalpel/suicide_act(mob/user)
+ user.visible_message("[user] is slitting [user.p_their()] [pick("wrists", "throat", "stomach")] with [src]! It looks like [user.p_theyre()] trying to commit suicide!")
+ return (BRUTELOSS)
+
+
+/obj/item/circular_saw
+ name = "circular saw"
+ desc = "For heavy duty cutting."
+ icon = 'icons/obj/surgery.dmi'
+ icon_state = "saw"
+ lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
+ hitsound = 'sound/weapons/circsawhit.ogg'
+ throwhitsound = 'sound/weapons/pierce.ogg'
+ item_flags = SURGICAL_TOOL
+ flags_1 = CONDUCT_1
+ force = 15
+ w_class = WEIGHT_CLASS_NORMAL
+ throwforce = 9
+ throw_speed = 2
+ throw_range = 5
+ materials = list(MAT_METAL=10000, MAT_GLASS=6000)
+ attack_verb = list("attacked", "slashed", "sawed", "cut")
+ sharpness = IS_SHARP
+ tool_behaviour = TOOL_SAW
+ toolspeed = 1
+
+/obj/item/circular_saw/Initialize()
+ . = ..()
+ AddComponent(/datum/component/butchering, 40 * toolspeed, 100, 5, 'sound/weapons/circsawhit.ogg') //saws are very accurate and fast at butchering
+
+
+/obj/item/circular_saw/augment
+ name = "circular saw"
+ desc = "A small but very fast spinning saw. Edges dulled to prevent accidental cutting inside of the surgeon."
+ icon = 'icons/obj/surgery.dmi'
+ icon_state = "saw"
+ hitsound = 'sound/weapons/circsawhit.ogg'
+ throwhitsound = 'sound/weapons/pierce.ogg'
+ flags_1 = CONDUCT_1
+ force = 10
+ w_class = WEIGHT_CLASS_SMALL
+ throwforce = 9
+ throw_speed = 2
+ throw_range = 5
+ materials = list(MAT_METAL=10000, MAT_GLASS=6000)
+ toolspeed = 0.5
+ attack_verb = list("attacked", "slashed", "sawed", "cut")
+ sharpness = IS_SHARP
+
+/obj/item/surgical_drapes
+ name = "surgical drapes"
+ desc = "Nanotrasen brand surgical drapes provide optimal safety and infection control."
+ icon = 'icons/obj/surgery.dmi'
+ icon_state = "surgical_drapes"
+ w_class = WEIGHT_CLASS_TINY
+ attack_verb = list("slapped")
+
+/obj/item/surgical_drapes/attack(mob/living/M, mob/user)
+ if(!attempt_initiate_surgery(src, M, user))
+ ..()
+
+/obj/item/organ_storage //allows medical cyborgs to manipulate organs without hands
+ name = "organ storage bag"
+ desc = "A container for holding body parts."
+ icon = 'icons/obj/storage.dmi'
+ icon_state = "evidenceobj"
+ item_flags = SURGICAL_TOOL
+
+/obj/item/organ_storage/afterattack(obj/item/I, mob/user, proximity)
+ . = ..()
+ if(!proximity)
+ return
+ if(contents.len)
+ to_chat(user, "[src] already has something inside it.")
+ return
+ if(!isorgan(I) && !isbodypart(I))
+ to_chat(user, "[src] can only hold body parts!")
+ return
+
+ user.visible_message("[user] puts [I] into [src].", "You put [I] inside [src].")
+ icon_state = "evidence"
+ var/xx = I.pixel_x
+ var/yy = I.pixel_y
+ I.pixel_x = 0
+ I.pixel_y = 0
+ var/image/img = image("icon"=I, "layer"=FLOAT_LAYER)
+ img.plane = FLOAT_PLANE
+ I.pixel_x = xx
+ I.pixel_y = yy
+ add_overlay(img)
+ add_overlay("evidence")
+ desc = "An organ storage container holding [I]."
+ I.forceMove(src)
+ w_class = I.w_class
+
+/obj/item/organ_storage/attack_self(mob/user)
+ if(contents.len)
+ var/obj/item/I = contents[1]
+ user.visible_message("[user] dumps [I] from [src].", "You dump [I] from [src].")
+ cut_overlays()
+ I.forceMove(get_turf(src))
+ icon_state = "evidenceobj"
+ desc = "A container for holding body parts."
+ else
+ to_chat(user, "[src] is empty.")
+ return
+
+/obj/item/surgical_processor //allows medical cyborgs to scan and initiate advanced surgeries
+ name = "\improper Surgical Processor"
+ desc = "A device for scanning and initiating surgeries from a disk or operating computer."
+ icon = 'icons/obj/device.dmi'
+ icon_state = "spectrometer"
+ item_flags = NOBLUDGEON
+ var/list/advanced_surgeries = list()
+
+/obj/item/surgical_processor/afterattack(obj/item/O, mob/user, proximity)
+ . = ..()
+ if(!proximity)
+ return
+ if(istype(O, /obj/item/disk/surgery))
+ to_chat(user, "You load the surgery protocol from [O] into [src].")
+ var/obj/item/disk/surgery/D = O
+ if(do_after(user, 10, target = O))
+ advanced_surgeries |= D.surgeries
+ return TRUE
+ if(istype(O, /obj/machinery/computer/operating))
+ to_chat(user, "You copy surgery protocols from [O] into [src].")
+ var/obj/machinery/computer/operating/OC = O
+ if(do_after(user, 10, target = O))
+ advanced_surgeries |= OC.advanced_surgeries
+ return TRUE
+ return
+
+/obj/item/scalpel/advanced
+ name = "laser scalpel"
+ desc = "An advanced scalpel which uses laser technology to cut. It's set to scalpel mode."
+ icon = 'icons/obj/surgery.dmi'
+ icon_state = "scalpel_a"
+ hitsound = 'sound/weapons/blade1.ogg'
+ force = 16
+ toolspeed = 0.7
+ light_color = LIGHT_COLOR_GREEN
+
+/obj/item/scalpel/advanced/Initialize()
+ . = ..()
+ set_light(1)
+
+/obj/item/scalpel/advanced/attack_self(mob/user)
+ playsound(get_turf(user),'sound/machines/click.ogg',50,TRUE)
+ var/obj/item/circular_saw/advanced/saw = new /obj/item/circular_saw/advanced(drop_location())
+ to_chat(user, "You incease the power, now it can cut bones.")
+ qdel(src)
+ user.put_in_active_hand(saw)
+
+/obj/item/circular_saw/advanced
+ name = "laser scalpel"
+ desc = "An advanced scalpel which uses laser technology to cut. It's set to saw mode."
+ icon = 'icons/obj/surgery.dmi'
+ icon_state = "saw_a"
+ hitsound = 'sound/weapons/blade1.ogg'
+ force = 17
+ toolspeed = 0.7
+ sharpness = IS_SHARP_ACCURATE
+ light_color = LIGHT_COLOR_GREEN
+
+/obj/item/circular_saw/advanced/Initialize()
+ . = ..()
+ set_light(2)
+
+/obj/item/circular_saw/advanced/attack_self(mob/user)
+ playsound(get_turf(user),'sound/machines/click.ogg',50,TRUE)
+ var/obj/item/scalpel/advanced/scalpel = new /obj/item/scalpel/advanced(drop_location())
+ to_chat(user, "You lower the power.")
+ qdel(src)
+ user.put_in_active_hand(scalpel)
+
+/obj/item/retractor/advanced
+ name = "mechanical pinches"
+ desc = "An agglomerate of rods and gears. It resembles a retractor."
+ icon = 'icons/obj/surgery.dmi'
+ icon_state = "retractor_a"
+ toolspeed = 0.7
+
+/obj/item/retractor/advanced/attack_self(mob/user)
+ playsound(get_turf(user),'sound/items/change_drill.ogg',50,TRUE)
+ var/obj/item/hemostat/advanced/hemostat = new /obj/item/hemostat/advanced(drop_location())
+ to_chat(user, "You set the [src] to hemostat mode.")
+ qdel(src)
+ user.put_in_active_hand(hemostat)
+
+/obj/item/hemostat/advanced
+ name = "mechanical pinches"
+ desc = "An agglomerate of rods and gears. It resembles an hemostat."
+ icon = 'icons/obj/surgery.dmi'
+ icon_state = "hemostat_a"
+ toolspeed = 0.7
+
+/obj/item/hemostat/advanced/attack_self(mob/user)
+ playsound(get_turf(user),'sound/items/change_drill.ogg',50,TRUE)
+ var/obj/item/retractor/advanced/retractor = new /obj/item/retractor/advanced(drop_location())
+ to_chat(user, "You set the [src] to retractor mode.")
+ qdel(src)
+ user.put_in_active_hand(retractor)
+
+/obj/item/surgicaldrill/advanced
+ name = "searing tool"
+ desc = "It projects a high power laser used for medical application. It's set to drilling mode."
+ icon = 'icons/obj/surgery.dmi'
+ icon_state = "surgicaldrill_a"
+ hitsound = 'sound/items/welder.ogg'
+ toolspeed = 0.7
+ light_color = LIGHT_COLOR_RED
+
+/obj/item/surgicaldrill/advanced/Initialize()
+ . = ..()
+ set_light(1)
+
+/obj/item/surgicaldrill/advanced/attack_self(mob/user)
+ playsound(get_turf(user),'sound/weapons/tap.ogg',50,TRUE)
+ var/obj/item/cautery/advanced/cautery = new /obj/item/cautery/advanced(drop_location())
+ to_chat(user, "You dilate the lenses, setting it to mending mode.")
+ qdel(src)
+ user.put_in_active_hand(cautery)
+
+/obj/item/cautery/advanced
+ name = "searing tool"
+ desc = "It projects a high power laser used for medical application. It's set to mending mode."
+ icon = 'icons/obj/surgery.dmi'
+ icon_state = "cautery_a"
+ hitsound = 'sound/items/welder2.ogg'
+ force = 15
+ toolspeed = 0.7
+ light_color = LIGHT_COLOR_RED
+
+/obj/item/cautery/advanced/Initialize()
+ . = ..()
+ set_light(1)
+
+/obj/item/cautery/advanced/attack_self(mob/user)
+ playsound(get_turf(user),'sound/items/welderdeactivate.ogg',50,TRUE)
+ var/obj/item/surgicaldrill/advanced/surgicaldrill = new /obj/item/surgicaldrill/advanced(drop_location())
+ to_chat(user, "You focus the lensess, it is now set to drilling mode.")
+ qdel(src)
+ user.put_in_active_hand(surgicaldrill)
diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi
index e13ce103..017556c5 100644
Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ
diff --git a/icons/obj/surgery.dmi b/icons/obj/surgery.dmi
index d26e7a00..8d1aae4b 100644
Binary files a/icons/obj/surgery.dmi and b/icons/obj/surgery.dmi differ
diff --git a/sound/effects/cartoon_pop.ogg b/sound/effects/cartoon_pop.ogg
new file mode 100644
index 00000000..aca8c7ad
Binary files /dev/null and b/sound/effects/cartoon_pop.ogg differ