From 10069637a56c87ff39b83e8efec4bc7eb76baf8a Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Fri, 11 Oct 2019 12:10:31 -0700 Subject: [PATCH] Merge pull request #9441 from lolman360/whydowehave1000branches Ports /tg/ advanced surgerytools and tool defines. --- code/__DEFINES/tools.dm | 36 +- .../mecha/equipment/tools/mining_tools.dm | 3 + code/game/mecha/equipment/tools/work_tools.dm | 2 + code/game/objects/items/holy_weapons.dm | 7 +- code/game/objects/items/melee/energy.dm | 468 ++-- code/game/objects/items/storage/backpack.dm | 1213 +++++----- code/game/objects/items/storage/belt.dm | 1825 +++++++-------- code/game/objects/items/twohanded.dm | 2 + code/game/objects/items/weaponry.dm | 1408 +++++------ .../mob/living/simple_animal/bot/bot.dm | 2084 ++++++++--------- .../projectiles/guns/ballistic/revolver.dm | 4 +- .../projectiles/guns/ballistic/shotgun.dm | 2 +- .../research/designs/medical_designs.dm | 59 +- code/modules/research/techweb/all_nodes.dm | 2 +- .../bioware/experimental_dissection..dm | 2 +- code/modules/surgery/advanced/brainwashing.dm | 3 +- code/modules/surgery/advanced/lobotomy.dm | 2 +- .../surgery/advanced/necrotic_revival.dm | 2 +- code/modules/surgery/advanced/pacification.dm | 4 +- code/modules/surgery/advanced/toxichealing.dm | 2 +- .../modules/surgery/advanced/viral_bonding.dm | 2 +- code/modules/surgery/amputation.dm | 2 +- code/modules/surgery/brain_surgery.dm | 2 +- code/modules/surgery/core_removal.dm | 2 +- code/modules/surgery/coronary_bypass.dm | 4 +- code/modules/surgery/embalming.dm | 6 +- code/modules/surgery/eye_surgery.dm | 2 +- code/modules/surgery/implant_removal.dm | 2 +- code/modules/surgery/limb_augmentation.dm | 2 +- code/modules/surgery/lipoplasty.dm | 112 +- code/modules/surgery/lobectomy.dm | 4 +- code/modules/surgery/mechanic_steps.dm | 12 +- code/modules/surgery/organ_manipulation.dm | 2 +- code/modules/surgery/organic_steps.dm | 14 +- code/modules/surgery/plastic_surgery.dm | 102 +- code/modules/surgery/tools.dm | 933 ++++---- icons/obj/surgery.dmi | Bin 38294 -> 38328 bytes 37 files changed, 4175 insertions(+), 4158 deletions(-) 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/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/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/obj/surgery.dmi b/icons/obj/surgery.dmi index d26e7a0006664d58960b2fb9000a9f4dbeae3e67..8d1aae4be55dcebebfd690fbf94cbe96f56407e4 100644 GIT binary patch literal 38328 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