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..c18411ef 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/mws_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..8983485c 100644 --- a/code/game/objects/items/miscellaneous.dm +++ b/code/game/objects/items/miscellaneous.dm @@ -1,3 +1,54 @@ +/obj/item/choice_beacon + name = "choice beacon" + desc = "Hey, why are you viewing this?!! Please let Centcom know about this odd occurance." + icon = 'icons/obj/device.dmi' + icon_state = "gangtool-blue" + item_state = "radio" + var/list/stored_options + var/force_refresh = FALSE //if set to true, the beacon will recalculate its display options whenever opened + +/obj/item/choice_beacon/attack_self(mob/user) + if(canUseBeacon(user)) + generate_options(user) + +/obj/item/choice_beacon/proc/generate_display_names() // return the list that will be used in the choice selection. entries should be in (type.name = type) fashion. see choice_beacon/hero for how this is done. + return list() + +/obj/item/choice_beacon/proc/canUseBeacon(mob/living/user) + if(user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) + return TRUE + else + playsound(src, 'sound/machines/buzz-sigh.ogg', 40, 1) + return FALSE + +/obj/item/choice_beacon/proc/generate_options(mob/living/M) + if(!stored_options || force_refresh) + stored_options = generate_display_names() + if(!stored_options.len) + return + var/choice = input(M,"Which item would you like to order?","Select an Item") as null|anything in stored_options + if(!choice || !M.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) + return + + spawn_option(stored_options[choice],M) + qdel(src) + +/obj/item/choice_beacon/proc/create_choice_atom(atom/choice, mob/owner) + return new choice() + +/obj/item/choice_beacon/proc/spawn_option(obj/choice,mob/living/M) + var/obj/new_item = new choice() + var/obj/structure/closet/supplypod/bluespacepod/pod = new() + pod.explosionSize = list(0,0,0,0) + new_item.forceMove(pod) + var/msg = "After making your selection, you notice a strange target on the ground. It might be best to step back!" + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(istype(H.ears, /obj/item/radio/headset)) + msg = "You hear something crackle in your ears for a moment before a voice speaks. \"Please stand by for a message from Central Command. Message as follows: Item request received. Your package is inbound, please stand back from the landing site. Message ends.\"" + to_chat(M, msg) + new /obj/effect/DPtarget(get_turf(src), pod) + /obj/item/caution desc = "Caution! Wet Floor!" name = "wet floor sign" @@ -19,3 +70,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..8bb37fd6 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/hos/multiphase_box/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/cargo/centcom_podlauncher.dm b/code/modules/cargo/centcom_podlauncher.dm index 92cc1cd1..18d32d3b 100644 --- a/code/modules/cargo/centcom_podlauncher.dm +++ b/code/modules/cargo/centcom_podlauncher.dm @@ -565,7 +565,7 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm qdel(temp_pod) //Delete the temp_pod qdel(selector) //Delete the selector effect . = ..() - + /datum/centcom_podlauncher/proc/supplypod_punish_log(var/list/whoDyin) var/podString = effectBurst ? "5 pods" : "a pod" var/whomString = "" diff --git a/code/modules/goonchat/browserassets/css/browserOutput.css b/code/modules/goonchat/browserassets/css/browserOutput.css index d4dc3cb2..3455a97b 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..1444cccd 100644 --- a/code/modules/projectiles/boxes_magazines/external/rechargable.dm +++ b/code/modules/projectiles/boxes_magazines/external/rechargable.dm @@ -12,3 +12,105 @@ /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_icon() + cut_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.color = batt.type_color + cap.pixel_x = current * x_offset //Caps don't need a pixel_y offset + add_overlay(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 + add_overlay(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_icon() + cut_overlays() + var/mutable_appearance/ends = mutable_appearance(icon, "[initial(icon_state)]_ends", color = type_color) + ends.color = type_color + add_overlay(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..55f057d2 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 aa3d0dab..00857298 100644 --- a/code/modules/projectiles/guns/ballistic/revolver.dm +++ b/code/modules/projectiles/guns/ballistic/revolver.dm @@ -358,3 +358,115 @@ 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_icon() + cut_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 + add_overlay(mode_bar) + + //Barrel color + var/mutable_appearance/barrel_color = mutable_appearance(icon, "[initial(icon_state)]_barrel", color = batt_color) + barrel_color.alpha = 150 + add_overlay(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.color = batt_color + add_overlay(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