From 01798c98b62829c6584c4d454d6c1cf83a4c88d2 Mon Sep 17 00:00:00 2001 From: mwerezak Date: Fri, 13 Feb 2015 00:40:18 -0500 Subject: [PATCH] Rewrites syringe and dart guns --- .../objects/items/weapons/storage/boxes.dm | 18 +- code/modules/admin/verbs/vox_raiders.dm | 4 +- code/modules/mob/living/carbon/human/human.dm | 9 +- .../mob/living/carbon/human/human_defense.dm | 7 + code/modules/mob/living/living_defense.dm | 9 +- code/modules/projectiles/gun.dm | 1 + code/modules/projectiles/guns/projectile.dm | 29 +- code/modules/projectiles/projectile.dm | 1 + code/modules/reagents/dartgun.dm | 266 ++++++------------ .../reagents/reagent_containers/syringes.dm | 15 +- code/modules/reagents/syringe_gun.dm | 235 ++++++++-------- code/modules/research/designs.dm | 6 +- icons/obj/ammo.dmi | Bin 4637 -> 4887 bytes icons/obj/syringe.dmi | Bin 3323 -> 3325 bytes maps/exodus-1.dmm | 2 +- maps/exodus-2.dmm | 2 +- 16 files changed, 269 insertions(+), 335 deletions(-) diff --git a/code/game/objects/items/weapons/storage/boxes.dm b/code/game/objects/items/weapons/storage/boxes.dm index ddc0709a62c..a1681641ae8 100644 --- a/code/game/objects/items/weapons/storage/boxes.dm +++ b/code/game/objects/items/weapons/storage/boxes.dm @@ -79,7 +79,7 @@ /obj/item/weapon/storage/box/syringes name = "box of syringes" desc = "A box full of syringes." - desc = "A biohazard alert warning is printed on the box" +// desc = "A biohazard alert warning is printed on the box" icon_state = "syringe" New() @@ -92,6 +92,22 @@ new /obj/item/weapon/reagent_containers/syringe( src ) new /obj/item/weapon/reagent_containers/syringe( src ) +/obj/item/weapon/storage/box/syringegun + name = "box of compressed gas cartridges" + desc = "A box full of compressed gas cartridges." + icon_state = "syringe" + + New() + ..() + new /obj/item/ammo_casing/gas_cartridge( src ) + new /obj/item/ammo_casing/gas_cartridge( src ) + new /obj/item/ammo_casing/gas_cartridge( src ) + new /obj/item/ammo_casing/gas_cartridge( src ) + new /obj/item/ammo_casing/gas_cartridge( src ) + new /obj/item/ammo_casing/gas_cartridge( src ) + new /obj/item/ammo_casing/gas_cartridge( src ) + + /obj/item/weapon/storage/box/beakers name = "box of beakers" icon_state = "beaker" diff --git a/code/modules/admin/verbs/vox_raiders.dm b/code/modules/admin/verbs/vox_raiders.dm index 6676acec327..6c34bb92c61 100644 --- a/code/modules/admin/verbs/vox_raiders.dm +++ b/code/modules/admin/verbs/vox_raiders.dm @@ -37,7 +37,7 @@ var/global/vox_tick = 1 equip_to_slot_or_del(new /obj/item/weapon/storage/belt/utility/full(src), slot_belt) equip_to_slot_or_del(new /obj/item/clothing/glasses/thermal/monocle(src), slot_glasses) // REPLACE WITH CODED VOX ALTERNATIVE. equip_to_slot_or_del(new /obj/item/weapon/card/emag(src), slot_l_store) - equip_to_slot_or_del(new /obj/item/weapon/gun/dartgun/vox/raider(src), slot_r_hand) + equip_to_slot_or_del(new /obj/item/weapon/gun/projectile/dartgun/vox/raider(src), slot_r_hand) equip_to_slot_or_del(new /obj/item/device/multitool(src), slot_l_hand) if(4) // Vox medic! @@ -46,7 +46,7 @@ var/global/vox_tick = 1 equip_to_slot_or_del(new /obj/item/weapon/storage/belt/utility/full(src), slot_belt) // Who needs actual surgical tools? equip_to_slot_or_del(new /obj/item/clothing/glasses/hud/health(src), slot_glasses) // REPLACE WITH CODED VOX ALTERNATIVE. equip_to_slot_or_del(new /obj/item/weapon/circular_saw(src), slot_l_store) - equip_to_slot_or_del(new /obj/item/weapon/gun/dartgun/vox/medical, slot_r_hand) + equip_to_slot_or_del(new /obj/item/weapon/gun/projectile/dartgun/vox/medical, slot_r_hand) equip_to_slot_or_del(new /obj/item/clothing/mask/breath(src), slot_wear_mask) equip_to_slot_or_del(new /obj/item/weapon/tank/nitrogen(src), slot_back) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index dd5bfe7d594..38ffc73f4e8 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -1220,10 +1220,11 @@ /mob/living/carbon/human/can_inject(var/mob/user, var/error_msg, var/target_zone) . = 1 - if(!user) - target_zone = pick("chest","chest","chest","left leg","right leg","left arm", "right arm", "head") - else if(!target_zone) - target_zone = user.zone_sel.selecting + if(!target_zone) + if(!user) + target_zone = pick("chest","chest","chest","left leg","right leg","left arm", "right arm", "head") + else + target_zone = user.zone_sel.selecting switch(target_zone) if("head") diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index a45a11f4533..858bee60c17 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -389,6 +389,13 @@ emp_act src.anchored = 1 src.pinned += O +/mob/living/carbon/human/embed(var/obj/O, var/def_zone=null) + if(!def_zone) ..() + + var/datum/organ/external/affecting = get_organ(def_zone) + if(affecting) + affecting.embed(O) + /mob/living/carbon/human/proc/bloody_hands(var/mob/living/source, var/amount = 2) if (gloves) diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 1783bad912b..2fa9cdc4b60 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -148,8 +148,7 @@ if(W.sharp) //Projectile is suitable for pinning. //Handles embedding for non-humans and simple_animals. - O.loc = src - src.embedded += O + embed(O) var/turf/T = near_wall(dir,2) @@ -158,7 +157,11 @@ visible_message("[src] is pinned to the wall by [O]!","You are pinned to the wall by [O]!") src.anchored = 1 src.pinned += O - src.verbs += /mob/proc/yank_out_object + +/mob/living/proc/embed(var/obj/O, var/def_zone=null) + O.loc = src + src.embedded += O + src.verbs += /mob/proc/yank_out_object //This is called when the mob is thrown into a dense turf /mob/living/proc/turf_collision(var/turf/T, var/speed) diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index d0158816ca5..dd7aec246fc 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -165,6 +165,7 @@ if(recoil) spawn() shake_camera(user, recoil + 1, recoil) + update_icon() //does the actual shooting /obj/item/weapon/gun/proc/process_projectile(obj/projectile, mob/user, atom/target, var/target_zone, var/params=null, var/pointblank=0, var/reflex=0) diff --git a/code/modules/projectiles/guns/projectile.dm b/code/modules/projectiles/guns/projectile.dm index b467b31a1e1..26b42308ad7 100644 --- a/code/modules/projectiles/guns/projectile.dm +++ b/code/modules/projectiles/guns/projectile.dm @@ -1,4 +1,4 @@ -#define HOLD_CASINGS 0 //do not do anything after firing. Manual action, like pump shotguns +#define HOLD_CASINGS 0 //do not do anything after firing. Manual action, like pump shotguns, or guns that want to define custom behaviour #define EJECT_CASINGS 1 //drop spent casings on the ground after firing #define CYCLE_CASINGS 2 //experimental: cycle casings, like a revolver. Also works for multibarrelled guns @@ -53,16 +53,19 @@ ..() if(chambered) chambered.expend() + + //check chambered again in case it deleted itself + if(chambered && handle_casings != HOLD_CASINGS) switch(handle_casings) if(EJECT_CASINGS) //eject casing onto ground. chambered.loc = get_turf(src) - chambered = null if(CYCLE_CASINGS) //cycle the casing back to the end. if(ammo_magazine) ammo_magazine.stored_ammo += chambered else loaded += chambered - chambered = null + chambered = null + //Attempts to load A into src, depending on the type of thing being loaded and the load_method //Maybe this should be broken up into separate procs for each load method? @@ -81,6 +84,7 @@ AM.loc = src ammo_magazine = AM user.visible_message("[user] inserts [AM] into [src].", "You insert [AM] into [src]!") + playsound(src.loc, 'sound/weapons/flipblade.ogg', 50, 1) if(SPEEDLOADER) if(loaded.len >= max_shells) user << "[src] is full!" @@ -96,6 +100,7 @@ count++ if(count) user.visible_message("[user] reloads [src].", "You load [count] round\s into [src]!") + playsound(src.loc, 'sound/weapons/empty.ogg', 50, 1) AM.update_icon() else if(istype(A, /obj/item/ammo_casing)) var/obj/item/ammo_casing/C = A @@ -109,20 +114,22 @@ C.loc = src loaded.Insert(1, C) //add to the head of the list user.visible_message("[user] inserts \a [C] into [src].", "You insert \a [C] into [src]!") + playsound(src.loc, 'sound/weapons/empty.ogg', 50, 1) update_icon() -//attempts to unload src -/obj/item/weapon/gun/projectile/proc/unload_ammo(mob/user) +//attempts to unload src. If allow_dump is set to 0, the speedloader unloading method will be disabled +/obj/item/weapon/gun/projectile/proc/unload_ammo(mob/user, var/allow_dump=1) if(ammo_magazine) user.put_in_hands(ammo_magazine) user.visible_message("[user] removes [ammo_magazine] from [src].", "You remove [ammo_magazine] from [src]!") + playsound(src.loc, 'sound/weapons/empty.ogg', 50, 1) ammo_magazine.update_icon() ammo_magazine = null else if(loaded.len) //presumably, if it can be speed-loaded, it can be speed-unloaded. - if(load_method & SPEEDLOADER) + if(allow_dump && load_method & SPEEDLOADER) var/count = 0 var/turf/T = get_turf(user) if(T) @@ -145,17 +152,11 @@ load_ammo(A, user) /obj/item/weapon/gun/projectile/attack_self(mob/user as mob) - if (aim_targets) //TODO replace untargeting with a hotkey - return ..() unload_ammo(user) /obj/item/weapon/gun/projectile/attack_hand(mob/user as mob) - //allow guns with both SPEEDLOADER and SINGLE_CASING a way to remove casings without dumping everything on the floor - if((load_method & SINGLE_CASING) && loaded.len && (src in user)) - var/obj/item/ammo_casing/C = loaded[loaded.len] - loaded.len-- - user.put_in_hands(C) - user.visible_message("[user] removes \a [C] from [src].", "You remove \a [C] from [src]!") + if(src in user) + unload_ammo(user, allow_dump=0) else return ..() diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 131e1523544..f16359ad4bb 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -229,6 +229,7 @@ /obj/item/projectile/process() spawn while(src) if(kill_count-- < 1) + on_impact(src.loc) //for any final impact behaviours del(src) if((!( current ) || loc == current)) current = locate(min(max(x + xo, 1), world.maxx), min(max(y + yo, 1), world.maxy), z) diff --git a/code/modules/reagents/dartgun.dm b/code/modules/reagents/dartgun.dm index d256f4f5f3c..7fa6d52e2e7 100644 --- a/code/modules/reagents/dartgun.dm +++ b/code/modules/reagents/dartgun.dm @@ -1,66 +1,94 @@ -/obj/item/weapon/dart_cartridge +/obj/item/projectile/bullet/chemdart + name = "dart" + icon_state = "cbbolt" + damage = 3 + sharp = 1 + embed = 1 + var/reagent_amount = 15 + kill_count = 10 //short range + +/obj/item/projectile/bullet/chemdart/New() + reagents = new/datum/reagents(reagent_amount) + reagents.my_atom = src + +/obj/item/projectile/bullet/chemdart/on_hit(var/atom/target, var/blocked = 0, var/def_zone = null) + if(blocked < 2 && isliving(target)) + var/mob/living/L = target + if(L.can_inject(target_zone=def_zone)) + reagents.trans_to(L, reagent_amount) + +/obj/item/ammo_casing/chemdart + name = "chemical dart" + desc = "A small hollow dart." + icon_state = "dart" + caliber = "dart" + projectile_type = /obj/item/projectile/bullet/chemdart + +/obj/item/ammo_magazine/chemdart name = "dart cartridge" desc = "A rack of hollow darts." - icon = 'icons/obj/ammo.dmi' - icon_state = "darts-5" + icon_state = "darts" item_state = "rcdammo" - opacity = 0 - density = 0 - anchored = 0.0 origin_tech = "materials=2" - var/darts = 5 + mag_type = MAGAZINE + caliber = "dart" + max_ammo = 5 + multiple_sprites = 1 -/obj/item/weapon/dart_cartridge/update_icon() - if(!darts) - icon_state = "darts-0" - else if(darts > 5) - icon_state = "darts-5" - else - icon_state = "darts-[darts]" - return 1 - -/obj/item/weapon/gun/dartgun +/obj/item/weapon/gun/projectile/dartgun name = "dart gun" desc = "A small gas-powered dartgun, capable of delivering chemical cocktails swiftly across short distances." icon_state = "dartgun-empty" + caliber = "dart" + fire_sound = 'sound/weapons/empty.ogg' + fire_sound_text = "a metallic click" + recoil = 0 + silenced = 1 + load_method = MAGAZINE + magazine_type = /obj/item/ammo_magazine/chemdart + auto_eject = 0 + var/list/beakers = list() //All containers inside the gun. var/list/mixing = list() //Containers being used for mixing. - var/obj/item/weapon/dart_cartridge/cartridge = null //Container of darts. var/max_beakers = 3 var/dart_reagent_amount = 15 var/container_type = /obj/item/weapon/reagent_containers/glass/beaker var/list/starting_chems = null -/obj/item/weapon/gun/dartgun/update_icon() - - if(!cartridge) - icon_state = "dartgun-empty" - return 1 - - if(!cartridge.darts) - icon_state = "dartgun-0" - else if(cartridge.darts > 5) - icon_state = "dartgun-5" - else - icon_state = "dartgun-[cartridge.darts]" - return 1 - -/obj/item/weapon/gun/dartgun/New() - +/obj/item/weapon/gun/projectile/dartgun/dartgun/New() ..() if(starting_chems) for(var/chem in starting_chems) var/obj/B = new container_type(src) - B.reagents.add_reagent(chem, 50) + B.reagents.add_reagent(chem, 60) beakers += B - cartridge = new /obj/item/weapon/dart_cartridge(src) update_icon() -/obj/item/weapon/gun/dartgun/examine(mob/user) - update_icon() - if (!..(user, 2)) - return +/obj/item/weapon/gun/projectile/dartgun/update_icon() + if(!ammo_magazine) + icon_state = "dartgun-empty" + return 1 + + if(!ammo_magazine.stored_ammo || ammo_magazine.stored_ammo.len) + icon_state = "dartgun-0" + else if(ammo_magazine.stored_ammo.len > 5) + icon_state = "dartgun-5" + else + icon_state = "dartgun-[ammo_magazine.stored_ammo.len]" + return 1 + +/obj/item/weapon/gun/projectile/dartgun/consume_next_projectile() + . = ..() + var/obj/item/projectile/bullet/chemdart/dart = . + if(istype(dart)) + fill_dart(dart) + +/obj/item/weapon/gun/projectile/dartgun/examine(mob/user) + //update_icon() + //if (!..(user, 2)) + // return + ..() if (beakers.len) user << "\blue [src] contains:" for(var/obj/item/weapon/reagent_containers/glass/beaker/B in beakers) @@ -68,28 +96,7 @@ for(var/datum/reagent/R in B.reagents.reagent_list) user << "\blue [R.volume] units of [R.name]" -/obj/item/weapon/gun/dartgun/attackby(obj/item/I as obj, mob/user as mob) - if(istype(I, /obj/item/weapon/dart_cartridge)) - - var/obj/item/weapon/dart_cartridge/D = I - - if(!D.darts) - user << "\blue [D] is empty." - return 0 - - if(cartridge) - if(cartridge.darts <= 0) - src.remove_cartridge() - else - user << "\blue There's already a cartridge in [src]." - return 0 - - user.drop_item() - cartridge = D - D.loc = src - user << "\blue You slot [D] into [src]." - update_icon() - return +/obj/item/weapon/gun/projectile/dartgun/attackby(obj/item/I as obj, mob/user as mob) if(istype(I, /obj/item/weapon/reagent_containers/glass)) if(!istype(I, container_type)) user << "\blue [I] doesn't seem to fit into [src]." @@ -103,112 +110,16 @@ beakers += B user << "\blue You slot [B] into [src]." src.updateUsrDialog() - -/obj/item/weapon/gun/dartgun/can_fire() - if(!cartridge) - return 0 - else - return cartridge.darts - -/obj/item/weapon/gun/dartgun/proc/has_selected_beaker_reagents() - return 0 - -/obj/item/weapon/gun/dartgun/proc/remove_cartridge() - if(cartridge) - usr << "\blue You pop the cartridge out of [src]." - var/obj/item/weapon/dart_cartridge/C = cartridge - C.loc = get_turf(src) - C.update_icon() - cartridge = null - src.update_icon() - -/obj/item/weapon/gun/dartgun/proc/get_mixed_syringe() - if (!cartridge) - return 0 - if(!cartridge.darts) - return 0 - - var/obj/item/weapon/reagent_containers/syringe/dart = new(src) - - if(mixing.len) - var/mix_amount = dart_reagent_amount/mixing.len - for(var/obj/item/weapon/reagent_containers/glass/beaker/B in mixing) - B.reagents.trans_to(dart,mix_amount) - - return dart - -/obj/item/weapon/gun/dartgun/proc/fire_dart(atom/target, mob/user) - if (locate (/obj/structure/table, src.loc)) - return - else - var/turf/trg = get_turf(target) - var/obj/effect/syringe_gun_dummy/D = new/obj/effect/syringe_gun_dummy(get_turf(src)) - var/obj/item/weapon/reagent_containers/syringe/S = get_mixed_syringe() - if(!S) - user << "\red There are no darts in [src]!" - return - if(!S.reagents) - user << "\red There are no reagents available!" - return - cartridge.darts-- - src.update_icon() - S.reagents.trans_to(D, S.reagents.total_volume) - del(S) - D.icon_state = "syringeproj" - D.name = "syringe" - D.flags |= NOREACT - playsound(user.loc, 'sound/items/syringeproj.ogg', 50, 1) - - for(var/i=0, i<6, i++) - if(!D) break - if(D.loc == trg) break - step_towards(D,trg) - - if(D) - for(var/mob/living/carbon/M in D.loc) - if(!istype(M,/mob/living/carbon)) continue - if(M == user) continue - //Syringe gun attack logging by Yvarov - var/R - if(D.reagents) - for(var/datum/reagent/A in D.reagents.reagent_list) - R += A.id + " (" - R += num2text(A.volume) + ")," - if (istype(M, /mob)) - M.attack_log += "\[[time_stamp()]\] [user]/[user.ckey] shot [M]/[M.ckey] with a dartgun ([R])" - user.attack_log += "\[[time_stamp()]\] [user]/[user.ckey] shot [M]/[M.ckey] with a dartgun ([R])" - msg_admin_attack("[user] ([user.ckey]) shot [M] ([M.ckey]) with a dartgun ([R]) (JMP)") - - else - M.attack_log += "\[[time_stamp()]\] UNKNOWN SUBJECT (No longer exists) shot [M]/[M.ckey] with a dartgun ([R])" - msg_admin_attack("UNKNOWN shot [M] ([M.ckey]) with a dartgun ([R]) (JMP)") - - if(D.reagents) - D.reagents.trans_to(M, 15) - M << "You feel a slight prick." - - del(D) - break - if(D) - for(var/atom/A in D.loc) - if(A == user) continue - if(A.density) del(D) - - sleep(1) - - if (D) spawn(10) del(D) - - return - -/obj/item/weapon/gun/dartgun/afterattack(obj/target, mob/user , flag) - if(!isturf(target.loc) || target == user) return ..() -/obj/item/weapon/gun/dartgun/can_hit(var/mob/living/target as mob, var/mob/living/user as mob) - return 1 - -/obj/item/weapon/gun/dartgun/attack_self(mob/user) +//fills the given dart with reagents +/obj/item/weapon/gun/projectile/dartgun/proc/fill_dart(var/obj/item/projectile/bullet/chemdart/dart) + if(mixing.len) + var/mix_amount = dart.reagent_amount/mixing.len + for(var/obj/item/weapon/reagent_containers/glass/beaker/B in mixing) + B.reagents.trans_to(dart, mix_amount) +/obj/item/weapon/gun/projectile/dartgun/attack_self(mob/user) user.set_machine(src) var/dat = "[src] mixing control:

