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