diff --git a/_maps/map_files/Deltastation/DeltaStation2_skyrat.dmm b/_maps/map_files/Deltastation/DeltaStation2_skyrat.dmm
index f7d4b54737b..17d8535f23b 100644
--- a/_maps/map_files/Deltastation/DeltaStation2_skyrat.dmm
+++ b/_maps/map_files/Deltastation/DeltaStation2_skyrat.dmm
@@ -109464,7 +109464,7 @@
/obj/effect/turf_decal/tile/red{
dir = 8
},
-/obj/machinery/vending/security_ammo,
+/obj/machinery/ammo_workbench,
/turf/open/floor/iron/dark,
/area/security/brig)
"uIv" = (
diff --git a/_maps/map_files/IceBoxStation/IceBoxStation_skyrat.dmm b/_maps/map_files/IceBoxStation/IceBoxStation_skyrat.dmm
index ca0713a6fd5..b13c46633c2 100644
--- a/_maps/map_files/IceBoxStation/IceBoxStation_skyrat.dmm
+++ b/_maps/map_files/IceBoxStation/IceBoxStation_skyrat.dmm
@@ -44913,7 +44913,7 @@
/obj/structure/window/reinforced{
dir = 4
},
-/obj/machinery/vending/security_ammo,
+/obj/machinery/ammo_workbench,
/turf/open/floor/iron/dark,
/area/security/office)
"qcl" = (
@@ -55454,7 +55454,7 @@
/turf/open/floor/iron,
/area/hallway/primary/central)
"vGa" = (
-/obj/machinery/vending/security_ammo,
+/obj/machinery/ammo_workbench,
/turf/open/floor/iron/dark,
/area/security/office)
"vGb" = (
diff --git a/_maps/map_files/KiloStation/KiloStation_skyrat.dmm b/_maps/map_files/KiloStation/KiloStation_skyrat.dmm
index 735dcf99e18..f17b5989a09 100644
--- a/_maps/map_files/KiloStation/KiloStation_skyrat.dmm
+++ b/_maps/map_files/KiloStation/KiloStation_skyrat.dmm
@@ -77093,7 +77093,7 @@
/obj/effect/turf_decal/siding/red/corner{
dir = 4
},
-/obj/machinery/vending/security_ammo,
+/obj/machinery/ammo_workbench,
/turf/open/floor/iron/dark,
/area/security/brig)
"fLz" = (
diff --git a/_maps/map_files/MetaStation/MetaStation_skyrat.dmm b/_maps/map_files/MetaStation/MetaStation_skyrat.dmm
index b6a2461dc0f..13fb243c445 100644
--- a/_maps/map_files/MetaStation/MetaStation_skyrat.dmm
+++ b/_maps/map_files/MetaStation/MetaStation_skyrat.dmm
@@ -28815,7 +28815,7 @@
dir = 4
},
/obj/structure/cable,
-/obj/machinery/vending/security_ammo,
+/obj/machinery/ammo_workbench,
/turf/open/floor/iron/dark,
/area/security/range)
"dBN" = (
diff --git a/_maps/map_files/NSSJourney/NSSJourney.dmm b/_maps/map_files/NSSJourney/NSSJourney.dmm
index f68f3431709..94202e6d63f 100644
--- a/_maps/map_files/NSSJourney/NSSJourney.dmm
+++ b/_maps/map_files/NSSJourney/NSSJourney.dmm
@@ -45051,7 +45051,7 @@
/obj/structure/window/reinforced{
dir = 4
},
-/obj/machinery/vending/security_ammo,
+/obj/machinery/ammo_workbench,
/turf/open/floor/iron/dark,
/area/security/office)
"kVn" = (
@@ -49916,7 +49916,7 @@
/turf/open/floor/iron/white,
/area/science/explab)
"pJj" = (
-/obj/machinery/vending/security_ammo,
+/obj/machinery/ammo_workbench,
/turf/open/floor/iron/dark,
/area/security/office)
"pKi" = (
diff --git a/_maps/map_files/WaterKiloStation/WaterKiloStation.dmm b/_maps/map_files/WaterKiloStation/WaterKiloStation.dmm
index 4b6fd9c1abb..d13b686af54 100644
--- a/_maps/map_files/WaterKiloStation/WaterKiloStation.dmm
+++ b/_maps/map_files/WaterKiloStation/WaterKiloStation.dmm
@@ -31031,7 +31031,7 @@
/obj/effect/turf_decal/siding/red/corner{
dir = 4
},
-/obj/machinery/vending/security_ammo,
+/obj/machinery/ammo_workbench,
/turf/open/floor/iron/dark,
/area/security/brig)
"bFM" = (
diff --git a/_maps/map_files/tramstation/tramstation_skyrat.dmm b/_maps/map_files/tramstation/tramstation_skyrat.dmm
index 2f1d9e29619..079d793a025 100644
--- a/_maps/map_files/tramstation/tramstation_skyrat.dmm
+++ b/_maps/map_files/tramstation/tramstation_skyrat.dmm
@@ -71451,7 +71451,7 @@
},
/area/service/chapel)
"xnt" = (
-/obj/machinery/vending/security_ammo,
+/obj/machinery/ammo_workbench,
/obj/effect/turf_decal/bot,
/turf/open/floor/iron/showroomfloor,
/area/security/office)
diff --git a/_maps/map_files/tramstation/tramstation_skyrat_old.dmm b/_maps/map_files/tramstation/tramstation_skyrat_old.dmm
index 381c93183f6..7b9f228b85e 100644
--- a/_maps/map_files/tramstation/tramstation_skyrat_old.dmm
+++ b/_maps/map_files/tramstation/tramstation_skyrat_old.dmm
@@ -57496,7 +57496,7 @@
/turf/open/floor/plating/airless,
/area/mine/explored)
"rmF" = (
-/obj/machinery/vending/security_ammo,
+/obj/machinery/ammo_workbench,
/obj/effect/turf_decal/bot,
/turf/open/floor/iron/showroomfloor,
/area/security/office)
diff --git a/_maps/shuttles/pirate_enclave.dmm b/_maps/shuttles/pirate_enclave.dmm
index 2c2adbbe659..61db40fdd35 100644
--- a/_maps/shuttles/pirate_enclave.dmm
+++ b/_maps/shuttles/pirate_enclave.dmm
@@ -285,9 +285,10 @@
"kR" = (
/obj/structure/table/wood,
/obj/effect/turf_decal/siding/wood,
-/obj/item/storage/box/donkpockets/donkpocketpizza{
- pixel_y = 6
+/obj/item/storage/backpack/duffelbag/syndie/x4{
+ pixel_y = 8
},
+/obj/item/storage/box/donkpockets/donkpocketteriyaki,
/turf/open/floor/wood,
/area/shuttle/syndicate/slaver)
"kZ" = (
@@ -1295,16 +1296,8 @@
/turf/open/floor/carpet/red,
/area/shuttle/syndicate/slaver)
"ZD" = (
-/obj/structure/table/reinforced,
-/obj/item/storage/backpack/duffelbag/syndie/x4{
- pixel_y = 8
- },
-/obj/item/grenade/smokebomb{
- pixel_x = -5
- },
-/obj/item/grenade/smokebomb{
- pixel_x = 5
- },
+/obj/machinery/ammo_workbench/unlocked,
+/obj/item/stack/sheet/iron/five,
/turf/open/floor/iron/dark,
/area/shuttle/syndicate/slaver)
diff --git a/code/modules/projectiles/ammunition/ballistic/revolver.dm b/code/modules/projectiles/ammunition/ballistic/revolver.dm
index 8784bdeece3..307a114ab70 100644
--- a/code/modules/projectiles/ammunition/ballistic/revolver.dm
+++ b/code/modules/projectiles/ammunition/ballistic/revolver.dm
@@ -41,6 +41,7 @@
name = ".38 Rubber bullet casing"
desc = "A .38 rubber bullet casing, manufactured to exceedingly bouncy standards."
projectile_type = /obj/projectile/bullet/c38/match/bouncy
+ harmful = FALSE //SKYRAT EDIT ADDITION
/obj/item/ammo_casing/c38/dumdum
name = ".38 DumDum bullet casing"
diff --git a/code/modules/projectiles/ammunition/ballistic/shotgun.dm b/code/modules/projectiles/ammunition/ballistic/shotgun.dm
index 5e21c533c28..e4e91bcfe3a 100644
--- a/code/modules/projectiles/ammunition/ballistic/shotgun.dm
+++ b/code/modules/projectiles/ammunition/ballistic/shotgun.dm
@@ -27,6 +27,7 @@
icon_state = "bshell"
custom_materials = list(/datum/material/iron=250)
projectile_type = /obj/projectile/bullet/shotgun_beanbag
+ harmful = FALSE //SKYRAT EDIT ADDITION
/obj/item/ammo_casing/shotgun/incendiary
name = "incendiary slug"
@@ -85,6 +86,7 @@
pellets = 6
variance = 25
custom_materials = list(/datum/material/iron=4000)
+ harmful = FALSE //SKYRAT EDIT ADDITION
/obj/item/ammo_casing/shotgun/incapacitate
name = "custom incapacitating shot"
diff --git a/code/modules/research/machinery/circuit_imprinter.dm b/code/modules/research/machinery/circuit_imprinter.dm
index fabe05ac597..8f032a146ac 100644
--- a/code/modules/research/machinery/circuit_imprinter.dm
+++ b/code/modules/research/machinery/circuit_imprinter.dm
@@ -15,7 +15,8 @@
"Research Machinery",
"Misc. Machinery",
"Computer Parts",
- "Circuitry"
+ "Circuitry",
+ "Weapons Machinery" //SKYRAT EDIT CHANGE
)
production_animation = "circuit_imprinter_ani"
allowed_buildtypes = IMPRINTER
diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm
index 3edc6a51313..6d6b99afb2c 100644
--- a/code/modules/research/techweb/all_nodes.dm
+++ b/code/modules/research/techweb/all_nodes.dm
@@ -1308,6 +1308,7 @@
prereq_ids = list("adv_engi", "weaponry")
design_ids = list(
"pin_loyalty",
+ "ammo_workbench", //SKYRAT EDIT ADDITION
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 10000)
required_experiments = list(/datum/experiment/explosion/medium)
@@ -1392,6 +1393,7 @@
"c38_hotshot",
"c38_iceblox",
"techshotshell",
+ "ammoworkbench_disk_lethal", //SKYRAT EDIT ADDITION
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
diff --git a/modular_skyrat/modules/ammo_workbench/code/ammo_workbench.dm b/modular_skyrat/modules/ammo_workbench/code/ammo_workbench.dm
new file mode 100644
index 00000000000..8f3a92f2654
--- /dev/null
+++ b/modular_skyrat/modules/ammo_workbench/code/ammo_workbench.dm
@@ -0,0 +1,535 @@
+/obj/machinery/ammo_workbench
+ name = "Ammunitions Workbench"
+ desc = "A machine, somewhat akin to a lathe, made specifically for manufacturing ammunition. It has a slot for magazines."
+ icon = 'modular_skyrat/modules/ammo_workbench/icons/ammo_workbench.dmi'
+ icon_state = "ammobench"
+ density = TRUE
+ use_power = IDLE_POWER_USE
+ circuit = /obj/item/circuitboard/machine/ammo_workbench
+ var/busy = FALSE
+ var/hacked = FALSE
+ var/disabled = FALSE
+ var/shocked = FALSE
+ var/hack_wire
+ var/disable_wire
+ var/error_message = ""
+ var/error_type = ""
+ var/disk_error = ""
+ var/disk_error_type = ""
+ var/shock_wire
+ var/timer_id
+ var/turbo_boost = FALSE
+ var/obj/item/ammo_box/loaded_magazine = null
+ var/obj/item/disk/ammo_workbench/loaded_datadisk = null
+ /// A list of all currently allowed ammo types.
+ var/list/allowed_ammo_types = list()
+ var/list/allowed_harmful = FALSE
+ var/list/loaded_datadisks = list()
+ var/time_per_round = 20
+ var/creation_efficiency = 1.6
+
+/obj/machinery/ammo_workbench/unlocked
+ allowed_harmful = TRUE
+
+/obj/item/circuitboard/machine/ammo_workbench
+ name = "Ammunition Workbench (Machine Board)"
+ icon_state = "circuit_map"
+ build_path = /obj/machinery/ammo_workbench
+ req_components = list(
+ /obj/item/stock_parts/manipulator = 2,
+ /obj/item/stock_parts/matter_bin = 2,
+ /obj/item/stock_parts/micro_laser = 2
+ )
+
+/obj/machinery/ammo_workbench/Initialize()
+ AddComponent(/datum/component/material_container, SSmaterials.materials_by_category[MAT_CATEGORY_ITEM_MATERIAL], 0, MATCONTAINER_EXAMINE, allowed_items = /obj/item/stack, _after_insert = CALLBACK(src, .proc/AfterMaterialInsert))
+ . = ..()
+ wires = new /datum/wires/ammo_workbench(src)
+
+/obj/machinery/ammo_workbench/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "AmmoWorkbench")
+ ui.open()
+
+ if(shocked)
+ shock(user, 80)
+
+/obj/machinery/ammo_workbench/ui_data(mob/user)
+ var/list/data = list()
+
+ data["loaded_datadisks"] = list()
+ data["datadisk_loaded"] = FALSE
+ data["datadisk_name"] = null
+ data["datadisk_desc"] = null
+
+ data["disk_error"] = disk_error
+ data["disk_error_type"] = disk_error_type
+
+ if(loaded_datadisk)
+ data["datadisk_loaded"] = TRUE
+ data["datadisk_name"] = initial(loaded_datadisk.name)
+ data["datadisk_desc"] = initial(loaded_datadisk.desc)
+
+ for(var/type in loaded_datadisks)
+ var/obj/item/disk/ammo_workbench/disk = type
+ data["loaded_datadisks"] += list(list("loaded_disk_name" = initial(disk.name), "loaded_disk_desc" = initial(disk.desc)))
+
+ data["mag_loaded"] = FALSE
+ data["error"] = null
+ data["error_type"] = null
+ data["system_busy"] = busy
+
+ data["efficiency"] = creation_efficiency
+ data["time"] = time_per_round / 10
+ data["hacked"] = allowed_harmful
+ data["turboBoost"] = turbo_boost
+
+ data["materials"] = list()
+ var/datum/component/material_container/mat_container = GetComponent(/datum/component/material_container)
+ if (mat_container)
+ for(var/mat in mat_container.materials)
+ var/datum/material/M = mat
+ var/amount = mat_container.materials[M]
+ var/sheet_amount = amount / MINERAL_MATERIAL_AMOUNT
+ var/ref = REF(M)
+ data["materials"] += list(list("name" = M.name, "id" = ref, "amount" = sheet_amount))
+
+ if(error_message)
+ data["error"] = error_message
+ data["error_type"] = error_type
+ else if(busy)
+ data["error"] = "SYSTEM IS BUSY"
+ data["error_type"] = ""
+
+ if(!loaded_magazine)
+ data["error"] = "NO MAGAZINE IS INSERTED"
+ data["error_type"] = ""
+ return data
+ else
+ data["mag_loaded"] = TRUE
+
+ data["available_rounds"] = list()
+ var/obj/item/ammo_casing/ammo_type = loaded_magazine.ammo_type
+
+ var/list/round_types = typesof(ammo_type)
+ for(var/casing as anything in round_types)
+ var/obj/item/ammo_casing/our_casing = casing
+ if(initial(our_casing.harmful) && !allowed_harmful)
+ continue
+ data["available_rounds"] += list(list(
+ "name" = initial(our_casing.name),
+ "typepath" = our_casing
+ ))
+
+ data["mag_name"] = loaded_magazine.name
+ data["caliber"] = initial(ammo_type.caliber)
+ data["current_rounds"] = loaded_magazine.stored_ammo.len
+ data["max_rounds"] = loaded_magazine.max_ammo
+
+ return data
+
+/obj/machinery/ammo_workbench/ui_act(action, params)
+ . = ..()
+ if(.)
+ return
+ switch(action)
+ if("EjectMag")
+ ejectItem()
+ . = TRUE
+
+ if("FillMagazine")
+ var/type_to_pass = text2path(params["selected_type"])
+ fill_magazine_start(type_to_pass)
+ . = TRUE
+
+ if("Release")
+
+ var/datum/component/material_container/mat_container = GetComponent(/datum/component/material_container)
+
+ if(!mat_container)
+ return
+ var/datum/material/mat = locate(params["id"])
+
+ var/amount = mat_container.materials[mat]
+ if(!amount)
+ return
+
+ var/stored_amount = CEILING(amount / MINERAL_MATERIAL_AMOUNT, 0.1)
+
+ if(!stored_amount)
+ return
+
+ var/desired = 0
+ if (params["sheets"])
+ desired = text2num(params["sheets"])
+
+ var/sheets_to_remove = round(min(desired,50,stored_amount))
+
+ mat_container.retrieve_sheets(sheets_to_remove, mat, loc)
+ . = TRUE
+
+ if("ReadDisk")
+ loadDisk()
+
+ if("EjectDisk")
+ ejectDisk()
+
+ if("turboBoost")
+ toggleTurboBoost()
+
+/obj/machinery/ammo_workbench/proc/toggleTurboBoost()
+ turbo_boost = !turbo_boost
+
+ if(turbo_boost)
+ time_per_round = 1
+ creation_efficiency = 3
+ else
+ RefreshParts()
+
+/obj/machinery/ammo_workbench/proc/ejectItem()
+ if(loaded_magazine)
+ loaded_magazine.forceMove(drop_location())
+ loaded_magazine = null
+ busy = FALSE
+ error_message = ""
+ error_type = ""
+ if(timer_id)
+ deltimer(timer_id)
+ timer_id = null
+ update_appearance()
+
+/obj/machinery/ammo_workbench/proc/fill_magazine_start(casing_type)
+ if(machine_stat & (NOPOWER|BROKEN))
+ busy = FALSE
+ if(timer_id)
+ deltimer(timer_id)
+ timer_id = null
+ return
+
+ if(error_message)
+ error_message = ""
+ error_type = ""
+
+ var/list/allowed_types = typecacheof(loaded_magazine.ammo_type)
+
+ if(!(casing_type in allowed_types))
+ error_message = "AMMUNITION MISSMATCH"
+ error_type = "bad"
+ return
+
+ var/obj/item/ammo_casing/our_casing = casing_type
+
+ if(initial(our_casing.harmful) && !allowed_harmful)
+ error_message = "SYSTEM CORRUPTION DETECTED"
+ error_type = "bad"
+ return
+
+ if(!loaded_magazine)
+ error_message = "NO MAGAZINE INSERTED"
+ error_type = ""
+ return
+
+ if(loaded_magazine.stored_ammo.len >= loaded_magazine.max_ammo)
+ error_message = "MAGAZINE IS FULL"
+ error_type = "good"
+ return
+
+ if(busy)
+ return
+
+ busy = TRUE
+
+ timer_id = addtimer(CALLBACK(src, .proc/fill_round, casing_type), time_per_round, TIMER_STOPPABLE)
+
+/obj/machinery/ammo_workbench/proc/fill_round(casing_type)
+ if(machine_stat & (NOPOWER|BROKEN))
+ busy = FALSE
+ if(timer_id)
+ deltimer(timer_id)
+ timer_id = null
+ return
+
+ if(!loaded_magazine)
+ return
+
+ var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
+
+ var/obj/item/ammo_casing/new_casing = new casing_type
+
+ var/list/required_materials = new_casing.get_material_composition()
+
+ if(!materials.has_materials(required_materials))
+ error_message = "INSUFFICIENT MATERIALS"
+ error_type = "bad"
+ ammo_fill_finish(FALSE)
+ qdel(new_casing)
+ return
+
+ if(istype(new_casing, loaded_magazine.ammo_type))
+ if(!loaded_magazine.give_round(new_casing))
+ error_message = "AMMUNITION MISSMATCH"
+ error_type = "bad"
+ ammo_fill_finish(FALSE)
+ qdel(new_casing)
+ return
+ materials.use_materials(required_materials)
+ loaded_magazine.update_appearance()
+ flick("ammobench_process", src)
+ use_power(3000)
+ playsound(loc, 'sound/machines/piston_raise.ogg', 60, 1)
+ else
+ qdel(new_casing)
+ ammo_fill_finish(FALSE)
+ return
+
+ if(loaded_magazine.stored_ammo.len >= loaded_magazine.max_ammo)
+ ammo_fill_finish()
+ error_message = "MAGAZINE IS FULL"
+ error_type = "good"
+ return
+
+ updateDialog()
+
+ timer_id = addtimer(CALLBACK(src, .proc/fill_round, casing_type), time_per_round, TIMER_STOPPABLE)
+
+/obj/machinery/ammo_workbench/proc/ammo_fill_finish(successfully = TRUE)
+ updateDialog()
+ if(successfully)
+ playsound(loc, 'sound/machines/ping.ogg', 40, TRUE)
+ else
+ playsound(loc, 'sound/machines/buzz-sigh.ogg', 40, TRUE)
+ update_appearance()
+ busy = FALSE
+ if(timer_id)
+ deltimer(timer_id)
+ timer_id = null
+
+/obj/machinery/ammo_workbench/proc/loadDisk()
+ disk_error = ""
+ disk_error_type = ""
+ if(!loaded_datadisk)
+ return FALSE
+ if(loaded_datadisk.type in loaded_datadisks)
+ disk_error = "ERROR: DISK BLUEPRINT ALREADY IN SYSTEM MEMEORY"
+ return FALSE
+
+ if(istype(loaded_datadisk, /obj/item/disk/ammo_workbench/lethal))
+ allowed_harmful = TRUE
+ loaded_datadisks += loaded_datadisk.type
+ disk_error = "DISK LOADED SUCCESSFULLY"
+ disk_error_type = "good"
+ return TRUE
+
+ loaded_datadisks += loaded_datadisk.type
+
+/obj/machinery/ammo_workbench/proc/ejectDisk()
+ if(loaded_datadisk)
+ loaded_datadisk.forceMove(drop_location())
+ loaded_datadisk = null
+ disk_error = ""
+ disk_error_type = ""
+
+/datum/design/board/ammo_workbench
+ name = "Machine Design (Ammunitions Workbench)"
+ desc = "A machine, somewhat akin to a lathe, made specifically for manufacturing ammunition. It has a slot for magazines."
+ id = "ammo_workbench"
+ build_path = /obj/item/circuitboard/machine/ammo_workbench
+ category = list("Weapons Machinery")
+ departmental_flags = DEPARTMENTAL_FLAG_SECURITY
+
+
+//MISC MACHINE PROCS
+
+/obj/machinery/ammo_workbench/RefreshParts()
+ var/time_efficiency = 20
+ for(var/obj/item/stock_parts/micro_laser/new_laser in component_parts)
+ time_efficiency -= new_laser.rating * 2
+ time_per_round = clamp(time_efficiency, 1, 20)
+
+ var/efficiency = 1.8
+ for(var/obj/item/stock_parts/manipulator/new_manipulator in component_parts)
+ efficiency -= new_manipulator.rating * 0.2
+ creation_efficiency = max(1,efficiency) // creation_efficiency goes 1.6 -> 1.4 -> 1.2 -> 1 per level of manipulator efficiency
+
+ var/mat_capacity = 0
+ for(var/obj/item/stock_parts/matter_bin/new_matter_bin in component_parts)
+ mat_capacity += new_matter_bin.rating * 75000
+ var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
+ materials.max_amount = mat_capacity
+
+/obj/machinery/ammo_workbench/update_overlays()
+ . = ..()
+ if(loaded_magazine)
+ . += "ammobench_loaded"
+
+/obj/machinery/ammo_workbench/proc/AfterMaterialInsert(obj/item/item_inserted, id_inserted, amount_inserted)
+ if(istype(item_inserted, /obj/item/stack/ore/bluespace_crystal))
+ use_power(MINERAL_MATERIAL_AMOUNT / 10)
+ else if(item_inserted.has_material_type(/datum/material/glass))
+ flick("autolathe_r", src)//plays glass insertion animation by default otherwise
+ else
+ flick("autolathe_o", src)//plays metal insertion animation
+
+ use_power(min(1000, amount_inserted / 100))
+ updateUsrDialog()
+
+/obj/machinery/ammo_workbench/Destroy()
+ QDEL_NULL(wires)
+ if(timer_id)
+ deltimer(timer_id)
+ timer_id = null
+ if(loaded_magazine)
+ loaded_magazine.forceMove(loc)
+ loaded_magazine = null
+ . = ..()
+ return ..()
+
+/obj/machinery/ammo_workbench/proc/shock(mob/user, prb)
+ if(machine_stat & (BROKEN|NOPOWER)) // unpowered, no shock
+ return FALSE
+ if(!prob(prb))
+ return FALSE
+ do_sparks(5, TRUE, src)
+ if (electrocute_mob(user, get_area(src), src, 0.7, TRUE))
+ return TRUE
+ else
+ return FALSE
+
+/obj/machinery/ammo_workbench/attackby(obj/item/O, mob/user, params)
+ if (default_deconstruction_screwdriver(user, "[initial(icon_state)]_t", initial(icon_state), O))
+ return
+ if(default_deconstruction_crowbar(O))
+ return
+ if(panel_open && is_wire_tool(O))
+ wires.interact(user)
+ return TRUE
+ if(is_refillable() && O.is_drainable())
+ return FALSE //inserting reagents into the machine
+ if(Insert_Item(O, user))
+ return TRUE
+ else
+ return ..()
+
+
+/obj/machinery/ammo_workbench/proc/Insert_Item(obj/item/O, mob/living/user)
+ if(user.combat_mode)
+ return FALSE
+ if(!is_insertion_ready(user))
+ return FALSE
+ if(istype(O, /obj/item/ammo_box))
+ if(!user.transferItemToLoc(O, src))
+ return FALSE
+ if(loaded_magazine)
+ to_chat(user, "You swap quickly swap [O] for [loaded_magazine].")
+ loaded_magazine.forceMove(drop_location())
+ user.put_in_hands(loaded_magazine)
+ loaded_magazine = null
+ busy = FALSE
+ error_message = ""
+ error_type = ""
+ if(timer_id)
+ deltimer(timer_id)
+ timer_id = null
+ loaded_magazine = O
+ to_chat(user, "You insert [O] to into [src]'s reciprocal.")
+ flick("h_lathe_load", src)
+ update_appearance()
+ playsound(loc, 'sound/weapons/autoguninsert.ogg', 35, 1)
+ return TRUE
+ if(istype(O, /obj/item/disk/ammo_workbench))
+ if(!user.transferItemToLoc(O, src))
+ return FALSE
+ loaded_datadisk = O
+ to_chat(user, "You insert [O] to into [src]'s floppydisk port.")
+ flick("h_lathe_load", src)
+ update_appearance()
+ playsound(loc, 'sound/machines/terminal_insert_disc.ogg', 35, 1)
+ return TRUE
+ return FALSE
+
+/obj/machinery/ammo_workbench/proc/is_insertion_ready(mob/user, obj/item/O)
+ if(panel_open)
+ to_chat(user, "You can't load [src] while it's opened!")
+ return FALSE
+ if(disabled)
+ to_chat(user, "The insertion belts of [src] won't engage!")
+ return FALSE
+ if(machine_stat & BROKEN)
+ to_chat(user, "[src] is broken.")
+ return FALSE
+ if(machine_stat & NOPOWER)
+ to_chat(user, "[src] has no power.")
+ return FALSE
+ if(istype(O, /obj/item/disk/ammo_workbench) && loaded_datadisk)
+ to_chat(user, "[src] already has a disk inserted.")
+ return FALSE
+ return TRUE
+
+/obj/machinery/ammo_workbench/proc/reset(wire)
+ switch(wire)
+ if(WIRE_HACK)
+ if(!wires.is_cut(wire))
+ adjust_hacked(FALSE)
+ if(WIRE_SHOCK)
+ if(!wires.is_cut(wire))
+ shocked = FALSE
+ if(WIRE_DISABLE)
+ if(!wires.is_cut(wire))
+ disabled = FALSE
+
+/obj/machinery/ammo_workbench/proc/adjust_hacked(state)
+ hacked = state
+
+
+// WIRE DATUM
+/datum/wires/ammo_workbench
+ holder_type = /obj/machinery/ammo_workbench
+ proper_name = "Ammunitions Workbench"
+
+/datum/wires/ammo_workbench/New(atom/holder)
+ wires = list(
+ WIRE_HACK, WIRE_DISABLE,
+ WIRE_SHOCK, WIRE_ZAP
+ )
+ add_duds(6)
+ ..()
+
+/datum/wires/ammo_workbench/interactable(mob/user)
+ if(!..())
+ return FALSE
+ var/obj/machinery/ammo_workbench/A = holder
+ if(A.panel_open)
+ return TRUE
+
+/datum/wires/ammo_workbench/get_status()
+ var/obj/machinery/ammo_workbench/A = holder
+ var/list/status = list()
+ status += "The red light is [A.disabled ? "on" : "off"]."
+ status += "The blue light is [A.hacked ? "on" : "off"]."
+ return status
+
+/datum/wires/ammo_workbench/on_pulse(wire)
+ var/obj/machinery/ammo_workbench/A = holder
+ switch(wire)
+ if(WIRE_HACK)
+ A.adjust_hacked(!A.hacked)
+ addtimer(CALLBACK(A, /obj/machinery/ammo_workbench.proc/reset, wire), 60)
+ if(WIRE_SHOCK)
+ A.shocked = !A.shocked
+ addtimer(CALLBACK(A, /obj/machinery/ammo_workbench.proc/reset, wire), 60)
+ if(WIRE_DISABLE)
+ A.disabled = !A.disabled
+ addtimer(CALLBACK(A, /obj/machinery/ammo_workbench.proc/reset, wire), 60)
+
+/datum/wires/ammo_workbench/on_cut(wire, mend)
+ var/obj/machinery/ammo_workbench/A = holder
+ switch(wire)
+ if(WIRE_HACK)
+ A.adjust_hacked(!mend)
+ if(WIRE_HACK)
+ A.shocked = !mend
+ if(WIRE_DISABLE)
+ A.disabled = !mend
+ if(WIRE_ZAP)
+ A.shock(usr, 50)
diff --git a/modular_skyrat/modules/ammo_workbench/code/design_disks.dm b/modular_skyrat/modules/ammo_workbench/code/design_disks.dm
new file mode 100644
index 00000000000..e6796ec271b
--- /dev/null
+++ b/modular_skyrat/modules/ammo_workbench/code/design_disks.dm
@@ -0,0 +1,18 @@
+/obj/item/disk/ammo_workbench
+ name = "Armadyne Munitions Blueprint Datadisk"
+ desc = "You shouldn't be seeing this!"
+ var/list/loaded_ammo_types
+
+/obj/item/disk/ammo_workbench/lethal
+ name = "Lethal Munitions Datadisk"
+ desc = "An Armadyne datadisk filled with lethal casing design blueprints for the Ammunitions Workbench."
+ icon_state = "datadisk0"
+
+/datum/design/disk/ammo_workbench_lethal
+ name = "Ammo Workbench: Lethal Munitions Datadisk"
+ id = "ammoworkbench_disk_lethal"
+ build_type = PROTOLATHE
+ materials = list(/datum/material/iron = 1000)
+ build_path = /obj/item/disk/ammo_workbench/lethal
+ category = list("intial", "Security", "Ammo")
+ departmental_flags = DEPARTMENTAL_FLAG_SECURITY
diff --git a/modular_skyrat/modules/ammo_workbench/icons/ammo_workbench.dmi b/modular_skyrat/modules/ammo_workbench/icons/ammo_workbench.dmi
new file mode 100644
index 00000000000..05eacaa2df2
Binary files /dev/null and b/modular_skyrat/modules/ammo_workbench/icons/ammo_workbench.dmi differ
diff --git a/modular_skyrat/modules/blueshield/code/modules/jobs/job_types/blueshield.dm b/modular_skyrat/modules/blueshield/code/modules/jobs/job_types/blueshield.dm
index 10fe147068c..40493a40b17 100644
--- a/modular_skyrat/modules/blueshield/code/modules/jobs/job_types/blueshield.dm
+++ b/modular_skyrat/modules/blueshield/code/modules/jobs/job_types/blueshield.dm
@@ -28,7 +28,7 @@
/obj/item/stack/spacecash/c500 = 3,
/obj/item/disk/nuclear/fake/obvious = 2,
/obj/item/clothing/head/collectable/captain = 4,
- /obj/projectile/bullet/advanced/b10mm/b460 = 1
+ /obj/projectile/bullet/b460 = 1
)
/datum/outfit/job/blueshield
diff --git a/modular_skyrat/modules/blueshield/code/modules/projectiles/guns/ballistic/ballistic.dm b/modular_skyrat/modules/blueshield/code/modules/projectiles/guns/ballistic/ballistic.dm
index 3ee70e88240..b79a60cb9ad 100644
--- a/modular_skyrat/modules/blueshield/code/modules/projectiles/guns/ballistic/ballistic.dm
+++ b/modular_skyrat/modules/blueshield/code/modules/projectiles/guns/ballistic/ballistic.dm
@@ -1,12 +1,12 @@
-/obj/item/ammo_casing/b10mm/b460
- name = ".460 Rowland Magnum casing."
+/obj/item/ammo_casing/b460
+ name = ".460 Rowland Magnum bullet casing"
desc = "A .460 Rowland Magnum casing."
icon = 'modular_skyrat/modules/sec_haul/icons/guns/ammo_cartridges.dmi'
icon_state = "sl-casing"
caliber = "460"
- projectile_type = /obj/projectile/bullet/advanced/b10mm/b460
+ projectile_type = /obj/projectile/bullet/b460
-/obj/projectile/bullet/advanced/b10mm/b460
+/obj/projectile/bullet/b460
name = ".460 RM JHP bullet"
damage = 36
stamina = 12
@@ -14,13 +14,13 @@
weak_against_armour = TRUE
speed = 2.25
-/obj/item/gun/ballistic/automatic/pistol/blueshield
+/obj/item/gun/ballistic/automatic/pistol/m45a5
name = "\improper M45A5 Elite"
desc = "A hand-assembled custom sporting handgun by Alpha Centauri Armories, chambered in .460 Rowland Magnum. This model has a highly modular structure, to acommodate for ammo costs."
icon = 'modular_skyrat/modules/blueshield/icons/obj/guns/M45A5.dmi'
icon_state = "m45a5"
w_class = WEIGHT_CLASS_NORMAL
- mag_type = /obj/item/ammo_box/magazine/multi_sprite/ladon/blueshield
+ mag_type = /obj/item/ammo_box/magazine/m45a5
can_suppress = FALSE
fire_delay = 1.75
fire_sound_volume = 60 //Handgun equivalent of .44 with a muzzle brake lmao
@@ -33,34 +33,21 @@
emp_damageable = FALSE
fire_sound = 'modular_skyrat/modules/sec_haul/sound/dp_fire.ogg'
-/obj/item/ammo_box/magazine/multi_sprite/ladon/blueshield
+/obj/item/ammo_box/magazine/m45a5
name = "ACA modular magazine"
- desc = "A magazine able to chamber .460 Rowland Magnum and Peacekeeper 10mm rounds as an alternative by its length switch. Made for the M45A5, as it's the only available sidearm with a smart multi-caliber mechanism."
+ desc = "A magazine able to chamber .460 Rowland Magnun. Made for the M45A5, as it's the only available sidearm with a smart multi-caliber mechanism."
icon = 'modular_skyrat/modules/blueshield/icons/obj/guns/M45A5.dmi'
icon_state = "rowlandmodular"
- ammo_type = /obj/item/ammo_casing/b10mm
- caliber = "10mm"
+ ammo_type = /obj/item/ammo_casing/b460
+ caliber = ".460"
max_ammo = 10 //Increased length single stacks.
multiple_sprites = AMMO_BOX_FULL_EMPTY_BASIC
- possible_types = list(AMMO_TYPE_LETHAL, AMMO_TYPE_HOLLOWPOINT, AMMO_TYPE_RUBBER, AMMO_TYPE_IHDF, ".460")
-
-/obj/item/ammo_box/magazine/multi_sprite/ladon/blueshield/b460
- ammo_type = /obj/item/ammo_casing/b10mm/b460
- round_type = ".460"
-
-/obj/item/ammo_box/magazine/multi_sprite/ladon/blueshield/hp
- ammo_type = /obj/item/ammo_casing/b10mm/hp
- round_type = AMMO_TYPE_HOLLOWPOINT
-
-/obj/item/ammo_box/magazine/multi_sprite/ladon/blueshield/ihdf
- ammo_type = /obj/item/ammo_casing/b10mm/ihdf
- round_type = AMMO_TYPE_IHDF
/obj/item/storage/box/gunset/blueshield
name = "CFA 'Lynx' Gunset"
w_class = WEIGHT_CLASS_NORMAL
-/obj/item/gun/ballistic/automatic/pistol/blueshield/nomag
+/obj/item/gun/ballistic/automatic/pistol/m45a5/nomag
spawnwithmagazine = FALSE
/obj/item/storage/box/gunset/blueshield/PopulateContents()
diff --git a/modular_skyrat/modules/blueshield/icons/obj/guns/M45A5.dmi b/modular_skyrat/modules/blueshield/icons/obj/guns/M45A5.dmi
index 8d23e24b286..014cf287dcd 100644
Binary files a/modular_skyrat/modules/blueshield/icons/obj/guns/M45A5.dmi and b/modular_skyrat/modules/blueshield/icons/obj/guns/M45A5.dmi differ
diff --git a/modular_skyrat/modules/modular_weapons/code/modules/projectiles/projectile/bullets/modular_projectiles.dm b/modular_skyrat/modules/modular_weapons/code/modules/projectiles/projectile/bullets/modular_projectiles.dm
index af4f1198fa5..9c223313841 100644
--- a/modular_skyrat/modules/modular_weapons/code/modules/projectiles/projectile/bullets/modular_projectiles.dm
+++ b/modular_skyrat/modules/modular_weapons/code/modules/projectiles/projectile/bullets/modular_projectiles.dm
@@ -21,6 +21,7 @@
desc = "A .32 rubber bullet casing."
caliber = "c32acp"
projectile_type = /obj/projectile/bullet/c32/rubber
+ harmful = FALSE
/obj/projectile/bullet/c32/rubber
name = ".32 rubber bullet"
@@ -61,6 +62,7 @@
desc = "A 10mm Magnum bullet casing. This fires a non-lethal projectile to cause compliance by pain and bruising. Don't aim for the head."
caliber = CALIBER_10MM
projectile_type = /obj/projectile/bullet/c10mm/rubber
+ harmful = FALSE
/obj/projectile/bullet/c10mm/rubber
name = "10mm Magnum rubber ball"
@@ -117,6 +119,7 @@
name = "4.6x30mm rubber bullet casing"
desc = "A 4.6x30mm rubber bullet casing."
projectile_type = /obj/projectile/bullet/c46x30mm_rubber
+ harmful = FALSE
///// HK 4.6x30mm /////
////////////////////////
@@ -127,6 +130,7 @@
desc = "A 5.56mm rubber bullet casing."
caliber = CALIBER_A556
projectile_type = /obj/projectile/bullet/a556/rubber
+ harmful = FALSE
/obj/projectile/bullet/a556/rubber
name = "5.56mm rubber bullet"
@@ -162,6 +166,7 @@
icon_state = "762-casing"
caliber = CALIBER_A762
projectile_type = /obj/projectile/bullet/a762/rubber
+ harmful = FALSE
/obj/projectile/bullet/a762/rubber
name = "7.62mm rubber bullet"
@@ -173,7 +178,7 @@
ricochet_decay_damage = 0.7
shrapnel_type = null
sharpness = NONE
- embedding = null
+ embedding = null
////// 7.62 //////
////////////////////////
diff --git a/modular_skyrat/modules/sec_haul/code/guns/bullets.dm b/modular_skyrat/modules/sec_haul/code/guns/bullets.dm
index 3da7bf170f2..72d32e87431 100644
--- a/modular_skyrat/modules/sec_haul/code/guns/bullets.dm
+++ b/modular_skyrat/modules/sec_haul/code/guns/bullets.dm
@@ -344,7 +344,7 @@
icon_state = "sr-casing"
caliber = CALIBER_473MM
projectile_type = /obj/projectile/bullet/advanced/b473/rubber
- harmful = TRUE
+ harmful = FALSE
/obj/projectile/bullet/advanced/b473/rubber
name = "4.73x33mm rubber bullet"
@@ -366,7 +366,7 @@
icon_state = "si-casing"
caliber = CALIBER_473MM
projectile_type = /obj/projectile/bullet/advanced/b473/ihdf
- harmful = TRUE
+ harmful = FALSE
/obj/projectile/bullet/advanced/b473/ihdf
name = "4.73x33mm ihdf bullet"
diff --git a/modular_skyrat/modules/sec_haul/code/junior_officer/pepperball_gun.dm b/modular_skyrat/modules/sec_haul/code/junior_officer/pepperball_gun.dm
index 21a7c50f2c1..7272687f6ba 100644
--- a/modular_skyrat/modules/sec_haul/code/junior_officer/pepperball_gun.dm
+++ b/modular_skyrat/modules/sec_haul/code/junior_officer/pepperball_gun.dm
@@ -32,6 +32,7 @@
desc = "A pepperball casing."
caliber = CALIBER_PEPPERBALL
projectile_type = /obj/projectile/bullet/pepperball
+ harmful = FALSE
/obj/projectile/bullet/pepperball
name = "pepperball orb"
diff --git a/tgstation.dme b/tgstation.dme
index 849676f7dc3..85698487fd8 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -3824,6 +3824,8 @@
#include "modular_skyrat\modules\ambitions\code\ambition_templates.dm"
#include "modular_skyrat\modules\ambitions\code\antag_datums.dm"
#include "modular_skyrat\modules\ambitions\code\mind.dm"
+#include "modular_skyrat\modules\ammo_workbench\code\ammo_workbench.dm"
+#include "modular_skyrat\modules\ammo_workbench\code\design_disks.dm"
#include "modular_skyrat\modules\antagonists\code\gamemodes.dm"
#include "modular_skyrat\modules\antagonists\code\gang_things.dm"
#include "modular_skyrat\modules\antagonists\eldritch_cult\knowledge\ash_lore_skyrat.dm"
diff --git a/tgui/packages/tgui/interfaces/AmmoWorkbench.js b/tgui/packages/tgui/interfaces/AmmoWorkbench.js
new file mode 100644
index 00000000000..370a827f593
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/AmmoWorkbench.js
@@ -0,0 +1,276 @@
+import { toTitleCase } from 'common/string';
+import { useBackend, useSharedState, useLocalState } from '../backend';
+import { Box, Button, NumberInput, NoticeBox, ProgressBar, Section, Flex, Stack, RoundGauge, Tabs, Table } from '../components';
+import { Window } from '../layouts';
+
+export const AmmoWorkbench = (props, context) => {
+ const [tab, setTab] = useSharedState(context, 'tab', 1);
+ return (
+
+
+
+ setTab(1)}>
+ Ammunitions
+
+ setTab(2)}>
+ Materials
+
+ setTab(3)}>
+ Datadisks
+
+
+ {tab === 1 && (
+
+ )}
+ {tab === 2 && (
+
+ )}
+ {tab === 3 && (
+
+ )}
+
+
+ );
+};
+
+export const AmmunitionsTab = (props, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ mag_loaded,
+ system_busy,
+ hacked,
+ error,
+ error_type,
+ mag_name,
+ turboBoost,
+ current_rounds,
+ max_rounds,
+ efficiency,
+ time,
+ available_rounds = [],
+ } = data;
+ return (
+ <>
+ {!!error && (
+
+ {error}
+
+ )}
+
+
+ Current Efficiency: null}
+ />
+
+
+ Time Per Round: {time} seconds
+
+ act('turboBoost')}>
+ Turbo Boost
+
+
+
+ {!!mag_loaded && (
+
+
+
+ )}
+
+
+ {!!mag_loaded && (
+
+ {available_rounds.map(available_round => (
+
+
+
+
+ ))}
+
+ )}
+
+ {!!hacked &&(
+
+ !WARNING! - ARMADYNE SAFETY PROTOCOLS ARE NOT ENGAGED!
+ MISUSE IS NOT COVERED UNDER WARRANTY.
+ SOME MUNITION TYPES MAY CONSTITUTE A WAR CRIME IN YOUR AREA.
+ PLEASE CONTACT AN ARMADYNE ADMINISTRATOR IMMEDIATELY.
+
+ )}
+ >
+ );
+};
+
+export const MaterialsTab = (props, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ materials = [],
+ } = data;
+ return (
+
+
+ {materials.map(material => (
+ act('Release', {
+ id: material.id,
+ sheets: amount,
+ })} />
+ ))}
+
+
+ );
+};
+
+export const DatadiskTab = (props, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ loaded_datadisks = [],
+ datadisk_loaded,
+ datadisk_name,
+ datadisk_desc,
+ disk_error,
+ disk_error_type,
+ } = data;
+ return (
+ <>
+ {!!disk_error && (
+
+ {disk_error}
+
+ )}
+
+
+
+
+ {loaded_datadisks.map(loaded_datadisk => (
+
+ {loaded_datadisk.loaded_disk_name}
+
+ Description: {loaded_datadisk.loaded_disk_desc}
+
+
+ ))}
+
+
+ >
+ );
+};
+
+const MaterialRow = (props, context) => {
+ const { material, onRelease } = props;
+
+ const [
+ amount,
+ setAmount,
+ ] = useLocalState(context, "amount" + material.name, 1);
+
+ const amountAvailable = Math.floor(material.amount);
+ return (
+
+
+ {toTitleCase(material.name).replace('Alloy', '')}
+
+
+
+ {material.value && material.value + ' cr'}
+
+
+
+
+ {amountAvailable} sheets
+
+
+
+ setAmount(value)} />
+ onRelease(amount)} />
+
+
+ );
+};