" @@ -230,9 +141,9 @@ else dat += "There are no beakers inserted!

" - if(cartridge) - if(cartridge.darts) - dat += "The dart cartridge has [cartridge.darts] shots remaining." + if(ammo_magazine) + if(ammo_magazine.stored_ammo && ammo_magazine.stored_ammo.len) + dat += "The dart cartridge has [ammo_magazine.stored_ammo.len] shots remaining." else dat += "The dart cartridge is empty!" dat += " \[Eject\]" @@ -240,7 +151,7 @@ user << browse(dat, "window=dartgun") onclose(user, "dartgun", src) -/obj/item/weapon/gun/dartgun/proc/check_beaker_mixing(var/obj/item/B) +/obj/item/weapon/gun/projectile/dartgun/proc/check_beaker_mixing(var/obj/item/B) if(!mixing || !beakers) return 0 for(var/obj/item/M in mixing) @@ -248,7 +159,7 @@ return 1 return 0 -/obj/item/weapon/gun/dartgun/Topic(href, href_list) +/obj/item/weapon/gun/projectile/dartgun/Topic(href, href_list) src.add_fingerprint(usr) if(href_list["stop_mix"]) var/index = text2num(href_list["stop_mix"]) @@ -271,23 +182,16 @@ beakers -= B B.loc = get_turf(src) else if (href_list["eject_cart"]) - remove_cartridge() + unload_ammo(usr) src.updateUsrDialog() return -/obj/item/weapon/gun/dartgun/Fire(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, params, reflex = 0) - if(cartridge) - spawn(0) fire_dart(target,user) - else - usr << "\red [src] is empty." - - -/obj/item/weapon/gun/dartgun/vox +/obj/item/weapon/gun/projectile/dartgun/vox name = "alien dart gun" desc = "A small gas-powered dartgun, fitted for nonhuman hands." -/obj/item/weapon/gun/dartgun/vox/medical +/obj/item/weapon/gun/projectile/dartgun/vox/medical starting_chems = list("kelotane","bicaridine","anti_toxin") -/obj/item/weapon/gun/dartgun/vox/raider +/obj/item/weapon/gun/projectile/dartgun/vox/raider starting_chems = list("space_drugs","stoxin","impedrezene") \ No newline at end of file diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm index c1d9b0fdec8..8aa34a7dcab 100644 --- a/code/modules/reagents/reagent_containers/syringes.dm +++ b/code/modules/reagents/reagent_containers/syringes.dm @@ -18,6 +18,7 @@ w_class = 1 sharp = 1 var/mode = SYRINGE_DRAW + var/image/filling //holds a reference to the current filling overlay on_reagent_change() update_icon() @@ -217,7 +218,7 @@ item_state = "syringe_[rounded_vol]" if(reagents.total_volume) - var/image/filling = image('icons/obj/reagentfillings.dmi', src, "syringe10") + filling = image('icons/obj/reagentfillings.dmi', src, "syringe10") filling.icon_state = "syringe[rounded_vol]" @@ -225,7 +226,7 @@ overlays += filling - /obj/item/weapon/reagent_containers/syringe/proc/syringestab(mob/living/carbon/target as mob, mob/living/carbon/user as mob) + proc/syringestab(mob/living/carbon/target as mob, mob/living/carbon/user as mob) user.attack_log += "\[[time_stamp()]\] Attacked [target.name] ([target.ckey]) with [src.name] (INTENT: [uppertext(user.a_intent)])" target.attack_log += "\[[time_stamp()]\] Attacked by [user.name] ([user.ckey]) with [src.name] (INTENT: [uppertext(user.a_intent)])" @@ -268,13 +269,19 @@ src.reagents.reaction(target, INGEST) var/syringestab_amount_transferred = rand(0, (reagents.total_volume - 5)) //nerfed by popular demand src.reagents.trans_to(target, syringestab_amount_transferred) + src.break_syringe(target, user) + + proc/break_syringe(mob/living/carbon/target, mob/living/carbon/user) src.desc += " It is broken." src.mode = SYRINGE_BROKEN - src.add_blood(target) - src.add_fingerprint(usr) + if(target) + src.add_blood(target) + if(user) + src.add_fingerprint(user) src.update_icon() + /obj/item/weapon/reagent_containers/ld50_syringe name = "Lethal Injection Syringe" desc = "A syringe used for lethal injections." diff --git a/code/modules/reagents/syringe_gun.dm b/code/modules/reagents/syringe_gun.dm index faecb798681..23a9eaa36b0 100644 --- a/code/modules/reagents/syringe_gun.dm +++ b/code/modules/reagents/syringe_gun.dm @@ -1,137 +1,130 @@ +/obj/item/ammo_casing/gas_cartridge + name = "compressed gas cartridge" + desc = "An impact-triggered compressed gas cartridge that can fitted to a syringe for rapid injection. It's not very useful until primed though." //i.e. only works when shot out of a syringe gun. + icon_state = "syringe-cartridge" + caliber = "syringe" + projectile_type = /obj/item/projectile/bullet/syringe + w_class = 2 //mainly so that they can be yanked out + var/obj/item/weapon/reagent_containers/syringe/syringe +/obj/item/ammo_casing/gas_cartridge/update_icon() + underlays.Cut() + if(syringe) + underlays += image(syringe.icon, src, syringe.icon_state) + underlays += syringe.filling +/obj/item/ammo_casing/gas_cartridge/attackby(obj/item/I, mob/user) + if(istype(I, /obj/item/weapon/reagent_containers/syringe)) + syringe = I + user << "You carefully insert [syringe] into [src]." + user.remove_from_mob(syringe) + syringe.loc = src + var/obj/item/projectile/bullet/syringe/S = BB + if(istype(S)) + S.damage = 1 + S.sharp = 1 + update_icon() -/obj/item/weapon/gun/syringe +/obj/item/ammo_casing/gas_cartridge/attack_self(mob/user) + if(syringe) + user << "You remove [syringe] from [src]." + user.put_in_hands(syringe) + syringe = null + var/obj/item/projectile/bullet/syringe/S = BB + if(istype(S)) + S.damage = initial(S.damage) + S.sharp = initial(S.sharp) + update_icon() + +//This was kind of rushed, there may very well be a simpler way to implement this. +//Sort of hacky, though nearly not as bad as the previous implementation: +//Basically the syringe gun is supposed to launch the entire syringe+cartrige assemby, but hitby() isn't powerfull enough to do what we need. +//Instead, we fire a projectile that transfers the reagents, and teleport the cartridge once we impact something. +/obj/item/projectile/bullet/syringe + name = "syringe dart" + icon_state = "cbbolt" + damage = 3 + check_armour = "bullet" + sharp = 0 + embed = 0 //we handle this ourselves + var/obj/item/ammo_casing/gas_cartridge/cartridge + var/embedded = 0 + kill_count = 10 //short range + +/obj/item/projectile/bullet/syringe/New(newloc) + ..() + //ensure that cartridge is always set + cartridge = newloc + if(!istype(cartridge)) + del(src) + +/obj/item/projectile/bullet/syringe/on_hit(var/atom/target, var/blocked = 0, var/def_zone = null) + //..() //not really necessary + if(blocked < 2 && cartridge.syringe && isliving(target)) + var/mob/living/L = target + + //inject + if(L.can_inject(target_zone=def_zone)) + if(cartridge.syringe.reagents) + cartridge.syringe.reagents.trans_to(L, 15) + cartridge.syringe.update_icon() + cartridge.update_icon() + + //embed + L.embed(cartridge, def_zone) + embedded = 1 + +/obj/item/projectile/bullet/syringe/on_impact(atom/A) + if(!embedded) + cartridge.loc = src.loc + if(cartridge.syringe) + cartridge.syringe.break_syringe(iscarbon(A)? A : null) + cartridge.update_icon() + + + +/obj/item/weapon/gun/projectile/syringe name = "syringe gun" desc = "A spring loaded rifle designed to fit syringes, designed to incapacitate unruly patients from a distance." icon = 'icons/obj/gun.dmi' icon_state = "syringegun" item_state = "syringegun" - w_class = 3.0 - throw_speed = 2 - throw_range = 10 - force = 4.0 - var/list/syringes = new/list() - var/max_syringes = 1 + w_class = 3 + force = 7 matter = list("metal" = 2000) + slot_flags = SLOT_BELT + + caliber = "syringe" + fire_sound = 'sound/weapons/empty.ogg' + fire_sound_text = "a metallic thunk" + recoil = 0 + handle_casings = HOLD_CASINGS + load_method = SINGLE_CASING + max_shells = 1 + var/drawn = 0 -/obj/item/weapon/gun/syringe/examine(mob/user) - if(..(user, 2)) - user << "\blue [syringes.len] / [max_syringes] syringes." +/obj/item/weapon/gun/projectile/syringe/consume_next_projectile() + if(chambered) + return chambered.BB + return null -/obj/item/weapon/gun/syringe/attackby(obj/item/I as obj, mob/user as mob) - if(istype(I, /obj/item/weapon/reagent_containers/syringe)) - var/obj/item/weapon/reagent_containers/syringe/S = I - if(S.mode != 2)//SYRINGE_BROKEN in syringes.dm - if(syringes.len < max_syringes) - user.drop_item() - I.loc = src - syringes += I - user << "\blue You put the syringe in [src]." - user << "\blue [syringes.len] / [max_syringes] syringes." - else - usr << "\red [src] cannot hold more syringes." - else - usr << "\red This syringe is broken!" +/obj/item/weapon/gun/projectile/syringe/attack_self(mob/living/user as mob) + if(!chambered && loaded.len) + playsound(src.loc, 'sound/weapons/flipblade.ogg', 50, 1) + user.visible_message("[user] draws back the bolt on [src], clicking it into place.", "You draw back the bolt on the [src], loading the spring!") + var/obj/item/ammo_casing/AC = loaded[1] //load next casing. + loaded -= AC //Remove casing from loaded list. + chambered = AC + max_shells -= 1 //to prevent people from storing an extra syringe + update_icon() - -/obj/item/weapon/gun/syringe/afterattack(obj/target, mob/user , flag) - if(!isturf(target.loc) || target == user) return +/obj/item/weapon/gun/projectile/syringe/handle_post_fire() ..() + chambered = null + max_shells = initial(max_shells) -/obj/item/weapon/gun/syringe/can_fire() - return syringes.len - -/obj/item/weapon/gun/syringe/can_hit(var/mob/living/target as mob, var/mob/living/user as mob) - return 1 //SHOOT AND LET THE GOD GUIDE IT (probably will hit a wall anyway) - -/obj/item/weapon/gun/syringe/Fire(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, params, reflex = 0) - if(syringes.len) - spawn(0) fire_syringe(target,user) - else - usr << "\red [src] is empty." - -/obj/item/weapon/gun/syringe/proc/fire_syringe(atom/target, mob/user) - if (locate (/obj/structure/table, src.loc)) - return - else - var/turf/trg = get_turf(target) - var/obj/effect/syringe_gun_dummy/D = new/obj/effect/syringe_gun_dummy(get_turf(src)) - var/obj/item/weapon/reagent_containers/syringe/S = syringes[1] - if((!S) || (!S.reagents)) //ho boy! wot runtimes! - return - S.reagents.trans_to(D, S.reagents.total_volume) - syringes -= S - del(S) - D.icon_state = "syringeproj" - D.name = "syringe" - playsound(user.loc, 'sound/items/syringeproj.ogg', 50, 1) - - for(var/i=0, i<6, i++) - if(!D) break - if(D.loc == trg) break - step_towards(D,trg) - - if(D) - for(var/mob/living/carbon/M in D.loc) - if(!istype(M,/mob/living/carbon)) continue - if(M == user) continue - //Syringe gun attack logging by Yvarov - var/R - if(D.reagents) - for(var/datum/reagent/A in D.reagents.reagent_list) - R += A.id + " (" - R += num2text(A.volume) + ")," - if (istype(M, /mob)) - M.attack_log += "\[[time_stamp()]\] [user]/[user.ckey] shot [M]/[M.ckey] with a syringegun ([R])" - user.attack_log += "\[[time_stamp()]\] [user]/[user.ckey] shot [M]/[M.ckey] with a syringegun ([R])" - msg_admin_attack("[user] ([user.ckey]) shot [M] ([M.ckey]) with a syringegun ([R]) (JMP)") - - else - M.attack_log += "\[[time_stamp()]\] UNKNOWN SUBJECT (No longer exists) shot [M]/[M.ckey] with a syringegun ([R])" - msg_admin_attack("UNKNOWN shot [M] ([M.ckey]) with a syringegun ([R]) (JMP)") - - var/mob/living/T - if(istype(M,/mob/living)) - T = M - - M.visible_message("[M] is hit by the syringe!") - - if(T && istype(T) && T.can_inject()) - if(D.reagents) - D.reagents.trans_to(M, 15) - else - M.visible_message("The syringe bounces off [M]!") - - del(D) - break - if(D) - for(var/atom/A in D.loc) - if(A == user) continue - if(A.density) del(D) - - sleep(1) - - if (D) spawn(10) del(D) - - return - -/obj/item/weapon/gun/syringe/rapidsyringe +/obj/item/weapon/gun/projectile/syringe/rapid name = "rapid syringe gun" - desc = "A modification of the syringe gun design, using a rotating cylinder to store up to four syringes." + desc = "A modification of the syringe gun design, using a rotating cylinder to store up to four syringes. The spring still needs to be drawn between shots." icon_state = "rapidsyringegun" - max_syringes = 4 - - -/obj/effect/syringe_gun_dummy - name = "" - desc = "" - icon = 'icons/obj/chemical.dmi' - icon_state = "null" - anchored = 1 - density = 0 - - New() - var/datum/reagents/R = new/datum/reagents(15) - reagents = R - R.my_atom = src \ No newline at end of file + max_shells = 4 diff --git a/code/modules/research/designs.dm b/code/modules/research/designs.dm index e04618cf208..faf7e34d98d 100644 --- a/code/modules/research/designs.dm +++ b/code/modules/research/designs.dm @@ -451,7 +451,7 @@ datum/design/circuit/tcom/server name = "server mainframe" id = "tcom-server" build_path = /obj/item/weapon/circuitboard/telecomms/server - + datum/design/circuit/tcom/processor name = "processor unit" id = "tcom-processor" @@ -1312,7 +1312,7 @@ datum/design/item/weapon/rapidsyringe id = "rapidsyringe" req_tech = list("combat" = 3, "materials" = 3, "engineering" = 3, "biotech" = 2) materials = list("$metal" = 5000, "$glass" = 1000) - build_path = /obj/item/weapon/gun/syringe/rapidsyringe + build_path = /obj/item/weapon/gun/projectile/syringe/rapid /* datum/design/item/weapon/largecrossbow name = "Energy Crossbow" @@ -1567,7 +1567,7 @@ datum/design/item/mesons req_tech = list("magnets" = 2, "engineering" = 2) materials = list("$metal" = 50, "$glass" = 50) build_path = /obj/item/clothing/glasses/meson - + datum/design/item/binaryencrypt name = "Binary encryption key" desc = "Allows for deciphering the binary channel on-the-fly." diff --git a/icons/obj/ammo.dmi b/icons/obj/ammo.dmi index c1e14968f807d74af17961a76d53fc5ccc6fbec5..acade2ce1b265ed5f2222160b69622521ccbb9cf 100644 GIT binary patch delta 4500 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 GVEzN?>c2Dq delta 4249 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 N002ovPDHLkV1l-E+lc@G delta 2523 zcmV<12_*LY8T%Qq2mt}!lL-N13OlfCPSX$euJ_iHb^(Kb`7I#rM+OCu0wl@_?CtFp zk?dP$`xHQmptNk^oIImEN}4uZzfSwc*mx({dJ=sZ(CKvAv^Ml(Vwvqz07Pmy1R_#H z0T8L70EpC307Pm`ABAYBuNTKVI#h4j?RJZ$OP8ANXliN-EiElE-_au^nV1S7&u&LU zeZ6|=sJNtm1e-T+1_11TstB1y&tT=oAE^14Z`y>qx;n9P;XrwL zc~s(1gqREg>P4@`ss44NuEfZOdBexEN$fC>{66Y6qLPY(tM z2N8<^6gO+i#%w=SnUZn*X10Nua{hO?v=+OW4}lYRT}%@AE-v0neX5Z<=pvY6>S$o{XuCiY=qx z+-|q1ICa8&+c?+{RipB1W5i6#}|!)wLTwvKdMemg&|g7{#y@d7oi?x6 ztGzroZ~RL8sbQaCQD!mvll4xt_S&=AJq#nqTYD1CPCo5hW1j*bQo~0fh}2L3L~1Ai zA~h5Mk(!`J=+IWj$G6&ks4_7=|KdFVL05sSj*oA({ZM6MeE!75=MQC5OQOzyP!C{r ze0;0zhbj}}^Cu=ge<+(;5_SH0AAm>=onVuJ10Iur0}_9u3c%|4@Pt@F(E9*Z$A>2* z*<|Sxtd0*)NHU=hK%{0~4-g8gN25^#K1F9tHpm-+8F!Umf9o=Lx{-`0#`n@#h_D zX6sCAkPo0#{;saBsOpp&7RBoL@PsUcmbcy#SvffvzI6-jM~_Bbrx+KAq$OLoZdDuJ zXj^}P_W?*6($v@}3i9)ze3}{?qrUk{0g$w!sj+`i6geFD@|KEG0?NaFLy+rIYyFpAIr&#M{0Isof{ zO+$an<7!`jF0}SLu4Z7J@2!1Oc#z-F_>bQv_I<_9p` zZwklf|KY~jg)f1rDSy)9^S{1PmpM-M!=3-vH=6VS2{`_6WaZ=lh;952@IC-ZGnyJ3 zMR7?9Y&ILdk(Y~ zwRQ|dYPzk_y8c)w0Fn|Y6;&%hvorvv=|<~j+W-^*SpbQeA0nFgvqojpS8lUmmNuPE zFo_ti*Q>V5+6twlCj&FFMQfh|AeNITC)n(&l@KFK-&LSiB4Dv-Y7K37?( zF0Wm)MqR$#-Hn=>8ueR$LEBsO4a`m2O861XXwdIMLC8 zk3ap?bVnPis_>ir`vJ@!HjR9=2KUSU7FGnQ7N1_W`2l1^6+j>m5K3!)@#00bWmhWm zFKTPU#^=9q;X=^wqa!02`DhJJUHfH3G2-2BxA6OXhQAe;lwjX~zI~?KA3S(a3=R$= zp7H6m;DU_eXZR@*_Q=%2=x2=tMKua`}kGy%xv5GR(wNM6+*@5 z-%wR$=rB8eG=S}htOp1epZ>ss1E#)(74rjV%M)+=q2>oL`X3T;{4x-9A0Sfa^>TMN zo~x`>zm4ec?^l<9Q|;ylAqqiGu-;i!?%w_MrI+x*>C-_QDivH?(Y3JBlXM{Dvp`O# z)AsFEtHg`D&j0?0aZyYhz_6X-S61O@gd z5tb}jf~lz~(|nB2pMIqL8)1s`Yvov85y0)+w^3VLYnZ2}rw97@{0$8aVav=n!mK6b zC(2G0C)V%xqpYk9-6k_;8pY>7e*CzIL