diff --git a/code/datums/action.dm b/code/datums/action.dm
index de13fc002dde..5ea5ba4044c0 100644
--- a/code/datums/action.dm
+++ b/code/datums/action.dm
@@ -253,6 +253,9 @@
/datum/action/item_action/toggle_mister
name = "Toggle Mister"
+/datum/action/item_action/toggle_grinder
+ name = "Toggle Grinder"
+
/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/modules/cargo/packs/tools.dm b/code/modules/cargo/packs/tools.dm
index f5b5503349af..78bdd3a2db4f 100644
--- a/code/modules/cargo/packs/tools.dm
+++ b/code/modules/cargo/packs/tools.dm
@@ -107,9 +107,9 @@
/datum/supply_pack/tools/anglegrinder
name = "Angle Grinder"
- desc = "Contain one angle grinder, a tool used for quick structure deconstruction and salvaging"
- cost = 1000
- contains = list(/obj/item/anglegrinder)
+ desc = "Contains one angle grinder pack, a tool used for quick structure deconstruction and salvaging"
+ cost = 2000
+ contains = list(/obj/item/anglegrinderpack)
crate_name = "Angle Grinder"
/*
diff --git a/code/modules/mining/equipment/angle_grinder.dm b/code/modules/mining/equipment/angle_grinder.dm
index 1b8df35def23..0abbed33ad8b 100644
--- a/code/modules/mining/equipment/angle_grinder.dm
+++ b/code/modules/mining/equipment/angle_grinder.dm
@@ -1,10 +1,150 @@
-/*
- * Configure features by editing __DEFINES/anglegrinder.dm
-*/
+/obj/item/anglegrinderpack
+ name = "grinder pack"
+ desc = "Supplies the high voltage needed to run the attached grinder."
+ icon = 'icons/obj/mining.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'
+ w_class = WEIGHT_CLASS_BULKY
+ slot_flags = ITEM_SLOT_BACK
+ slowdown = 1
+ actions_types = list(/datum/action/item_action/toggle_grinder)
+ 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/obj/item/anglegrinder/grinder
+ var/obj/item/stock_parts/cell/cell
+ var/preload_cell_type = /obj/item/stock_parts/cell/high
+ var/power_cost = 5
+
+/obj/item/anglegrinderpack/Initialize()
+ . = ..()
+ grinder = make_grinder()
+ if(preload_cell_type)
+ if(!ispath(preload_cell_type,/obj/item/stock_parts/cell))
+ log_mapping("[src] at [AREACOORD(src)] had an invalid preload_cell_type: [preload_cell_type].")
+ else
+ cell = new preload_cell_type(src)
+
+/obj/item/anglegrinderpack/Destroy()
+ QDEL_NULL(grinder)
+ return ..()
+
+/obj/item/anglegrinderpack/ui_action_click(mob/user)
+ toggle_grinder(user)
+
+/obj/item/anglegrinder/item_action_slot_check(slot, mob/user)
+ if(slot == user.getBackSlot())
+ return 1
+
+/obj/item/anglegrinderpack/proc/toggle_grinder(mob/living/user)
+ if(!istype(user))
+ return
+ if(user.get_item_by_slot(user.getBackSlot()) != src)
+ to_chat(user, "[src] must be worn properly to use!")
+ return
+ if(user.incapacitated())
+ return
+ if(!cell || !cell.charge)
+ return
+
+ if(QDELETED(grinder))
+ grinder = make_grinder()
+ if(grinder in src)
+ //Detach the grinder into the user's hands
+ if(!user.put_in_hands(grinder))
+ to_chat(user, "You need a free hand to hold the grinder!")
+ return
+ else
+ //Remove from their hands and put back "into" the pack
+ remove_grinder()
+
+/obj/item/anglegrinderpack/verb/toggle_grinder_verb()
+ set name = "Toggle Angle Grinder"
+ set category = "Object"
+ toggle_grinder(usr)
+
+/obj/item/anglegrinderpack/proc/make_grinder()
+ return new /obj/item/anglegrinder(src)
+
+/obj/item/anglegrinderpack/equipped(mob/user, slot)
+ ..()
+ if(slot != ITEM_SLOT_BACK)
+ remove_grinder()
+
+/obj/item/anglegrinderpack/proc/remove_grinder()
+ if(!QDELETED(grinder))
+ if(ismob(grinder.loc))
+ var/mob/M = grinder.loc
+ M.temporarilyRemoveItemFromInventory(grinder, TRUE)
+ grinder.forceMove(src)
+
+/obj/item/anglegrinderpack/attack_hand(mob/user)
+ if (user.get_item_by_slot(user.getBackSlot()) == src)
+ toggle_grinder(user)
+ else
+ return ..()
+
+/obj/item/anglegrinderpack/MouseDrop(obj/over_object)
+ var/mob/M = loc
+ if(istype(M) && istype(over_object, /atom/movable/screen/inventory/hand))
+ var/atom/movable/screen/inventory/hand/H = over_object
+ M.putItemFromInventoryInHandIfPossible(src, H.held_index)
+ return ..()
+
+/obj/item/anglegrinderpack/attackby(obj/item/W, mob/user, params)
+ if(W == grinder)
+ remove_grinder()
+ return 1
+ else
+ return ..()
+
+/obj/item/anglegrinderpack/dropped(mob/user)
+ ..()
+ remove_grinder()
+
+/obj/item/anglegrinderpack/proc/consume_charge(cost = power_cost)
+ if(cell.charge >= cost)
+ cell.charge -= cost
+ return TRUE
+ else
+ cell.charge = 0
+ remove_grinder()
+ return FALSE
+
+/obj/item/anglegrinderpack/attackby(obj/item/I, mob/living/user, params)
+ if(I.tool_behaviour == TOOL_SCREWDRIVER)
+ cell.update_appearance()
+ cell.forceMove(get_turf(src))
+ cell = null
+ to_chat(user, "You remove the cell from [src].")
+ return
+ if(istype(I, /obj/item/stock_parts/cell))
+ var/obj/item/stock_parts/cell/newcell = I
+ if(cell)
+ to_chat(user, "[src] already has a cell!")
+ return
+ else
+ if(newcell.maxcharge < power_cost)
+ to_chat(user, "[src] requires a higher capacity cell.")
+ return
+ if(!user.transferItemToLoc(I, src))
+ return
+ cell = I
+ to_chat(user, "You install [cell] in [src].")
+ return
+
+/obj/item/anglegrinderpack/examine(mob/user)
+ . = ..()
+ if(!cell)
+ . += "The cell is missing!"
+ else
+ . += "[src] is [round(cell.percent())]% charged."
/obj/item/anglegrinder
name = "angle grinder"
- desc = "A powerful salvage tool used to cut apart walls and airlocks. A peeling hazard sticker recommends ear and eye protection."
+ desc = "A powerful salvage tool used to cut apart walls and airlocks. A hazard sticker recommends ear and eye protection."
icon = 'icons/obj/mining.dmi'
icon_state = "anglegrinder_off"
lefthand_file = 'icons/mob/inhands/weapons/chainsaw_lefthand.dmi'
@@ -12,32 +152,42 @@
flags_1 = CONDUCT_1
force = 13
w_class = WEIGHT_CLASS_HUGE
- slot_flags = ITEM_SLOT_BACK
- slowdown = 1
- throwforce = 13
- throw_speed = 2
- throw_range = 4
- custom_materials = list(/datum/material/iron=13000)
+ item_flags = ABSTRACT
attack_verb = list("sliced", "torn", "cut", "chopped", "diced")
hitsound = 'sound/weapons/anglegrinder.ogg'
usesound = 'sound/weapons/anglegrinder.ogg'
sharpness = IS_SHARP
- tool_behaviour = null // is set to TOOL_DECONSTRUCT once weildedk
+ tool_behaviour = null // is set to TOOL_DECONSTRUCT once wielded
toolspeed = 1
var/wielded = FALSE // track wielded status on item
+ var/obj/item/anglegrinderpack/pack
// Trick to make the deconstruction that need a lit welder work. (bypassing fuel test)
/obj/item/anglegrinder/tool_use_check(mob/living/user, amount)
- return TRUE
+ if(pack.consume_charge())
+ return TRUE
+ else
+ to_chat(user, "You need more charge to complete this task!")
+ return FALSE
/obj/item/anglegrinder/use(used)
return TRUE
/obj/item/anglegrinder/Initialize()
. = ..()
+ pack = loc
+ if(!istype(pack))
+ return INITIALIZE_HINT_QDEL
RegisterSignal(src, COMSIG_TWOHANDED_WIELD, PROC_REF(on_wield))
RegisterSignal(src, COMSIG_TWOHANDED_UNWIELD, PROC_REF(on_unwield))
+/obj/item/anglegrinder/doMove(atom/destination)
+ if(destination && (destination != pack.loc || !ismob(destination)))
+ if (loc != pack)
+ to_chat(pack.loc, "[src] snaps back onto [pack].")
+ destination = pack
+ ..()
+
/obj/item/anglegrinder/ComponentInitialize()
. = ..()
AddComponent(/datum/component/butchering, 30, 100, 0, 'sound/weapons/anglegrinder.ogg', TRUE)
diff --git a/icons/mob/clothing/back.dmi b/icons/mob/clothing/back.dmi
index e8702376efce..26929cf5fff2 100644
Binary files a/icons/mob/clothing/back.dmi and b/icons/mob/clothing/back.dmi differ
diff --git a/icons/obj/mining.dmi b/icons/obj/mining.dmi
index 3500abc521b5..efffc5cebb4a 100644
Binary files a/icons/obj/mining.dmi and b/icons/obj/mining.dmi differ
diff --git a/sound/weapons/anglegrinder.ogg b/sound/weapons/anglegrinder.ogg
index b6db8b5ca0a2..c0bc5b593a18 100644
Binary files a/sound/weapons/anglegrinder.ogg and b/sound/weapons/anglegrinder.ogg differ