diff --git a/code/__DEFINES/dcs/signals/signals.dm b/code/__DEFINES/dcs/signals/signals.dm
index f3295b737904..54f421b6763d 100644
--- a/code/__DEFINES/dcs/signals/signals.dm
+++ b/code/__DEFINES/dcs/signals/signals.dm
@@ -182,6 +182,8 @@
#define COMSIG_ATOM_CROWBAR_ACT "atom_crowbar_act"
///from base of atom/analyser_act(): (mob/living/user, obj/item/I)
#define COMSIG_ATOM_ANALYSER_ACT "atom_analyser_act"
+///from base of atom/deconstruct_act(): (mob/living/user, obj/item/I)
+#define COMSIG_ATOM_DECONSTRUCT_ACT "atom_deconstruct_act"
///for any tool behaviors: (mob/living/user, obj/item/I, list/recipes)
#define COMSIG_ATOM_TOOL_ACT(tooltype) "tool_act_[tooltype]"
diff --git a/code/__DEFINES/tools.dm b/code/__DEFINES/tools.dm
index 35860ac927f4..eb2696c0afbb 100644
--- a/code/__DEFINES/tools.dm
+++ b/code/__DEFINES/tools.dm
@@ -15,6 +15,7 @@
#define TOOL_SCALPEL "scalpel"
#define TOOL_SAW "saw"
#define TOOL_KNIFE "knife" //luv me kuh-nyfe
+#define TOOL_DECONSTRUCT "deconstruct"
// 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.
diff --git a/code/datums/action.dm b/code/datums/action.dm
index fb2d8b5e967f..9bc58c399dd4 100644
--- a/code/datums/action.dm
+++ b/code/datums/action.dm
@@ -249,6 +249,9 @@
/datum/action/item_action/toggle_mister
name = "Toggle Mister"
+/datum/action/item_action/toggle_gear_handle
+ name = "Toggle Gear Handle"
+
/datum/action/item_action/activate_injector
name = "Activate Injector"
diff --git a/code/datums/components/twohanded.dm b/code/datums/components/twohanded.dm
index 51c9268d13ab..d81165c620e5 100644
--- a/code/datums/components/twohanded.dm
+++ b/code/datums/components/twohanded.dm
@@ -8,8 +8,8 @@
dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS // Only one of the component can exist on an item
var/wielded = FALSE /// Are we holding the two handed item properly
var/force_multiplier = 0 /// The multiplier applied to force when wielded, does not work with force_wielded, and force_unwielded
- var/force_wielded = 0 /// The force of the item when weilded
- var/force_unwielded = 0 /// The force of the item when unweilded
+ var/force_wielded = 0 /// The force of the item when wielded
+ var/force_unwielded = 0 /// The force of the item when unwielded
var/wieldsound = FALSE /// Play sound when wielded
var/unwieldsound = FALSE /// Play sound when unwielded
var/attacksound = FALSE /// Play sound on attack when wielded
diff --git a/code/datums/elements/tool_bang.dm b/code/datums/elements/tool_bang.dm
new file mode 100644
index 000000000000..bc002e936de4
--- /dev/null
+++ b/code/datums/elements/tool_bang.dm
@@ -0,0 +1,40 @@
+/**
+ * Tool bang bespoke element
+ *
+ * Bang the user when using this tool
+ */
+/datum/element/tool_bang
+ element_flags = ELEMENT_BESPOKE
+ id_arg_index = 2
+ /// Strength of the bang
+ var/bang_strength
+
+/datum/element/tool_bang/Attach(datum/target, bang_strength)
+ . = ..()
+ if(!isitem(target))
+ return ELEMENT_INCOMPATIBLE
+
+ src.bang_strength = bang_strength
+
+ RegisterSignal(target, COMSIG_TOOL_IN_USE, PROC_REF(prob_bang))
+ RegisterSignal(target, COMSIG_TOOL_START_USE, PROC_REF(bang))
+
+/datum/element/tool_bang/Detach(datum/source, force)
+ . = ..()
+ UnregisterSignal(source, list(COMSIG_TOOL_IN_USE, COMSIG_TOOL_START_USE))
+
+/datum/element/tool_bang/proc/prob_bang(datum/source, mob/living/user)
+ SIGNAL_HANDLER
+
+ if(prob(90))
+ return
+ bang(source, user)
+
+/datum/element/tool_bang/proc/bang(datum/source, mob/living/user)
+ SIGNAL_HANDLER
+
+ if(user && get_dist(get_turf(source), get_turf(user)) <= 1)
+ if(istype(user, /mob/living/carbon))
+ var/mob/living/carbon/carbon = user
+ carbon.soundbang_act(min(bang_strength,1), 0, 1, 5)
+
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index b96e8a53c824..a140ec099085 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -1282,6 +1282,8 @@
. = welder_act(user, I)
if(TOOL_ANALYZER)
. = analyzer_act(user, I)
+ if(TOOL_DECONSTRUCT)
+ . |= deconstruct_act(user, I)
if(. || signal_result & COMPONENT_BLOCK_TOOL_ATTACK) //Either the proc or the signal handled the tool's events in some way.
return TRUE
@@ -1362,6 +1364,10 @@
/atom/proc/analyzer_act(mob/living/user, obj/item/I)
return SEND_SIGNAL(src, COMSIG_ATOM_ANALYSER_ACT, user, I)
+///Deconstruct act
+/atom/proc/deconstruct_act(mob/living/user, obj/item/I)
+ return SEND_SIGNAL(src, COMSIG_ATOM_DECONSTRUCT_ACT, user, I)
+
///Generate a tag for this atom
/atom/proc/GenerateTag()
return
diff --git a/code/game/machinery/constructable_frame.dm b/code/game/machinery/constructable_frame.dm
index f196fc6dc770..a62780aad05a 100644
--- a/code/game/machinery/constructable_frame.dm
+++ b/code/game/machinery/constructable_frame.dm
@@ -13,14 +13,22 @@
. += "It has \a [circuit] installed."
-/obj/structure/frame/deconstruct(disassembled = TRUE)
+/obj/structure/frame/deconstruct(disassembled = TRUE, scrapped = FALSE)
if(!(flags_1 & NODECONSTRUCT_1))
new /obj/item/stack/sheet/metal(loc, 5)
- if(circuit)
+ if(circuit && !scrapped)
circuit.forceMove(loc)
circuit = null
qdel(src)
+/obj/structure/frame/deconstruct_act(mob/living/user, obj/item/I)
+ . = ..()
+ if(!I.tool_start_check(user, amount=0))
+ return FALSE
+ if(I.use_tool(src, user, 3 SECONDS, volume=0))
+ to_chat(user, "You cut apart \the [src].", "You cut apart \the [src].")
+ deconstruct()
+ return TRUE
/obj/structure/frame/machine
name = "machine frame"
diff --git a/code/game/machinery/deployable.dm b/code/game/machinery/deployable.dm
index 41760633726e..3dedf5887d0c 100644
--- a/code/game/machinery/deployable.dm
+++ b/code/game/machinery/deployable.dm
@@ -79,6 +79,14 @@
return
return ..()
+/obj/structure/barricade/wooden/deconstruct_act(mob/living/user, obj/item/I)
+ . = ..()
+ if(!I.tool_start_check(user, amount=0))
+ return FALSE
+ if (I.use_tool(src, user, 2 SECONDS, volume=0))
+ to_chat(user, "You cut apart [src].")
+ deconstruct()
+ return TRUE
/obj/structure/barricade/wooden/crude
name = "crude plank barricade"
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index e70edb721788..b30c42c97863 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -1243,6 +1243,21 @@
return
INVOKE_ASYNC(src, (density ? PROC_REF(open) : PROC_REF(close)), 2)
+/obj/machinery/door/airlock/deconstruct_act(mob/living/user, obj/item/I)
+ . = ..()
+ if(!I.tool_start_check(user, amount=0))
+ return FALSE
+ var/decon_time = 5 SECONDS
+ if(welded)
+ decon_time += 5 SECONDS
+ if(locked)
+ decon_time += 5 SECONDS
+ if(seal)
+ decon_time += 15 SECONDS
+ if (I.use_tool(src, user, decon_time, volume=100))
+ to_chat(user, "You cut open the [src].")
+ deconstruct(FALSE, user)
+ return TRUE
/obj/machinery/door/airlock/open(forced=0)
if(operating || welded || locked || seal || !wires)
diff --git a/code/game/mecha/equipment/tools/work_tools.dm b/code/game/mecha/equipment/tools/work_tools.dm
index ee8481255f2d..684b2d9caa2b 100644
--- a/code/game/mecha/equipment/tools/work_tools.dm
+++ b/code/game/mecha/equipment/tools/work_tools.dm
@@ -491,6 +491,86 @@
return 1
//WS Edit End - Readded from Smartwire Revert
+/obj/item/mecha_parts/mecha_equipment/salvage_saw
+ name = "109-C Salvage Saw"
+ desc = "Equipment for cutting open walls and airlocks."
+ icon_state = "mecha_saw"
+ equip_cooldown = 5
+ energy_drain = 10
+ force = 15
+ var/dam_force = 30
+ harmful = TRUE
+ tool_behaviour = TOOL_DECONSTRUCT
+ toolspeed = 0.5
+ var/datum/effect_system/spark_spread/spark_system
+
+/obj/item/mecha_parts/mecha_equipment/salvage_saw/can_attach(obj/mecha/M as obj)
+ if(..())
+ if(istype(M, /obj/mecha/working) || istype(M, /obj/mecha/combat))
+ return 1
+ return 0
+
+/obj/item/mecha_parts/mecha_equipment/salvage_saw/attach()
+ ..()
+ toolspeed = 0.5
+ return
+
+/obj/item/mecha_parts/mecha_equipment/salvage_saw/detach()
+ ..()
+ toolspeed = 10 //yeah sure, use a mech tool without a mech. see how far that gets you
+ return ..()
+
+/obj/item/mecha_parts/mecha_equipment/salvage_saw/action(atom/target)
+ if(!action_checks(target))
+ return
+ if(isliving(target))
+ if(chassis.occupant.a_intent == INTENT_HARM)
+ var/mob/living/M = target
+ saw_mob(M, chassis.occupant)
+ return
+ else
+ target.add_overlay(GLOB.cutting_effect)
+ if(target.deconstruct_act(chassis.occupant, src))
+ do_sparks(2, TRUE, src)
+ chassis.stopped--
+ target.cut_overlay(GLOB.cutting_effect)
+ if(!chassis.stopped)
+ occupant_message("[src] finishes cutting, allowing movement again.")
+
+/obj/item/mecha_parts/mecha_equipment/salvage_saw/tool_start_check(user, amount)
+ if(!chassis.stopped)
+ occupant_message("[src] begins cutting, locking in place!")
+ chassis.stopped++
+ return TRUE
+
+/obj/item/mecha_parts/mecha_equipment/salvage_saw/proc/saw_mob(mob/living/target, mob/user)
+ target.visible_message("[chassis] is sawing [target] with [src]!", \
+ "[chassis] is sawing you with [src]!")
+ if(!do_after_cooldown(target))
+ return
+ log_combat(user, target, "sawed", "[name]", "(INTENT: [uppertext(user.a_intent)]) (DAMTYPE: [uppertext(damtype)])")
+ if(target.stat == DEAD && target.getBruteLoss() >= 400)
+ log_combat(user, target, "gibbed", name)
+ target.gib()
+ else
+ var/obj/item/bodypart/target_part = target.get_bodypart(ran_zone(BODY_ZONE_CHEST))
+ target.apply_damage(15, BRUTE, BODY_ZONE_CHEST, target.run_armor_check(target_part, "melee"))
+
+ //blood splatters
+ var/splatter_dir = get_dir(chassis, target)
+ if(isalien(target))
+ new /obj/effect/temp_visual/dir_setting/bloodsplatter/xenosplatter(target.drop_location(), splatter_dir)
+ else
+ var/splatter_color = null
+ if(iscarbon(target))
+ var/mob/living/carbon/carbon_target = target
+ splatter_color = carbon_target.dna.blood_type.color
+ new /obj/effect/temp_visual/dir_setting/bloodsplatter(target.drop_location(), splatter_dir, splatter_color)
+
+ //organs go everywhere
+ if(target_part && prob(10))
+ target_part.dismember(BRUTE)
+
//Dunno where else to put this so shrug
/obj/item/mecha_parts/mecha_equipment/conversion_kit
name = "Exosuit Conversion Kit"
diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm
index a1b46fd2fbfa..8010f672989d 100644
--- a/code/game/mecha/mecha.dm
+++ b/code/game/mecha/mecha.dm
@@ -15,6 +15,7 @@
light_on = FALSE
var/ruin_mecha = FALSE //if the mecha starts on a ruin, don't automatically give it a tracking beacon to prevent metagaming.
var/can_move = 0 //time of next allowed movement
+ var/stopped = FALSE
var/mob/living/carbon/occupant = null
var/step_in = 10 //make a step in step_in/10 sec.
var/dir_in = 2//What direction will the mech face when entered/powered on? Defaults to South.
@@ -585,6 +586,8 @@
/obj/mecha/proc/domove(direction)
if(can_move >= world.time)
return 0
+ if(stopped)
+ return 0
if(!Process_Spacemove(direction))
return 0
if(!has_charge(step_energy_drain))
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index a1302008cf89..154f6bde143a 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -2,6 +2,10 @@ GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/e
GLOBAL_DATUM_INIT(welding_sparks, /mutable_appearance, mutable_appearance('icons/effects/welding_effect.dmi', "welding_sparks", GASFIRE_LAYER, ABOVE_LIGHTING_PLANE))
+GLOBAL_DATUM_INIT(cutting_effect, /mutable_appearance, mutable_appearance('icons/effects/cutting_effect.dmi', "cutting_effect", GASFIRE_LAYER, ABOVE_LIGHTING_PLANE))
+
+GLOBAL_DATUM_INIT(advanced_cutting_effect, /mutable_appearance, mutable_appearance('icons/effects/cutting_effect.dmi', "advanced_cutting_effect", GASFIRE_LAYER, ABOVE_LIGHTING_PLANE))
+
GLOBAL_DATUM_INIT(cleaning_bubbles, /mutable_appearance, mutable_appearance('icons/effects/effects.dmi', "bubbles", ABOVE_MOB_LAYER, GAME_PLANE))
GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
diff --git a/code/game/objects/items/gear_packs.dm b/code/game/objects/items/gear_packs.dm
new file mode 100644
index 000000000000..65db4ffa28c9
--- /dev/null
+++ b/code/game/objects/items/gear_packs.dm
@@ -0,0 +1,285 @@
+/obj/item/gear_pack
+ name = "gear pack"
+ desc = "A large backpack that usually holds things"
+ icon = 'icons/obj/hydroponics/equipment.dmi'
+ icon_state = "waterbackpack"
+ item_state = "waterbackpack"
+ lefthand_file = 'icons/mob/inhands/equipment/backpack_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/equipment/backpack_righthand.dmi'
+ w_class = WEIGHT_CLASS_HUGE
+ slot_flags = ITEM_SLOT_BACK
+ item_flags = SLOWS_WHILE_IN_HAND
+ max_integrity = 300
+ slowdown = 1
+ drag_slowdown = 1
+ actions_types = list(/datum/action/item_action/toggle_gear_handle)
+ 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/on = FALSE
+ var/obj/item/stock_parts/cell/cell
+ var/preload_cell_type = /obj/item/stock_parts/cell/high
+ var/powered = FALSE
+ var/activate_sound = "sparks"
+ var/obj/item/gear_handle/gear_handle_type = /obj/item/gear_handle
+ var/obj/item/gear_handle/gear_handle
+
+/obj/item/gear_pack/get_cell()
+ return cell
+
+/obj/item/gear_pack/Initialize()
+ . = ..()
+ drag_slowdown = slowdown
+ gear_handle = new gear_handle_type(src)
+ cell = new preload_cell_type(src)
+ update_power()
+ return
+
+/obj/item/gear_pack/examine(mob/user)
+ . = ..()
+ . += "It is [ on ? "currently" : "not"] active."
+ if(cell)
+ . += "A small readout reports [PERCENT(cell.charge / cell.maxcharge)]% charge."
+
+/obj/item/gear_pack/fire_act(exposed_temperature, exposed_volume)
+ . = ..()
+ if(gear_handle?.loc == src)
+ gear_handle.fire_act(exposed_temperature, exposed_volume)
+
+/obj/item/gear_pack/extinguish()
+ . = ..()
+ if(gear_handle?.loc == src)
+ gear_handle.extinguish()
+
+/obj/item/gear_pack/proc/update_power()
+ if(!QDELETED(cell))
+ if(QDELETED(gear_handle) || cell.charge < gear_handle.usecost)
+ powered = FALSE
+ else
+ powered = TRUE
+ else
+ powered = FALSE
+ update_icon()
+
+/obj/item/gear_pack/update_overlays()
+ . = ..()
+
+ if(powered)
+ . += "[initial(icon_state)]-powered"
+ if(!QDELETED(cell))
+ var/ratio = cell.charge / cell.maxcharge
+ ratio = CEILING(ratio*4, 1) * 25
+ . += "[initial(icon_state)]-charge[ratio]"
+ if(!cell)
+ . += "[initial(icon_state)]-nocell"
+ if(!on)
+ . += "[initial(icon_state)]-attachment"
+
+/obj/item/gear_pack/CheckParts(list/parts_list)
+ ..()
+ cell = locate(/obj/item/stock_parts/cell) in contents
+ update_power()
+
+/obj/item/gear_pack/ui_action_click()
+ toggle_gear_handle()
+
+//ATTACK HAND IGNORING PARENT RETURN VALUE
+/obj/item/gear_pack/attack_hand(mob/user)
+ if(loc == user)
+ if(slot_flags == ITEM_SLOT_BACK)
+ if(user.get_item_by_slot(ITEM_SLOT_BACK) == src)
+ ui_action_click()
+ else
+ to_chat(user, "Put the [src] on your back first!")
+
+ else if(slot_flags == ITEM_SLOT_BELT)
+ if(user.get_item_by_slot(ITEM_SLOT_BELT) == src)
+ ui_action_click()
+ else
+ to_chat(user, "Strap the [src]'s belt on first!")
+ return
+ return ..()
+
+/obj/item/gear_pack/MouseDrop(obj/over_object)
+ . = ..()
+ if(ismob(loc))
+ var/mob/M = loc
+ if(!M.incapacitated() && istype(over_object, /atom/movable/screen/inventory/hand))
+ var/atom/movable/screen/inventory/hand/H = over_object
+ M.putItemFromInventoryInHandIfPossible(src, H.held_index)
+
+/obj/item/gear_pack/attackby(obj/item/W, mob/user, params)
+ if(W == gear_handle)
+ toggle_gear_handle()
+ else if(istype(W, /obj/item/stock_parts/cell))
+ var/obj/item/stock_parts/cell/C = W
+ if(cell)
+ to_chat(user, "[src] already has a cell!")
+ else
+ if(C.maxcharge < gear_handle.usecost)
+ to_chat(user, "[src] requires a higher capacity cell.")
+ return
+ if(!user.transferItemToLoc(W, src))
+ return
+ cell = W
+ to_chat(user, "You install a cell in [src].")
+ update_power()
+
+ else if(W.tool_behaviour == TOOL_SCREWDRIVER)
+ if(cell)
+ cell.update_icon()
+ cell.forceMove(get_turf(src))
+ cell = null
+ to_chat(user, "You remove the cell from [src].")
+ update_power()
+ else
+ return ..()
+
+/obj/item/gear_pack/emp_act(severity)
+ . = ..()
+ if(cell && !(. & EMP_PROTECT_CONTENTS))
+ deductcharge(1000 / severity)
+ if(. & EMP_PROTECT_SELF)
+ return
+ update_power()
+
+/obj/item/gear_pack/proc/toggle_gear_handle()
+ set name = "Toggle gear_handle"
+ set category = "Object"
+ on = !on
+
+ var/mob/living/carbon/user = usr
+ if(on)
+ //Detach the gear_handle into the user's hands
+ playsound(src, 'sound/items/handling/multitool_pickup.ogg', 100)
+ if(!usr.put_in_hands(gear_handle))
+ on = FALSE
+ to_chat(user, "You need a free hand to hold the [gear_handle]!")
+ update_power()
+ return
+ else
+ //Remove from their hands and back onto the gear pack
+ remove_gear_handle(user)
+
+ update_power()
+ for(var/X in actions)
+ var/datum/action/A = X
+ A.UpdateButtonIcon()
+
+
+/obj/item/gear_pack/equipped(mob/user, slot)
+ ..()
+ if((slot_flags == ITEM_SLOT_BACK && slot != ITEM_SLOT_BACK) || (slot_flags == ITEM_SLOT_BELT && slot != ITEM_SLOT_BELT))
+ remove_gear_handle(user)
+ update_power()
+
+/obj/item/gear_pack/item_action_slot_check(slot, mob/user)
+ if(slot == user.getBackSlot())
+ return 1
+
+/obj/item/gear_pack/proc/remove_gear_handle(mob/user)
+ if(ismob(gear_handle.loc))
+ var/mob/M = gear_handle.loc
+ M.dropItemToGround(gear_handle, TRUE)
+ return
+
+/obj/item/gear_pack/Destroy()
+ if(on)
+ var/M = get(gear_handle, /mob)
+ remove_gear_handle(M)
+ QDEL_NULL(gear_handle)
+ QDEL_NULL(cell)
+ return ..()
+
+/obj/item/gear_pack/proc/deductcharge(chrgdeductamt)
+ if(cell)
+ if(cell.charge < (gear_handle.usecost+chrgdeductamt))
+ powered = FALSE
+ update_power()
+ if(cell.use(chrgdeductamt))
+ update_power()
+ return TRUE
+ else
+ return FALSE
+
+/obj/item/gear_handle
+
+ name = "gear handle"
+ desc = "handles the gear."
+ icon = 'icons/obj/hydroponics/equipment.dmi'
+ icon_state = "mister"
+ item_state = "mister"
+ lefthand_file = 'icons/mob/inhands/equipment/mister_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/equipment/mister_righthand.dmi'
+
+ force = 0
+ throwforce = 6
+ w_class = WEIGHT_CLASS_BULKY
+ resistance_flags = INDESTRUCTIBLE
+ base_icon_state = "mister"
+
+ var/req_pack = TRUE
+ var/usecost = 1000
+ var/obj/item/gear_pack/pack
+
+/obj/item/gear_handle/Initialize()
+ . = ..()
+ ADD_TRAIT(src, TRAIT_NO_STORAGE_INSERT, GENERIC_ITEM_TRAIT)
+ if (!loc || !istype(loc, /obj/item/gear_pack))
+ return INITIALIZE_HINT_QDEL
+ if(!req_pack)
+ return
+ pack = loc
+ update_icon()
+
+/obj/item/gear_handle/Destroy()
+ pack = null
+ return ..()
+
+/obj/item/gear_handle/equipped(mob/user, slot)
+ . = ..()
+ if(!req_pack)
+ return
+ RegisterSignal(user, COMSIG_MOVABLE_MOVED, PROC_REF(check_range))
+
+/obj/item/gear_handle/Moved()
+ . = ..()
+ check_range()
+
+
+/obj/item/gear_handle/fire_act(exposed_temperature, exposed_volume)
+ . = ..()
+ if((req_pack && pack) && loc != pack)
+ pack.fire_act(exposed_temperature, exposed_volume)
+
+/obj/item/gear_handle/proc/check_range()
+ SIGNAL_HANDLER
+
+ if(!req_pack ||!pack)
+ return
+ if(!in_range(src,pack))
+ var/mob/living/L = loc
+ if(istype(L))
+ to_chat(L, "[pack]'s [src] overextends and comes out of your hands!")
+ else
+ visible_message("[src] snaps back into [pack].")
+ snap_back()
+
+/obj/item/gear_handle/dropped(mob/user)
+ . = ..()
+ if(!req_pack)
+ return ..()
+ if(user)
+ UnregisterSignal(user, COMSIG_MOVABLE_MOVED)
+ if(user != loc)
+ to_chat(user, "[src] snap back into the main unit.")
+ snap_back()
+ return
+
+/obj/item/gear_handle/proc/snap_back()
+ if(!pack)
+ return
+ playsound()
+ pack.on = FALSE
+ forceMove(pack)
+ pack.update_power()
diff --git a/code/game/objects/structures/beds_chairs/bed.dm b/code/game/objects/structures/beds_chairs/bed.dm
index 6c5f46e94a3b..bfc1dbecb3c1 100644
--- a/code/game/objects/structures/beds_chairs/bed.dm
+++ b/code/game/objects/structures/beds_chairs/bed.dm
@@ -37,7 +37,7 @@
return attack_hand(user)
/obj/structure/bed/attackby(obj/item/W, mob/user, params)
- if(W.tool_behaviour == TOOL_WRENCH && !(flags_1&NODECONSTRUCT_1))
+ if((W.tool_behaviour == TOOL_WRENCH || W.tool_behaviour == TOOL_DECONSTRUCT) && !(flags_1&NODECONSTRUCT_1))
W.play_tool_sound(src)
deconstruct(TRUE)
else
diff --git a/code/game/objects/structures/beds_chairs/chair.dm b/code/game/objects/structures/beds_chairs/chair.dm
index 045bf39ae9b6..40e0d9388515 100644
--- a/code/game/objects/structures/beds_chairs/chair.dm
+++ b/code/game/objects/structures/beds_chairs/chair.dm
@@ -60,7 +60,7 @@
qdel(src)
/obj/structure/chair/attackby(obj/item/W, mob/user, params)
- if(W.tool_behaviour == TOOL_WRENCH && !(flags_1&NODECONSTRUCT_1))
+ if((W.tool_behaviour == TOOL_WRENCH || W.tool_behaviour == TOOL_DECONSTRUCT) && !(flags_1&NODECONSTRUCT_1))
W.play_tool_sound(src)
deconstruct()
else if(istype(W, /obj/item/assembly/shock_kit))
diff --git a/code/game/objects/structures/catwalk.dm b/code/game/objects/structures/catwalk.dm
index 20986f9e6c27..2202e84d70e4 100644
--- a/code/game/objects/structures/catwalk.dm
+++ b/code/game/objects/structures/catwalk.dm
@@ -57,7 +57,7 @@
. += "The supporting rods look like they could be welded."
/obj/structure/catwalk/attackby(obj/item/C, mob/user, params)
- if(C.tool_behaviour == TOOL_WELDER && !(resistance_flags & INDESTRUCTIBLE))
+ if((C.tool_behaviour == TOOL_WELDER || C.tool_behaviour == TOOL_DECONSTRUCT) && !(resistance_flags & INDESTRUCTIBLE))
to_chat(user, "You slice off [src]")
deconstruct()
return
diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm
index 7731bf48d2ff..cd1c880eae74 100644
--- a/code/game/objects/structures/crates_lockers/closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets.dm
@@ -255,10 +255,24 @@
if(user in src)
return
if(src.tool_interact(W,user))
- return 1 // No afterattack
+ return TRUE // No afterattack
else
return ..()
+/obj/structure/closet/proc/try_deconstruct(obj/item/W, mob/user)
+ if(W.tool_behaviour == cutting_tool || W.tool_behaviour == TOOL_DECONSTRUCT)
+ if(!W.tool_start_check(user, amount = 0))
+ return
+ to_chat(user, span_notice("You begin cutting \the [src] apart..."))
+ if(W.use_tool(src, user, 40, volume = 50))
+ if(!opened)
+ return
+ user.visible_message(span_notice("[user] slices apart \the [src]."),
+ span_notice("You cut \the [src] apart with \the [W]."),
+ span_hear("You hear welding."))
+ deconstruct(TRUE)
+ return TRUE
+
/obj/structure/closet/proc/tool_interact(obj/item/W, mob/user)//returns TRUE if attackBy call shouldnt be continued (because tool was used/closet was of wrong type), FALSE if otherwise
. = TRUE
if(opened)
@@ -300,6 +314,13 @@
user.visible_message("[user] [anchored ? "anchored" : "unanchored"] \the [src] [anchored ? "to" : "from"] the ground.", \
"You [anchored ? "anchored" : "unanchored"] \the [src] [anchored ? "to" : "from"] the ground.", \
"You hear a ratchet.")
+
+ else if(W.tool_behaviour == TOOL_DECONSTRUCT && locked)
+ user.visible_message("[user] is cutting \the [src] open !", "You begin to cut \the [src] open.")
+ if (W.use_tool(src, user, 10 SECONDS, volume=0))
+ bust_open()
+ user.visible_message("[user] busted \the [src] open !", "You finish cutting \the [src] open.")
+
else if(user.a_intent != INTENT_HARM)
var/item_is_id = W.GetID()
if(!item_is_id)
diff --git a/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm b/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm
index 7135b3d199a2..b0674a2d2b60 100644
--- a/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm
+++ b/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm
@@ -20,6 +20,13 @@
var/move_delay = FALSE
var/egged = 0
+/obj/structure/closet/cardboard/try_deconstruct(obj/item/W, mob/user)
+ if(W.tool_behaviour == cutting_tool)
+ user.visible_message(span_notice("[user] cut apart \the [src]."), \
+ span_notice("You cut \the [src] apart with \the [W]."))
+ deconstruct(TRUE)
+ return TRUE
+
/obj/structure/closet/cardboard/relaymove(mob/living/user, direction)
if(opened || move_delay || user.incapacitated() || !isturf(loc) || !has_gravity(loc))
return
diff --git a/code/game/objects/structures/door_assembly.dm b/code/game/objects/structures/door_assembly.dm
index 4246075e49f6..43052f1f0dbb 100644
--- a/code/game/objects/structures/door_assembly.dm
+++ b/code/game/objects/structures/door_assembly.dm
@@ -333,6 +333,14 @@
new mineral_path(T, 2)
qdel(src)
+/obj/structure/door_assembly/deconstruct_act(mob/living/user, obj/item/I)
+ . = ..()
+ if(!I.tool_start_check(user, amount=0))
+ return FALSE
+ if (I.use_tool(src, user, 3 SECONDS, volume=100))
+ to_chat(user, "You slice [src] apart.")
+ deconstruct(FALSE)
+ return TRUE
/obj/structure/door_assembly/rcd_vals(mob/user, obj/item/construction/rcd/the_rcd)
if(the_rcd.mode == RCD_DECONSTRUCT)
diff --git a/code/game/objects/structures/false_walls.dm b/code/game/objects/structures/false_walls.dm
index d5a8c3e496c1..48bf8817e1e7 100644
--- a/code/game/objects/structures/false_walls.dm
+++ b/code/game/objects/structures/false_walls.dm
@@ -107,9 +107,6 @@
else if(W.tool_behaviour == TOOL_WELDER)
if(W.use_tool(src, user, 0, volume=50))
dismantle(user, TRUE)
- else if(istype(W, /obj/item/pickaxe/drill/jackhammer))
- W.play_tool_sound(src)
- dismantle(user, TRUE)
else
return ..()
diff --git a/code/game/objects/structures/girders.dm b/code/game/objects/structures/girders.dm
index 362de185e9ba..df0d3cf1f43c 100644
--- a/code/game/objects/structures/girders.dm
+++ b/code/game/objects/structures/girders.dm
@@ -36,26 +36,7 @@
playsound(src, 'sound/machines/clockcult/integration_cog_install.ogg', 50, TRUE)
add_fingerprint(user)
- if(istype(W, /obj/item/gun/energy/plasmacutter))
- to_chat(user, "You start slicing apart the girder...")
- if(W.use_tool(src, user, 10, volume=100))
- to_chat(user, "You slice apart the girder.")
- var/obj/item/stack/sheet/metal/M = new (loc, 2)
- M.add_fingerprint(user)
- qdel(src)
-
- return
-
- else if(istype(W, /obj/item/pickaxe/drill/jackhammer))
- to_chat(user, "You smash through the girder!")
- new /obj/item/stack/sheet/metal(get_turf(src))
- W.play_tool_sound(src)
- qdel(src)
-
- return
-
-
- else if(istype(W, /obj/item/stack))
+ if(istype(W, /obj/item/stack))
if(iswallturf(loc))
to_chat(user, "There is already a wall present!")
return
@@ -231,6 +212,15 @@
else
return ..()
+/obj/structure/girder/deconstruct_act(mob/living/user, obj/item/I)
+ . = ..()
+ if(!I.tool_start_check(user, amount=0))
+ return FALSE
+ if(I.use_tool(src, user, 3 SECONDS, volume=0))
+ to_chat(user, "You cut apart \the [src].", "You cut apart \the [src].")
+ deconstruct()
+ return TRUE
+
// Screwdriver behavior for girders
/obj/structure/girder/screwdriver_act(mob/user, obj/item/tool)
if(..())
@@ -373,13 +363,6 @@
transfer_fingerprints_to(R)
qdel(src)
- else if(istype(W, /obj/item/pickaxe/drill/jackhammer))
- to_chat(user, "Your jackhammer smashes through the girder!")
- var/obj/item/stack/sheet/mineral/hidden/hellstone/R = new(drop_location(), 2)
- transfer_fingerprints_to(R)
- W.play_tool_sound(src)
- qdel(src)
-
else if(istype(W, /obj/item/stack/sheet/mineral/hidden/hellstone))
var/obj/item/stack/sheet/mineral/hidden/hellstone/R = W
if(R.get_amount() < 1)
@@ -447,13 +430,6 @@
transfer_fingerprints_to(B)
qdel(src)
- else if(istype(W, /obj/item/pickaxe/drill/jackhammer))
- to_chat(user, "Your jackhammer smashes through the girder!")
- var/obj/item/stack/tile/bronze/B = new(drop_location(), 2)
- transfer_fingerprints_to(B)
- W.play_tool_sound(src)
- qdel(src)
-
else if(istype(W, /obj/item/stack/tile/bronze))
var/obj/item/stack/tile/bronze/B = W
if(B.get_amount() < 2)
diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm
index 5bca53e84dd6..b1897ee661d4 100644
--- a/code/game/objects/structures/grille.dm
+++ b/code/game/objects/structures/grille.dm
@@ -227,6 +227,15 @@
qdel(src)
..()
+/obj/structure/grille/deconstruct_act(mob/living/user, obj/item/I)
+ . = ..()
+ if(!I.tool_start_check(user, amount=0))
+ return FALSE
+ if (I.use_tool(src, user, 1 SECONDS, volume=100))
+ to_chat(user, "You slice [src] apart.")
+ deconstruct(FALSE)
+ return TRUE
+
/obj/structure/grille/obj_break()
if(!broken && !(flags_1 & NODECONSTRUCT_1))
new broken_type(src.loc)
diff --git a/code/game/objects/structures/lattice.dm b/code/game/objects/structures/lattice.dm
index 30999b58a620..9aaefb8c014e 100644
--- a/code/game/objects/structures/lattice.dm
+++ b/code/game/objects/structures/lattice.dm
@@ -40,6 +40,15 @@
var/turf/T = get_turf(src)
return T.attackby(C, user) //hand this off to the turf instead (for building plating, catwalks, etc)
+/obj/structure/lattice/deconstruct_act(mob/living/user, obj/item/I)
+ . = ..()
+ if(!I.tool_start_check(user, amount=0))
+ return FALSE
+ if(I.use_tool(src, user, 1 SECONDS, volume=0))
+ to_chat(user, "You cut apart \the [src].", "You cut apart \the [src].")
+ deconstruct()
+ return TRUE
+
/obj/structure/lattice/deconstruct(disassembled = TRUE)
if(!(flags_1 & NODECONSTRUCT_1))
new build_material(get_turf(src), number_of_mats)
diff --git a/code/game/objects/structures/railings.dm b/code/game/objects/structures/railings.dm
index 95c24145399f..d8f5c543a168 100644
--- a/code/game/objects/structures/railings.dm
+++ b/code/game/objects/structures/railings.dm
@@ -65,6 +65,15 @@
deconstruct()
return TRUE
+/obj/structure/railing/deconstruct_act(mob/living/user, obj/item/I)
+ . = ..()
+ if(!I.tool_start_check(user, amount=0))
+ return FALSE
+ if (I.use_tool(src, user, 3 SECONDS, volume=0))
+ to_chat(user, "You cut apart the railing.")
+ deconstruct()
+ return TRUE
+
/obj/structure/railing/deconstruct(disassembled)
. = ..()
if(!loc) //quick check if it's qdeleted already.
diff --git a/code/game/objects/structures/safe.dm b/code/game/objects/structures/safe.dm
index 5f3e2914bc47..44a9f7f94717 100644
--- a/code/game/objects/structures/safe.dm
+++ b/code/game/objects/structures/safe.dm
@@ -75,6 +75,13 @@ FLOOR SAFES
if(istype(I, /obj/item/clothing/neck/stethoscope))
attack_hand(user)
return
+
+ else if(I.tool_behaviour == TOOL_DECONSTRUCT)
+ user.visible_message("[user] begin to cut through the lock of \the [src].","You start cutting trough the lock of [src].")
+ if(I.use_tool(src, user, 60 SECONDS))
+ broken = TRUE
+ user.visible_message("[user] successfully cuts trough the lock of \the [src].","You successfully cut trough the lock of [src].")
+
else
to_chat(user, "You can't put [I] into the safe while it is closed!")
return
diff --git a/code/game/objects/structures/salvaging.dm b/code/game/objects/structures/salvaging.dm
index dbd75dac488f..f4aad715db19 100644
--- a/code/game/objects/structures/salvaging.dm
+++ b/code/game/objects/structures/salvaging.dm
@@ -34,6 +34,16 @@
qdel(src)
return TRUE
+/obj/structure/salvageable/deconstruct_act(mob/living/user, obj/item/tool)
+ . = ..()
+ user.visible_message("[user] starts slicing [src].", \
+ "You start salvaging anything useful from [src]...")
+ if(tool.use_tool(src, user, 6 SECONDS))
+ user.visible_message("[user] dismantles [src].", \
+ "You salvage [src].")
+ dismantle(user)
+ qdel(src)
+ return TRUE
//Types themself, use them, but not the parent object
diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm
index a7404ef68a6c..d5b1710b6296 100644
--- a/code/game/objects/structures/tables_racks.dm
+++ b/code/game/objects/structures/tables_racks.dm
@@ -162,7 +162,7 @@
/obj/structure/table/attackby(obj/item/I, mob/user, params)
var/list/modifiers = params2list(params)
if(!(flags_1 & NODECONSTRUCT_1) && user.a_intent != INTENT_HELP)
- if(I.tool_behaviour == TOOL_SCREWDRIVER && deconstruction_ready)
+ if((I.tool_behaviour == TOOL_SCREWDRIVER) && deconstruction_ready)
to_chat(user, "You start disassembling [src]...")
if(I.use_tool(src, user, 20, volume=50))
deconstruct(TRUE)
@@ -227,6 +227,15 @@
else
return ..()
+/obj/structure/table/deconstruct_act(mob/living/user, obj/item/I)
+ . = ..()
+ if(!I.tool_start_check(user, amount=0))
+ return FALSE
+ if (I.use_tool(src, user, 1 SECONDS, volume=0))
+ to_chat(user, span_warning("You cut [src] into sheets."))
+ deconstruct(wrench_disassembly = TRUE)
+ return TRUE
+
/obj/structure/table/proc/AfterPutItemOnTable(obj/item/I, mob/living/user)
return
diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm
index 5420cc06b490..5064883c5de9 100644
--- a/code/game/objects/structures/window.dm
+++ b/code/game/objects/structures/window.dm
@@ -27,6 +27,7 @@
var/real_explosion_block //ignore this, just use explosion_block
var/breaksound = "shatter"
var/hitsound = 'sound/effects/Glasshit.ogg'
+ var/decon_time = 5 SECONDS
flags_ricochet = RICOCHET_HARD
ricochet_chance_mod = 0.4
@@ -289,6 +290,15 @@
qdel(src)
update_nearby_icons()
+/obj/structure/window/deconstruct_act(mob/living/user, obj/item/I)
+ . = ..()
+ if(!I.tool_start_check(user, amount=0))
+ return FALSE
+ if (I.use_tool(src, user, decon_time, volume=100))
+ to_chat(user, span_warning("You shatter [src] with the [I]."))
+ deconstruct(FALSE)
+ return TRUE
+
/obj/structure/window/proc/spawnDebris(location)
. = list()
. += new /obj/item/shard(location)
@@ -399,6 +409,7 @@
glass_type = /obj/item/stack/sheet/rglass
rad_insulation = RAD_HEAVY_INSULATION
ricochet_chance_mod = 0.8
+ decon_time = 20 SECONDS
//this is shitcode but all of construction is shitcode and needs a refactor, it works for now
//If you find this like 4 years later and construction still hasn't been refactored, I'm so sorry for this
@@ -408,7 +419,7 @@
switch(state)
if(RWINDOW_SECURE)
- if(I.tool_behaviour == TOOL_WELDER && user.a_intent == INTENT_HARM)
+ if((I.tool_behaviour == TOOL_WELDER) && user.a_intent == INTENT_HARM)
user.visible_message("[user] holds \the [I] to the security screws on \the [src]...",
"You begin heating the security screws on \the [src]...")
if(I.use_tool(src, user, 150, volume = 100))
@@ -531,6 +542,7 @@
damage_deflection = 11 //WS Edit - Weakens R-Windows
explosion_block = 2
glass_type = /obj/item/stack/sheet/plasmarglass
+ decon_time = 25 SECONDS
//entirely copypasted code
//take this out when construction is made a component or otherwise modularized in some way
@@ -746,6 +758,7 @@
glass_type = /obj/item/stack/sheet/plastitaniumglass
glass_amount = 2
rad_insulation = RAD_HEAVY_INSULATION
+ decon_time = 30 SECONDS
/obj/structure/window/plasma/reinforced/plastitanium/unanchored
anchored = FALSE
diff --git a/code/game/turfs/closed/_closed.dm b/code/game/turfs/closed/_closed.dm
index 766d7e0e5a24..ea0c0d4ed4b1 100644
--- a/code/game/turfs/closed/_closed.dm
+++ b/code/game/turfs/closed/_closed.dm
@@ -269,6 +269,21 @@
return FALSE
+/turf/closed/deconstruct_act(mob/living/user, obj/item/I)
+ var/act_duration = breakdown_duration
+ if(!I.tool_start_check(user, amount=0))
+ return FALSE
+ to_chat(user, "You begin slicing through the outer plating...")
+ while(I.use_tool(src, user, act_duration, volume=100))
+ if(iswallturf(src))
+ to_chat(user, "You slice through some of the outer plating...")
+ if(!alter_integrity(-(I.wall_decon_damage),user,FALSE,TRUE))
+ return TRUE
+ else
+ break
+
+ return FALSE
+
/turf/closed/mech_melee_attack(obj/mecha/M)
M.do_attack_animation(src)
switch(M.damtype)
diff --git a/code/modules/cargo/packs/tools.dm b/code/modules/cargo/packs/tools.dm
index 25ed4aaab554..60dd4c92eb1c 100644
--- a/code/modules/cargo/packs/tools.dm
+++ b/code/modules/cargo/packs/tools.dm
@@ -111,6 +111,13 @@
crate_name = "tank transfer valve crate"
crate_type = /obj/structure/closet/crate/secure/science
+/datum/supply_pack/tools/anglegrinder
+ name = "Angle Grinder"
+ desc = "Contains one angle grinder pack, a tool used for quick structure deconstruction and salvaging"
+ cost = 2000
+ contains = list(/obj/item/gear_pack/anglegrinder)
+ crate_name = "Angle Grinder"
+
/*
Liquid tanks
*/
diff --git a/code/modules/mining/abandoned_crates.dm b/code/modules/mining/abandoned_crates.dm
index ea43fe78f176..48ffa3fbecbf 100644
--- a/code/modules/mining/abandoned_crates.dm
+++ b/code/modules/mining/abandoned_crates.dm
@@ -124,6 +124,10 @@
qdel(src)
..()
+// No busting open (used to disallow angle grinder cheesing
+/obj/structure/closet/crate/secure/loot/bust_open()
+ boom()
+
/obj/structure/closet/crate/secure/loot/proc/spawn_loot()
var/loot = rand(1,100) //100 different crates with varying chances of spawning
switch(loot)
diff --git a/code/modules/mining/equipment/angle_grinder.dm b/code/modules/mining/equipment/angle_grinder.dm
new file mode 100644
index 000000000000..290cf0d153d7
--- /dev/null
+++ b/code/modules/mining/equipment/angle_grinder.dm
@@ -0,0 +1,144 @@
+/obj/item/gear_pack/anglegrinder
+ name = "grinder pack"
+ desc = "Supplies the high voltage needed to run the attached grinder."
+ icon = 'icons/obj/item/gear_packs.dmi'
+ item_state = "anglegrinderpack"
+ icon_state = "anglegrinderpack"
+ lefthand_file = 'icons/mob/inhands/equipment/backpack_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/equipment/backpack_righthand.dmi'
+ gear_handle_type = /obj/item/gear_handle/anglegrinder
+
+/obj/item/gear_handle/anglegrinder
+ name = "angle grinder"
+ desc = "A powerful salvage tool used to cut apart walls and airlocks. A hazard sticker recommends ear and eye protection."
+ icon = 'icons/obj/item/gear_packs.dmi'
+ icon_state = "anglegrinder"
+ item_state = "anglegrinder"
+ lefthand_file = 'icons/mob/inhands/equipment/gear_handle_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/equipment/gear_handle_righthand.dmi'
+ flags_1 = CONDUCT_1
+ force = 13
+ armour_penetration = 5
+ w_class = WEIGHT_CLASS_BULKY
+ item_flags = ABSTRACT
+ attack_verb = list("lacerated", "ripped", "sliced", "sawed", "cut", "chopped", "diced")
+ hitsound = 'sound/weapons/anglegrinder.ogg'
+ usesound = 'sound/weapons/anglegrinder.ogg'
+ tool_behaviour = null // is set to TOOL_DECONSTRUCT once wielded
+ toolspeed = 1
+ wall_decon_damage = 200
+ usecost = 5
+ pack = /obj/item/gear_pack/anglegrinder
+ var/startsound = 'sound/weapons/chainsawhit.ogg'
+ var/adv = FALSE
+ var/wielded = FALSE // track wielded status on item
+ var/two_hand_force = 24
+
+/obj/item/gear_handle/anglegrinder/tool_start_check(mob/living/user, amount)
+ if(!pack)
+ to_chat(user, "how do you not have a pack for this. what.")
+ return FALSE
+ if(!pack.cell)
+ to_chat(user, "You need a cell to start!")
+ return FALSE
+ var/obj/item/stock_parts/cell/cell = pack.get_cell()
+ if(cell.charge < usecost)
+ to_chat(user, "You need more charge to complete this task!")
+ return FALSE
+ return TRUE
+
+/obj/item/gear_handle/anglegrinder/tool_use_check(mob/living/user, amount)
+ if(!pack.cell)
+ return FALSE
+ if(pack.deductcharge(usecost))
+ return TRUE
+ else
+ to_chat(user, "You need more charge to complete this task!")
+ return FALSE
+
+/obj/item/gear_handle/anglegrinder/use(used)
+ return TRUE
+
+/obj/item/gear_handle/anglegrinder/Initialize()
+ . = ..()
+ RegisterSignal(src, COMSIG_TWOHANDED_WIELD, PROC_REF(on_wield))
+ RegisterSignal(src, COMSIG_TWOHANDED_UNWIELD, PROC_REF(on_unwield))
+
+/obj/item/gear_handle/anglegrinder/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/butchering, 30, 100, 0, startsound, TRUE)
+ AddComponent(/datum/component/two_handed, force_unwielded=force, force_wielded=two_hand_force, wieldsound=startsound)
+ AddElement(/datum/element/tool_bang, 2)
+
+/// triggered on wield of two handed item
+/obj/item/gear_handle/anglegrinder/proc/on_wield(obj/item/source, mob/user)
+ SIGNAL_HANDLER
+
+ tool_behaviour = TOOL_DECONSTRUCT
+ wielded = TRUE
+ sharpness = IS_SHARP
+ icon_state = "[initial(item_state)]-wield"
+ item_state = "[initial(item_state)]-wield"
+
+/// triggered on unwield of two handed item
+/obj/item/gear_handle/anglegrinder/proc/on_unwield(obj/item/source, mob/user)
+ SIGNAL_HANDLER
+
+ tool_behaviour = null
+ wielded = FALSE
+ sharpness = initial(sharpness)
+ icon_state = initial(icon_state)
+ item_state = initial(item_state)
+
+/obj/item/gear_handle/anglegrinder/get_dismemberment_chance()
+ if(wielded)
+ . = ..()
+
+/obj/item/gear_handle/anglegrinder/use_tool(atom/target, mob/living/user, delay, amount=1, volume=0, datum/callback/extra_checks)
+ if(adv)
+ target.add_overlay(GLOB.advanced_cutting_effect)
+ . = ..()
+ target.cut_overlay(GLOB.advanced_cutting_effect)
+ else
+ target.add_overlay(GLOB.cutting_effect)
+ . = ..()
+ target.cut_overlay(GLOB.cutting_effect)
+
+/obj/item/gear_pack/anglegrinder/energy
+ name = "energy supply pack"
+ desc = "a highly inefficient GEC-E-014 Supply Pack, used to generate and contain an energy field."
+ item_state = "energyanglegrinderpack"
+ icon_state = "energyanglegrinderpack"
+ gear_handle_type = /obj/item/gear_handle/anglegrinder/energy
+
+/obj/item/gear_handle/anglegrinder/energy
+ name = "energy saw"
+ desc = "An early prototype for handheld energy weapons, designed by a joint GEC-Cybersun lab to create an energy field for combat use."
+ icon_state = "energyanglegrinder"
+ item_state = "energyanglegrinder"
+ force = 5
+ two_hand_force = 28
+ armour_penetration = 16
+ w_class = WEIGHT_CLASS_BULKY
+ item_flags = ABSTRACT
+ attack_verb = list("lacerated", "ripped", "burned", "sliced", "cauterized", "seared", "diced")
+ hitsound = 'sound/weapons/blade1.ogg'
+ usesound = 'sound/weapons/blade1.ogg'
+ startsound = 'sound/weapons/saberon.ogg'
+ toolspeed = 0.7
+ usecost = 10
+ pack = /obj/item/gear_pack/anglegrinder/energy
+ light_system = MOVABLE_LIGHT
+ light_range = 3
+ light_color = LIGHT_COLOR_ELECTRIC_GREEN
+ light_on = FALSE
+ adv = TRUE
+
+/obj/item/gear_handle/anglegrinder/energy/on_wield(obj/item/source, mob/user)
+ . = ..()
+ set_light_on(TRUE)
+
+/obj/item/gear_handle/anglegrinder/energy/on_unwield(obj/item/source, mob/user)
+ . = ..()
+ set_light_on(FALSE)
+
diff --git a/code/modules/projectiles/ammunition/energy/plasma.dm b/code/modules/projectiles/ammunition/energy/plasma.dm
index 00de4a90ffee..d593086157fd 100644
--- a/code/modules/projectiles/ammunition/energy/plasma.dm
+++ b/code/modules/projectiles/ammunition/energy/plasma.dm
@@ -2,10 +2,9 @@
projectile_type = /obj/projectile/plasma
select_name = "plasma burst"
fire_sound = 'sound/weapons/plasma_cutter.ogg'
- delay = 15
- e_cost = 25
+ delay = 30
+ e_cost = 100
/obj/item/ammo_casing/energy/plasma/adv
projectile_type = /obj/projectile/plasma/adv
- delay = 10
- e_cost = 10
+ delay = 20
diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm
index c63c8358e2de..067a4bbc5d97 100644
--- a/code/modules/projectiles/guns/energy/special.dm
+++ b/code/modules/projectiles/guns/energy/special.dm
@@ -122,11 +122,12 @@
heat = 3800
usesound = list('sound/items/welder.ogg', 'sound/items/welder2.ogg')
- tool_behaviour = TOOL_WELDER
+ tool_behaviour = TOOL_DECONSTRUCT
wall_decon_damage = 200
- toolspeed = 0.7 //plasmacutters can be used as welders, and are faster than standard welders
+ toolspeed = 0.9 //plasmacutters can be used like angle grinders, and are a bit faster
internal_cell = TRUE //so you don't cheese through the need for plasma - WS EDIT
- var/charge_weld = 25 //amount of charge used up to start action (multiplied by amount) and per progress_flash_divisor ticks of welding
+ var/charge_cut = 100 //amount of charge used up to start action (multiplied by amount) and per progress_flash_divisor ticks of cutting
+ var/adv = FALSE
/obj/item/gun/energy/plasmacutter/ComponentInitialize()
. = ..()
@@ -155,16 +156,16 @@
else
..()
-// Can we weld? Plasma cutter does not use charge continuously.
+// Can we cut? Plasma cutter does not use charge continuously.
// Amount cannot be defaulted to 1: most of the code specifies 0 in the call.
/obj/item/gun/energy/plasmacutter/tool_use_check(mob/living/user, amount)
if(QDELETED(cell))
to_chat(user, "[src] does not have a cell, and cannot be used!")
return FALSE
- // Amount cannot be used if drain is made continuous, e.g. amount = 5, charge_weld = 25
+ // Amount cannot be used if drain is made continuous, e.g. amount = 5, charge_cut = 25
// Then it'll drain 125 at first and 25 periodically, but fail if charge dips below 125 even though it still can finish action
- // Alternately it'll need to drain amount*charge_weld every period, which is either obscene or makes it free for other uses
- if(amount ? cell.charge < charge_weld * amount : cell.charge < charge_weld)
+ // Alternately it'll need to drain amount*charge_cut every period, which is either obscene or makes it free for other uses
+ if(amount ? cell.charge < charge_cut * amount : cell.charge < charge_cut)
to_chat(user, "You need more charge to complete this task!")
return FALSE
@@ -186,13 +187,19 @@
return TRUE
/obj/item/gun/energy/plasmacutter/use(amount)
- return (!QDELETED(cell) && cell.use(amount ? amount * charge_weld : charge_weld))
+ return (!QDELETED(cell) && cell.use(amount ? amount * charge_cut : charge_cut))
/obj/item/gun/energy/plasmacutter/use_tool(atom/target, mob/living/user, delay, amount=1, volume=0, datum/callback/extra_checks)
if(amount)
- target.add_overlay(GLOB.welding_sparks)
+ if(adv)
+ target.add_overlay(GLOB.advanced_cutting_effect)
+ else
+ target.add_overlay(GLOB.cutting_effect)
. = ..()
- target.cut_overlay(GLOB.welding_sparks)
+ if(adv)
+ target.cut_overlay(GLOB.advanced_cutting_effect)
+ else
+ target.cut_overlay(GLOB.cutting_effect)
else
. = ..(amount=1)
@@ -201,11 +208,9 @@
icon_state = "adv_plasmacutter"
item_state = "adv_plasmacutter"
force = 15
+ wall_decon_damage = 300
ammo_type = list(/obj/item/ammo_casing/energy/plasma/adv)
- wall_decon_damage = 200
- toolspeed = 0.4
-
/obj/item/gun/energy/wormhole_projector
name = "bluespace wormhole projector"
desc = "A projector that emits high density quantum-coupled bluespace beams." //WS Edit - Any anomaly core for phazons
diff --git a/code/modules/projectiles/projectile/special/plasma.dm b/code/modules/projectiles/projectile/special/plasma.dm
index d957ad924572..68071bd2c557 100644
--- a/code/modules/projectiles/projectile/special/plasma.dm
+++ b/code/modules/projectiles/projectile/special/plasma.dm
@@ -1,10 +1,10 @@
/obj/projectile/plasma
name = "plasma blast"
icon_state = "plasmacutter"
- damage_type = BRUTE
- damage = 5
+ damage_type = BURN
+ damage = 15
range = 4
- dismemberment = 20
+ dismemberment = 10
/// chance that the plasmablast ruins the ore
var/slag_chance = 33
impact_effect_type = /obj/effect/temp_visual/impact_effect/purple_laser
diff --git a/code/modules/surgery/organic_steps.dm b/code/modules/surgery/organic_steps.dm
index 8959a7f99715..39fb1d71b258 100644
--- a/code/modules/surgery/organic_steps.dm
+++ b/code/modules/surgery/organic_steps.dm
@@ -152,13 +152,17 @@
implements = list(
TOOL_SAW = 100,
/obj/item/melee/axe/fire = 50,
+ /obj/item/gear_handle/anglegrinder = 50,
/obj/item/melee/arm_blade = 40,
/obj/item/hatchet = 40,
/obj/item/melee/knife/butcher = 33,
+ /obj/item/gun/energy/plasmacutter = 30,
/obj/item = 10) //10% success (sort of) with any sharp item with a force>=10
time = 5.4 SECONDS
preop_sound = list(
/obj/item/circular_saw = 'sound/surgery/saw.ogg',
+ /obj/item/gear_handle/anglegrinder = 'sound/surgery/saw.ogg',
+ /obj/item/gun/energy/plasmacutter = 'sound/weapons/plasma_cutter.ogg',
/obj/item/melee/arm_blade = 'sound/surgery/scalpel1.ogg',
/obj/item/melee/axe/fire = 'sound/surgery/scalpel1.ogg',
/obj/item/hatchet = 'sound/surgery/scalpel1.ogg',
diff --git a/icons/effects/cutting_effect.dmi b/icons/effects/cutting_effect.dmi
new file mode 100644
index 000000000000..e8b4abeec5d0
Binary files /dev/null and b/icons/effects/cutting_effect.dmi differ
diff --git a/icons/mecha/mecha_equipment.dmi b/icons/mecha/mecha_equipment.dmi
index 5e08a834a892..76549c15a3e0 100644
Binary files a/icons/mecha/mecha_equipment.dmi and b/icons/mecha/mecha_equipment.dmi differ
diff --git a/icons/mob/clothing/back.dmi b/icons/mob/clothing/back.dmi
index 5508bc67523c..fc00f899ed93 100644
Binary files a/icons/mob/clothing/back.dmi and b/icons/mob/clothing/back.dmi differ
diff --git a/icons/mob/inhands/equipment/gear_handle_lefthand.dmi b/icons/mob/inhands/equipment/gear_handle_lefthand.dmi
new file mode 100644
index 000000000000..169f91ce6eba
Binary files /dev/null and b/icons/mob/inhands/equipment/gear_handle_lefthand.dmi differ
diff --git a/icons/mob/inhands/equipment/gear_handle_righthand.dmi b/icons/mob/inhands/equipment/gear_handle_righthand.dmi
new file mode 100644
index 000000000000..172f18e6095a
Binary files /dev/null and b/icons/mob/inhands/equipment/gear_handle_righthand.dmi differ
diff --git a/icons/obj/item/gear_packs.dmi b/icons/obj/item/gear_packs.dmi
new file mode 100644
index 000000000000..76fb94bd4ff3
Binary files /dev/null and b/icons/obj/item/gear_packs.dmi differ
diff --git a/icons/obj/mining.dmi b/icons/obj/mining.dmi
index 337e3bf6d8da..efffc5cebb4a 100644
Binary files a/icons/obj/mining.dmi and b/icons/obj/mining.dmi differ
diff --git a/shiptest.dme b/shiptest.dme
index e460d129f655..483840923778 100644
--- a/shiptest.dme
+++ b/shiptest.dme
@@ -679,6 +679,7 @@
#include "code\datums\elements\selfknockback.dm"
#include "code\datums\elements\snail_crawl.dm"
#include "code\datums\elements\squish.dm"
+#include "code\datums\elements\tool_bang.dm"
#include "code\datums\elements\tool_flash.dm"
#include "code\datums\elements\turf_transparency.dm"
#include "code\datums\elements\undertile.dm"
@@ -1186,6 +1187,7 @@
#include "code\game\objects\items\etherealdiscoball.dm"
#include "code\game\objects\items\extinguisher.dm"
#include "code\game\objects\items\flamethrower.dm"
+#include "code\game\objects\items\gear_packs.dm"
#include "code\game\objects\items\gift.dm"
#include "code\game\objects\items\granters.dm"
#include "code\game\objects\items\handcuffs.dm"
@@ -2431,6 +2433,7 @@
#include "code\modules\mining\ores_coins.dm"
#include "code\modules\mining\satchel_ore_boxdm.dm"
#include "code\modules\mining\shelters.dm"
+#include "code\modules\mining\equipment\angle_grinder.dm"
#include "code\modules\mining\equipment\explorer_gear.dm"
#include "code\modules\mining\equipment\kinetic_crusher.dm"
#include "code\modules\mining\equipment\lazarus_injector.dm"
diff --git a/sound/weapons/anglegrinder.ogg b/sound/weapons/anglegrinder.ogg
new file mode 100644
index 000000000000..c0bc5b593a18
Binary files /dev/null and b/sound/weapons/anglegrinder.ogg differ