diff --git a/code/game/gamemodes/objective_items.dm b/code/game/gamemodes/objective_items.dm
index d84c104a..a5a60dc8 100644
--- a/code/game/gamemodes/objective_items.dm
+++ b/code/game/gamemodes/objective_items.dm
@@ -36,6 +36,7 @@
targetitem = /obj/item/gun/energy/e_gun/hos
difficulty = 10
excludefromjob = list("Head Of Security")
+ altitems = list(/obj/item/gun/ballistic/revolver/mws)
/datum/objective_item/steal/handtele
name = "a hand teleporter."
diff --git a/code/game/machinery/recharger.dm b/code/game/machinery/recharger.dm
index a5819817..33829b74 100644
--- a/code/game/machinery/recharger.dm
+++ b/code/game/machinery/recharger.dm
@@ -17,7 +17,9 @@
/obj/item/ammo_box/magazine/recharge,
/obj/item/modular_computer,
/obj/item/gun/ballistic/automatic/magrifle_e,
- /obj/item/gun/ballistic/automatic/pistol/mag_e))
+ /obj/item/gun/ballistic/automatic/pistol/mag_e,
+ /obj/item/ammo_casing/msw_batt,
+ /obj/item/ammo_box/magazine/mws_mag))
/obj/machinery/recharger/RefreshParts()
for(var/obj/item/stock_parts/capacitor/C in component_parts)
@@ -121,6 +123,29 @@
using_power = 1
update_icon(using_power)
return
+
+ if(istype(charging, /obj/item/ammo_casing/mws_batt))
+ var/obj/item/ammo_casing/mws_batt/R = charging
+ if(R.cell.charge < R.cell.maxcharge)
+ R.cell.give(R.cell.chargerate * recharge_coeff)
+ use_power(250 * recharge_coeff)
+ using_power = 1
+ if(R.BB == null)
+ R.chargeshot()
+ update_icon(using_power)
+
+ if(istype(charging, /obj/item/ammo_box/magazine/mws_mag))
+ var/obj/item/ammo_box/magazine/mws_mag/R = charging
+ for(var/B in R.stored_ammo)
+ var/obj/item/ammo_casing/mws_batt/batt = B
+ if(batt.cell.charge < batt.cell.maxcharge)
+ batt.cell.give(batt.cell.chargerate * recharge_coeff)
+ use_power(250 * recharge_coeff)
+ using_power = 1
+ if(batt.BB == null)
+ batt.chargeshot()
+ update_icon(using_power)
+
else
return PROCESS_KILL
diff --git a/code/game/objects/items/miscellaneous.dm b/code/game/objects/items/miscellaneous.dm
index 9837936e..619bfd63 100644
--- a/code/game/objects/items/miscellaneous.dm
+++ b/code/game/objects/items/miscellaneous.dm
@@ -19,3 +19,17 @@
icon_state = "skub"
w_class = WEIGHT_CLASS_BULKY
attack_verb = list("skubbed")
+
+/obj/item/choice_beacon/hosgun
+ name = "personal weapon beacon"
+ desc = "Use this to summon your personal Head of Security issued firearm!"
+
+/obj/item/choice_beacon/hosgun/generate_display_names()
+ var/static/list/hos_gun_list
+ if(!hos_gun_list)
+ hos_gun_list = list()
+ var/list/templist = subtypesof(/obj/item/storage/secure/briefcase/hos/) //we have to convert type = name to name = type, how lovely!
+ for(var/V in templist)
+ var/atom/A = V
+ hos_gun_list[initial(A.name)] = A
+ return hos_gun_list
diff --git a/code/game/objects/items/storage/secure.dm b/code/game/objects/items/storage/secure.dm
index e177a9f9..5a06759b 100644
--- a/code/game/objects/items/storage/secure.dm
+++ b/code/game/objects/items/storage/secure.dm
@@ -150,6 +150,36 @@
for(var/i = 0, i < STR.max_items - 2, i++)
new /obj/item/stack/spacecash/c1000(src)
+/obj/item/storage/secure/briefcase/mws_pack
+ name = "\improper \'MWS\' gun kit"
+ desc = "A storage case for a multi-purpose handgun. Variety hour!"
+
+/obj/item/storage/secure/briefcase/mws_pack/PopulateContents()
+ new /obj/item/gun/ballistic/revolver/mws(src)
+ new /obj/item/ammo_box/magazine/mws_mag(src)
+ for(var/path in subtypesof(/obj/item/ammo_casing/mws_batt))
+ new path(src)
+
+/obj/item/storage/secure/briefcase/hos/mws_pack_hos
+ name = "\improper \'MWS\' gun kit"
+ desc = "A storage case for a multi-purpose handgun. Variety hour!"
+
+/obj/item/storage/secure/briefcase/hos/mws_pack_hos/PopulateContents()
+ new /obj/item/gun/ballistic/revolver/mws(src)
+ new /obj/item/ammo_box/magazine/mws_mag(src)
+ new /obj/item/ammo_casing/mws_batt/lethal(src)
+ new /obj/item/ammo_casing/mws_batt/lethal(src)
+ new /obj/item/ammo_casing/mws_batt/stun(src)
+ new /obj/item/ammo_casing/mws_batt/stun(src)
+ new /obj/item/ammo_casing/mws_batt/ion(src)
+
+/obj/item/storage/secure/briefcase/hos/multiphase_box
+ name = "\improper X-01 Multiphase energy gun box"
+ desc = "A storage case for a high-tech energy firearm."
+
+/obj/item/storage/secure/briefcase/mws_pack_hos/PopulateContents()
+ new /obj/item/gun/energy/e_gun/hos(src)
+
// -----------------------------
// Secure Safe
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/security.dm b/code/game/objects/structures/crates_lockers/closets/secure/security.dm
index adf373e4..36cc23b7 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm
@@ -90,12 +90,13 @@
new /obj/item/storage/box/flashbangs(src)
new /obj/item/shield/riot/tele(src)
new /obj/item/storage/belt/security/full(src)
- new /obj/item/gun/energy/e_gun/hos(src)
+ new /obj/item/choice_beacon/hosgun(src)
new /obj/item/flashlight/seclite(src)
new /obj/item/pinpointer/nuke(src)
new /obj/item/circuitboard/machine/techfab/department/security(src)
new /obj/item/storage/photo_album/HoS(src)
new /obj/item/clothing/suit/hooded/wintercoat/hos(src)
+
/obj/structure/closet/secure_closet/warden
name = "\proper warden's locker"
req_access = list(ACCESS_ARMORY)
diff --git a/code/modules/goonchat/browserassets/css/browserOutput.css b/code/modules/goonchat/browserassets/css/browserOutput.css
index 2ca28d99..dfd54e85 100644
--- a/code/modules/goonchat/browserassets/css/browserOutput.css
+++ b/code/modules/goonchat/browserassets/css/browserOutput.css
@@ -403,6 +403,12 @@ h1.alert, h2.alert {color: #000000;}
.his_grace {color: #15D512; font-family: "Courier New", cursive, sans-serif; font-style: italic;}
.spooky {color: #FF6100;}
.velvet {color: #660015; font-weight: bold; animation: velvet 5000ms infinite;}
+
+.lethal {color: #bf3d3d; font-weight: bold;}
+.stun {color: #0f81bc; font-weight: bold;}
+.ion {color: #d084d6; font-weight: bold;}
+.xray {color: #32c025; font-weight: bold;}
+
@keyframes velvet {
0% { color: #400020; }
40% { color: #FF0000; }
diff --git a/code/modules/projectiles/boxes_magazines/external/rechargable.dm b/code/modules/projectiles/boxes_magazines/external/rechargable.dm
index 7ed0cde5..21e3083f 100644
--- a/code/modules/projectiles/boxes_magazines/external/rechargable.dm
+++ b/code/modules/projectiles/boxes_magazines/external/rechargable.dm
@@ -12,3 +12,104 @@
/obj/item/ammo_box/magazine/recharge/attack_self() //No popping out the "bullets"
return
+
+// MWS Magazine //
+/obj/item/ammo_box/magazine/mws_mag
+ name = "microbattery magazine"
+ desc = "A microbattery holder for the 'Big Iron'"
+
+ icon = 'icons/obj/ammo.dmi'
+ icon_state = "mws_mag"
+ caliber = "mws"
+ ammo_type = /obj/item/ammo_casing/mws_batt
+ start_empty = TRUE
+ max_ammo = 3
+
+ var/list/modes = list()
+
+/obj/item/ammo_box/magazine/mws_mag/update_overlays()
+ .=..()
+ if(!stored_ammo.len)
+ return //Why bother
+
+ var/x_offset = 5
+ var/current = 0
+ for(var/B in stored_ammo)
+ var/obj/item/ammo_casing/mws_batt/batt = B
+ var/mutable_appearance/cap = mutable_appearance(icon, "[initial(icon_state)]_cap", color = batt.type_color)
+ cap.pixel_x = current * x_offset //Caps don't need a pixel_y offset
+ . += cap
+ if(batt.cell.charge > 0)
+ var/ratio = CEILING(clamp(batt.cell.charge / batt.cell.maxcharge, 0, 1) * 4, 1) //4 is how many lights we have a sprite for
+ var/mutable_appearance/charge = mutable_appearance(icon, "[initial(icon_state)]_charge-[ratio]", color = "#29EAF4") //Could use battery color but eh.
+ charge.pixel_x = current * x_offset
+ . += charge
+
+ current++ //Increment for offsets
+
+// MWS Batteries //
+/obj/item/ammo_casing/mws_batt
+ name = "\'MWS\' microbattery - UNKNOWN"
+ desc = "A miniature battery for an energy weapon."
+ icon = 'icons/obj/ammo.dmi'
+ icon_state = "mws_batt"
+ slot_flags = SLOT_BELT | SLOT_EARS
+ throwforce = 1
+
+ caliber = "mws"
+ var/type_color = null
+ var/type_name = null
+
+ var/obj/item/stock_parts/cell/cell
+ var/cell_type = /obj/item/stock_parts/cell{charge = 600; maxcharge = 600}
+
+ var/e_cost = 100
+ projectile_type = /obj/item/projectile/beam
+
+/obj/item/ammo_casing/mws_batt/Initialize()
+ . = ..()
+ pixel_x = rand(-10, 10)
+ pixel_y = rand(-10, 10)
+ cell = new cell_type(src)
+ cell.give(cell.maxcharge)
+ update_icon()
+
+/obj/item/ammo_casing/mws_batt/update_overlays()
+ .=..()
+
+ var/mutable_appearance/ends = mutable_appearance(icon, "[initial(icon_state)]_ends", color = type_color)
+ . += ends
+
+/obj/item/ammo_casing/mws_batt/get_cell()
+ return cell
+
+/obj/item/ammo_casing/mws_batt/proc/chargeshot()
+ if(cell.charge >= e_cost)
+ cell.use(e_cost)
+ newshot()
+ return
+
+// Specific batteries //
+/obj/item/ammo_casing/mws_batt/lethal
+ name = "'MWS' microbattery - LETHAL"
+ type_color = "#bf3d3d"
+ type_name = "LETHAL"
+ projectile_type = /obj/item/projectile/beam
+
+/obj/item/ammo_casing/mws_batt/stun
+ name = "'MWS' microbattery - STUN"
+ type_color = "#0f81bc"
+ type_name = "STUN"
+ projectile_type = /obj/item/projectile/beam/disabler
+
+/obj/item/ammo_casing/mws_batt/xray
+ name = "'MWS' microbattery - XRAY"
+ type_color = "#32c025"
+ type_name = "XRAY"
+ projectile_type = /obj/item/projectile/beam/xray
+
+/obj/item/ammo_casing/mws_batt/ion
+ name = "'MWS' microbattery - ION"
+ type_color = "#d084d6"
+ type_name = "ION"
+ projectile_type = /obj/item/projectile/ion
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index 9d670a91..68fbfc31 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -95,7 +95,7 @@
zoom(user, FALSE) //we can only stay zoomed in if it's in our hands //yeah and we only unzoom if we're actually zoomed using the gun!!
//called after the gun has successfully fired its chambered ammo.
-/obj/item/gun/proc/process_chamber()
+/obj/item/gun/proc/process_chamber(mob/living/user))
return FALSE
//check if there's enough ammo/energy/whatever to shoot one time
@@ -251,7 +251,7 @@
shoot_with_empty_chamber(user)
firing_burst = FALSE
return FALSE
- process_chamber()
+ process_chamber(user)
update_icon()
return TRUE
@@ -293,7 +293,7 @@
else
shoot_with_empty_chamber(user)
return
- process_chamber()
+ process_chamber(user)
update_icon()
semicd = TRUE
addtimer(CALLBACK(src, .proc/reset_semicd), fire_delay)
diff --git a/code/modules/projectiles/guns/ballistic/revolver.dm b/code/modules/projectiles/guns/ballistic/revolver.dm
index 3c0d2800..fee38e33 100644
--- a/code/modules/projectiles/guns/ballistic/revolver.dm
+++ b/code/modules/projectiles/guns/ballistic/revolver.dm
@@ -358,3 +358,114 @@
user.emote("scream")
user.drop_all_held_items()
user.Knockdown(80)
+
+// -------------- HoS Modular Weapon System -------------
+// ---------- Code originally from VoreStation ----------
+/obj/item/gun/ballistic/revolver/mws
+ name = "MWS-01 'Big Iron'"
+ desc = "Modular Weapons System"
+
+ icon = 'icons/obj/guns/projectile.dmi'
+ icon_state = "mws"
+
+ fire_sound = 'sound/weapons/Taser.ogg'
+
+ mag_type = /obj/item/ammo_box/magazine/mws_mag
+ spawnwithmagazine = FALSE
+
+ recoil = 0
+
+ var/charge_sections = 6
+
+/obj/item/gun/ballistic/revolver/mws/examine(mob/user)
+ . = ..()
+ . += "Alt-click to remove the magazine."
+
+/obj/item/gun/ballistic/revolver/mws/shoot_with_empty_chamber(mob/living/user as mob|obj)
+ process_chamber(user)
+ if(!chambered || !chambered.BB)
+ to_chat(user, "*click*")
+ playsound(src, "gun_dry_fire", 30, 1)
+
+
+/obj/item/gun/ballistic/revolver/mws/process_chamber(mob/living/user)
+ if(chambered && !chambered.BB) //if BB is null, i.e the shot has been fired...
+ var/obj/item/ammo_casing/mws_batt/shot = chambered
+ if(shot.cell.charge >= shot.e_cost)
+ shot.chargeshot()
+ else
+ for(var/B in magazine.stored_ammo)
+ var/obj/item/ammo_casing/mws_batt/other_batt = B
+ if(istype(other_batt,shot) && other_batt.cell.charge >= other_batt.e_cost)
+ switch_to(other_batt, user)
+ break
+ update_icon()
+
+/obj/item/gun/ballistic/revolver/mws/proc/switch_to(obj/item/ammo_casing/mws_batt/new_batt, mob/living/user)
+ if(ishuman(user))
+ if(chambered && new_batt.type == chambered.type)
+ to_chat(user,"[src] is now using the next [new_batt.type_name] power cell.")
+ else
+ to_chat(user,"[src] is now firing [new_batt.type_name].")
+
+ chambered = new_batt
+ update_icon()
+
+/obj/item/gun/ballistic/revolver/mws/attack_self(mob/living/user)
+ if(!chambered)
+ return
+
+ var/list/stored_ammo = magazine.stored_ammo
+
+ if(stored_ammo.len == 1)
+ return //silly you.
+
+ //Find an ammotype that ISN'T the same, or exhaust the list and don't change.
+ var/our_slot = stored_ammo.Find(chambered)
+
+ for(var/index in 1 to stored_ammo.len)
+ var/true_index = ((our_slot + index - 1) % stored_ammo.len) + 1 // Stupid ONE BASED lists!
+ var/obj/item/ammo_casing/mws_batt/next_batt = stored_ammo[true_index]
+ if(chambered != next_batt && !istype(next_batt, chambered.type) && next_batt.cell.charge >= next_batt.e_cost)
+ switch_to(next_batt, user)
+ break
+
+/obj/item/gun/ballistic/revolver/mws/AltClick(mob/living/user)
+ .=..()
+ if(magazine)
+ user.put_in_hands(magazine)
+ magazine.update_icon()
+ if(magazine.ammo_count())
+ playsound(src, 'sound/weapons/gun_magazine_remove_full.ogg', 70, 1)
+ else
+ playsound(src, "gun_remove_empty_magazine", 70, 1)
+ magazine = null
+ to_chat(user, "You pull the magazine out of [src].")
+ if(chambered)
+ chambered = null
+ update_icon()
+
+/obj/item/gun/ballistic/revolver/mws/update_overlays()
+ .=..()
+ if(!chambered)
+ return
+
+ var/obj/item/ammo_casing/mws_batt/batt = chambered
+ var/batt_color = batt.type_color //Used many times
+
+ //Mode bar
+ var/image/mode_bar = image(icon, icon_state = "[initial(icon_state)]_type")
+ mode_bar.color = batt_color
+ . += mode_bar
+
+ //Barrel color
+ var/mutable_appearance/barrel_color = mutable_appearance(icon, "[initial(icon_state)]_barrel", color = batt_color)
+ barrel_color.alpha = 150
+ . += barrel_color
+
+ //Charge bar
+ var/ratio = can_shoot() ? CEILING(clamp(batt.cell.charge / batt.cell.maxcharge, 0, 1) * charge_sections, 1) : 0
+ for(var/i = 0, i < ratio, i++)
+ var/mutable_appearance/charge_bar = mutable_appearance(icon, "[initial(icon_state)]_charge", color = batt_color)
+ charge_bar.pixel_x = i
+ . += charge_bar
diff --git a/icons/obj/ammo.dmi b/icons/obj/ammo.dmi
index 677d3521..64e0ce04 100644
Binary files a/icons/obj/ammo.dmi and b/icons/obj/ammo.dmi differ
diff --git a/icons/obj/guns/projectile.dmi b/icons/obj/guns/projectile.dmi
index 5c41ffe8..a149a89e 100644
Binary files a/icons/obj/guns/projectile.dmi and b/icons/obj/guns/projectile.dmi differ