diff --git a/code/citadel/hypospraymkii.dm b/code/citadel/hypospraymkii.dm new file mode 100644 index 0000000000..587a980cd5 --- /dev/null +++ b/code/citadel/hypospraymkii.dm @@ -0,0 +1,214 @@ +#define HYPO_SPRAY 0 +#define HYPO_INJECT 1 + +//A vial-loaded hypospray. Cartridge-based! +/obj/item/reagent_containers/hypospray/mkii + name = "hypospray mk.II" + icon = 'icons/obj/citadel/hypospray.dmi' + icon_state = "hypo2" + var/list/allowed_containers = list(/obj/item/reagent_containers/glass/bottle/vial/small) + desc = "A new development from DeForest Medical, this new hypospray takes 30-unit vials as the drug supply for easy swapping." + volume = 0 + amount_per_transfer_from_this = 5 + possible_transfer_amounts = list(5,10,15) + var/mode = HYPO_INJECT + var/obj/item/reagent_containers/glass/bottle/vial/vial + var/loaded_vial = /obj/item/reagent_containers/glass/bottle/vial/small + var/spawnwithvial = TRUE + var/start_vial = null + +/obj/item/reagent_containers/hypospray/mkii/CMO + name = "hypospray mk.II deluxe" + allowed_containers = list(/obj/item/reagent_containers/glass/bottle/vial/small, /obj/item/reagent_containers/glass/bottle/vial/large) + icon_state = "cmo2" + ignore_flags = 1 + desc = "The Chief Medical Officer's hypospray is identically functional to the base model, excepting that it can take larger vials in addition to regular sized. It is also able to penetrate harder materials and deliver more reagents per spray." + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + loaded_vial = /obj/item/reagent_containers/glass/bottle/vial/large/preloaded/CMO + possible_transfer_amounts = list(5,10,15,30,60) //cmo hypo should be able to dump lots into it + +/obj/item/reagent_containers/hypospray/mkii/Initialize() + . = ..() + if(!spawnwithvial) + update_icon() + return + if (!start_vial) + start_vial = new loaded_vial(src) + vial = start_vial + update_icon() + +/obj/item/reagent_containers/hypospray/mkii/update_icon() + ..() + icon_state = "[initial(icon_state)][vial ? "" : "-e"]" + if(ismob(loc)) + var/mob/M = loc + M.update_inv_hands() + return + +/obj/item/reagent_containers/hypospray/mkii/examine(mob/user) + . = ..() + to_chat(user, "[src] is set to [mode ? "Inject" : "Spray"] contents on application.") + +/obj/item/reagent_containers/hypospray/mkii/proc/unload_hypo(obj/item/I, mob/user) + if((istype(I, /obj/item/reagent_containers/glass/bottle/vial))) + var/obj/item/reagent_containers/glass/bottle/vial/V = I + reagents.trans_to(V, reagents.total_volume) + reagents.maximum_volume = 0 + V.forceMove(user.loc) + user.put_in_hands(V) + to_chat(user, "You remove the vial from the [src].") + vial = null + update_icon() + playsound(loc, 'sound/weapons/empty.ogg', 50, 1) + else + to_chat(user, "This hypo isn't loaded!") + return + +/obj/item/reagent_containers/hypospray/mkii/attackby(obj/item/I, mob/living/user) + if((istype(I, /obj/item/reagent_containers/glass/bottle/vial) && vial != null)) + to_chat(user, "[src] can not hold more than one vial!") + return FALSE + if((istype(I, /obj/item/reagent_containers/glass/bottle/vial))) + var/obj/item/reagent_containers/glass/bottle/vial/V = I + if(!is_type_in_list(V, allowed_containers)) + to_chat(user, "\The [src] doesn't accept this vial.") + return + vial = V + reagents.maximum_volume = V.volume + V.reagents.trans_to(src, V.reagents.total_volume) + if(!user.transferItemToLoc(V,src)) + return + user.visible_message("[user] has loads vial into \the [src].","You have loaded [vial] into \the [src].") + update_icon() + playsound(loc, 'sound/weapons/autoguninsert.ogg', 50, 1) + return TRUE + else + to_chat(user, "This doesn't fit in \the [src].") + return FALSE + return FALSE + +/obj/item/reagent_containers/hypospray/mkii/attack(obj/item/I, mob/user, params) + return + +/obj/item/reagent_containers/hypospray/mkii/afterattack(atom/target, mob/user, proximity) + if(!proximity) + return + + if(!ismob(target)) + return + + var/mob/living/L + if(isliving(target)) + L = target + if(!L.can_inject(user, 1)) + return + + if(!L && !target.is_injectable()) //only checks on non-living mobs, due to how can_inject() handles + to_chat(user, "You cannot directly fill [target]!") + return + + if(target.reagents.total_volume >= target.reagents.maximum_volume) + to_chat(user, "[target] is full.") + return + + var/contained = reagents.log_list() + add_logs(user, L, "attemped to inject", src, addition="which had [contained]") +//Always log attemped injections for admins + if(vial != null) + switch(mode) + if(HYPO_INJECT) + if(L) //living mob + if(!L.can_inject(user, TRUE)) + return + if(L != user) + L.visible_message("[user] is trying to inject [L] with [src]!", \ + "[user] is trying to inject [L] with the [src]!") + if(!do_mob(user, L, extra_checks=CALLBACK(L, /mob/living/proc/can_inject,user,1))) + return + if(!reagents.total_volume) + return + if(L.reagents.total_volume >= L.reagents.maximum_volume) + return + L.visible_message("[user] uses the [src] on [L]!", \ + "[user] uses the [src] on [L]!") + else + if(!do_mob(user, L, extra_checks=CALLBACK(L, /mob/living/proc/can_inject,user,1))) + return + if(!reagents.total_volume) + return + if(L.reagents.total_volume >= L.reagents.maximum_volume) + return + log_attack("[user.name] ([user.ckey]) applied [src] to [L.name] ([L.ckey]), which had [contained] (INTENT: [uppertext(user.a_intent)]) (MODE: [src.mode])") + L.log_message("applied [src] to themselves ([contained]).", INDIVIDUAL_ATTACK_LOG) + + var/fraction = min(amount_per_transfer_from_this/reagents.total_volume, 1) + reagents.reaction(L, INJECT, fraction) + reagents.trans_to(target, amount_per_transfer_from_this) + if(amount_per_transfer_from_this >= 15) + playsound(loc,'sound/items/hypospray_long.ogg',50, 1, -1) + if(amount_per_transfer_from_this < 15) + playsound(loc, pick('sound/items/hypospray.ogg','sound/items/hypospray2.ogg'), 50, 1, -1) + to_chat(user, "You inject [amount_per_transfer_from_this] units of the solution. The hypospray's cartridge now contains [reagents.total_volume] units.") + + if(HYPO_SPRAY) + if(L) //living mob + if(!L.can_inject(user, TRUE)) + return + if(L != user) + L.visible_message("[user] is trying to inject [L] with [src]!", \ + "[user] is trying to inject [L] with the [src]!") + if(!do_mob(user, L, extra_checks=CALLBACK(L, /mob/living/proc/can_inject,user,1))) + return + if(!reagents.total_volume) + return + if(L.reagents.total_volume >= L.reagents.maximum_volume) + return + L.visible_message("[user] uses the [src] on [L]!", \ + "[user] uses the [src] on [L]!") + else + if(!do_mob(user, L, extra_checks=CALLBACK(L, /mob/living/proc/can_inject,user,1))) + return + if(!reagents.total_volume) + return + if(L.reagents.total_volume >= L.reagents.maximum_volume) + return + log_attack("[user.name] ([user.ckey]) applied [src] to [L.name] ([L.ckey]), which had [contained] (INTENT: [uppertext(user.a_intent)]) (MODE: [src.mode])") + L.log_message("applied [src] to themselves ([contained]).", INDIVIDUAL_ATTACK_LOG) + var/fraction = min(amount_per_transfer_from_this/reagents.total_volume, 1) + reagents.reaction(L, PATCH, fraction) + reagents.trans_to(target, amount_per_transfer_from_this) + if(amount_per_transfer_from_this >= 15) + playsound(loc,'sound/items/hypospray_long.ogg',50, 1, -1) + if(amount_per_transfer_from_this < 15) + playsound(loc, pick('sound/items/hypospray.ogg','sound/items/hypospray2.ogg'), 50, 1, -1) + to_chat(user, "You spray [amount_per_transfer_from_this] units of the solution. The hypospray's cartridge now contains [reagents.total_volume] units.") + else + to_chat(user, "[src] doesn't work here!") + return + +/obj/item/reagent_containers/hypospray/mkii/AltClick(mob/living/user) + if(user) + if(user.incapacitated()) + return + else if(!contents) + to_chat(user, "This Hypo needs to be loaded first!") + return + else + for(var/obj/item/I in contents) + unload_hypo(I,user) + +/obj/item/reagent_containers/hypospray/mkii/verb/modes() + set name = "Change Application Method" + set category = "Object" + set src in usr + var/mob/M = usr + var/choice = alert(M, "Which application mode should this be? Current mode is: [mode ? "Spray" : "Inject"]", "", "Spray", "Cancel", "Inject") + switch(choice) + if("Cancel") + return + if("Inject") + mode = HYPO_INJECT + to_chat(M, "[src] is now set to inject contents on application.") + if("Spray") + mode = HYPO_SPRAY + to_chat(M, "[src] is now set to spray contents on application.") \ No newline at end of file diff --git a/code/citadel/hypovial.dm b/code/citadel/hypovial.dm new file mode 100644 index 0000000000..61c61ed6c3 --- /dev/null +++ b/code/citadel/hypovial.dm @@ -0,0 +1,105 @@ +/obj/item/reagent_containers/glass/bottle/vial + name = "hypospray vial" + desc = "This is a vial suitable for loading into mk II hyposprays." + icon = 'icons/obj/citadel/vial.dmi' + icon_state = "hypovial" + w_class = WEIGHT_CLASS_SMALL //Why would it be the same size as a beaker? + container_type = OPENCONTAINER + spillable = FALSE + resistance_flags = ACID_PROOF + var/comes_with = list() //Easy way of doing this. + volume = 10 + +/obj/item/reagent_containers/glass/bottle/vial/Initialize() + . = ..() + if(!icon_state) + icon_state = "hypovial" + update_icon() + +/obj/item/reagent_containers/glass/bottle/vial/on_reagent_change() + update_icon() + +/obj/item/reagent_containers/glass/bottle/vial/update_icon() + cut_overlays() + if(reagents.total_volume) + var/mutable_appearance/filling = mutable_appearance('icons/obj/citadel/vial.dmi', "[icon_state]10") + + var/percent = round((reagents.total_volume / volume) * 100) + switch(percent) + if(0 to 9) + filling.icon_state = "[icon_state]10" + if(10 to 29) + filling.icon_state = "[icon_state]25" + if(30 to 49) + filling.icon_state = "[icon_state]50" + if(50 to 69) + filling.icon_state = "[icon_state]75" + if(70 to INFINITY) + filling.icon_state = "[icon_state]100" + + filling.color = mix_color_from_reagents(reagents.reagent_list) + add_overlay(filling) + +/obj/item/reagent_containers/glass/bottle/vial/small + volume = 30 + +/obj/item/reagent_containers/glass/bottle/vial/large + name = "large hypospray vial" + desc = "This is a vial suitable for loading into the Chief Medical Officer's Hypospray mk II." + icon_state = "hypoviallarge" + volume = 60 + +/obj/item/reagent_containers/glass/bottle/vial/New() + ..() + for(var/R in comes_with) + reagents.add_reagent(R,comes_with[R]) + +/obj/item/reagent_containers/glass/bottle/vial/small/preloaded/bicaridine + name = "vial (bicaridine)" + icon_state = "hypovial-b" + comes_with = list("bicaridine" = 30) + +/obj/item/reagent_containers/glass/bottle/vial/small/preloaded/antitoxin + name = "vial (Anti-Tox)" + icon_state = "hypovial-a" + comes_with = list("antitoxin" = 30) + +/obj/item/reagent_containers/glass/bottle/vial/small/preloaded/kelotane + name = "vial (kelotane)" + icon_state = "hypovial-k" + comes_with = list("kelotane" = 30) + +/obj/item/reagent_containers/glass/bottle/vial/small/preloaded/dexalin + name = "vial (dexalin)" + icon_state = "hypovial-d" + comes_with = list("dexalin" = 30) + +/obj/item/reagent_containers/glass/bottle/vial/small/preloaded/tricordrazine + name = "vial (tricordrazine)" + icon_state = "hypovial" + comes_with = list("tricordrazine" = 30) + +/obj/item/reagent_containers/glass/bottle/vial/large/preloaded/CMO + name = "large vial (CMO Special)" + icon_state = "hypoviallarge-cmos" + comes_with = list("epinephrine" = 15, "kelotane" = 15, "charcoal" = 15, "bicaridine" = 15) + +/obj/item/reagent_containers/glass/bottle/vial/large/preloaded/bicaridine + name = "large vial (bicaridine)" + icon_state = "hypoviallarge-b" + comes_with = list("bicaridine" = 60) + +/obj/item/reagent_containers/glass/bottle/vial/large/preloaded/antitoxin + name = "large vial (Anti-Tox)" + icon_state = "hypoviallarge-a" + comes_with = list("antitoxin" = 60) + +/obj/item/reagent_containers/glass/bottle/vial/large/preloaded/kelotane + name = "large vial (kelotane)" + icon_state = "hypoviallarge-k" + comes_with = list("kelotane" = 60) + +/obj/item/reagent_containers/glass/bottle/vial/large/preloaded/dexalin + name = "large vial (dexalin)" + icon_state = "hypoviallarge-d" + comes_with = list("dexalin" = 60) diff --git a/icons/obj/citadel/hypospray.dmi b/icons/obj/citadel/hypospray.dmi new file mode 100644 index 0000000000..f5e89227c7 Binary files /dev/null and b/icons/obj/citadel/hypospray.dmi differ diff --git a/icons/obj/citadel/vial.dmi b/icons/obj/citadel/vial.dmi new file mode 100644 index 0000000000..23bceb93b9 Binary files /dev/null and b/icons/obj/citadel/vial.dmi differ diff --git a/sound/items/hypospray.ogg b/sound/items/hypospray.ogg new file mode 100644 index 0000000000..b70d3fd5b5 Binary files /dev/null and b/sound/items/hypospray.ogg differ diff --git a/sound/items/hypospray2.ogg b/sound/items/hypospray2.ogg new file mode 100644 index 0000000000..14835e9bb6 Binary files /dev/null and b/sound/items/hypospray2.ogg differ diff --git a/sound/items/hypospray_long.ogg b/sound/items/hypospray_long.ogg new file mode 100644 index 0000000000..d7da6c839f Binary files /dev/null and b/sound/items/hypospray_long.ogg differ diff --git a/tgstation.dme b/tgstation.dme index 5690081ec3..d230786d4c 100755 --- a/tgstation.dme +++ b/tgstation.dme @@ -197,6 +197,8 @@ #include "code\citadel\cit_uniforms.dm" #include "code\citadel\cit_vendors.dm" #include "code\citadel\dogborgstuff.dm" +#include "code\citadel\hypospraymkii.dm" +#include "code\citadel\hypovial.dm" #include "code\citadel\plasmacases.dm" #include "code\citadel\crew_objectives\cit_crewobjectives_cargo.dm" #include "code\citadel\crew_objectives\cit_crewobjectives_civilian.dm"