From f852d982358e938ee1c09af70541919162215936 Mon Sep 17 00:00:00 2001 From: VampyrBytes Date: Wed, 4 May 2016 15:52:34 +0100 Subject: [PATCH 001/275] Datum Emotes MK II --- code/__HELPERS/global_lists.dm | 8 + code/_globalvars/lists/mobs.dm | 1 + code/datums/Emote system/emote.dm | 368 ++++++++++++++++++ code/datums/Emote system/emote_handler.dm | 84 ++++ code/datums/Emote system/emotes.dm | 91 +++++ code/game/gamemodes/blob/overmind.dm | 2 + code/game/gamemodes/miniantags/borer/borer.dm | 2 + code/modules/mob/dead/observer/say.dm | 21 +- code/modules/mob/emote.dm | 8 +- .../mob/living/carbon/alien/humanoid/emote.dm | 4 +- .../mob/living/carbon/alien/larva/emote.dm | 4 +- code/modules/mob/living/carbon/brain/emote.dm | 4 +- code/modules/mob/living/carbon/human/emote.dm | 3 +- code/modules/mob/living/carbon/slime/emote.dm | 4 +- code/modules/mob/living/say.dm | 5 +- code/modules/mob/living/silicon/emote.dm | 4 +- code/modules/mob/living/silicon/pai/emote.dm | 2 + .../modules/mob/living/silicon/robot/emote.dm | 4 +- code/modules/mob/living/silicon/say.dm | 4 +- .../mob/living/simple_animal/bot/emote.dm | 4 +- .../living/simple_animal/friendly/diona.dm | 14 +- code/modules/mob/mob.dm | 1 + code/modules/mob/mob_defines.dm | 2 + code/modules/mob/say.dm | 16 +- paradise.dme | 3 + 25 files changed, 620 insertions(+), 43 deletions(-) create mode 100644 code/datums/Emote system/emote.dm create mode 100644 code/datums/Emote system/emote_handler.dm create mode 100644 code/datums/Emote system/emotes.dm diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm index 36bb824e642..a8c548bdd7a 100644 --- a/code/__HELPERS/global_lists.dm +++ b/code/__HELPERS/global_lists.dm @@ -46,6 +46,14 @@ if(S.flags & IS_WHITELISTED) whitelisted_species += S.name + paths = subtypesof(/datum/emote) + for(var/T in paths) + var/datum/emote/E = new T + if(istype(T, /datum/emote/custom)) + continue + emotes += E + + init_subtypes(/datum/table_recipe, table_recipes) return 1 diff --git a/code/_globalvars/lists/mobs.dm b/code/_globalvars/lists/mobs.dm index b6dc1c58b8f..e1e8b818356 100644 --- a/code/_globalvars/lists/mobs.dm +++ b/code/_globalvars/lists/mobs.dm @@ -4,6 +4,7 @@ var/global/list/all_languages[0] var/global/list/language_keys[0] // Table of say codes for all languages var/global/list/all_superheroes[0] var/global/list/all_nations[0] +var/global/list/emotes[0] var/global/list/whitelisted_species = list() var/list/clients = list() //list of all clients diff --git a/code/datums/Emote system/emote.dm b/code/datums/Emote system/emote.dm new file mode 100644 index 00000000000..62c3c6a14d8 --- /dev/null +++ b/code/datums/Emote system/emote.dm @@ -0,0 +1,368 @@ +/******************************************************************************************************* + Emotes +Remember, only 1 instance of each emote is made, and used by all mobs that can access it. As such, the +ONLY place you can safely set object variables during runtime is in New(). Otherwise override the emote. +The only exception to this is custom emotes, which are created as needed and only last until they are finished +VampyrBytes + +*******************************************************************************************************/ + +/datum/emote + var/name = "" + var/desc = "" + var/list/commands[0] // commands that trigger the emote. Set these in New() + var/text = "" + var/selfText = "" // the version of text that you should see - eg, if text is screams, you want scream here, as You screams is bad grammer + var/startText = "" // if you need to put something in before [user]. Should end with a space + var/mimeText = "" + var/sound // sound file + var/vol = 50 + var/audible = 0 + var/muzzledNoise = "" // if the emote is audible and you're muzzled, this is what type of noise you make (eg weak, loud). End with a space + var/restrained = "" // 1 if being restrained prevents this emote + var/cooldown = 0 + + var/canTarget = 0 // 1 if the emote accepts a target Emotes only recieve 1 parameter, so its either or with these 2 + var/takesParam = 0 // 1 if the emote uses a non target parameter + + var/targetMob = 0 // 0 if target can be any atom, 1 if it has to be a mob + var/targetText = "at" // what goes inbetween user and target + var/spanClass = "notice" + var/baseLevel = 1 + var/baseSet = 0 + + +/datum/emote/New() + var/pathString = "[type]" + var/count = 0 + for(var/i in 1 to lentext(pathString)) + var/char = copytext(pathString, i, i+1) + if(char == "/") + count++ + if(count == 4) + baseLevel = 0 + break + +/datum/emote/proc/doEmote(var/mob/user, var/param) + if(!istype(user)) + return 0 + if(cooldown) + if(handle_emote_CD(user)) + return 0 + + var/message = createMessage(user, param) + + if(canTarget && param) + param = getTarget(user, param) + + if(message) + message = addTarget(user, param, message) + message += "" + outputMessage(user, message) + + if(audible) + if(!(user.mind && user.mind.miming)) + playSound(user, vol) + + doAction(user, param) + return 1 + +// for things that the emote does that aren't text or sound based +/datum/emote/proc/doAction(var/mob/user, var/param) + return + +// returns the reason the user can't currently do the emote +/datum/emote/proc/prevented(var/mob/user) + if(user.stat == DEAD) + return "you are dead" + if(restrained && user.restrained()) + return "you are restrained" + +// return 1 if this emote can be used by this type of user +/datum/emote/proc/available(var/mob/user) + return + +/datum/emote/proc/createMessage(var/mob/user, var/param) + if(!text) + return + var/message + if(audible && user.mind && user.mind.miming) + message = mimeMessage(user) + else if(audible && user.is_muzzled()) + message = muzzleMessage(user) + else if(takesParam && param) + message = paramMessage(user, param) + else + message = "[startText + " "][user] [text]" + return message + +/datum/emote/proc/mimeMessage(var/mob/user) + if(!mimeText) + return + + var/message = "[startText + " "][user] [mimeText]" + return message + +/datum/emote/proc/muzzleMessage(var/mob/user) + var/message = "makes a [muzzledNoise + " "]noise" + return message + +// if the emote takes a non target parameter, set up and return the with parameter version in here +/datum/emote/proc/paramMessage(var/mob/user, var/param) + return + +/datum/emote/proc/addTarget(var/mob/user, var/target = "", var/message = "") + if(!canTarget) + return message + target = getTarget(user, target) + if(!target) + return message + if(ismob(target)) + message += " [targetText] [target]" + else + message += " [targetText] \the [target]" + return message + +/datum/emote/proc/getTarget(var/mob/user, var/target = "") + if(!target) + return + if(targetMob) + return getMobTarget(user, target) + else + return getAtomTarget(user, target) + +/datum/emote/proc/getMobTarget(var/mob/user, var/target = "") + for (var/mob/M in view(null, user)) + if (target == M.name) + return M + +/datum/emote/proc/getAtomTarget(var/mob/user, var/target = "") + if (target) + for (var/atom/A as mob|obj|turf in view(null, user)) + if (target == A.name) + return A + +/datum/emote/proc/outputMessage(var/mob/user, var/message = "") + if(!message) + return + log_emote("[user.name]/[user.key] : [message]") + if(audible) + audible_message(message, user) + else + visible_message(message, user) + sendToDead(message) + +// What you should see when you perform the emote +/datum/emote/proc/createSelfMessage(var/mob/user, var/message = "") + var/selfMessage + if(startText) + selfMessage = replacetext(message, "[user]", "you") + else + selfMessage = replacetext(message, "[user]", "You") + if(selfText) + selfMessage = replacetext(selfMessage, text, selfText) + return selfMessage + +/datum/emote/proc/visible_message(var/message = "", var/mob/user) + var/selfMessage = createSelfMessage(user, message) + for(var/mob/M in viewers(user)) + if(M.see_invisible < user.invisibility) + continue //can't view the invisible + var/msg = message + if(selfMessage && M==user) + msg = selfMessage + if(M.sdisabilities & BLIND || M.blinded || M.paralysis) + if(M.sdisabilities & DEAF || M.ear_deaf) + continue + msg = createBlindMessage(message, user) + if(msg) + outputAudibleMessage(msg, M, user, 1) + else + outputVisibleMessage(msg, M, user) + +/datum/emote/proc/audible_message(var/message = "", var/mob/user) + var/selfMessage = createSelfMessage(user, message) + testing(selfMessage) + for(var/mob/M in get_mobs_in_view(7, user)) + var/msg = message + if(selfMessage && M==src) + msg = selfMessage + if(M.sdisabilities & DEAF || M.ear_deaf) + if(M.sdisabilities & BLIND || M.blinded || M.paralysis) + continue + msg = createDeafMessage(user, message) + if(msg) + outputVisibleMessage(msg, M, user, 1) + else + outputAudibleMessage(msg, M, user) + +/datum/emote/proc/outputVisibleMessage(var/message, var/mob/recipient, var/mob/user, var/retest = 0) + if(retest) + if(!user) + return + var/found = 0 + for(var/mob/M in viewers(user)) + if(M == recipient) + found = 1 + if(recipient.see_invisible < user.invisibility) + return + break + if(!found) + return + if(recipient.sdisabilities & DEAF || recipient.ear_deaf) + var/msg = createDeafMessage(user, message) + if(msg) + message = msg + if(recipient.stat == UNCONSCIOUS || (recipient.sleeping > 0 && recipient.stat != 2)) + to_chat(recipient, "... You can almost hear someone talking ...") + else + to_chat(recipient, message) + +/datum/emote/proc/outputAudibleMessage(var/message = "", var/mob/recipient, var/mob/user, var/retest = 0) + if(retest) + if(!user) + return + var/found = 0 + for(var/mob/M in get_mobs_in_view(7, user)) + if(M == recipient) + found = 1 + break + if(!found) + return + if(recipient.sdisabilities & BLIND || recipient.blinded || recipient.paralysis) + var/msg = createBlindMessage(user, message) + if(msg) + message = msg + if(recipient.stat == UNCONSCIOUS || (recipient.sleeping > 0 && recipient.stat != 2)) + to_chat(recipient, "... You can almost hear someone talking ...") + else + to_chat(recipient, message) + +// set up different messages for blind people here. Empty will mean no message for +//non-audible emotes and the standard message for audible ones +/datum/emote/proc/createBlindMessage(var/mob/user, var/message) + return + +// set up different messages for deaf people here. Empty will mean no message for +// audible emotes and standard for non-audible ones +/datum/emote/proc/createDeafMessage(var/mob/user, var/message) + return + +/datum/emote/proc/sendToDead(message) + for(var/mob/M in dead_mob_list) + if(!M.client || istype(M, /mob/new_player)) + continue //skip monkeys, leavers and new players + if(M.stat == DEAD && (M.client.prefs.toggles & CHAT_GHOSTSIGHT) && !(M in viewers(src,null))) + M.show_message(message) + +/datum/emote/proc/playSound(var/mob/user) + if(sound) + playsound(user, sound, vol) + +//Emote Cooldown System (it's so simple!) +/datum/emote/proc/handle_emote_CD(var/mob/user) + if(user.emote_cd == 2) return 1 // Cooldown emotes were disabled by an admin, prevent use + if(user.emote_cd == 1) return 1 // Already on CD, prevent use + + user.emote_cd = 1 // Starting cooldown + spawn(cooldown) + if(user.emote_cd == 2) return 1 // Don't reset if cooldown emotes were disabled by an admin during the cooldown + user.emote_cd = 0 // Cooldown complete, ready for more! + + return 0 // Proceed with emote +//--FalseIncarnate + +/datum/emote/proc/addVerbs(var/mob/user) + if(!istype(user)) + return + new /obj/emoteVerb(user, src, commands) + +// Dummy object to allow access to the verb, needed as verbs require a valid loc for setting the src +/obj/emoteVerb + var/datum/emote/emote + var/mob/owner + +/obj/emoteVerb/New(var/mob/user, var/datum/emote/toAccess) + if(!(istype(user))) + return + if(!(istype(toAccess))) + return + if(!toAccess.commands) + return + owner = user + emote = toAccess + name = emote.name + loc = user + user.verbs += new/obj/emoteVerb/proc/runEmote(src, emote.commands[1], emote.desc) + +/obj/emoteVerb/proc/runEmote() + set src = usr.contents + set category = "Emotes" + usr.emoteHandler.runEmote(emote.commands[1]) + +/obj/emoteVerb/Destroy() + owner.verbs -= new/obj/emoteVerb/proc/runEmote(src, emote.commands[1]) + ..() + +/obj/emoteVerb/custom + +obj/emoteVerb/custom/New(var/mob/user) + if(!(istype(user))) + return + owner = user + name = "custom" + user.verbs += new/obj/emoteVerb/proc/runEmote(src, "custom", "Make your own emote") + +/obj/emoteVerb/custom/runEmote(message as text, audible as num) + set src = usr.contents + set category = "Emotes" + usr.emoteHandler.runEmote("me", null, message, audible) + +/************************************************************************************************************************** + Custom Emotes +As these are made as needed, they aren't searched for the appropriate one. As such, you need to specify conditions for which +one is used in /datum/emote_handler/customEmote(). As the checks for the type are done there, you chouldn't need to override +available, but if you do, make sure you return ..() so the check for use_me is still done - VampyrBytes + +***************************************************************************************************************************/ + +/datum/emote/custom + name = "Custom emote" + baseLevel = 0 + +/datum/emote/custom/New(var/mob/user, var/message = "", var/isAudible) + if(!message) + message = getMessage(user) + text = message + audible = isAudible + if(user.mind && user.mind.miming) + audible = 0 + +/datum/emote/custom/proc/getMessage(var/mob/user) + var/input = sanitize(copytext(input(user,"Choose an emote to display.") as text|null,1,MAX_MESSAGE_LEN)) + return input + +/datum/emote/custom/available(var/mob/user) + if(user.use_me) + return 1 + +/datum/emote/custom/ghost + name = "Ghost emote" + startText = "DEAD: " + spanClass = "game deadsay" + +/datum/emote/custom/ghost/prevented(var/mob/user) + if(user.client.prefs.muted & MUTE_DEADCHAT) + return "you are muted from deadchat" + if(!(user.client.prefs.toggles & CHAT_DEAD)) + return "you have deadchat muted" + if(!user.client.holder) + if(!config.dsay_allowed) + return "deadchat is globally muted" + +/datum/emote/custom/ghost/outputMessage(var/mob/user, var/message = "") + if(!message) + return + log_emote("Ghost/[user.key] : [message]") + sendToDead(message) + diff --git a/code/datums/Emote system/emote_handler.dm b/code/datums/Emote system/emote_handler.dm new file mode 100644 index 00000000000..afcb4f0253b --- /dev/null +++ b/code/datums/Emote system/emote_handler.dm @@ -0,0 +1,84 @@ +/datum/emoteHandler + var/mob/owner + var/list/commands + +/datum/emoteHandler/New(var/mob/user) + owner = user + setupCommands() + +/datum/emoteHandler/proc/setupCommands(var/reset = 0) + if(reset) + deleteEmoteVerbs() + commands = new/list() + for(var/e in emotes) + var/datum/emote/emote = e + if(emote.baseLevel) + var/datum/emote/found = searchTree(emote) + if(found) + for(var/command in found.commands) + commands[command] = found + found.addVerbs(owner) + +/datum/emoteHandler/proc/deleteEmoteVerbs() + for(var/obj/emoteVerb/E in owner.contents) + qdel(E) + +/datum/emoteHandler/proc/runEmote(var/command = "", var/param = "", var/message = "", var/audible = 0) // message and audible only used in custom emotes + if(!command) + return 0 + if(command == "help") + showCommands() + return 1 + + var/datum/emote/emote + //testing("[command]") + if(command == "me") + emote = customEmote(message, audible) + if(!emote) + return 0 + if(!commands[command] && !(command == "me")) + to_chat(owner, "Unknown emote, please check *help for emotes available to your character") + return 0 + if(!emote) + emote = commands[command] + if(!emote.available(owner)) // something's changed, remake the commands list, then try again to see if they've got a different version + setupCommands() + return runEmote(command, param, message) + + var/prevented = emote.prevented(owner) + if(prevented) + to_chat(owner, "You can't do that because [prevented]!") + return 0 + return emote.doEmote(owner) + +/datum/emoteHandler/proc/showCommands() + var/emoteList = "Available emotes are " + var/commandAdded = 0 + for (var/c in commands) + if(commandAdded) + emoteList += ", " + emoteList += c + commandAdded = 1 + + to_chat(owner, emoteList) + +// recursive search of subtypes - starts checking at the lowest level and breaks out if it finds one that can be done +/datum/emoteHandler/proc/searchTree(var/datum/emote/emote) + var/list/subtypes = subtypesof(emote.type) + var/datum/emote/found + for(var/t in subtypes) + var/datum/emote/em = new t // not keen on this, but it's this or loop through the emotes list and the + found = searchTree(em) // thought of a nested for loop in a recursive proc makes me want to *cry - VB + if(found) + return (found) + if(emote.available(owner)) + return emote + +/datum/emoteHandler/proc/customEmote(var/custom, var/audible) + var/datum/emote/custom/emote + if(isobserver(owner)) + emote = new /datum/emote/custom/ghost + else + emote = new /datum/emote/custom(owner, custom, audible) + if (emote.available(owner)) + return emote diff --git a/code/datums/Emote system/emotes.dm b/code/datums/Emote system/emotes.dm new file mode 100644 index 00000000000..5ab3b76b56d --- /dev/null +++ b/code/datums/Emote system/emotes.dm @@ -0,0 +1,91 @@ +/************************************************************************************ + Emotes +New() must call ..() to set the baseLevel for the emoteHandler search. As the commands +are set in New(), this means that the emote will pick up all the commands from the emotes +above it. If you don't want this, make the call to ..() then use commands = new /list() +*************************************************************************************/ + +/datum/emote/scream + name = "scream" + text = "screams!" + selfText = "scream!" + audible = 1 + mimeText = "acts out a scream" + muzzledNoise = "very loud " + cooldown = 50 + vol = 80 + +/datum/emote/scream/New() + ..() + commands += "scream" + commands += "screams" + +/datum/emote/scream/available(var/mob/user) + if(isliving(user)) + return 1 + +/datum/emote/scream/machine + name = "machine scream" + sound = 'sound/goonstation/voice/robot_scream.ogg' + +/datum/emote/scream/machine/available(var/mob/user) + if(issilicon(user)) + return 1 + if(istype(user, /mob/living/simple_animal/bot)) + return 1 + +/datum/emote/scream/human + name = "human scream" + +/datum/emote/scream/human/available(var/mob/user) + if(ishuman(user)) + return 1 + +/datum/emote/scream/human/playSound(var/mob/user) + var/mob/living/carbon/human/H = user + if(H.gender == FEMALE) + playsound(H, "[H.species.female_scream_sound]", vol, 1, 0, pitch = H.get_age_pitch()) + else + playsound(H, "[H.species.male_scream_sound]", vol, 1, 0, pitch = H.get_age_pitch()) + +/datum/emote/fart + name = "fart" + cooldown = 50 + +/datum/emote/fart/New() + ..() + commands += "fart" + commands += "farts" + +/datum/emote/fart/available(var/mob/user) + if(ishuman(user)) + return 1 + +/datum/emote/fart/doAction(var/mob/user) + // todo change this to work with superfarts + if(TOXIC_FARTS in user.mutations) + for(var/mob/M in range(get_turf(user),2)) + if (M.internal != null && M.wear_mask && (M.wear_mask.flags & AIRTIGHT)) + continue + if (M == user) + continue + M.reagents.add_reagent("space_drugs",rand(1,10)) + + if(locate(/obj/item/weapon/storage/bible) in get_turf(user)) + to_chat(viewers(user), "[user] farts on the Bible!") + to_chat(viewers(user), "A mysterious force smites [user]!") + var/datum/effect/system/spark_spread/s = new /datum/effect/system/spark_spread + s.set_up(3, 1, user) + s.start() + user.gib() + +/datum/emote/fart/createMessage(var/mob/user) + var/message + if(TOXIC_FARTS in user.mutations) + message = "[user] unleashes a [pick("horrible","terrible","foul","disgusting","awful")] fart." + else + message = "[user] [pick("passes wind","farts")]." + return message + + + diff --git a/code/game/gamemodes/blob/overmind.dm b/code/game/gamemodes/blob/overmind.dm index 1238adebcf5..8b3d599f50a 100644 --- a/code/game/gamemodes/blob/overmind.dm +++ b/code/game/gamemodes/blob/overmind.dm @@ -103,8 +103,10 @@ if(isovermind(M) || isobserver(M)) M.show_message(rendered, 2) +/* /mob/camera/blob/emote(var/act,var/m_type=1,var/message = null) return +*/ /mob/camera/blob/blob_act() return diff --git a/code/game/gamemodes/miniantags/borer/borer.dm b/code/game/gamemodes/miniantags/borer/borer.dm index 9e25a50a811..401a1731b7c 100644 --- a/code/game/gamemodes/miniantags/borer/borer.dm +++ b/code/game/gamemodes/miniantags/borer/borer.dm @@ -33,8 +33,10 @@ return 0 return B.host.say_understands(other, speaking) +/* /mob/living/captive_brain/emote(var/message) return +*/ /mob/living/captive_brain/resist_borer() var/mob/living/simple_animal/borer/B = loc diff --git a/code/modules/mob/dead/observer/say.dm b/code/modules/mob/dead/observer/say.dm index 7f63a8f1261..022fbb85a5d 100644 --- a/code/modules/mob/dead/observer/say.dm +++ b/code/modules/mob/dead/observer/say.dm @@ -16,29 +16,14 @@ . = src.say_dead(message) - +/* /mob/dead/observer/emote(var/act, var/type, var/message) message = sanitize(copytext(message, 1, MAX_MESSAGE_LEN)) - if(!message) - return + if(act = "me") + return ..() - if(act != "me") - return - log_emote("Ghost/[src.key] : [message]") - - if(src.client) - if(src.client.prefs.muted & MUTE_DEADCHAT) - to_chat(src, "\red You cannot emote in deadchat (muted).") - return - - if(src.client.handle_spam_prevention(message, MUTE_DEADCHAT)) - return - - . = src.emote_dead(message) - -/* for (var/mob/M in hearers(null, null)) if (!M.stat) if(M.job == "Chaplain") diff --git a/code/modules/mob/emote.dm b/code/modules/mob/emote.dm index 8d03cdd969a..1c82e1c2755 100644 --- a/code/modules/mob/emote.dm +++ b/code/modules/mob/emote.dm @@ -1,6 +1,7 @@ #define EMOTE_COOLDOWN 20 //Time in deciseconds that the cooldown lasts //Emote Cooldown System (it's so simple!) +/* /mob/proc/handle_emote_CD(cooldown = EMOTE_COOLDOWN) if(emote_cd == 2) return 1 // Cooldown emotes were disabled by an admin, prevent use if(src.emote_cd == 1) return 1 // Already on CD, prevent use @@ -12,11 +13,13 @@ return 0 // Proceed with emote //--FalseIncarnate - +*/ // All mobs should have custom emote, really.. /mob/proc/custom_emote(var/m_type=1,var/message = null) - if(stat || !use_me && usr == src) + emoteHandler.runEmote("me", null, message, m_type) + +/* if(stat || !use_me && usr == src) to_chat(usr, "You are unable to emote.") return @@ -122,3 +125,4 @@ else if(M.stat == DEAD && (M.client.prefs.toggles & CHAT_DEAD)) // Show the emote to regular ghosts with deadchat toggled on M.show_message(message, 2) +*/ \ No newline at end of file diff --git a/code/modules/mob/living/carbon/alien/humanoid/emote.dm b/code/modules/mob/living/carbon/alien/humanoid/emote.dm index caf3cc4e5ad..79445eec13a 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/emote.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/emote.dm @@ -1,3 +1,4 @@ +/* /mob/living/carbon/alien/humanoid/emote(var/act,var/m_type=1,var/message = null) var/param = null if (findtext(act, "-", 1, null)) @@ -112,4 +113,5 @@ playsound(src.loc, 'sound/voice/hiss5.ogg', 40, 1, 1) if (act == "deathgasp") playsound(src.loc, 'sound/voice/hiss6.ogg', 80, 1, 1) - ..(act, m_type, message) \ No newline at end of file + ..(act, m_type, message) +*/ \ No newline at end of file diff --git a/code/modules/mob/living/carbon/alien/larva/emote.dm b/code/modules/mob/living/carbon/alien/larva/emote.dm index b617a1fc883..72be5c029fa 100644 --- a/code/modules/mob/living/carbon/alien/larva/emote.dm +++ b/code/modules/mob/living/carbon/alien/larva/emote.dm @@ -1,3 +1,4 @@ +/* /mob/living/carbon/alien/larva/emote(var/act,var/m_type=1,var/message = null) var/param = null if (findtext(act, "-", 1, null)) @@ -123,4 +124,5 @@ for(var/mob/O in hearers(src, null)) O.show_message(message, m_type) //Foreach goto(746) - return \ No newline at end of file + return +*/ \ No newline at end of file diff --git a/code/modules/mob/living/carbon/brain/emote.dm b/code/modules/mob/living/carbon/brain/emote.dm index fd2a4879c9d..b173ac63460 100644 --- a/code/modules/mob/living/carbon/brain/emote.dm +++ b/code/modules/mob/living/carbon/brain/emote.dm @@ -1,3 +1,4 @@ +/* /mob/living/carbon/brain/emote(var/act,var/m_type=1,var/message = null) if(!(container && istype(container, /obj/item/device/mmi)))//No MMI, no emotes return @@ -47,4 +48,5 @@ to_chat(src, "alarm, alert, notice, flash,blink, whistle, beep, boop") if(message && !stat) - ..(act, m_type, message) \ No newline at end of file + ..(act, m_type, message) +*/ \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm index 657d40983de..96c05ee627a 100644 --- a/code/modules/mob/living/carbon/human/emote.dm +++ b/code/modules/mob/living/carbon/human/emote.dm @@ -1,3 +1,4 @@ +/* /mob/living/carbon/human/emote(var/act,var/m_type=1,var/message = null,var/force) if (stat == DEAD) @@ -836,7 +837,7 @@ visible_message(message) if(2) audible_message(message) - +*/ /mob/living/carbon/human/verb/pose() set name = "Set Pose" set desc = "Sets a description which will be shown when someone examines you." diff --git a/code/modules/mob/living/carbon/slime/emote.dm b/code/modules/mob/living/carbon/slime/emote.dm index 64838a1704d..dfb0b9cada8 100644 --- a/code/modules/mob/living/carbon/slime/emote.dm +++ b/code/modules/mob/living/carbon/slime/emote.dm @@ -1,3 +1,4 @@ +/* /mob/living/carbon/slime/emote(var/act, var/m_type=1, var/message = null) if (findtext(act, "-", 1, null)) var/t1 = findtext(act, "-", 1, null) @@ -71,4 +72,5 @@ for(var/mob/O in hearers(src, null)) O.show_message(message, m_type) //Foreach goto(746) - return \ No newline at end of file + return +*/ \ No newline at end of file diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index 3260015f64b..28f30b58714 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -287,7 +287,8 @@ proc/get_radio_key_from_channel(var/channel) /mob/living/emote(var/act, var/type, var/message) //emote code is terrible, this is so that anything that isn't if(stat) return 0 //already snowflaked to shit can call the parent and handle emoting sanely - + return ..() +/* if(..(act, type, message)) return 1 @@ -312,7 +313,7 @@ proc/get_radio_key_from_channel(var/channel) else //everything else failed, emote is probably invalid if(act == "help") return //except help, because help is handled individually to_chat(src, "\blue Unusable emote '[act]'. Say *help for a list.") - +*/ /mob/living/whisper(message as text) message = trim_strip_html_properly(message) diff --git a/code/modules/mob/living/silicon/emote.dm b/code/modules/mob/living/silicon/emote.dm index c5c3603466e..0b9d0a7bb0f 100644 --- a/code/modules/mob/living/silicon/emote.dm +++ b/code/modules/mob/living/silicon/emote.dm @@ -1,3 +1,4 @@ +/* /mob/living/silicon/emote(var/act, var/m_type=1, var/message = null) var/param = null if (findtext(act, "-", 1, null)) @@ -117,4 +118,5 @@ if("help") to_chat(src, "yes, no, beep, ping, buzz") - ..(act, m_type, message) \ No newline at end of file + ..(act, m_type, message) +*/ \ No newline at end of file diff --git a/code/modules/mob/living/silicon/pai/emote.dm b/code/modules/mob/living/silicon/pai/emote.dm index a418c0972d5..40c32f9fcc3 100644 --- a/code/modules/mob/living/silicon/pai/emote.dm +++ b/code/modules/mob/living/silicon/pai/emote.dm @@ -1,2 +1,4 @@ +/* // what the hell was the point of this file! /mob/living/silicon/pai/emote(var/act, var/m_type=1, var/message = null) ..(act, m_type, message) +*/ \ No newline at end of file diff --git a/code/modules/mob/living/silicon/robot/emote.dm b/code/modules/mob/living/silicon/robot/emote.dm index 30273aec02c..3d438296ec3 100644 --- a/code/modules/mob/living/silicon/robot/emote.dm +++ b/code/modules/mob/living/silicon/robot/emote.dm @@ -1,3 +1,4 @@ +/* /mob/living/silicon/robot/emote(var/act, var/m_type=1, var/message = null) var/param = null if (findtext(act, "-", 1, null)) @@ -159,4 +160,5 @@ if ("help") to_chat(src, "salute, bow-(none)/mob, clap, flap, aflap, twitch, twitches, nod, deathgasp, glare-(none)/mob, stare-(none)/mob, look,\n law, halt") - ..(act, m_type, message) \ No newline at end of file + ..(act, m_type, message) +*/ \ No newline at end of file diff --git a/code/modules/mob/living/silicon/say.dm b/code/modules/mob/living/silicon/say.dm index a546e164901..be3b778cb3c 100644 --- a/code/modules/mob/living/silicon/say.dm +++ b/code/modules/mob/living/silicon/say.dm @@ -129,11 +129,13 @@ return 1 /mob/living/silicon/ai/emote(var/act, var/type, var/message) +/* var/obj/machinery/hologram/holopad/T = src.holo if(T && T.hologram && T.master == src) //Is the AI using a holopad? src.holopad_emote(message) else //Emote normally, then. - ..() +*/ + ..() #undef IS_AI #undef IS_ROBOT diff --git a/code/modules/mob/living/simple_animal/bot/emote.dm b/code/modules/mob/living/simple_animal/bot/emote.dm index 4054e520ecf..ba8b1d86daf 100644 --- a/code/modules/mob/living/simple_animal/bot/emote.dm +++ b/code/modules/mob/living/simple_animal/bot/emote.dm @@ -1,3 +1,4 @@ +/* /mob/living/simple_animal/bot/emote(var/act, var/m_type=1, var/message = null) var/param = null if (findtext(act, "-", 1, null)) @@ -116,4 +117,5 @@ playsound(src.loc, 'sound/goonstation/voice/robot_scream.ogg', 80, 0) m_type = 2 - ..(act, m_type, message) \ No newline at end of file + ..(act, m_type, message) +*/ \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/friendly/diona.dm b/code/modules/mob/living/simple_animal/friendly/diona.dm index 4606558d271..7a8a682e7af 100644 --- a/code/modules/mob/living/simple_animal/friendly/diona.dm +++ b/code/modules/mob/living/simple_animal/friendly/diona.dm @@ -250,18 +250,19 @@ return /mob/living/simple_animal/diona/emote(var/act, var/m_type=1, var/message = null) - if(stat) + if(stat) return - + ..() + /* var/on_CD = 0 switch(act) if("chirp") - on_CD = handle_emote_CD() + on_CD = handle_emote_CD() else - on_CD = 0 + on_CD = 0 if(on_CD == 1) - return + return switch(act) //IMPORTANT: Emotes MUST NOT CONFLICT anywhere along the chain. if("chirp") @@ -269,4 +270,5 @@ m_type = 2 //audible playsound(src, 'sound/misc/nymphchirp.ogg', 40, 1, 1) - ..(act, m_type, message) \ No newline at end of file + ..(act, m_type, message) + */ \ No newline at end of file diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 658959001a1..36c7d172488 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -21,6 +21,7 @@ else living_mob_list += src prepare_huds() + emoteHandler = new /datum/emoteHandler(src) ..() /atom/proc/prepare_huds() diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 8b4b39e006a..5151d9adab9 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -232,3 +232,5 @@ var/datum/vision_override/vision_type = null //Vision override datum. var/list/permanent_huds = list() + + var/datum/emoteHandler/emoteHandler \ No newline at end of file diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm index e5b12ed022b..43248d54db4 100644 --- a/code/modules/mob/say.dm +++ b/code/modules/mob/say.dm @@ -34,10 +34,7 @@ message = strip_html_properly(message) set_typing_indicator(0) - if(use_me) - custom_emote(usr.emote_type, message) - else - usr.emote(message) + emoteHandler.runEmote("me", null, message) /mob/proc/say_dead(var/message) if(!src.client.holder) @@ -98,8 +95,15 @@ /mob/proc/emote(var/act, var/type, var/message) - if(act == "me") - return custom_emote(type, message) + var/param + if (findtext(act, "-", 1, null)) + var/t1 = findtext(act, "-", 1, null) + param = copytext(act, t1 + 1, length(act) + 1) + act = copytext(act, 1, t1) + + emoteHandler.runEmote(act, param, message, type) + + /mob/proc/get_ear() // returns an atom representing a location on the map from which this diff --git a/paradise.dme b/paradise.dme index 3a5da1134eb..b8e3c3dd148 100644 --- a/paradise.dme +++ b/paradise.dme @@ -258,6 +258,9 @@ #include "code\datums\diseases\advance\symptoms\weakness.dm" #include "code\datums\diseases\advance\symptoms\weight.dm" #include "code\datums\diseases\advance\symptoms\youth.dm" +#include "code\datums\Emote system\emote.dm" +#include "code\datums\Emote system\emote_handler.dm" +#include "code\datums\Emote system\emotes.dm" #include "code\datums\helper_datums\construction_datum.dm" #include "code\datums\helper_datums\events.dm" #include "code\datums\helper_datums\global_iterator.dm" From 974d09da59a9ed8d50dac838c3060b31ee674571 Mon Sep 17 00:00:00 2001 From: VampyrBytes Date: Thu, 5 May 2016 17:40:08 +0100 Subject: [PATCH 002/275] Params Adds support for params. A few other little tweaks too --- .../{Emote system => Emote_system}/emote.dm | 137 +++++++++++------- .../emote_handler.dm | 5 +- .../{Emote system => Emote_system}/emotes.dm | 34 +++++ code/modules/mob/say.dm | 9 +- paradise.dme | 6 +- 5 files changed, 129 insertions(+), 62 deletions(-) rename code/datums/{Emote system => Emote_system}/emote.dm (81%) rename code/datums/{Emote system => Emote_system}/emote_handler.dm (92%) rename code/datums/{Emote system => Emote_system}/emotes.dm (78%) diff --git a/code/datums/Emote system/emote.dm b/code/datums/Emote_system/emote.dm similarity index 81% rename from code/datums/Emote system/emote.dm rename to code/datums/Emote_system/emote.dm index 62c3c6a14d8..f0a7e6ddb1b 100644 --- a/code/datums/Emote system/emote.dm +++ b/code/datums/Emote_system/emote.dm @@ -22,11 +22,11 @@ VampyrBytes var/restrained = "" // 1 if being restrained prevents this emote var/cooldown = 0 - var/canTarget = 0 // 1 if the emote accepts a target Emotes only recieve 1 parameter, so its either or with these 2 - var/takesParam = 0 // 1 if the emote uses a non target parameter - - var/targetMob = 0 // 0 if target can be any atom, 1 if it has to be a mob + var/canTarget = 0 // 1 if the emote accepts a target + var/targetMob = 0 // 0 if target can be any atom, 1 if it has to be a mob, var/targetText = "at" // what goes inbetween user and target + var/takesNumber = 0 // 1 if the emote uses a number parameter + var/spanClass = "notice" var/baseLevel = 1 var/baseSet = 0 @@ -43,20 +43,31 @@ VampyrBytes baseLevel = 0 break -/datum/emote/proc/doEmote(var/mob/user, var/param) +/datum/emote/proc/doEmote(var/mob/user) if(!istype(user)) return 0 if(cooldown) if(handle_emote_CD(user)) return 0 - var/message = createMessage(user, param) + var/number + var/target - if(canTarget && param) - param = getTarget(user, param) + if(takesNumber) + number = getNumber(user) + + if(canTarget) + target = getTarget(user) + + if(number == "failed" || target == "failed") // If you need to test the input, override the appopriate get proc, call the parent, + return // then test it. If it fails, tell the user why, then return "failed" to halt the emote + + var/message = createMessage(user, number) + + if(canTarget && message) + message = addTarget(user, target, message) if(message) - message = addTarget(user, param, message) message += "" outputMessage(user, message) @@ -64,13 +75,30 @@ VampyrBytes if(!(user.mind && user.mind.miming)) playSound(user, vol) - doAction(user, param) + doAction(user, target, number) return 1 // for things that the emote does that aren't text or sound based -/datum/emote/proc/doAction(var/mob/user, var/param) +/datum/emote/proc/doAction(var/mob/user, var/atom/target, var/number) return +/datum/emote/proc/getNumber(var/mob/user) + var/number = input("How many") as null|num + return number + +/datum/emote/proc/getTarget(var/mob/user) + if(targetMob) + return getMobTarget() + return getAtomTarget() + +/datum/emote/proc/getMobTarget() + var/mob/target = input("Select target", "Target Mob") as null|mob in view() + return target + +/datum/emote/proc/getAtomTarget() + var/atom/target = input("Select target", "Target") as null|mob|obj|turf in oview() + return target + // returns the reason the user can't currently do the emote /datum/emote/proc/prevented(var/mob/user) if(user.stat == DEAD) @@ -82,39 +110,58 @@ VampyrBytes /datum/emote/proc/available(var/mob/user) return -/datum/emote/proc/createMessage(var/mob/user, var/param) +/datum/emote/proc/createMessage(var/mob/user, var/number) if(!text) return var/message - if(audible && user.mind && user.mind.miming) + if(takesNumber && number) + message = getParamMessage(user, number) + else if(audible && user.mind && user.mind.miming) message = mimeMessage(user) else if(audible && user.is_muzzled()) message = muzzleMessage(user) - else if(takesParam && param) - message = paramMessage(user, param) else - message = "[startText + " "][user] [text]" + message = "[user] [text]" + if(startText) + message = "[startText] [message]" + message = "[message]" return message /datum/emote/proc/mimeMessage(var/mob/user) if(!mimeText) return - var/message = "[startText + " "][user] [mimeText]" + var/message = "[user] [mimeText]" return message /datum/emote/proc/muzzleMessage(var/mob/user) - var/message = "makes a [muzzledNoise + " "]noise" + var/message = "[user] makes a " + if(muzzledNoise) + message += "[muzzledNoise] " + message += "noise" + return message + +/datum/emote/proc/getParamMessage(var/mob/user, var/param) + var/message + if(audible && user.mind && user.mind.miming) + message = paramMimeMessage(user, param) + else if (audible && user.is_muzzled()) + message = muzzleMessage(user) + else + message = paramMessage(user, param) return message // if the emote takes a non target parameter, set up and return the with parameter version in here /datum/emote/proc/paramMessage(var/mob/user, var/param) return -/datum/emote/proc/addTarget(var/mob/user, var/target = "", var/message = "") +// as above, but for mimes trying to do audible messages +/datum/emote/proc/paramMimeMessage(var/mob/user, var/param) + return + +/datum/emote/proc/addTarget(var/mob/user, var/atom/target, var/message = "") if(!canTarget) return message - target = getTarget(user, target) if(!target) return message if(ismob(target)) @@ -123,35 +170,6 @@ VampyrBytes message += " [targetText] \the [target]" return message -/datum/emote/proc/getTarget(var/mob/user, var/target = "") - if(!target) - return - if(targetMob) - return getMobTarget(user, target) - else - return getAtomTarget(user, target) - -/datum/emote/proc/getMobTarget(var/mob/user, var/target = "") - for (var/mob/M in view(null, user)) - if (target == M.name) - return M - -/datum/emote/proc/getAtomTarget(var/mob/user, var/target = "") - if (target) - for (var/atom/A as mob|obj|turf in view(null, user)) - if (target == A.name) - return A - -/datum/emote/proc/outputMessage(var/mob/user, var/message = "") - if(!message) - return - log_emote("[user.name]/[user.key] : [message]") - if(audible) - audible_message(message, user) - else - visible_message(message, user) - sendToDead(message) - // What you should see when you perform the emote /datum/emote/proc/createSelfMessage(var/mob/user, var/message = "") var/selfMessage @@ -163,6 +181,16 @@ VampyrBytes selfMessage = replacetext(selfMessage, text, selfText) return selfMessage +/datum/emote/proc/outputMessage(var/mob/user, var/message = "") + if(!message) + return + log_emote("[user.name]/[user.key] : [message]") + if(audible) + audible_message(message, user) + else + visible_message(message, user) + sendToDead(message) + /datum/emote/proc/visible_message(var/message = "", var/mob/user) var/selfMessage = createSelfMessage(user, message) for(var/mob/M in viewers(user)) @@ -182,7 +210,6 @@ VampyrBytes /datum/emote/proc/audible_message(var/message = "", var/mob/user) var/selfMessage = createSelfMessage(user, message) - testing(selfMessage) for(var/mob/M in get_mobs_in_view(7, user)) var/msg = message if(selfMessage && M==src) @@ -272,6 +299,11 @@ VampyrBytes return 0 // Proceed with emote //--FalseIncarnate + +/****************************************************************************************** + Emote Verbs +******************************************************************************************/ + /datum/emote/proc/addVerbs(var/mob/user) if(!istype(user)) return @@ -318,6 +350,7 @@ obj/emoteVerb/custom/New(var/mob/user) set category = "Emotes" usr.emoteHandler.runEmote("me", null, message, audible) + /************************************************************************************************************************** Custom Emotes As these are made as needed, they aren't searched for the appropriate one. As such, you need to specify conditions for which @@ -346,6 +379,10 @@ available, but if you do, make sure you return ..() so the check for use_me is s if(user.use_me) return 1 +// Yeah, no +/datum/emote/custom/createSelfMessage(var/mob/user, var/message = "") + return message + /datum/emote/custom/ghost name = "Ghost emote" startText = "DEAD: " diff --git a/code/datums/Emote system/emote_handler.dm b/code/datums/Emote_system/emote_handler.dm similarity index 92% rename from code/datums/Emote system/emote_handler.dm rename to code/datums/Emote_system/emote_handler.dm index afcb4f0253b..a20199155d2 100644 --- a/code/datums/Emote system/emote_handler.dm +++ b/code/datums/Emote_system/emote_handler.dm @@ -19,11 +19,12 @@ commands[command] = found found.addVerbs(owner) + /datum/emoteHandler/proc/deleteEmoteVerbs() for(var/obj/emoteVerb/E in owner.contents) qdel(E) -/datum/emoteHandler/proc/runEmote(var/command = "", var/param = "", var/message = "", var/audible = 0) // message and audible only used in custom emotes +/datum/emoteHandler/proc/runEmote(var/command = "", var/message = "", var/audible = 0) // message and audible only used in custom emotes if(!command) return 0 if(command == "help") @@ -43,7 +44,7 @@ emote = commands[command] if(!emote.available(owner)) // something's changed, remake the commands list, then try again to see if they've got a different version setupCommands() - return runEmote(command, param, message) + return runEmote(command, message, audible) var/prevented = emote.prevented(owner) if(prevented) diff --git a/code/datums/Emote system/emotes.dm b/code/datums/Emote_system/emotes.dm similarity index 78% rename from code/datums/Emote system/emotes.dm rename to code/datums/Emote_system/emotes.dm index 5ab3b76b56d..691ad7ab26e 100644 --- a/code/datums/Emote system/emotes.dm +++ b/code/datums/Emote_system/emotes.dm @@ -88,4 +88,38 @@ above it. If you don't want this, make the call to ..() then use commands = new return message +/datum/emote/signal + name = "signal" + desc = "raise x number of fingers" + text = "raises" + selfText = "raise" + canTarget = 1 + restrained = 1 + targetMob = 1 + takesNumber = 1 +/datum/emote/signal/New() + ..() + commands += "signal" + commands += "signals" + +/datum/emote/signal/available(var/mob/user) + if(ishuman(user)) + return 1 + +/datum/emote/signal/getNumber(var/mob/user) + var/number = ..() + var/fingersAvailable = 0 + if(!user.r_hand) + fingersAvailable += 5 + if(!user.l_hand) + fingersAvailable += 5 + if(fingersAvailable < number) + to_chat(user, "You don't have enough fingers free") + return "failed" + return number + +/datum/emote/signal/paramMessage(var/mob/user, var/param) + var/message = "[user] raises [param] finger\s" + testing(message) + return message diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm index 43248d54db4..709c1d778b3 100644 --- a/code/modules/mob/say.dm +++ b/code/modules/mob/say.dm @@ -29,7 +29,7 @@ /mob/verb/me_verb(message as text) set name = "Me" - set category = "IC" + set category = "Emotes" message = strip_html_properly(message) @@ -95,13 +95,8 @@ /mob/proc/emote(var/act, var/type, var/message) - var/param - if (findtext(act, "-", 1, null)) - var/t1 = findtext(act, "-", 1, null) - param = copytext(act, t1 + 1, length(act) + 1) - act = copytext(act, 1, t1) - emoteHandler.runEmote(act, param, message, type) + emoteHandler.runEmote(act, message, type) diff --git a/paradise.dme b/paradise.dme index b8e3c3dd148..a1cfc939216 100644 --- a/paradise.dme +++ b/paradise.dme @@ -258,9 +258,9 @@ #include "code\datums\diseases\advance\symptoms\weakness.dm" #include "code\datums\diseases\advance\symptoms\weight.dm" #include "code\datums\diseases\advance\symptoms\youth.dm" -#include "code\datums\Emote system\emote.dm" -#include "code\datums\Emote system\emote_handler.dm" -#include "code\datums\Emote system\emotes.dm" +#include "code\datums\Emote_system\emote.dm" +#include "code\datums\Emote_system\emote_handler.dm" +#include "code\datums\Emote_system\emotes.dm" #include "code\datums\helper_datums\construction_datum.dm" #include "code\datums\helper_datums\events.dm" #include "code\datums\helper_datums\global_iterator.dm" From e61d774a7c8794ec38bf938d56d91b3cf9afe020 Mon Sep 17 00:00:00 2001 From: VampyrBytes Date: Sun, 8 May 2016 05:34:31 +0100 Subject: [PATCH 003/275] Makes emotes case insensitive (#4386) Also prevents unconscious emoting --- code/datums/Emote_system/emote.dm | 2 ++ code/datums/Emote_system/emote_handler.dm | 2 +- code/modules/mob/say.dm | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/code/datums/Emote_system/emote.dm b/code/datums/Emote_system/emote.dm index f0a7e6ddb1b..75c8314ce7a 100644 --- a/code/datums/Emote_system/emote.dm +++ b/code/datums/Emote_system/emote.dm @@ -103,6 +103,8 @@ VampyrBytes /datum/emote/proc/prevented(var/mob/user) if(user.stat == DEAD) return "you are dead" + if(user.stat == UNCONSCIOUS) + return "you are unconscious" if(restrained && user.restrained()) return "you are restrained" diff --git a/code/datums/Emote_system/emote_handler.dm b/code/datums/Emote_system/emote_handler.dm index a20199155d2..ef4441857e5 100644 --- a/code/datums/Emote_system/emote_handler.dm +++ b/code/datums/Emote_system/emote_handler.dm @@ -16,7 +16,7 @@ var/datum/emote/found = searchTree(emote) if(found) for(var/command in found.commands) - commands[command] = found + commands[lowertext(command)] = found found.addVerbs(owner) diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm index 709c1d778b3..8078a101861 100644 --- a/code/modules/mob/say.dm +++ b/code/modules/mob/say.dm @@ -96,6 +96,7 @@ /mob/proc/emote(var/act, var/type, var/message) + act = lowertext(act) emoteHandler.runEmote(act, message, type) From a642606d18271d1d9ba76359173a94c88ee47b0e Mon Sep 17 00:00:00 2001 From: VampyrBytes Date: Sun, 8 May 2016 09:23:30 +0100 Subject: [PATCH 004/275] fixes bug with species specific emotes Emotes were being allocated before a human's species was set --- code/datums/Emote_system/emote_handler.dm | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/code/datums/Emote_system/emote_handler.dm b/code/datums/Emote_system/emote_handler.dm index ef4441857e5..c036954bbaa 100644 --- a/code/datums/Emote_system/emote_handler.dm +++ b/code/datums/Emote_system/emote_handler.dm @@ -7,17 +7,18 @@ setupCommands() /datum/emoteHandler/proc/setupCommands(var/reset = 0) - if(reset) - deleteEmoteVerbs() - commands = new/list() - for(var/e in emotes) - var/datum/emote/emote = e - if(emote.baseLevel) - var/datum/emote/found = searchTree(emote) - if(found) - for(var/command in found.commands) - commands[lowertext(command)] = found - found.addVerbs(owner) + spawn(0) // needed so at mob creation species is set before emotes are allocated + if(reset) + deleteEmoteVerbs() + commands = new/list() + for(var/e in emotes) + var/datum/emote/emote = e + if(emote.baseLevel) + var/datum/emote/found = searchTree(emote) + if(found) + for(var/command in found.commands) + commands[lowertext(command)] = found + found.addVerbs(owner) /datum/emoteHandler/proc/deleteEmoteVerbs() From bcfe01b03867b0ca01c216a1aafef03af4ba8f71 Mon Sep 17 00:00:00 2001 From: VampyrBytes Date: Tue, 10 May 2016 03:19:00 +0100 Subject: [PATCH 005/275] 1st batch of emotes + tweaks --- code/datums/Emote_system/emote.dm | 6 +- code/datums/Emote_system/emote_handler.dm | 9 +- code/datums/Emote_system/emotes.dm | 195 ++++++++++++++++++++++ 3 files changed, 205 insertions(+), 5 deletions(-) diff --git a/code/datums/Emote_system/emote.dm b/code/datums/Emote_system/emote.dm index 75c8314ce7a..f6b8feaebbc 100644 --- a/code/datums/Emote_system/emote.dm +++ b/code/datums/Emote_system/emote.dm @@ -29,7 +29,7 @@ VampyrBytes var/spanClass = "notice" var/baseLevel = 1 - var/baseSet = 0 + var/allowParent = 0 // 1 if you want the parent available as well as this one /datum/emote/New() @@ -107,6 +107,10 @@ VampyrBytes return "you are unconscious" if(restrained && user.restrained()) return "you are restrained" + if(isbrain(user)) + var/mob/living/carbon/brain/brain = user + if(!(brain.container && istype(brain.container, /obj/item/device/mmi))) + return "you need to be in an mmi to do this" // return 1 if this emote can be used by this type of user /datum/emote/proc/available(var/mob/user) diff --git a/code/datums/Emote_system/emote_handler.dm b/code/datums/Emote_system/emote_handler.dm index c036954bbaa..422892b847b 100644 --- a/code/datums/Emote_system/emote_handler.dm +++ b/code/datums/Emote_system/emote_handler.dm @@ -13,7 +13,7 @@ commands = new/list() for(var/e in emotes) var/datum/emote/emote = e - if(emote.baseLevel) + if(emote.baseLevel || emote.allowParent) var/datum/emote/found = searchTree(emote) if(found) for(var/command in found.commands) @@ -33,7 +33,6 @@ return 1 var/datum/emote/emote - //testing("[command]") if(command == "me") emote = customEmote(message, audible) if(!emote) @@ -69,8 +68,10 @@ var/list/subtypes = subtypesof(emote.type) var/datum/emote/found for(var/t in subtypes) - var/datum/emote/em = new t // not keen on this, but it's this or loop through the emotes list and the - found = searchTree(em) // thought of a nested for loop in a recursive proc makes me want to *cry - VB + var/datum/emote/em = new t + if(em.allowParent) + continue + found = searchTree(em, 1) if(found) return (found) if(emote.available(owner)) diff --git a/code/datums/Emote_system/emotes.dm b/code/datums/Emote_system/emotes.dm index 691ad7ab26e..509ff9eca2a 100644 --- a/code/datums/Emote_system/emotes.dm +++ b/code/datums/Emote_system/emotes.dm @@ -5,6 +5,201 @@ are set in New(), this means that the emote will pick up all the commands from t above it. If you don't want this, make the call to ..() then use commands = new /list() *************************************************************************************/ +/datum/emote/airguitar + name = "airguitar" + desc = "Play an air guitar" + text = "is strumming the air and headbanging like a safari chimp." + selfText = "are strumming the air and headbanging like a safari chimp." + restrained = 1 + +/datum/emote/airguitar/New() + ..() + commands += "airguitar" + +/datum/emote/airguitar/available(var/mob/user) + if(ishuman(user)) + return 1 + if(isrobot(user)) + return 1 + +/datum/emote/alarm + name = "alarm" + desc = "Sound an alarm" + text = "sounds an alarm" + selfText = "sound an alarm" + audible = 1 + +/datum/emote/alarm/New() + ..() + commands += "alarm" + +/datum/emote/alarm/available(var/mob/user) + if(isbrain(user)) + return 1 + +/datum/emote/alarm/createBlindMessage(var/mob/user, var/messaage) + return "You hear an alarm" + +/datum/emote/alert + name = "alert" + desc = "Sound an alert" + text = "lets out a distressed noise" + selfText = "let out a distressed noise)" + audible = 1 + +/datum/emote/alert/New() + ..() + commands += "alert" + +/datum/emote/alert/available(var/mob/user) + if(isbrain(user)) + return 1 +/datum/emote/alert/createBlindMessage(var/mob/user, var/message) + return "you hear an alert" + +/datum/emote/beep + name = "beep" + desc = "let out a beep" + text = "beeps" + selfText = "beep" + audible = 1 + +/datum/emote/beep/New() + ..() + commands += "beep" + commands += "beeps" + +/datum/emote/beep/available(var/mob/user) + if(isbrain(user)) + return 1 + +/datum/emote/beep/createBlindMessage(var/mob/user, var/message) + return "You hear a beep" + +/datum/emote/beep/targetted + cooldown = 1 + sound = 'sound/machines/twobeep.ogg' + canTarget = 1 + targetMob = 1 + +/datum/emote/beep/targetted/available(var/mob/user) + if(issilicon(user)) + return 1 + if(isbot(user)) + return 1 + if(user.get_species() == "Machine") + return 1 + +/datum/emote/blink + name = "blink" + desc = "blink" + text = "blinks" + selfText = "blink" + +/datum/emote/blink/New() + ..() + commands += "blink" + commands += "blinks" + +/datum/emote/blink/available(var/mob/user) + if(ishuman(user)) + return 1 + if(isbrain(user)) + return 1 + +/datum/emote/blink/rapid + name = "rapid blink" + desc = "blink rapidly" + allowParent = 1 + +/datum/emote/blink/rapid/New() + ..() + commands = new/list() + commands += "blink_r" + commands += "blinks_r" + +/datum/emote/blink/rapid/available(var/mob/user) + if(ishuman(user)) + return + +/datum/emote/blink/rapid/createMessage(var/mob/user, var/number) + var/message = ..() + message += " rapidly" + return message + +/datum/emote/blush + name = "blush" + desc = "blush" + text = "blushes" + selfText = "blush" + +/datum/emote/blush/New() + ..() + commands += "blush" + commands += "blushes" + +/datum/emote/blush/available(var/mob/user) + if(ishuman(user)) + return 1 + +/datum/emote/boop + name = "boop" + desc = "boop" + text = "boops" + selfText = "boop" + +/datum/emote/boop/New() + ..() + commands += "boop" + commands += "boops" + +/datum/emote/boop/available(var/mob/user) + if(isbrain(user)) + return 1 + +/datum/emote/bounce + name = "bounce" + desc = "bounce" + text = "bounces in place" + selfText = "bounce in place" + +/datum/emote/bounce/New() + ..() + commands += "bounce" + commands += "bounces" + +/datum/emote/bounce/available(var/mob/user) + if(isslime(user)) + return 1 + +/datum/emote/bow + name = "bow" + desc = "bow" + text = "bows" + selfText = "bow" + canTarget = 1 + targetMob = 1 + targetText = "to" + +/datum/emote/bow/New() + ..() + commands += "bow" + commands += "bows" + +/datum/emote/bow/available(var/mob/user) + if(isrobot(user)) + return 1 + if(ishuman(user)) + return 1 + +/datum/emote/bow/prevented(var/mob/user) + . = ..() + if(!. && user.buckled) + return "you are buckled to something" + + + + /datum/emote/scream name = "scream" text = "screams!" From 06331768930bca71c2009ca2ea31fe5f40805dd2 Mon Sep 17 00:00:00 2001 From: VampyrBytes Date: Tue, 10 May 2016 04:33:21 +0100 Subject: [PATCH 006/275] fixes derps in merge --- code/datums/Emote_system/emotes.dm | 2 +- .../carbon/human/species/{abdcutor.dm => abductor.dm} | 0 paradise.dme | 6 ++++++ 3 files changed, 7 insertions(+), 1 deletion(-) rename code/modules/mob/living/carbon/human/species/{abdcutor.dm => abductor.dm} (100%) diff --git a/code/datums/Emote_system/emotes.dm b/code/datums/Emote_system/emotes.dm index 7a3f9ea2b10..c00f5be5fd1 100644 --- a/code/datums/Emote_system/emotes.dm +++ b/code/datums/Emote_system/emotes.dm @@ -202,7 +202,7 @@ above it. If you don't want this, make the call to ..() then use commands = new desc = "burp" text = "burps" selfText = "burp" - muzzledNoise = "peculiar + muzzledNoise = "peculiar" /datum/emote/scream diff --git a/code/modules/mob/living/carbon/human/species/abdcutor.dm b/code/modules/mob/living/carbon/human/species/abductor.dm similarity index 100% rename from code/modules/mob/living/carbon/human/species/abdcutor.dm rename to code/modules/mob/living/carbon/human/species/abductor.dm diff --git a/paradise.dme b/paradise.dme index c3a14848062..dc3b70ad7bf 100644 --- a/paradise.dme +++ b/paradise.dme @@ -214,19 +214,23 @@ #include "code\datums\diseases\anxiety.dm" #include "code\datums\diseases\appendicitis.dm" #include "code\datums\diseases\beesease.dm" +#include "code\datums\diseases\berserker.dm" #include "code\datums\diseases\brainrot.dm" #include "code\datums\diseases\cold.dm" #include "code\datums\diseases\cold9.dm" #include "code\datums\diseases\fake_gbs.dm" #include "code\datums\diseases\flu.dm" #include "code\datums\diseases\fluspanish.dm" +#include "code\datums\diseases\food_poisoning.dm" #include "code\datums\diseases\gbs.dm" +#include "code\datums\diseases\kuru.dm" #include "code\datums\diseases\magnitis.dm" #include "code\datums\diseases\pierrot_throat.dm" #include "code\datums\diseases\retrovirus.dm" #include "code\datums\diseases\rhumba_beat.dm" #include "code\datums\diseases\transformation.dm" #include "code\datums\diseases\tuberculosis.dm" +#include "code\datums\diseases\vampire.dm" #include "code\datums\diseases\wizarditis.dm" #include "code\datums\diseases\advance\advance.dm" #include "code\datums\diseases\advance\presets.dm" @@ -773,6 +777,7 @@ #include "code\game\objects\items\stacks\tiles\tile_types.dm" #include "code\game\objects\items\weapons\AI_modules.dm" #include "code\game\objects\items\weapons\alien_specific.dm" +#include "code\game\objects\items\weapons\bee_briefcase.dm" #include "code\game\objects\items\weapons\cards_ids.dm" #include "code\game\objects\items\weapons\cash.dm" #include "code\game\objects\items\weapons\caution.dm" @@ -857,6 +862,7 @@ #include "code\game\objects\items\weapons\implants\implantuplink.dm" #include "code\game\objects\items\weapons\melee\energy.dm" #include "code\game\objects\items\weapons\melee\misc.dm" +#include "code\game\objects\items\weapons\storage\artistic_toolbox.dm" #include "code\game\objects\items\weapons\storage\backpack.dm" #include "code\game\objects\items\weapons\storage\bags.dm" #include "code\game\objects\items\weapons\storage\belt.dm" From 29152ceec04c9ba357a5ff670e7ce6ebb805d5e9 Mon Sep 17 00:00:00 2001 From: VampyrBytes Date: Wed, 11 May 2016 14:42:05 +0100 Subject: [PATCH 007/275] tweaks Better mime support system returns 0,1 or 2(0 failed, 1 for visible and 2 for audible) cleaned up creating message --- code/datums/Emote_system/emote.dm | 119 ++++++++++++++++------------- code/datums/Emote_system/emotes.dm | 2 + code/modules/mob/emote.dm | 2 +- code/modules/mob/say.dm | 4 +- 4 files changed, 73 insertions(+), 54 deletions(-) diff --git a/code/datums/Emote_system/emote.dm b/code/datums/Emote_system/emote.dm index f6b8feaebbc..3d3a2e669e4 100644 --- a/code/datums/Emote_system/emote.dm +++ b/code/datums/Emote_system/emote.dm @@ -45,43 +45,47 @@ VampyrBytes /datum/emote/proc/doEmote(var/mob/user) if(!istype(user)) - return 0 + return if(cooldown) if(handle_emote_CD(user)) - return 0 + return - var/number + var/message = "" + var/num var/target if(takesNumber) - number = getNumber(user) + num = getNumber(user) if(canTarget) target = getTarget(user) - if(number == "failed" || target == "failed") // If you need to test the input, override the appopriate get proc, call the parent, - return // then test it. If it fails, tell the user why, then return "failed" to halt the emote + if(num == "invalid" || target == "invalid") + return - var/message = createMessage(user, number) - - if(canTarget && message) - message = addTarget(user, target, message) + if(text) + message = createMessage(user, num, target) + if(message == "failed") + return if(message) - message += "" - outputMessage(user, message) + . = outputMessage(user, message) - if(audible) - if(!(user.mind && user.mind.miming)) - playSound(user, vol) + var/played = 0 + if(!doMime(user)) + played = playSound(user, vol) - doAction(user, target, number) - return 1 + if(played) + . = 2 + + doAction(user) + return // for things that the emote does that aren't text or sound based /datum/emote/proc/doAction(var/mob/user, var/atom/target, var/number) return +// return "invalid" from either of these getters if you've tested the input and it's failed /datum/emote/proc/getNumber(var/mob/user) var/number = input("How many") as null|num return number @@ -116,27 +120,43 @@ VampyrBytes /datum/emote/proc/available(var/mob/user) return -/datum/emote/proc/createMessage(var/mob/user, var/number) +/datum/emote/proc/createMessage(var/mob/user, var/param, var/target) if(!text) return var/message - if(takesNumber && number) - message = getParamMessage(user, number) - else if(audible && user.mind && user.mind.miming) - message = mimeMessage(user) - else if(audible && user.is_muzzled()) - message = muzzleMessage(user) - else - message = "[user] [text]" - if(startText) - message = "[startText] [message]" - message = "[message]" + + if(doMime(user)) + message = mimeMessage(user, param, target) + + if(!message) + if(muzzledNoise && user.is_muzzled()) + message = muzzleMessage(user) + + else if(takesNumber && param) + message = paramMessage(user, param) + else + message = "[user] [text]" + + if(message && target) + message = addTarget(user, target, message) + + message = addExtras(message) + return message -/datum/emote/proc/mimeMessage(var/mob/user) +/datum/emote/proc/addExtras(var/message) + if(!message) + return + if(startText) + message = "[startText] [message]" + message = "[message]" + return message + +/datum/emote/proc/mimeMessage(var/mob/user, var/param, var/target) if(!mimeText) return - + if(takesNumber) + return paramMimeMessage(user, param) var/message = "[user] [mimeText]" return message @@ -147,16 +167,6 @@ VampyrBytes message += "noise" return message -/datum/emote/proc/getParamMessage(var/mob/user, var/param) - var/message - if(audible && user.mind && user.mind.miming) - message = paramMimeMessage(user, param) - else if (audible && user.is_muzzled()) - message = muzzleMessage(user) - else - message = paramMessage(user, param) - return message - // if the emote takes a non target parameter, set up and return the with parameter version in here /datum/emote/proc/paramMessage(var/mob/user, var/param) return @@ -170,10 +180,7 @@ VampyrBytes return message if(!target) return message - if(ismob(target)) - message += " [targetText] [target]" - else - message += " [targetText] \the [target]" + message += " [targetText] \the [target]" return message // What you should see when you perform the emote @@ -191,11 +198,11 @@ VampyrBytes if(!message) return log_emote("[user.name]/[user.key] : [message]") - if(audible) - audible_message(message, user) - else - visible_message(message, user) sendToDead(message) + if(audible) + return audible_message(message, user) + else + return visible_message(message, user) /datum/emote/proc/visible_message(var/message = "", var/mob/user) var/selfMessage = createSelfMessage(user, message) @@ -213,8 +220,11 @@ VampyrBytes outputAudibleMessage(msg, M, user, 1) else outputVisibleMessage(msg, M, user) + return 1 /datum/emote/proc/audible_message(var/message = "", var/mob/user) + if(doMime(user)) + return visible_message(message, user) var/selfMessage = createSelfMessage(user, message) for(var/mob/M in get_mobs_in_view(7, user)) var/msg = message @@ -228,6 +238,7 @@ VampyrBytes outputVisibleMessage(msg, M, user, 1) else outputAudibleMessage(msg, M, user) + return 2 /datum/emote/proc/outputVisibleMessage(var/message, var/mob/recipient, var/mob/user, var/retest = 0) if(retest) @@ -252,6 +263,7 @@ VampyrBytes to_chat(recipient, message) /datum/emote/proc/outputAudibleMessage(var/message = "", var/mob/recipient, var/mob/user, var/retest = 0) + if(retest) if(!user) return @@ -291,6 +303,11 @@ VampyrBytes /datum/emote/proc/playSound(var/mob/user) if(sound) playsound(user, sound, vol) + return 1 + +/datum/emote/proc/doMime(var/mob/user) + if(mimeText && user.mind && user.mind.miming) + return 1 //Emote Cooldown System (it's so simple!) /datum/emote/proc/handle_emote_CD(var/mob/user) @@ -336,7 +353,7 @@ VampyrBytes /obj/emoteVerb/proc/runEmote() set src = usr.contents set category = "Emotes" - usr.emoteHandler.runEmote(emote.commands[1]) + return usr.emoteHandler.runEmote(emote.commands[1]) /obj/emoteVerb/Destroy() owner.verbs -= new/obj/emoteVerb/proc/runEmote(src, emote.commands[1]) @@ -354,7 +371,7 @@ obj/emoteVerb/custom/New(var/mob/user) /obj/emoteVerb/custom/runEmote(message as text, audible as num) set src = usr.contents set category = "Emotes" - usr.emoteHandler.runEmote("me", null, message, audible) + return usr.emoteHandler.runEmote("me", null, message, audible) /************************************************************************************************************************** diff --git a/code/datums/Emote_system/emotes.dm b/code/datums/Emote_system/emotes.dm index c00f5be5fd1..abbb89fce36 100644 --- a/code/datums/Emote_system/emotes.dm +++ b/code/datums/Emote_system/emotes.dm @@ -203,6 +203,8 @@ above it. If you don't want this, make the call to ..() then use commands = new text = "burps" selfText = "burp" muzzledNoise = "peculiar" + mimeText = "opens their mouth rather obnoxiously" + /datum/emote/scream diff --git a/code/modules/mob/emote.dm b/code/modules/mob/emote.dm index 1c82e1c2755..201e54ef839 100644 --- a/code/modules/mob/emote.dm +++ b/code/modules/mob/emote.dm @@ -17,7 +17,7 @@ // All mobs should have custom emote, really.. /mob/proc/custom_emote(var/m_type=1,var/message = null) - emoteHandler.runEmote("me", null, message, m_type) + return emoteHandler.runEmote("me", null, message, m_type) /* if(stat || !use_me && usr == src) to_chat(usr, "You are unable to emote.") diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm index 8078a101861..7362cdd78ab 100644 --- a/code/modules/mob/say.dm +++ b/code/modules/mob/say.dm @@ -34,7 +34,7 @@ message = strip_html_properly(message) set_typing_indicator(0) - emoteHandler.runEmote("me", null, message) + return emoteHandler.runEmote("me", null, message) /mob/proc/say_dead(var/message) if(!src.client.holder) @@ -97,7 +97,7 @@ /mob/proc/emote(var/act, var/type, var/message) act = lowertext(act) - emoteHandler.runEmote(act, message, type) + return emoteHandler.runEmote(act, message, type) From 3de48b83f8d0d2eaa50e3d5b579ee883cf5b610c Mon Sep 17 00:00:00 2001 From: VampyrBytes Date: Fri, 13 May 2016 10:41:42 +0100 Subject: [PATCH 008/275] better param support --- code/datums/Emote_system/emote.dm | 84 ++++++++++++++--------- code/datums/Emote_system/emote_handler.dm | 1 - code/datums/Emote_system/emotes.dm | 17 +++-- 3 files changed, 63 insertions(+), 39 deletions(-) diff --git a/code/datums/Emote_system/emote.dm b/code/datums/Emote_system/emote.dm index 3d3a2e669e4..47311cc994b 100644 --- a/code/datums/Emote_system/emote.dm +++ b/code/datums/Emote_system/emote.dm @@ -51,22 +51,16 @@ VampyrBytes return var/message = "" - var/num - var/target + var/list/params[0] - if(takesNumber) - num = getNumber(user) + params = getParams(user) - if(canTarget) - target = getTarget(user) - - if(num == "invalid" || target == "invalid") - return + for(var/p in params) + if(params[p] == "invalid") + return if(text) - message = createMessage(user, num, target) - if(message == "failed") - return + message = createMessage(user, params) if(message) . = outputMessage(user, message) @@ -78,13 +72,25 @@ VampyrBytes if(played) . = 2 - doAction(user) + doAction(user, params) + return // for things that the emote does that aren't text or sound based -/datum/emote/proc/doAction(var/mob/user, var/atom/target, var/number) +/datum/emote/proc/doAction(var/mob/user, var/list/params) return +/datum/emote/proc/getParams(var/mob/user) + var/list/params[0] + + if(takesNumber) + params["num"] = getNumber(user) + + if(canTarget) + params["target"] = getTarget(user) + + return params + // return "invalid" from either of these getters if you've tested the input and it's failed /datum/emote/proc/getNumber(var/mob/user) var/number = input("How many") as null|num @@ -120,30 +126,37 @@ VampyrBytes /datum/emote/proc/available(var/mob/user) return -/datum/emote/proc/createMessage(var/mob/user, var/param, var/target) +/datum/emote/proc/createMessage(var/mob/user, var/list/params) if(!text) return var/message if(doMime(user)) - message = mimeMessage(user, param, target) + message = mimeMessage(user, params) if(!message) if(muzzledNoise && user.is_muzzled()) message = muzzleMessage(user) - else if(takesNumber && param) - message = paramMessage(user, param) + else if(checkForParams(params)) + message = paramMessage(user, params) else message = "[user] [text]" - if(message && target) - message = addTarget(user, target, message) + if(message && "target" in params) + if(params["target"]) + message = addTarget(user, params, message) message = addExtras(message) return message +/datum/emote/proc/checkForParams(var/list/params) + for(var/p in params) + if(params[p] == "target") + continue + return 1 + /datum/emote/proc/addExtras(var/message) if(!message) return @@ -152,12 +165,16 @@ VampyrBytes message = "[message]" return message -/datum/emote/proc/mimeMessage(var/mob/user, var/param, var/target) +/datum/emote/proc/mimeMessage(var/mob/user, var/list/params) if(!mimeText) return - if(takesNumber) - return paramMimeMessage(user, param) + if(takesNumber && "num" in params) + if(params["num"] != null) + return paramMimeMessage(user, params) var/message = "[user] [mimeText]" + if(message && "target" in params) + if(params["target"]) + message = addTarget(user, params, message) return message /datum/emote/proc/muzzleMessage(var/mob/user) @@ -168,31 +185,30 @@ VampyrBytes return message // if the emote takes a non target parameter, set up and return the with parameter version in here -/datum/emote/proc/paramMessage(var/mob/user, var/param) +/datum/emote/proc/paramMessage(var/mob/user, var/list/params) return -// as above, but for mimes trying to do audible messages -/datum/emote/proc/paramMimeMessage(var/mob/user, var/param) +// as above, but for mimes when there is mimeText +/datum/emote/proc/paramMimeMessage(var/mob/user, var/list/params) return -/datum/emote/proc/addTarget(var/mob/user, var/atom/target, var/message = "") +/datum/emote/proc/addTarget(var/mob/user, var/list/params, var/message = "") if(!canTarget) return message - if(!target) + if(!params["target"]) return message - message += " [targetText] \the [target]" + message += " [targetText] \the [params["target"]]" return message // What you should see when you perform the emote /datum/emote/proc/createSelfMessage(var/mob/user, var/message = "") - var/selfMessage if(startText) - selfMessage = replacetext(message, "[user]", "you") + message = replacetext(message, "[user]", "you") else - selfMessage = replacetext(message, "[user]", "You") + message = replacetext(message, "[user]", "You") if(selfText) - selfMessage = replacetext(selfMessage, text, selfText) - return selfMessage + message = replacetext(message, text, selfText) + return message /datum/emote/proc/outputMessage(var/mob/user, var/message = "") if(!message) diff --git a/code/datums/Emote_system/emote_handler.dm b/code/datums/Emote_system/emote_handler.dm index 422892b847b..d30d542aaec 100644 --- a/code/datums/Emote_system/emote_handler.dm +++ b/code/datums/Emote_system/emote_handler.dm @@ -20,7 +20,6 @@ commands[lowertext(command)] = found found.addVerbs(owner) - /datum/emoteHandler/proc/deleteEmoteVerbs() for(var/obj/emoteVerb/E in owner.contents) qdel(E) diff --git a/code/datums/Emote_system/emotes.dm b/code/datums/Emote_system/emotes.dm index abbb89fce36..8a03a1febc9 100644 --- a/code/datums/Emote_system/emotes.dm +++ b/code/datums/Emote_system/emotes.dm @@ -207,6 +207,7 @@ above it. If you don't want this, make the call to ..() then use commands = new + /datum/emote/scream name = "scream" text = "screams!" @@ -252,6 +253,8 @@ above it. If you don't want this, make the call to ..() then use commands = new /datum/emote/fart name = "fart" + text = "farts" + selfText = "fart" cooldown = 50 /datum/emote/fart/New() @@ -289,6 +292,11 @@ above it. If you don't want this, make the call to ..() then use commands = new message = "[user] [pick("passes wind","farts")]." return message +/datum/emote/fart/createSelfMessage(var/mob/user, var/message) + message = ..() + message = replacetext(message, "unleashes", "unleash") + message = replacetext(message, "passes", "pass") + return message /datum/emote/signal name = "signal" @@ -311,6 +319,8 @@ above it. If you don't want this, make the call to ..() then use commands = new /datum/emote/signal/getNumber(var/mob/user) var/number = ..() + if(number == null) + return "invalid" var/fingersAvailable = 0 if(!user.r_hand) fingersAvailable += 5 @@ -318,10 +328,9 @@ above it. If you don't want this, make the call to ..() then use commands = new fingersAvailable += 5 if(fingersAvailable < number) to_chat(user, "You don't have enough fingers free") - return "failed" + return "invalid" return number -/datum/emote/signal/paramMessage(var/mob/user, var/param) - var/message = "[user] raises [param] finger\s" - testing(message) +/datum/emote/signal/paramMessage(var/mob/user, var/list/params) + var/message = "[user] raises [params["num"]] finger\s" return message From a355f84e1cf10859ad456bf92e846db8c6be4601 Mon Sep 17 00:00:00 2001 From: VampyrBytes Date: Sat, 14 May 2016 07:59:21 +0100 Subject: [PATCH 009/275] Adds default blind and deaf messages + bug fixes --- code/datums/Emote_system/emote.dm | 55 +++++++++++++++--------------- code/datums/Emote_system/emotes.dm | 11 +++++- 2 files changed, 38 insertions(+), 28 deletions(-) diff --git a/code/datums/Emote_system/emote.dm b/code/datums/Emote_system/emote.dm index 47311cc994b..65a09d23ea3 100644 --- a/code/datums/Emote_system/emote.dm +++ b/code/datums/Emote_system/emote.dm @@ -63,7 +63,7 @@ VampyrBytes message = createMessage(user, params) if(message) - . = outputMessage(user, message) + . = outputMessage(user, message, params) var/played = 0 if(!doMime(user)) @@ -168,9 +168,8 @@ VampyrBytes /datum/emote/proc/mimeMessage(var/mob/user, var/list/params) if(!mimeText) return - if(takesNumber && "num" in params) - if(params["num"] != null) - return paramMimeMessage(user, params) + if(checkForParams(params)) + return paramMimeMessage(user, params) var/message = "[user] [mimeText]" if(message && "target" in params) if(params["target"]) @@ -210,17 +209,17 @@ VampyrBytes message = replacetext(message, text, selfText) return message -/datum/emote/proc/outputMessage(var/mob/user, var/message = "") +/datum/emote/proc/outputMessage(var/mob/user, var/message = "", var/list/params) if(!message) return log_emote("[user.name]/[user.key] : [message]") sendToDead(message) if(audible) - return audible_message(message, user) + return audible_message(message, user, params) else - return visible_message(message, user) + return visible_message(message, user, params) -/datum/emote/proc/visible_message(var/message = "", var/mob/user) +/datum/emote/proc/visible_message(var/message = "", var/mob/user, var/list/params) var/selfMessage = createSelfMessage(user, message) for(var/mob/M in viewers(user)) if(M.see_invisible < user.invisibility) @@ -228,25 +227,25 @@ VampyrBytes var/msg = message if(selfMessage && M==user) msg = selfMessage - if(M.sdisabilities & BLIND || M.blinded || M.paralysis) + else if(M.sdisabilities & BLIND || M.blinded || M.paralysis) if(M.sdisabilities & DEAF || M.ear_deaf) continue - msg = createBlindMessage(message, user) + msg = createBlindMessage(message, user, params) if(msg) outputAudibleMessage(msg, M, user, 1) else - outputVisibleMessage(msg, M, user) + outputVisibleMessage(msg, M, user, params) return 1 -/datum/emote/proc/audible_message(var/message = "", var/mob/user) +/datum/emote/proc/audible_message(var/message = "", var/mob/user, var/list/params) if(doMime(user)) return visible_message(message, user) var/selfMessage = createSelfMessage(user, message) for(var/mob/M in get_mobs_in_view(7, user)) var/msg = message - if(selfMessage && M==src) + if(selfMessage && M==user) msg = selfMessage - if(M.sdisabilities & DEAF || M.ear_deaf) + else if(M.sdisabilities & DEAF || M.ear_deaf) if(M.sdisabilities & BLIND || M.blinded || M.paralysis) continue msg = createDeafMessage(user, message) @@ -256,7 +255,7 @@ VampyrBytes outputAudibleMessage(msg, M, user) return 2 -/datum/emote/proc/outputVisibleMessage(var/message, var/mob/recipient, var/mob/user, var/retest = 0) +/datum/emote/proc/outputVisibleMessage(var/message, var/mob/recipient, var/mob/user, var/list/params, var/retest = 0) if(retest) if(!user) return @@ -270,16 +269,16 @@ VampyrBytes if(!found) return if(recipient.sdisabilities & DEAF || recipient.ear_deaf) - var/msg = createDeafMessage(user, message) - if(msg) - message = msg + if(!(recipient == user)) + var/msg = createDeafMessage(user, message, params) + if(msg) + message = msg if(recipient.stat == UNCONSCIOUS || (recipient.sleeping > 0 && recipient.stat != 2)) to_chat(recipient, "... You can almost hear someone talking ...") else to_chat(recipient, message) -/datum/emote/proc/outputAudibleMessage(var/message = "", var/mob/recipient, var/mob/user, var/retest = 0) - +/datum/emote/proc/outputAudibleMessage(var/message = "", var/mob/recipient, var/mob/user, var/list/params, var/retest = 0) if(retest) if(!user) return @@ -291,9 +290,10 @@ VampyrBytes if(!found) return if(recipient.sdisabilities & BLIND || recipient.blinded || recipient.paralysis) - var/msg = createBlindMessage(user, message) - if(msg) - message = msg + if(!(recipient == user)) + var/msg = createBlindMessage(user, message, params) + if(msg) + message = msg if(recipient.stat == UNCONSCIOUS || (recipient.sleeping > 0 && recipient.stat != 2)) to_chat(recipient, "... You can almost hear someone talking ...") else @@ -301,13 +301,14 @@ VampyrBytes // set up different messages for blind people here. Empty will mean no message for //non-audible emotes and the standard message for audible ones -/datum/emote/proc/createBlindMessage(var/mob/user, var/message) - return +/datum/emote/proc/createBlindMessage(var/mob/user, var/message, var/list/params) + if(audible && selfText) + return "You hear someone [selfText]" // set up different messages for deaf people here. Empty will mean no message for // audible emotes and standard for non-audible ones -/datum/emote/proc/createDeafMessage(var/mob/user, var/message) - return +/datum/emote/proc/createDeafMessage(var/mob/user, var/message, var/list/params) + return mimeMessage(user, params) /datum/emote/proc/sendToDead(message) for(var/mob/M in dead_mob_list) diff --git a/code/datums/Emote_system/emotes.dm b/code/datums/Emote_system/emotes.dm index 8a03a1febc9..12d30be4c26 100644 --- a/code/datums/Emote_system/emotes.dm +++ b/code/datums/Emote_system/emotes.dm @@ -204,9 +204,18 @@ above it. If you don't want this, make the call to ..() then use commands = new selfText = "burp" muzzledNoise = "peculiar" mimeText = "opens their mouth rather obnoxiously" + audible = 1 +/datum/emote/burp/New() + ..() + commands += "burp" + commands += "burps" - +/datum/emote/burp/available(var/mob/user) + if(ishuman(user)) + return 1 + if(istype(user, /mob/living/carbon/alien/larva) || istype(user, /mob/living/carbon/alien/humanoid)) + return 1 /datum/emote/scream name = "scream" From 636081e47e16e22cb9152286bb481d14ecd6cb0b Mon Sep 17 00:00:00 2001 From: VampyrBytes Date: Mon, 16 May 2016 22:56:03 +0100 Subject: [PATCH 010/275] Cleans up message output + bugfixes and tweaks --- code/datums/Emote_system/emote.dm | 179 +++++++++++++----------------- 1 file changed, 76 insertions(+), 103 deletions(-) diff --git a/code/datums/Emote_system/emote.dm b/code/datums/Emote_system/emote.dm index 65a09d23ea3..f12d0e20214 100644 --- a/code/datums/Emote_system/emote.dm +++ b/code/datums/Emote_system/emote.dm @@ -14,14 +14,18 @@ VampyrBytes var/text = "" var/selfText = "" // the version of text that you should see - eg, if text is screams, you want scream here, as You screams is bad grammer var/startText = "" // if you need to put something in before [user]. Should end with a space + var/selfStart = 1 // whether the start text is used in what you see + + var/audible = 0 var/mimeText = "" var/sound // sound file var/vol = 50 - var/audible = 0 - var/muzzledNoise = "" // if the emote is audible and you're muzzled, this is what type of noise you make (eg weak, loud). End with a space - var/restrained = "" // 1 if being restrained prevents this emote + var/muzzleAffected = 0 // whether being muzzled affects this emote + var/muzzledNoise = "" // if the emote is audible and you're muzzled, this is what type of noise you make (eg weak, loud). var/cooldown = 0 + var/restrained = 0 // 1 if being restrained prevents this emote + var/canTarget = 0 // 1 if the emote accepts a target var/targetMob = 0 // 0 if target can be any atom, 1 if it has to be a mob, var/targetText = "at" // what goes inbetween user and target @@ -65,12 +69,10 @@ VampyrBytes if(message) . = outputMessage(user, message, params) - var/played = 0 - if(!doMime(user)) - played = playSound(user, vol) - if(played) - . = 2 + if(!doMime(user)) + if(playSound(user, vol)) + . = 2 doAction(user, params) @@ -88,7 +90,6 @@ VampyrBytes if(canTarget) params["target"] = getTarget(user) - return params // return "invalid" from either of these getters if you've tested the input and it's failed @@ -97,15 +98,17 @@ VampyrBytes return number /datum/emote/proc/getTarget(var/mob/user) + if(user.sdisabilities & BLIND || user.blinded || user.paralysis) + return if(targetMob) - return getMobTarget() - return getAtomTarget() + return getMobTarget(user) + return getAtomTarget(user) -/datum/emote/proc/getMobTarget() +/datum/emote/proc/getMobTarget(var/mob/user) var/mob/target = input("Select target", "Target Mob") as null|mob in view() return target -/datum/emote/proc/getAtomTarget() +/datum/emote/proc/getAtomTarget(var/mob/user) var/atom/target = input("Select target", "Target") as null|mob|obj|turf in oview() return target @@ -135,25 +138,23 @@ VampyrBytes message = mimeMessage(user, params) if(!message) - if(muzzledNoise && user.is_muzzled()) + if(muzzleAffected && user.is_muzzled()) message = muzzleMessage(user) - else if(checkForParams(params)) message = paramMessage(user, params) else - message = "[user] [text]" + message = "[user] [text]" if(message && "target" in params) if(params["target"]) message = addTarget(user, params, message) - message = addExtras(message) return message /datum/emote/proc/checkForParams(var/list/params) for(var/p in params) - if(params[p] == "target") + if(p == "target") continue return 1 @@ -170,14 +171,14 @@ VampyrBytes return if(checkForParams(params)) return paramMimeMessage(user, params) - var/message = "[user] [mimeText]" + var/message = "[user] [mimeText]" if(message && "target" in params) if(params["target"]) message = addTarget(user, params, message) return message /datum/emote/proc/muzzleMessage(var/mob/user) - var/message = "[user] makes a " + var/message = "[user] makes a " if(muzzledNoise) message += "[muzzledNoise] " message += "noise" @@ -201,109 +202,81 @@ VampyrBytes // What you should see when you perform the emote /datum/emote/proc/createSelfMessage(var/mob/user, var/message = "") - if(startText) + if(startText && selfStart) message = replacetext(message, "[user]", "you") else message = replacetext(message, "[user]", "You") if(selfText) message = replacetext(message, text, selfText) + if(!selfStart) + var/start = findtextEx(message, startText) + var/end = start + lentext(startText) + 1 + message = copytext(message, 1, start) + copytext(message, end, lentext(message) + 1) + return message /datum/emote/proc/outputMessage(var/mob/user, var/message = "", var/list/params) - if(!message) - return + var/visualOrAudible = audible + 1 + if(doMime(user)) + visualOrAudible = 1 + var/selfMessage = createSelfMessage(user, message) + log_emote("[user.name]/[user.key] : [message]") sendToDead(message) - if(audible) - return audible_message(message, user, params) - else - return visible_message(message, user, params) -/datum/emote/proc/visible_message(var/message = "", var/mob/user, var/list/params) - var/selfMessage = createSelfMessage(user, message) - for(var/mob/M in viewers(user)) - if(M.see_invisible < user.invisibility) - continue //can't view the invisible - var/msg = message + for(var/mob/M in getRecipients(user, visualOrAudible)) + var/msg = "" + if(selfMessage && M==user) msg = selfMessage - else if(M.sdisabilities & BLIND || M.blinded || M.paralysis) - if(M.sdisabilities & DEAF || M.ear_deaf) + + else if(M.stat == UNCONSCIOUS || (M.sleeping > 0 && M.stat != 2)) + if (visualOrAudible == 2) + msg = "... You can almost hear someone talking ..." + else continue - msg = createBlindMessage(message, user, params) - if(msg) - outputAudibleMessage(msg, M, user, 1) - else - outputVisibleMessage(msg, M, user, params) - return 1 -/datum/emote/proc/audible_message(var/message = "", var/mob/user, var/list/params) - if(doMime(user)) - return visible_message(message, user) - var/selfMessage = createSelfMessage(user, message) - for(var/mob/M in get_mobs_in_view(7, user)) - var/msg = message - if(selfMessage && M==user) - msg = selfMessage else if(M.sdisabilities & DEAF || M.ear_deaf) if(M.sdisabilities & BLIND || M.blinded || M.paralysis) continue - msg = createDeafMessage(user, message) - if(msg) - outputVisibleMessage(msg, M, user, 1) - else - outputAudibleMessage(msg, M, user) - return 2 + if((!(M in getRecipients(user, 1))) || M.see_invisible < user.invisibility) + continue + msg = createDeafMessage(user, message, params) -/datum/emote/proc/outputVisibleMessage(var/message, var/mob/recipient, var/mob/user, var/list/params, var/retest = 0) - if(retest) - if(!user) - return - var/found = 0 - for(var/mob/M in viewers(user)) - if(M == recipient) - found = 1 - if(recipient.see_invisible < user.invisibility) - return - break - if(!found) - return - if(recipient.sdisabilities & DEAF || recipient.ear_deaf) - if(!(recipient == user)) - var/msg = createDeafMessage(user, message, params) - if(msg) - message = msg - if(recipient.stat == UNCONSCIOUS || (recipient.sleeping > 0 && recipient.stat != 2)) - to_chat(recipient, "... You can almost hear someone talking ...") - else - to_chat(recipient, message) + if(!msg && visualOrAudible == 2) + continue + + else if(M.sdisabilities & BLIND || M.blinded || M.paralysis || (M.see_invisible < user.invisibility && visualOrAudible == 2)) + if(!(M in getRecipients(user, 2))) + continue + msg = createBlindMessage(user, message, params) + if(!msg && visualOrAudible == 1) + continue + + else if(M.see_invisible < user.invisibility) + continue + + if(!msg) + msg = message + + to_chat(M, msg) + return visualOrAudible + +/datum/emote/proc/getRecipients(var/mob/user, var/visualOrAudible) + var/list/recipients[0] + switch(visualOrAudible) + if(1) + recipients = viewers(user) + if(2) + recipients = get_mobs_in_view(7, user) + return recipients -/datum/emote/proc/outputAudibleMessage(var/message = "", var/mob/recipient, var/mob/user, var/list/params, var/retest = 0) - if(retest) - if(!user) - return - var/found = 0 - for(var/mob/M in get_mobs_in_view(7, user)) - if(M == recipient) - found = 1 - break - if(!found) - return - if(recipient.sdisabilities & BLIND || recipient.blinded || recipient.paralysis) - if(!(recipient == user)) - var/msg = createBlindMessage(user, message, params) - if(msg) - message = msg - if(recipient.stat == UNCONSCIOUS || (recipient.sleeping > 0 && recipient.stat != 2)) - to_chat(recipient, "... You can almost hear someone talking ...") - else - to_chat(recipient, message) // set up different messages for blind people here. Empty will mean no message for //non-audible emotes and the standard message for audible ones /datum/emote/proc/createBlindMessage(var/mob/user, var/message, var/list/params) if(audible && selfText) - return "You hear someone [selfText]" + return "You hear someone [selfText]" // set up different messages for deaf people here. Empty will mean no message for // audible emotes and standard for non-audible ones @@ -318,9 +291,10 @@ VampyrBytes M.show_message(message) /datum/emote/proc/playSound(var/mob/user) - if(sound) - playsound(user, sound, vol) - return 1 + if(!sound) + return + playsound(user, sound, vol) + return 1 /datum/emote/proc/doMime(var/mob/user) if(mimeText && user.mind && user.mind.miming) @@ -408,8 +382,7 @@ available, but if you do, make sure you return ..() so the check for use_me is s message = getMessage(user) text = message audible = isAudible - if(user.mind && user.mind.miming) - audible = 0 + /datum/emote/custom/proc/getMessage(var/mob/user) var/input = sanitize(copytext(input(user,"Choose an emote to display.") as text|null,1,MAX_MESSAGE_LEN)) From 97d9831bd9482df317c56ce611719d203a1d4f62 Mon Sep 17 00:00:00 2001 From: VampyrBytes Date: Tue, 17 May 2016 11:27:51 +0100 Subject: [PATCH 011/275] more tweaks --- code/datums/Emote_system/emote.dm | 37 ++++++++++++++++--------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/code/datums/Emote_system/emote.dm b/code/datums/Emote_system/emote.dm index f12d0e20214..6343c17c7c5 100644 --- a/code/datums/Emote_system/emote.dm +++ b/code/datums/Emote_system/emote.dm @@ -143,13 +143,12 @@ VampyrBytes else if(checkForParams(params)) message = paramMessage(user, params) else - message = "[user] [text]" + message = "\The [user] [text]" if(message && "target" in params) - if(params["target"]) - message = addTarget(user, params, message) - message = addExtras(message) + message = addTarget(user, params, message) + message = addExtras(user, params, message) return message /datum/emote/proc/checkForParams(var/list/params) @@ -158,7 +157,7 @@ VampyrBytes continue return 1 -/datum/emote/proc/addExtras(var/message) +/datum/emote/proc/addExtras(var/mob/user, var/list/params, var/message) if(!message) return if(startText) @@ -173,8 +172,7 @@ VampyrBytes return paramMimeMessage(user, params) var/message = "[user] [mimeText]" if(message && "target" in params) - if(params["target"]) - message = addTarget(user, params, message) + message = addTarget(user, params, message) return message /datum/emote/proc/muzzleMessage(var/mob/user) @@ -202,16 +200,20 @@ VampyrBytes // What you should see when you perform the emote /datum/emote/proc/createSelfMessage(var/mob/user, var/message = "") - if(startText && selfStart) - message = replacetext(message, "[user]", "you") - else - message = replacetext(message, "[user]", "You") - if(selfText) - message = replacetext(message, text, selfText) - if(!selfStart) - var/start = findtextEx(message, startText) - var/end = start + lentext(startText) + 1 - message = copytext(message, 1, start) + copytext(message, end, lentext(message) + 1) + if(!selfText) + return message + + message = replacetext(message, text, selfText) + + if(startText) + if(selfStart) + message = replacetext(message, "[user]", "you") + else + message = replacetext(message, "[user]", "You") + + var/start = findtextEx(message, startText) + var/end = start + lentext(startText) + 1 + message = copytext(message, 1, start) + copytext(message, end, lentext(message) + 1) return message @@ -271,7 +273,6 @@ VampyrBytes recipients = get_mobs_in_view(7, user) return recipients - // set up different messages for blind people here. Empty will mean no message for //non-audible emotes and the standard message for audible ones /datum/emote/proc/createBlindMessage(var/mob/user, var/message, var/list/params) From 5c6474ffc0976e8b9384f926ae778a0184251fa0 Mon Sep 17 00:00:00 2001 From: VampyrBytes Date: Wed, 18 May 2016 14:45:14 +0100 Subject: [PATCH 012/275] Adds better text macro support, default cooldown for emotes with sound and some cleaning --- code/datums/Emote_system/emote.dm | 92 ++++++++++++++++------- code/datums/Emote_system/emote_handler.dm | 2 +- code/modules/mob/emote.dm | 2 +- 3 files changed, 68 insertions(+), 28 deletions(-) diff --git a/code/datums/Emote_system/emote.dm b/code/datums/Emote_system/emote.dm index 6343c17c7c5..09a111b009f 100644 --- a/code/datums/Emote_system/emote.dm +++ b/code/datums/Emote_system/emote.dm @@ -6,6 +6,7 @@ The only exception to this is custom emotes, which are created as needed and onl VampyrBytes *******************************************************************************************************/ +#define EMOTE_COOLDOWN 20 //Time in deciseconds that the cooldown lasts /datum/emote var/name = "" @@ -18,11 +19,12 @@ VampyrBytes var/audible = 0 var/mimeText = "" + var/mimeSelf = "" //self version of mimeText var/sound // sound file var/vol = 50 var/muzzleAffected = 0 // whether being muzzled affects this emote var/muzzledNoise = "" // if the emote is audible and you're muzzled, this is what type of noise you make (eg weak, loud). - var/cooldown = 0 + var/cooldown = 0 // How long the cooldown should be on this emote. Defaults to EMOTE_COOLDOWN if not set and the emote plays a sound var/restrained = 0 // 1 if being restrained prevents this emote @@ -46,6 +48,8 @@ VampyrBytes if(count == 4) baseLevel = 0 break + if(sound && !cooldown) + cooldown = EMOTE_COOLDOWN /datum/emote/proc/doEmote(var/mob/user) if(!istype(user)) @@ -67,7 +71,8 @@ VampyrBytes message = createMessage(user, params) if(message) - . = outputMessage(user, message, params) + message = addExtras(user, params, message) + . = outputMessage(user, params, message) if(!doMime(user)) @@ -132,7 +137,7 @@ VampyrBytes /datum/emote/proc/createMessage(var/mob/user, var/list/params) if(!text) return - var/message + var/message = "" if(doMime(user)) message = mimeMessage(user, params) @@ -143,12 +148,11 @@ VampyrBytes else if(checkForParams(params)) message = paramMessage(user, params) else - message = "\The [user] [text]" + message = standardMessage(user) if(message && "target" in params) message = addTarget(user, params, message) - message = addExtras(user, params, message) return message /datum/emote/proc/checkForParams(var/list/params) @@ -157,7 +161,7 @@ VampyrBytes continue return 1 -/datum/emote/proc/addExtras(var/mob/user, var/list/params, var/message) +/datum/emote/proc/addExtras(var/mob/user, var/list/params, var/message = "") if(!message) return if(startText) @@ -165,18 +169,22 @@ VampyrBytes message = "[message]" return message +/datum/emote/proc/standardMessage(var/mob/user) + var/message = "\The [user] [text]" + return message + /datum/emote/proc/mimeMessage(var/mob/user, var/list/params) if(!mimeText) return if(checkForParams(params)) return paramMimeMessage(user, params) - var/message = "[user] [mimeText]" + var/message = "\The [user] [mimeText]" if(message && "target" in params) message = addTarget(user, params, message) return message /datum/emote/proc/muzzleMessage(var/mob/user) - var/message = "[user] makes a " + var/message = "\The [user] makes a " if(muzzledNoise) message += "[muzzledNoise] " message += "noise" @@ -195,37 +203,44 @@ VampyrBytes return message if(!params["target"]) return message - message += " [targetText] \the [params["target"]]" + if(params["target"] == user) + message += " [targetText] [getHimself(user)]" + else + message += " [targetText] \the [params["target"]]" return message // What you should see when you perform the emote -/datum/emote/proc/createSelfMessage(var/mob/user, var/message = "") +/datum/emote/proc/createSelfMessage(var/mob/user, var/list/params, var/message = "") if(!selfText) return message message = replacetext(message, text, selfText) + message = changeTextMacros(user, message) - if(startText) - if(selfStart) - message = replacetext(message, "[user]", "you") - else - message = replacetext(message, "[user]", "You") + if(mimeSelf) + message = replacetext(message, mimeText, mimeSelf) - var/start = findtextEx(message, startText) - var/end = start + lentext(startText) + 1 - message = copytext(message, 1, start) + copytext(message, end, lentext(message) + 1) + if(selfStart && startText) + message = replacetext(message, "\The [user]", "you") + else + message = replacetext(message, "\The [user]", "You") + + if(startText && !selfStart) + var/start = findtextEx(message, startText) + var/end = start + lentext(startText) + 1 + message = copytext(message, 1, start) + copytext(message, end, lentext(message) + 1) return message -/datum/emote/proc/outputMessage(var/mob/user, var/message = "", var/list/params) +/datum/emote/proc/outputMessage(var/mob/user, var/list/params, var/message = "") var/visualOrAudible = audible + 1 if(doMime(user)) visualOrAudible = 1 - var/selfMessage = createSelfMessage(user, message) + var/selfMessage = createSelfMessage(user, params, message) log_emote("[user.name]/[user.key] : [message]") sendToDead(message) - + testing(message) for(var/mob/M in getRecipients(user, visualOrAudible)) var/msg = "" @@ -243,7 +258,7 @@ VampyrBytes continue if((!(M in getRecipients(user, 1))) || M.see_invisible < user.invisibility) continue - msg = createDeafMessage(user, message, params) + msg = createDeafMessage(user, params, message) if(!msg && visualOrAudible == 2) continue @@ -251,7 +266,7 @@ VampyrBytes else if(M.sdisabilities & BLIND || M.blinded || M.paralysis || (M.see_invisible < user.invisibility && visualOrAudible == 2)) if(!(M in getRecipients(user, 2))) continue - msg = createBlindMessage(user, message, params) + msg = createBlindMessage(user, params, message) if(!msg && visualOrAudible == 1) continue @@ -275,16 +290,16 @@ VampyrBytes // set up different messages for blind people here. Empty will mean no message for //non-audible emotes and the standard message for audible ones -/datum/emote/proc/createBlindMessage(var/mob/user, var/message, var/list/params) +/datum/emote/proc/createBlindMessage(var/mob/user, var/list/params, var/message) if(audible && selfText) return "You hear someone [selfText]" // set up different messages for deaf people here. Empty will mean no message for // audible emotes and standard for non-audible ones -/datum/emote/proc/createDeafMessage(var/mob/user, var/message, var/list/params) +/datum/emote/proc/createDeafMessage(var/mob/user, var/list/params, var/message) return mimeMessage(user, params) -/datum/emote/proc/sendToDead(message) +/datum/emote/proc/sendToDead(var/message = "") for(var/mob/M in dead_mob_list) if(!M.client || istype(M, /mob/new_player)) continue //skip monkeys, leavers and new players @@ -307,6 +322,7 @@ VampyrBytes if(user.emote_cd == 1) return 1 // Already on CD, prevent use user.emote_cd = 1 // Starting cooldown + spawn(cooldown) if(user.emote_cd == 2) return 1 // Don't reset if cooldown emotes were disabled by an admin during the cooldown user.emote_cd = 0 // Cooldown complete, ready for more! @@ -314,6 +330,30 @@ VampyrBytes return 0 // Proceed with emote //--FalseIncarnate +/datum/emote/proc/changeTextMacros(var/mob/user, var/message = "") + message = swapHisToYour(user, message) + message = swapHimselfToYourself(user, message) + return message + +/datum/emote/proc/getHis(var/mob/user) + var/his = "[user]\his" + his = copytext(his, lentext("[user]") + 1) + return his + +/datum/emote/proc/swapHisToYour(var/mob/user, var/message = "") + var/his = getHis(user) + message = replacetext(message, his, "your") + return message + +/datum/emote/proc/getHimself(var/mob/user) + var/himself = "[user]\himself" + himself = copytext(himself, lentext("[user]") + 1) + return himself + +/datum/emote/proc/swapHimselfToYourself(var/mob/user, var/message) + var/himself = getHimself(user) + message = replacetext(message, himself, "yourself") + return message /****************************************************************************************** Emote Verbs diff --git a/code/datums/Emote_system/emote_handler.dm b/code/datums/Emote_system/emote_handler.dm index d30d542aaec..ca8c14914f3 100644 --- a/code/datums/Emote_system/emote_handler.dm +++ b/code/datums/Emote_system/emote_handler.dm @@ -70,7 +70,7 @@ var/datum/emote/em = new t if(em.allowParent) continue - found = searchTree(em, 1) + found = searchTree(em) if(found) return (found) if(emote.available(owner)) diff --git a/code/modules/mob/emote.dm b/code/modules/mob/emote.dm index 201e54ef839..2832d70c90a 100644 --- a/code/modules/mob/emote.dm +++ b/code/modules/mob/emote.dm @@ -1,4 +1,4 @@ -#define EMOTE_COOLDOWN 20 //Time in deciseconds that the cooldown lasts + //Emote Cooldown System (it's so simple!) /* From 0661c056053305a84a2fcb0e41e45f5301cdc730 Mon Sep 17 00:00:00 2001 From: VampyrBytes Date: Thu, 26 May 2016 12:13:29 +0100 Subject: [PATCH 013/275] tweaks and cleaning --- code/datums/Emote_system/emote.dm | 168 ++++++++++++++-------- code/datums/Emote_system/emote_handler.dm | 2 +- 2 files changed, 112 insertions(+), 58 deletions(-) diff --git a/code/datums/Emote_system/emote.dm b/code/datums/Emote_system/emote.dm index 09a111b009f..a8da598c573 100644 --- a/code/datums/Emote_system/emote.dm +++ b/code/datums/Emote_system/emote.dm @@ -7,6 +7,7 @@ VampyrBytes *******************************************************************************************************/ #define EMOTE_COOLDOWN 20 //Time in deciseconds that the cooldown lasts +#define HEARING_RANGE 7 /datum/emote var/name = "" @@ -33,7 +34,8 @@ VampyrBytes var/targetText = "at" // what goes inbetween user and target var/takesNumber = 0 // 1 if the emote uses a number parameter - var/spanClass = "notice" + var/emoteSpanClass = "notice" + var/userSpanClass = "em" var/baseLevel = 1 var/allowParent = 0 // 1 if you want the parent available as well as this one @@ -48,10 +50,15 @@ VampyrBytes if(count == 4) baseLevel = 0 break + if(sound && !cooldown) cooldown = EMOTE_COOLDOWN -/datum/emote/proc/doEmote(var/mob/user) + if(targetText) + if(!findtextEx(targetText, " ", lentext(targetText))) + targetText += " " + +/datum/emote/proc/doEmote(var/mob/user, var/command = "") if(!istype(user)) return if(cooldown) @@ -72,15 +79,18 @@ VampyrBytes if(message) message = addExtras(user, params, message) - . = outputMessage(user, params, message) + . = processMessage(user, params, message) - - if(!doMime(user)) + if(!doMime(user) && (!muzzleAffected || !isMuzzled(user))) if(playSound(user, vol)) . = 2 doAction(user, params) + for (var/obj/item/weapon/implant/I in user) + if (I.implanted) + I.trigger(command, user) + return // for things that the emote does that aren't text or sound based @@ -99,7 +109,7 @@ VampyrBytes // return "invalid" from either of these getters if you've tested the input and it's failed /datum/emote/proc/getNumber(var/mob/user) - var/number = input("How many") as null|num + var/number = input("How many?", "Enter number") as null|num return number /datum/emote/proc/getTarget(var/mob/user) @@ -141,20 +151,30 @@ VampyrBytes if(doMime(user)) message = mimeMessage(user, params) + if(message) + return message - if(!message) - if(muzzleAffected && user.is_muzzled()) - message = muzzleMessage(user) - else if(checkForParams(params)) - message = paramMessage(user, params) - else - message = standardMessage(user) + if(muzzleAffected && isMuzzled(user)) + message = muzzleMessage(user, params) + return message - if(message && "target" in params) - message = addTarget(user, params, message) + if(checkForParams(params)) + message = paramMessage(user, params) + return message + + message = standardMessage(user, params) return message +/datum/emote/proc/isMuzzled(var/mob/user) + if(user.sdisabilities & MUTE || user.is_muzzled()) + return 1 + if(!isliving(user)) + return + var/mob/living/L = user + if (L.silent) + return 1 + /datum/emote/proc/checkForParams(var/list/params) for(var/p in params) if(p == "target") @@ -166,11 +186,13 @@ VampyrBytes return if(startText) message = "[startText] [message]" - message = "[message]" + message = "[message]" return message -/datum/emote/proc/standardMessage(var/mob/user) - var/message = "\The [user] [text]" +/datum/emote/proc/standardMessage(var/mob/user, var/list/params) + var/message = "\The [user] [text]" + if("target" in params) + message = addTarget(user, params, message) return message /datum/emote/proc/mimeMessage(var/mob/user, var/list/params) @@ -178,13 +200,13 @@ VampyrBytes return if(checkForParams(params)) return paramMimeMessage(user, params) - var/message = "\The [user] [mimeText]" + var/message = "\The [user] [mimeText]" if(message && "target" in params) message = addTarget(user, params, message) return message -/datum/emote/proc/muzzleMessage(var/mob/user) - var/message = "\The [user] makes a " +/datum/emote/proc/muzzleMessage(var/mob/user, var/list/params) + var/message = "\The [user] makes a " if(muzzledNoise) message += "[muzzledNoise] " message += "noise" @@ -204,11 +226,12 @@ VampyrBytes if(!params["target"]) return message if(params["target"] == user) - message += " [targetText] [getHimself(user)]" - else - message += " [targetText] \the [params["target"]]" + message += " [targetText][getHimself(user)]" + return message + message += " [targetText]\the [params["target"]]" return message + // What you should see when you perform the emote /datum/emote/proc/createSelfMessage(var/mob/user, var/list/params, var/message = "") if(!selfText) @@ -220,23 +243,26 @@ VampyrBytes if(mimeSelf) message = replacetext(message, mimeText, mimeSelf) - if(selfStart && startText) - message = replacetext(message, "\The [user]", "you") - else - message = replacetext(message, "\The [user]", "You") - if(startText && !selfStart) var/start = findtextEx(message, startText) var/end = start + lentext(startText) + 1 message = copytext(message, 1, start) + copytext(message, end, lentext(message) + 1) + message = replaceMobWithYou(user, message) + return message -/datum/emote/proc/outputMessage(var/mob/user, var/list/params, var/message = "") +/datum/emote/proc/replaceMobWithYou(var/mob/M, var/message = "", var/mob/user) // user passed in in case an override needs to do something different if M != user (see johnny) VB + + message = replacetext(message, "\The [M]", "You") + message = replacetext(message, "\the [M]", "you") + + return message + +/datum/emote/proc/processMessage(var/mob/user, var/list/params, var/message = "") var/visualOrAudible = audible + 1 if(doMime(user)) visualOrAudible = 1 - var/selfMessage = createSelfMessage(user, params, message) log_emote("[user.name]/[user.key] : [message]") sendToDead(message) @@ -244,55 +270,83 @@ VampyrBytes for(var/mob/M in getRecipients(user, visualOrAudible)) var/msg = "" - if(selfMessage && M==user) - msg = selfMessage - - else if(M.stat == UNCONSCIOUS || (M.sleeping > 0 && M.stat != 2)) - if (visualOrAudible == 2) - msg = "... You can almost hear someone talking ..." - else + if(M==user) + msg = createSelfMessage(user, params, message) + if(msg) + outputMessage(M, msg) continue - else if(M.sdisabilities & DEAF || M.ear_deaf) + if(M.stat == UNCONSCIOUS || (M.sleeping > 0 && M.stat != 2)) + if (!visualOrAudible == 2) + continue + msg = "... You can almost hear someone talking ..." + outputMessage(M, msg) + continue + + if(M.sdisabilities & DEAF || M.ear_deaf) if(M.sdisabilities & BLIND || M.blinded || M.paralysis) continue - if((!(M in getRecipients(user, 1))) || M.see_invisible < user.invisibility) + if(!(M in getRecipients(user, 1)) || M.see_invisible < user.invisibility) continue - msg = createDeafMessage(user, params, message) + msg = createDeafMessage(user, params, message) if(!msg && visualOrAudible == 2) continue + if(msg) + outputMessage(M, msg) + continue - else if(M.sdisabilities & BLIND || M.blinded || M.paralysis || (M.see_invisible < user.invisibility && visualOrAudible == 2)) + if(M.sdisabilities & BLIND || M.blinded || M.paralysis || (M.see_invisible < user.invisibility && visualOrAudible == 2)) if(!(M in getRecipients(user, 2))) continue + msg = createBlindMessage(user, params, message) if(!msg && visualOrAudible == 1) continue + if(msg) + outputMessage(M, msg) + continue - else if(M.see_invisible < user.invisibility) + if(M.see_invisible < user.invisibility && visualOrAudible == 1) continue - if(!msg) - msg = message + msg = message + outputMessage(M, msg) + + if(visualOrAudible == 2) + handleListeningObjects(user, message) - to_chat(M, msg) return visualOrAudible +/datum/emote/proc/outputMessage(var/mob/M, var/msg = "") + msg = replaceMobWithYou(M, msg, M) + to_chat(M, msg) + +/datum/emote/proc/handleListeningObjects(var/mob/user, var/message = "") + // based on say code + var/omsg = replacetext(message, "[user] ", "") + var/list/listening_obj = new + for(var/atom/movable/A in view(HEARING_RANGE, user)) + if(istype(A, /mob)) + var/mob/M = A + for(var/obj/O in M.contents) + listening_obj |= O + else if(istype(A, /obj)) + var/obj/O = A + listening_obj |= O + for(var/obj/O in listening_obj) + O.hear_message(user, omsg) + /datum/emote/proc/getRecipients(var/mob/user, var/visualOrAudible) - var/list/recipients[0] - switch(visualOrAudible) - if(1) - recipients = viewers(user) - if(2) - recipients = get_mobs_in_view(7, user) - return recipients + if(visualOrAudible == 1) + return viewers(user) + return get_mobs_in_view(HEARING_RANGE, user) // set up different messages for blind people here. Empty will mean no message for //non-audible emotes and the standard message for audible ones /datum/emote/proc/createBlindMessage(var/mob/user, var/list/params, var/message) if(audible && selfText) - return "You hear someone [selfText]" + return "You hear someone [selfText]" // set up different messages for deaf people here. Empty will mean no message for // audible emotes and standard for non-audible ones @@ -306,7 +360,7 @@ VampyrBytes if(M.stat == DEAD && (M.client.prefs.toggles & CHAT_GHOSTSIGHT) && !(M in viewers(src,null))) M.show_message(message) -/datum/emote/proc/playSound(var/mob/user) +/datum/emote/proc/playSound(var/mob/user, var/list/params) if(!sound) return playsound(user, sound, vol) @@ -440,7 +494,7 @@ available, but if you do, make sure you return ..() so the check for use_me is s /datum/emote/custom/ghost name = "Ghost emote" startText = "DEAD: " - spanClass = "game deadsay" + emoteSpanClass = "game deadsay" /datum/emote/custom/ghost/prevented(var/mob/user) if(user.client.prefs.muted & MUTE_DEADCHAT) @@ -451,7 +505,7 @@ available, but if you do, make sure you return ..() so the check for use_me is s if(!config.dsay_allowed) return "deadchat is globally muted" -/datum/emote/custom/ghost/outputMessage(var/mob/user, var/message = "") +/datum/emote/custom/ghost/processMessage(var/mob/user, var/message = "") if(!message) return log_emote("Ghost/[user.key] : [message]") diff --git a/code/datums/Emote_system/emote_handler.dm b/code/datums/Emote_system/emote_handler.dm index ca8c14914f3..1417e83f9a8 100644 --- a/code/datums/Emote_system/emote_handler.dm +++ b/code/datums/Emote_system/emote_handler.dm @@ -49,7 +49,7 @@ if(prevented) to_chat(owner, "You can't do that because [prevented]!") return 0 - return emote.doEmote(owner) + return emote.doEmote(owner, command) /datum/emoteHandler/proc/showCommands() var/emoteList = "Available emotes are " From 2bd66d86928b7ec692cf319ba6fe4bbaf57c9be3 Mon Sep 17 00:00:00 2001 From: VampyrBytes Date: Thu, 26 May 2016 12:14:52 +0100 Subject: [PATCH 014/275] Every emote added! Thank god that's over with! --- code/datums/Emote_system/emotes.dm | 1895 ++++++++++++++++++++++++++-- 1 file changed, 1801 insertions(+), 94 deletions(-) diff --git a/code/datums/Emote_system/emotes.dm b/code/datums/Emote_system/emotes.dm index 12d30be4c26..190759df693 100644 --- a/code/datums/Emote_system/emotes.dm +++ b/code/datums/Emote_system/emotes.dm @@ -2,12 +2,12 @@ Emotes New() must call ..() to set the baseLevel for the emoteHandler search. As the commands are set in New(), this means that the emote will pick up all the commands from the emotes -above it. If you don't want this, make the call to ..() then use commands = new /list() +above it. If you don't want this, make the call to ..() then use commands.cut() *************************************************************************************/ /datum/emote/airguitar name = "airguitar" - desc = "Play an air guitar" + desc = "Makes the mob play an air guitar" text = "is strumming the air and headbanging like a safari chimp." selfText = "are strumming the air and headbanging like a safari chimp." restrained = 1 @@ -24,7 +24,7 @@ above it. If you don't want this, make the call to ..() then use commands = new /datum/emote/alarm name = "alarm" - desc = "Sound an alarm" + desc = "makes the mob sound an alarm" text = "sounds an alarm" selfText = "sound an alarm" audible = 1 @@ -37,14 +37,14 @@ above it. If you don't want this, make the call to ..() then use commands = new if(isbrain(user)) return 1 -/datum/emote/alarm/createBlindMessage(var/mob/user, var/messaage) +/datum/emote/alarm/createBlindMessage(var/mob/user, var/params, var/messaage) return "You hear an alarm" /datum/emote/alert name = "alert" - desc = "Sound an alert" + desc = "Makes the mob sound an alert" text = "lets out a distressed noise" - selfText = "let out a distressed noise)" + selfText = "let out a distressed noise" audible = 1 /datum/emote/alert/New() @@ -54,12 +54,10 @@ above it. If you don't want this, make the call to ..() then use commands = new /datum/emote/alert/available(var/mob/user) if(isbrain(user)) return 1 -/datum/emote/alert/createBlindMessage(var/mob/user, var/message) - return "you hear an alert" /datum/emote/beep name = "beep" - desc = "let out a beep" + desc = "makes the mob let out a beep" text = "beeps" selfText = "beep" audible = 1 @@ -73,26 +71,19 @@ above it. If you don't want this, make the call to ..() then use commands = new if(isbrain(user)) return 1 -/datum/emote/beep/createBlindMessage(var/mob/user, var/message) - return "You hear a beep" - /datum/emote/beep/targetted - cooldown = 1 sound = 'sound/machines/twobeep.ogg' + cooldown = 50 canTarget = 1 targetMob = 1 /datum/emote/beep/targetted/available(var/mob/user) - if(issilicon(user)) - return 1 - if(isbot(user)) - return 1 - if(user.get_species() == "Machine") + if(user.is_mechanical()) return 1 /datum/emote/blink name = "blink" - desc = "blink" + desc = "Makes the mob blink" text = "blinks" selfText = "blink" @@ -109,27 +100,27 @@ above it. If you don't want this, make the call to ..() then use commands = new /datum/emote/blink/rapid name = "rapid blink" - desc = "blink rapidly" + desc = "Makes the mob blink rapidly" allowParent = 1 /datum/emote/blink/rapid/New() ..() - commands = new/list() + commands = new /list() commands += "blink_r" commands += "blinks_r" /datum/emote/blink/rapid/available(var/mob/user) if(ishuman(user)) - return + return 1 -/datum/emote/blink/rapid/createMessage(var/mob/user, var/number) +/datum/emote/blink/rapid/standardMessage(var/mob/user) var/message = ..() message += " rapidly" return message /datum/emote/blush name = "blush" - desc = "blush" + desc = "Makes the mob blush" text = "blushes" selfText = "blush" @@ -144,7 +135,7 @@ above it. If you don't want this, make the call to ..() then use commands = new /datum/emote/boop name = "boop" - desc = "boop" + desc = "Makes the mob boop" text = "boops" selfText = "boop" @@ -159,7 +150,7 @@ above it. If you don't want this, make the call to ..() then use commands = new /datum/emote/bounce name = "bounce" - desc = "bounce" + desc = "Makes the mob bounce" text = "bounces in place" selfText = "bounce in place" @@ -174,7 +165,7 @@ above it. If you don't want this, make the call to ..() then use commands = new /datum/emote/bow name = "bow" - desc = "bow" + desc = "Makes the mob bow" text = "bows" selfText = "bow" canTarget = 1 @@ -199,12 +190,14 @@ above it. If you don't want this, make the call to ..() then use commands = new /datum/emote/burp name = "burp" - desc = "burp" + desc = "Makes the mob burp" text = "burps" selfText = "burp" - muzzledNoise = "peculiar" - mimeText = "opens their mouth rather obnoxiously" audible = 1 + mimeText = "opens their mouth rather obnoxiously" + mimeSelf = "open your mouth rather obnoxiously" + muzzleAffected = 1 + muzzledNoise = "peculiar" /datum/emote/burp/New() ..() @@ -214,16 +207,1194 @@ above it. If you don't want this, make the call to ..() then use commands = new /datum/emote/burp/available(var/mob/user) if(ishuman(user)) return 1 - if(istype(user, /mob/living/carbon/alien/larva) || istype(user, /mob/living/carbon/alien/humanoid)) + if(islarva(user) || isalienadult(user)) + return 1 + +/datum/emote/buzz + name = "buzz" + desc = "Makes the mob buzz" + text = "buzzes" + selfText = "buzz" + audible = 1 + sound = 'sound/machines/buzz-sigh.ogg' + cooldown = 50 + canTarget = 1 + targetMob = 1 + +/datum/emote/buzz/New() + ..() + commands += "buzz" + commands += "buzzes" + commands += "buzzs" + +/datum/emote/buzz/available(var/mob/user) + if(user.is_mechanical()) + return 1 + +/datum/emote/chirp + name = "chirp" + desc = "Makes the mob chirp" + text = "chirps" + selfText = "chirp" + audible = 1 + sound = 'sound/misc/nymphchirp.ogg' + vol = 40 + cooldown = 50 + +/datum/emote/chirp/New() + ..() + commands += "chirp" + commands += "chirps" + +/datum/emote/chirp/available(var/mob/user) + if(istype(user, /mob/living/simple_animal/diona)) + return 1 + +/datum/emote/chirp/playSound(var/mob/user) + if (!sound) + return + playsound(user, sound, vol, 1, 1) + return 1 + +/datum/emote/choke + name = "choke" + desc = "Makes the mob choke" + text = "chokes" + selfText = "choke" + audible = 1 + mimeText = "clutches" + mimeSelf = "clutch" + muzzleAffected = 1 + muzzledNoise = "strong" + +/datum/emote/choke/New() + ..() + commands += "choke" + commands += "chokes" + +/datum/emote/choke/available(var/mob/user) + if(ishuman(user)) + return 1 + if(islarva(user) || isalienadult(user)) + return 1 + +// This has to be done here not mimeText because \his will throw a compile error +// instead of a runtime if there's not a valid target in the string *frown VB +/datum/emote/choke/mimeMessage(var/mob/user, var/list/params) + var/message = "[user] [mimeText] \his throat desperately" + return message + +/datum/emote/chuckle + name = "chuckle" + desc = "chuckle" + text = "chuckles" + selfText = "chuckle" + audible = 1 + mimeText = "appears to chuckle" + mimeSelf = "appear to chuckle" + muzzleAffected = 1 + +/datum/emote/chuckle/New() + ..() + commands += "chuckle" + commands += "chuckles" + +/datum/emote/chuckle/available(var/mob/user) + if(ishuman(user)) + return 1 + +/datum/emote/clap + name = "clap" + desc = "clap" + text = "claps" + selfText = "clap" + audible = 1 + mimeText = "claps silently" + restrained = 1 + +/datum/emote/clap/New() + ..() + commands += "clap" + commands += "claps" + +/datum/emote/clap/available(var/mob/user) + if(isrobot(user)) + return 1 + if(ishuman(user)) + return 1 + +/datum/emote/collapse + name = "collapse" + desc = "Makes the mob collapse" + text = "collapses" + selfText = "collapse" + audible = 1 + mimeText = "collapses without a sound" + +/datum/emote/collapse/New() + ..() + commands += "collapse" + commands += "collapses" + +/datum/emote/collapse/available(var/mob/user) + if(islarva(user)) + return 1 + if(ishuman(user)) + return 1 + +/datum/emote/collapse/doAction(var/mob/user, var/list/params) + user.Paralyse(2) + +/datum/emote/cough + name = "cough" + desc = "Makes the mob cough" + text = "coughs" + selfText = "cough" + audible = 1 + mimeText = "appears to cough" + mimeSelf = "appear to cough" + muzzleAffected = 1 + muzzledNoise = "strong" + +/datum/emote/cough/New() + ..() + commands += "cough" + commands += "coughs" + +/datum/emote/cough/available(var/mob/user) + if(ishuman(user)) + return 1 + +/datum/emote/cry + name = "cry" + desc = "Makes the mob cry" + text = "cries" + selfText = "cry" + audible = 1 + mimeText = "cries silently" + muzzleAffected = 1 + muzzledNoise = "weak" + +/datum/emote/cry/New() + ..() + commands += "cry" + commands += "cries" + +/datum/emote/cry/available(var/mob/user) + if(ishuman(user)) + return 1 + +/datum/emote/dance + name = "dance" + desc = "makes the mob dance around happily" + text = "dances around happily" + selfText = "dance around happily" + restrained = 1 + +/datum/emote/dance/New() + ..() + commands += "dance" + commands += "dances" + +/datum/emote/dance/available(var/mob/user) + if(islarva(user)) + return 1 + +/datum/emote/dap + name = "dap" + desc = "Makes the mob give daps" + text = "gives daps" + selfText = "give daps" + restrained = 1 + canTarget = 1 + targetMob = 1 + targetText = "to" + +/datum/emote/dap/New() + ..() + commands += "dap" + commands += "daps" + +/datum/emote/dap/available(var/mob/user) + if(ishuman(user)) + return 1 + +/datum/emote/dap/addTarget(var/mob/user, var/list/params, var/message = "") + if(!params["target"] || params["target"] == user) + message = "[user] sadly can't find anybody to give daps to, and daps \himself. Shameful." + return message + return ..() + +/datum/emote/deathgasp + name = "deathgasp" + desc = "Makes the mob let out it's final gasp" + audible = 1 + +/datum/emote/deathgasp/New() + ..() + commands += "deathgasp" + commands += "deathgasps" + +/datum/emote/deathgasp/alien + text = "lets out a waning guttural screech, green blood bubbling from its maw..." + selfText = "let out a waning guttural screech, green blood bubbling from your maw..." + +/datum/emote/deathgasp/alien/available(var/mob/user) + if(isalienadult(user)) + return 1 + +// sorry, no selftext for humans as we'd have to add a var to species for it and that's yuk - VB +/datum/emote/deathgasp/human + text = "dummytext" + +/datum/emote/deathgasp/human/available(var/mob/user) + if(ishuman(user)) + return 1 + +/datum/emote/deathgasp/human/standardMessage(var/mob/user, var/list/params) + var/mob/living/carbon/human/U = user + var/message = "\The [user] [U.species.death_message]" + return message + +/datum/emote/deathgasp/robot + text = "shudders violently for a moment, then becomes motionless, its eyes slowly darkening." + selfText = "shudder violently for a moment, then become motionless, your eyes slowly darkening." + audible = 0 + +/datum/emote/drone + name = "drone" + desc = "Makes the mob drone and rumble" + text = "rumbles" + selfText = "rumble" + audible = 1 + sound = 'sound/voice/DraskTalk.ogg' + canTarget = 1 + targetMob = 1 + +/datum/emote/drone/New() + ..() + commands += "drone" + commands += "drones" + commands += "rumble" + commands += "rumbles" + commands += "hum" + commands += "hums" + +/datum/emote/drone/available(var/mob/user) + if(!ishuman(user)) + return + var/mob/living/carbon/human/H = user + if(H.species == "Drask") + return 1 + +/datum/emote/drone/addTarget(var/mob/user, var/list/params, var/message) + var/msg = ..() + if(msg == message) + return message + message = replacetext(message, "rumbles", "drones") + return message + +/datum/emote/drone/createSelfMessage(var/mob/user, var/list/params, var/message) + message = ..() + message = replacetext(message, "drones", "drone") + return message + +/datum/emote/deathgasp/robot/available(var/mob/user) + if(isrobot(user)) + return 1 + +/datum/emote/drool + name = "drool" + desc = "Makes the mob drool" + text = "drools" + selfText = "drool" + +/datum/emote/drool/New() + ..() + commands += "drool" + commands += "drools" + +/datum/emote/drool/available(var/mob/user) + if(isalienadult(user) || islarva(user)) + return 1 + if(ishuman(user)) + return 1 + +/datum/emote/eyebrow + name = "eyebrow" + desc = "Makes the mob raise an eyebrow" + text = "raises an eyebrow" + selfText = "raise an eyebrow" + +/datum/emote/eyebrow/New() + ..() + commands += "eyebrow" + +/datum/emote/eyebrow/available(var/mob/user) + if(ishuman(user)) + return 1 + +/datum/emote/faint + name = "faint" + desc = "makes the mob faint" + text = "faints" + selfText = "faint" + +/datum/emote/faint/New() + ..() + commands += "faint" + commands += "faints" + +/datum/emote/faint/available(var/mob/user) + if(ishuman(user)) + return 1 + +/datum/emote/faint/doAction(var/mob/user, var/list/params) + if(!user.sleeping) + user.sleeping += 1 + +/datum/emote/fart + name = "fart" + text = "farts" + selfText = "fart" + cooldown = 50 + audible = 1 + +/datum/emote/fart/New() + ..() + commands += "fart" + commands += "farts" + +/datum/emote/fart/available(var/mob/user) + if(ishuman(user)) + return 1 + +/datum/emote/fart/standardMessage(var/mob/user) + var/message + if(SUPER_FART in user.mutations) + return // super fart will make the message when triggered + + if(TOXIC_FARTS in user.mutations) + message = "\The [user] unleashes a [pick("horrible","terrible","foul","disgusting","awful")] fart." + else + message = "\The [user] [pick("passes wind","farts")]." + return message + +/datum/emote/fart/createSelfMessage(var/mob/user, var/list/params, var/message) + message = ..() + message = replacetext(message, "unleashes", "unleash") + message = replacetext(message, "passes", "pass") + return message + +/datum/emote/fart/doAction(var/mob/user) + if(TOXIC_FARTS in user.mutations) + for(var/mob/M in range(get_turf(user),2)) + if (M.internal != null && M.wear_mask && (M.wear_mask.flags & AIRTIGHT)) + continue + if (M == user) + continue + M.reagents.add_reagent("space_drugs",rand(1,10)) + + if(SUPER_FART in user.mutations) + var/mob/living/U = user + for(var/datum/action/spell_action/spell in U.actions) + if (spell.name == "Super Fart") + spell.Trigger() + break + + if(locate(/obj/item/weapon/storage/bible) in get_turf(user)) + if(SUPER_FART in user.mutations) + sleep(30) // need to wait for them to finish the super fart before gibbing them + + to_chat(viewers(user), "[user] farted on the Bible!") + to_chat(viewers(user), "A mysterious force smites [user]!") + var/datum/effect/system/spark_spread/s = new /datum/effect/system/spark_spread + s.set_up(3, 1, user) + s.start() + user.gib() + +/datum/emote/flap + name = "flap" + desc = "Makes the mob flap their wings" + text = "flaps" + selfText = "flap" + restrained = 1 + audible = 1 + +/datum/emote/flap/New() + ..() + commands += "flap" + commands += "flaps" + +/datum/emote/flap/available(var/mob/user) + if(ishuman(user)) + return 1 + if(isrobot(user)) + return 1 + +/datum/emote/flap/standardMessage(var/mob/user, var/list/params) + var/message = "\The [user] [text] \his wings" + return message + +/datum/emote/flap/angry + name = "angry flap" + desc = "makes the mob flap their wings angrily" + allowParent = 1 + +/datum/emote/flap/angry/New() + ..() + commands = new /list() + commands += "a_flap" + commands += "a_flaps" + +/datum/emote/flap/angry/standardMessage(var/mob/user) + var/message = ..() + message += " angrily" + return message + +/datum/emote/flash + name = "flash" + desc = "Makes the lights on the mob flash quickly" + text = "flash quickly" + selfText = "flash quickly" + startText = "The lights on" + +/datum/emote/flash/New() + ..() + commands += "flash" + commands += "flashes" + +/datum/emote/flash/available(var/mob/user) + if(isbrain(user)) + return 1 + +/datum/emote/flip + name = "flip" + desc = "Makes the mob flip, possibly in the direction of someone" + text = "flips" + selfText = "flip" + canTarget = 1 + targetMob = 1 + targetText = "in" + +/datum/emote/flip/New() + ..() + commands += "flip" + commands += "flips" + +/datum/emote/flip/available(var/mob/user) + if(isrobot(user)) + return 1 + +/datum/emote/flip/prevented(var/mob/user) + . = ..() + if(!. && user.buckled) + return "you are buckled to something" + +/datum/emote/flip/createMessage(var/mob/user, var/list/params) + var/message = "" + if(user.lying || user.weakened) + message = "\The [user] flops and flails around on the floor." + else + message = ..() + return message + +/datum/emote/flip/addTarget(var/mob/user, var/list/params, var/message) + message = ..() + if(params["target"]) + message += "'s general direction" + return message + +/datum/emote/flip/createSelfMessage(var/mob/user, var/list/params, var/message) + message = ..() + message = replacetext(message, "flops", "flop") + message = replacetext(message, "flails", "flail") + return message + +/datum/emote/flip/doAction(var/mob/user, var/list/params, var/message) + if(user.lying || user.weakened) + return + user.SpinAnimation(5,1) + +/datum/emote/flip/flipOver + desc = "Makes the mob flip, possibly in the direction of, or even over someone" + +/datum/emote/flip/flipOver/available(var/mob/user) + if(ishuman(user)) + return 1 + +/datum/emote/flip/flipOver/createMessage(var/mob/user, var/params) + var/message = "" + var/obj/item/weapon/grab/G = user.get_active_hand() + if(istype(G) && G.affecting && !G.affecting.buckled) + params["target"] = G.affecting + message = "\The [user] [text] over [params["target"]]!" + return message + return ..() + +/datum/emote/flip/flipOver/doAction(var/mob/user, var/params, var/message) + var/obj/item/weapon/grab/G = user.get_active_hand() + if(G == params["target"]) + var/turf/oldloc = user.loc + var/turf/newloc = G.affecting.loc + if(isturf(oldloc) && isturf(newloc)) + user.SpinAnimation(5,1) + user.forceMove(newloc) + G.affecting.forceMove(oldloc) + return + ..() + +/datum/emote/frown + name = "frown" + desc = "Makes the mob frown" + text = "frowns" + selfText = "frown" + +/datum/emote/frown/New() + ..() + commands += "frown" + commands += "frowns" + +/datum/emote/frown/available(var/mob/user) + if(ishuman(user)) + return 1 + +/datum/emote/gasp + name = "gasp" + desc = "Makes the mob gasp" + text = "gasps" + selfText = "gasp" + audible = 1 + mimeText = "appears to be gasping" + mimeSelf = "appear to be gasping" + muzzleAffected = 1 + muzzledNoise = "weak" + +/datum/emote/gasp/New() + ..() + commands += "gasp" + commands += "gasps" + +/datum/emote/gasp/available(var/mob/user) + if(isalienadult(user) || islarva(user)) + return 1 + if(ishuman(user)) + return 1 + +/datum/emote/giggle + name = "giggle" + desc = "Makes the mob giggle" + text = "giggles" + selfText = "giggle" + audible = 1 + mimeText = "giggles silently" + muzzleAffected = 1 + +/datum/emote/giggle/New() + ..() + commands += "giggle" + commands += "giggles" + +/datum/emote/giggle/available(var/mob/user) + if(ishuman(user)) + return 1 + +/datum/emote/glare + name = "glare" + desc = "Makes the mob glare" + text = "glares" + selfText = "glare" + canTarget = 1 + targetMob = 1 + +/datum/emote/glare/New() + ..() + commands += "glare" + commands += "glares" + +/datum/emote/glare/available(var/mob/user) + if(ishuman(user)) + return 1 + if(isrobot(user)) + return 1 + +/datum/emote/gnarl + name = "gnarl" + desc = "Makes the mob grarl and show its teeth" + text = "gnarls and shows its teeth" + selfText = "gnarl and show your teeth" + audible = 1 + muzzleAffected = 1 + +/datum/emote/gnarl/New() + ..() + commands += "gnarl" + commands += "gnarls" + +/datum/emote/gnarl/available(var/mob/user) + if(islarva(user)) + return 1 + +/datum/emote/grin + name = "grin" + desc = "Makes the mob grin" + text = "grins" + selfText = "grin" + +/datum/emote/grin/New() + ..() + commands += "grin" + commands += "grins" + +/datum/emote/grin/available(var/mob/user) + if(ishuman(user)) + return 1 + +/datum/emote/groan + name = "groan" + desc = "Makes the mob groan" + text = "groans" + selfText = "groan" + audible = 1 + mimeText = "appears to groan" + mimeSelf = "appear to groan" + muzzleAffected = 1 + +/datum/emote/groan/New() + ..() + commands += "groan" + commands += "groans" + +/datum/emote/available(var/mob/user) + if(ishuman(user)) + return 1 + +/datum/emote/grumble + name = "grumble" + desc = "Makes the mob grumble" + text = "grumbles" + selfText = "grumble" + audible = 1 + mimeText = "grumbles" + muzzleAffected = 1 + +/datum/emote/grumble/New() + ..() + commands += "grumble" + commands += "grumbles" + +/datum/emote/grumble/available(var/mob/user) + if(ishuman(user)) + return 1 + +/datum/emote/halt + name = "halt" + desc = "Makes the mob sound a halt warning. Only available with a security module" + text = "'s speakers skreech, \"Halt! Security!\"." + selfText = " speakers skreech, \"Halt! Security!\"." + audible = 1 + sound = 'sound/voice/halt.ogg' + +/datum/emote/halt/New() + ..() + commands += "halt" + +/datum/emote/halt/available(var/mob/user) + if(isrobot(user)) + return 1 + +/datum/emote/halt/prevented(var/mob/user) + . = ..() + if(.) + return + var/mob/living/silicon/robot/U = user + if (!(istype(U.module, /obj/item/weapon/robot_module/security))) + return "you are not security" + +/datum/emote/halt/standardMessage(var/mob/user) + var/message = "\The [user][text]" + return message + +/datum/emote/handshake + name = "handshake" + desc = "Makes the mob shake hands with a target" + text = "shakes hands" + selfText = "shake hands" + canTarget = 1 + targetMob = 1 + targetText = "with" + restrained = 1 + +/datum/emote/handshake/New() + ..() + commands += "handshake" + +/datum/emote/handshake/prevented(var/mob/user) + . = ..() + if(!. && user.r_hand) + return "you need your right hand free" + +/datum/emote/handshake/getMobTarget(var/mob/user) + var/mob/target = ..() + if(!target) + to_chat(user, "You need someone to shake hands with") + return "invalid" + +/datum/emote/handshake/addTarget(var/mob/user, var/list/params, var/message) + var/mob/target = params["target"] + if(target.r_hand) + message = "\The [user] holds out his hand to [params["target"]]" + return message + return ..() + +/datum/emote/hiss + name = "hiss" + desc = "Makes the mob hiss" + text = "hisses" + selfText = "hiss" + audible = 1 + +/datum/emote/hiss/New() + ..() + commands += "hiss" + commands += "hisses" + +/datum/emote/hiss/available(var/mob/user) + if(isalienadult(user) || islarva(user)) + return 1 + +/datum/emote/hug + name = "hug" + desc = "Makes the mob hug a target or themselves" + text = "hugs" + selfText = "hug" + canTarget = 1 + targetMob = 1 + +/datum/emote/hug/New() + ..() + commands += "hug" + commands += "hugs" + +/datum/emote/hug/available(var/mob/user) + if(ishuman(user)) + return 1 + +/datum/emote/hug/getTarget(var/mob/user) + var/mob/target = input("Select target", "Target Mob") as null|mob in view(1) + if(target) + return target + return user + +/datum/emote/jiggle + name = "jiggle" + desc = "Makes the mob jiggle" + text = "jiggles" + selfText = "jiggle" + +/datum/emote/jiggle/New() + ..() + commands += "jiggle" + commands += "jiggles" + +/datum/emote/jiggle/available(var/mob/user) + if(isslime(user)) + return 1 + +/datum/emote/johnny + name = "johnny" + desc = "Yeah, just try it" + text = "takes a drag from a cigarette and blows their name out in smoke." + selfText = "take a drag from a cigarette and blow their name out in smoke." + audible = 1 + mimeText = "takes a drag from a cigarette and blows" + mimeSelf = "take a drag from a cigarette and blow" + canTarget = 1 + targetMob = 1 + targetText = "" + +/datum/emote/johnny/New() + ..() + commands += "johnny" + +/datum/emote/johnny/available(var/mob/user) + if(ishuman(user)) + return 1 + +/datum/emote/johnny/getTarget(var/mob/user) + var/mob/target = ..() + if(target && target != user) + return target + to_chat(user, "You need a target that isn't yourself") + return "invalid" + +/datum/emote/johnny/createMessage(var/mob/user, var/params) + if(doMime(user)) + return ..() + var/message = "\The [user] says \"[params["target"]], please. They had a family.\" [user] [text]" + return message + +/datum/emote/johnny/createSelfMessage(var/mob/user, var/list/params, var/message = "") + message = ..() + message = replacetext(message, "says", "say") + return message + +/datum/emote/johnny/createDeafMessage(var/mob/user, vap/list/params, var/message) + message = "\The [user] says something, then [text]" + return message + +/datum/emote/johnny/replaceMobWithYou(var/mob/M, var/message, var/mob/user) + if(user && M != user) + return message + return ..() + +/datum/emote/jump + name = "jump" + desc = "Makes the mob jump" + text = "jumps" + selfText = "jump" + +/datum/emote/jump/New() + ..() + commands += "jump" + commands += "jumps" + +/datum/emote/jump/available(var/mob/user) + if(islarva(user)) + return 1 + +/datum/emote/laugh + name = "laugh" + desc = "Makes the mob laugh" + text = "laughs" + selfText = "laugh" + audible = 1 + mimeText = "acts out a laugh" + mimeSelf = "act out a laugh" + muzzleAffected = 1 + +/datum/emote/laugh/New() + ..() + commands += "laugh" + commands += "laughs" + +/datum/emote/laugh/available(var/mob/user) + if(ishuman(user)) + return 1 + +/datum/emote/law + name = "law" + desc = "Makes the mob prove it is the law" + text = "shows its legal authorization barcode." + selfText = "show your legal authorization barcode." + audible = 1 + sound = 'sound/voice/biamthelaw.ogg' + +/datum/emote/law/New() + ..() + commands += "law" + +/datum/emote/law/available(var/mob/user) + if(isrobot(user)) + return 1 + +/datum/emote/law/prevented(var/mob/user) + . = ..() + if(.) + return + var/mob/living/silicon/robot/U = user + if (!(istype(U.module, /obj/item/weapon/robot_module/security))) + return "You are not THE LAW, pal." + +/datum/emote/light + name = "light" + desc = "makes the mob light up" + text = "lights up for a bit, then stops." + selfText = "light up for a bit, then stop." + +/datum/emote/light/New() + ..() + commands += "light" + commands += "lights" + +/datum/emote/light/available(var/mob/user) + if(isslime(user)) + return 1 + +/datum/emote/look + name = "look" + desc = "Makes the mob look" + text = "looks" + selfText = "look" + canTarget = 1 + targetMob = 1 + +/datum/emote/look/New() + ..() + commands += "look" + commands += "looks" + +/datum/emote/look/available(var/mob/user) + if(ishuman(user)) + return 1 + if(isrobot(user)) + return 1 + +/datum/emote/moan + name = "moan" + desc = "Makes the mob moan" + text = "moans" + selfText = "moan" + audible = 1 + +/datum/emote/moan/New() + ..() + commands += "moan" + commands += "moans" + +/datum/emote/moan/available(var/mob/user) + if(isslime(user)) + return 1 + if(islarva(user)) + return 1 + +/datum/emote/mumble + name = "mumble" + desc = "Makes the mob mumble" + text = "mumbles" + selfText = "mumble" + audible = 1 + mimeText = "mumbles" + +/datum/emote/mumble/New() + ..() + commands += "mumble" + commands += "mumbles" + +/datum/emote/mumble/available(var/mob/user) + if(ishuman(user)) + return 1 + +/datum/emote/no + name = "no" + desc = "Makes the mob let out a negative blip" + text = "emits a negative blip" + selfText = "emit a negative blip" + audible = 1 + sound = 'sound/machines/synth_no.ogg' + +/datum/emote/no/New() + ..() + commands += "no" + +/datum/emote/no/available(var/mob/user) + if(user.is_mechanical()) + return 1 + +/datum/emote/nod + name = "nod" + desc = "Makes the mob nod" + text = "nods" + selfText = "nod" + +/datum/emote/nod/New() + ..() + commands += "nod" + commands += "nods" + +/datum/emote/nod/available(var/mob/user) + if(islarva(user)) + return 1 + if(isrobot(user)) + return 1 + if(ishuman(user)) + return 1 + +/datum/emote/notice + name = "notice" + desc = "Makes the mob play a loud tone" + text = "plays a loud tone" + selfText = "play a loud tone" + audible = 1 + +/datum/emote/notice/New() + ..() + commands += "notice" + +/datum/emote/notice/available(var/mob/user) + if(isbrain(user)) + return 1 + +/datum/emote/pale + name = "pale" + desc = "Makes the mob go pale" + text = "goes pale for a second." + selfText = "go pale for a second" + +/datum/emote/pale/New() + ..() + commands += "pale" + commands += "pales" + +/datum/emote/pale/available(var/mob/user) + if(ishuman(user)) + return 1 + +/datum/emote/ping + name = "ping" + desc = "Makes the mob ping" + text = "pings" + selfText = "ping" + audible = 1 + sound = 'sound/machines/ping.ogg' + canTarget = 1 + targetMob = 1 + +/datum/emote/ping/New() + ..() + commands += "ping" + commands += "pings" + +/datum/emote/ping/available(var/mob/user) + if(user.is_mechanical()) + return 1 + +/datum/emote/point + name = "point" + desc = "Makes the mob point" + text = "points" + selfText = "point" + canTarget = 1 + +/datum/emote/point/New() + ..() + commands += "point" + commands += "points" + +/datum/emote/point/available(var/mob/user) + if(ishuman(user)) + return 1 + +/datum/emote/point/createMessage(var/mob/user, var/list/params) + if(params["target"]) + return + return ..() + +/datum/emote/point/doAction(var/mob/user, var/list/params) + if(!params["target"]) + return + user.pointed(params["target"]) + +/datum/emote/quiver + name = "quiver" + desc = "Makes the mob quiver" + text = "quivers" + selfText = "quiver" + +/datum/emote/quiver/New() + ..() + commands += "quiver" + commands += "quivers" + +/datum/emote/quiver/available(var/mob/user) + if(ishuman(user)) + return 1 + +/datum/emote/raise + name = "raise" + desc = "Makes the mob raise a hand" + text = "raises a hand" + selfText = "raise a hand" + restrained = 1 + +/datum/emote/raise/New(var/mob/user) + ..() + commands += "raise" + commands += "raises" + +/datum/emote/raise/available(var/mob/user) + if(ishuman(user)) + return 1 + +/datum/emote/roar + name = "roar" + desc = "Makes the mob roar" + text = "roars" + selfText = "roar" + audible = 1 + muzzleAffected = 1 + muzzledNoise = "loud" + +/datum/emote/roar/New() + ..() + commands += "roar" + commands += "roars" + +/datum/emote/roar/available(var/mob/user) + if(isalienadult(user)) + return 1 + +/datum/emote/roll + name = "roll" + desc = "Makes the mob roll" + text = "rolls" + selfText = "roll" + +/datum/emote/roll/New() + ..() + commands += "roll" + commands += "rolls" + +/datum/emote/roll/available(var/mob/user) + if(islarva(user)) + return 1 + +/datum/emote/salute + name = "salute" + desc = "Makes the mob salute" + text = "salutes" + selfText = "salute" + canTarget = 1 + targetMob = 1 + targetText = "to" + +/datum/emote/salute/New() + ..() + commands += "salute" + commands += "salutes" + +/datum/emote/salute/available(var/mob/user) + if(ishuman(user)) + return 1 + if(isrobot(user)) + return 1 + +/datum/emote/salute/prevented(var/mob/user) + . = ..() + if(!. && user.buckled) + return "you are buckled to something" + +/datum/emote/scratch + name = "scratch" + desc = "Makes the mob scratch" + text = "scratches" + selfText = "scratch" + restrained = 1 + +/datum/emote/scratch/New() + ..() + commands += "scratch" + commands += "scratches" + +/datum/emote/scratch/available(var/mob/user) + if(islarva(user) || isalienadult(user)) return 1 /datum/emote/scream name = "scream" - text = "screams!" - selfText = "scream!" audible = 1 mimeText = "acts out a scream" - muzzledNoise = "very loud " + muzzledNoise = "very loud" cooldown = 50 vol = 80 @@ -232,18 +1403,12 @@ above it. If you don't want this, make the call to ..() then use commands = new commands += "scream" commands += "screams" -/datum/emote/scream/available(var/mob/user) - if(isliving(user)) - return 1 - /datum/emote/scream/machine name = "machine scream" sound = 'sound/goonstation/voice/robot_scream.ogg' /datum/emote/scream/machine/available(var/mob/user) - if(issilicon(user)) - return 1 - if(istype(user, /mob/living/simple_animal/bot)) + if(user.is_mechanical()) return 1 /datum/emote/scream/human @@ -255,80 +1420,147 @@ above it. If you don't want this, make the call to ..() then use commands = new /datum/emote/scream/human/playSound(var/mob/user) var/mob/living/carbon/human/H = user + if(H.gender == FEMALE) playsound(H, "[H.species.female_scream_sound]", vol, 1, 0, pitch = H.get_age_pitch()) else playsound(H, "[H.species.male_scream_sound]", vol, 1, 0, pitch = H.get_age_pitch()) + return 1 -/datum/emote/fart - name = "fart" - text = "farts" - selfText = "fart" - cooldown = 50 +/datum/emote/scretch + name = "scretch" + desc = "makes the mob scretch" //whatever that is! + text = "scretches" + selfText = "scretch" + audible = 1 + muzzleAffected = 1 -/datum/emote/fart/New() +/datum/emote/scretch/New() ..() - commands += "fart" - commands += "farts" + commands += "scretch" + commands += "scretches" -/datum/emote/fart/available(var/mob/user) +/datum/emote/scretch/available(var/mob/user) + if(isalienadult(user) || islarva(user)) + return 1 + +/datum/emote/shake + name = "shake" + desc = "Makes the mob shake its head" + text = "shakes" + selfText = "shake" + +/datum/emote/shake/New() + ..() + commands += "shake" + commands += "shakes" + +/datum/emote/shake/available(var/mob/user) + if(islarva(user)) + return 1 if(ishuman(user)) return 1 -/datum/emote/fart/doAction(var/mob/user) - // todo change this to work with superfarts - if(TOXIC_FARTS in user.mutations) - for(var/mob/M in range(get_turf(user),2)) - if (M.internal != null && M.wear_mask && (M.wear_mask.flags & AIRTIGHT)) - continue - if (M == user) - continue - M.reagents.add_reagent("space_drugs",rand(1,10)) - - if(locate(/obj/item/weapon/storage/bible) in get_turf(user)) - to_chat(viewers(user), "[user] farts on the Bible!") - to_chat(viewers(user), "A mysterious force smites [user]!") - var/datum/effect/system/spark_spread/s = new /datum/effect/system/spark_spread - s.set_up(3, 1, user) - s.start() - user.gib() - -/datum/emote/fart/createMessage(var/mob/user) - var/message - if(TOXIC_FARTS in user.mutations) - message = "[user] unleashes a [pick("horrible","terrible","foul","disgusting","awful")] fart." - else - message = "[user] [pick("passes wind","farts")]." +/datum/emote/shake/standardMessage(var/mob/user) + var/message = ..() + message += " [getHis(user)] head" return message -/datum/emote/fart/createSelfMessage(var/mob/user, var/message) - message = ..() - message = replacetext(message, "unleashes", "unleash") - message = replacetext(message, "passes", "pass") - return message +/datum/emote/shiver + name = "shiver" + desc = "Makes the mob shiver" + text = "shivers" + selfText = "shiver" + audible = 1 + mimeText = "shivers" -/datum/emote/signal - name = "signal" - desc = "raise x number of fingers" - text = "raises" - selfText = "raise" - canTarget = 1 +/datum/emote/shiver/New() + ..() + commands += "shiver" + commands += "shivers" + +/datum/emote/shiver/available(var/mob/user) + if(isalienadult(user) || islarva(user)) + return 1 + if(isslime(user)) + return 1 + if(ishuman(user)) + return 1 + +/datum/emote/shrug + name = "shrug" + desc = "Makes the mob shrug" + text = "shrugs" + selfText = "shrug" + +/datum/emote/shrug/New() + ..() + commands += "shrug" + commands += "shrugs" + +/datum/emote/shrug/available(var/mob/user) + if(ishuman(user)) + return 1 + +/datum/emote/sigh + name = "sigh" + desc = "Makes the mob sigh" + text = "sighs" + selfText = "sigh" + audible = 1 + mimeText = "sighs" + muzzleAffected = 1 + muzzledNoise = "weak" + +/datum/emote/sigh/New() + ..() + commands += "sigh" + commands += "sighs" + +/datum/emote/sigh/available(var/mob/user) + if(ishuman(user)) + return 1 + +/datum/emote/sign + name = "sign" + desc = "Makes the mob sign a number" + text = "signs" + selfText = "sign" restrained = 1 - targetMob = 1 takesNumber = 1 -/datum/emote/signal/New() +/datum/emote/sign/New() ..() - commands += "signal" - commands += "signals" + commands += "sign" + commands += "signs" -/datum/emote/signal/available(var/mob/user) +/datum/emote/sign/available(var/mob/user) + if(isalienadult(user) || islarva(user)) + return 1 + +/datum/emote/sign/getNumber(var/mob/user) + var/number = ..() + if(number == null) + to_chat(user, "You need a number to sign") + return "invalid" + return number + +/datum/emote/sign/paramMessage(var/mob/user, var/list/params) + var/message = "\The [user] [text] [params["num"]]" + return message + +/datum/emote/sign/fingers + desc = "Makes the mob raise a number of fingers" + text = "raises" + selfText = "raise" + +/datum/emote/sign/fingers/available(var/mob/user) if(ishuman(user)) return 1 -/datum/emote/signal/getNumber(var/mob/user) +/datum/emote/sign/fingers/getNumber(var/mob/user) var/number = ..() - if(number == null) + if(number == "invalid") return "invalid" var/fingersAvailable = 0 if(!user.r_hand) @@ -340,6 +1572,481 @@ above it. If you don't want this, make the call to ..() then use commands = new return "invalid" return number -/datum/emote/signal/paramMessage(var/mob/user, var/list/params) - var/message = "[user] raises [params["num"]] finger\s" +/datum/emote/sign/fingers/paramMessage(var/mob/user, var/list/params) + var/message = "\The [user] [text] [params["num"]] finger\s" // no, we can't just add " finger\s" to the parent version because the text macro won't work then :( VB return message + +/datum/emote/slap + name = "slap" + desc = "makes the mob slap someone" + text = "slaps" + selfText = "slap" + audible = 1 + sound = 'sound/effects/snap.ogg' + canTarget = 1 + targetMob = 1 + targetText = "" + +/datum/emote/slap/New() + ..() + commands += "slap" + commands += "slaps" + +/datum/emote/slap/available(var/mob/user) + if(ishuman(user)) + return 1 + +/datum/emote/slap/getMobTarget(var/mob/user) + var/mob/target = input("Select target", "Target Mob") as null|mob in view(1) + if(!target) + target = user + return target + +/datum/emote/slap/doAction(var/mob/user, var/list/params) + if(user == params["target"]) + var/mob/living/U = user + U.adjustFireLoss(4) + +/datum/emote/smile + name = "smile" + desc = "Makes the mob smile" + text = "smiles" + selfText = "smile" + +/datum/emote/smile/New() + ..() + commands += "smile" + commands += "smiles" + +/datum/emote/smile/available(var/mob/user) + if(ishuman(user)) + return 1 + +/datum/emote/snap + name = "snap" + desc = "Makes the mob snap it's fingers" + text = "snaps" + selfText = "snap" + audible = 1 + sound = 'sound/effects/fingersnap.ogg' + +/datum/emote/snap/New() + ..() + commands += "snap" + commands += "snaps" + +/datum/emote/snap/available(var/mob/user) + if(ishuman(user)) + return 1 + +/datum/emote/snap/prevented(var/mob/user) + . = ..() + if(.) + return + var/mob/living/carbon/human/H = user + var/obj/item/organ/external/L = H.get_organ("l_hand") + var/obj/item/organ/external/R = H.get_organ("r_hand") + var/left_hand_good = 0 + var/right_hand_good = 0 + if(L && (!(L.status & ORGAN_DESTROYED)) && (!(L.status & ORGAN_SPLINTED)) && (!(L.status & ORGAN_BROKEN))) + left_hand_good = 1 + if(R && (!(R.status & ORGAN_DESTROYED)) && (!(R.status & ORGAN_SPLINTED)) && (!(R.status & ORGAN_BROKEN))) + right_hand_good = 1 + + if (!left_hand_good && !right_hand_good) + return "You need at least one hand in good working order to snap your fingers." + +/datum/emote/snap/standardMessage(var/mob/user, var/list/params) + var/message = ..() + message += " [getHis(user)] fingers" + params["prob"] = prob(5) + if(!params["prob"]) + return message + message += " right off!" + +/datum/emote/snap/playSound(var/mob/user, var/list/params) + if(params["prob"]) + playsound(user.loc, 'sound/effects/snap.ogg', 50, 1) + return 1 + return ..() + +/datum/emote/sneeze + name = "sneeze" + desc = "Makes the mob sneezze" + text = "sneezes" + selfText = "sneeze" + audible = 1 + mimeText = "sneeze" + muzzleAffected = 1 + muzzledNoise = "strange" + +/datum/emote/sneeze/New() + ..() + commands += "sneeze" + commands += "sneezes" + +/datum/emote/sneeze/available(var/mob/user) + if(ishuman(user)) + return 1 + +/datum/emote/sniff + name = "sniff" + desc = "Makes the mob sniff" + text = "sniffs" + selfText = "sniff" + audible = 1 + mimeText = "sniffs" + +/datum/emote/sniff/New() + ..() + commands += "sniff" + commands += "sniffs" + +/datum/emote/sniff/available(var/mob/user) + if(ishuman(user)) + return 1 + +/datum/emote/snore + name = "snore" + desc = "Makes the mob snore" + text = "snores" + selfText = "snore" + audible = 1 + mimeText = "sleeps soundly" + mimeSelf = "sleep soundly" + muzzleAffected = 1 + +/datum/emote/snore/New() + ..() + commands += "snore" + commands += "snores" + +/datum/emote/snore/available(var/mob/user) + if(ishuman(user)) + return 1 + +/datum/emote/squish + name = "squish" + desc = "Makes the mob squish" + text = "squishes" + selfText = "squish" + audible = 1 + sound = 'sound/effects/slime_squish.ogg' + canTarget = 1 + targetMob = 1 + +/datum/emote/squish/New() + ..() + commands += "squish" + commands += "squishes" + +/datum/emote/available(var/mob/user) + if(isslime(user)) + return 1 + if(ishuman(user)) + var/mob/living/carbon/human/H = user + if(H.species.name == "Slime People") //Only Slime People can squish + return 1 + for(var/obj/item/organ/external/L in H.organs) // if your limbs are squishy you can squish too! + if(L.dna.species =="Slime People") + return 1 + +/datum/emote/stare + name = "stare" + desc = "Makes the mob stare" + text = "stares" + selfText = "stare" + canTarget = 1 + targetMob = 1 + +/datum/emote/stare/New() + ..() + commands += "stare" + commands += "stares" + +/datum/emote/stare/available(var/mob/user) + if(ishuman(user)) + return 1 + if(isrobot(user)) + return 1 + +/datum/emote/sulk + name = "sulk" + desc = "Makes the mob sulk" + text = "sulks down sadly" + selfText = "sulk down sadly" + +/datum/emote/sulk/New() + ..() + commands += "sulk" + commands += "sulks" + +/datum/emote/sulk/available(var/mob/user) + if(islarva(user)) + return 1 + +/datum/emote/sway + name = "sway" + desc = "Makes the mob sway" + text = "sways around dizzily" + selfText = "sway around dizzily" + +/datum/emote/sway/New() + ..() + commands += "sway" + commands += "sways" + +/datum/emote/sway/available(var/mob/user) + if(islarva(user)) + return 1 + if(isslime(user)) + return 1 + +/datum/emote/tail + name = "tail" + desc = "Makes the mob wave it's tail" + text = "waves it's tail" + selfText = "wave your tail" + +/datum/emote/tail/New() + ..() + commands += "tail" + +/datum/emote/tail/available(var/mob/user) + if(islarva(user) || isalienadult(user)) + return 1 + +/datum/emote/tail/wag + name = "wag" + desc = "Makes the mob start wagging its tail" + text = "starts" + selfText = "start" + +/datum/emote/tail/wag/New() + ..() + commands = new /list() + commands += "wag" + commands += "wags" + +/datum/emote/tail/wag/available(var/mob/user) + if(ishuman(user)) + return 1 + +/datum/emote/tail/wag/prevented(var/mob/user) + . = ..() + if(.) + return + var/mob/living/carbon/human/H = user + if(H.species.bodyflags & TAIL_WAGGING) + if(H.wear_suit && (H.wear_suit.flags_inv & HIDETAIL || istype(H.wear_suit, /obj/item/clothing/suit/space))) + return "your clothing is stopping you wag your tail" + return + if(!H.body_accessory) + return "you have no tail to wag!" + if(!H.body_accessory.try_restrictions()) + return "your clothing is stopping you wag your tail" + +/datum/emote/tail/wag/standardMessage(var/mob/user, var/list/params) + var/message = ..() + message += " wagging [getHis(user)] tail" + return message + +/datum/emote/tail/wag/doAction(var/mob/user, var/list/params) + var/mob/living/carbon/human/H = user + H.start_tail_wagging(1) + +/datum/emote/tail/wag/stop + name = "swag" + desc = "Makes the mob stop wagging its tail" + text = "stops" + selfText = "stop" + allowParent = 1 + +/datum/emote/tail/wag/stop/New() + ..() + commands = new /list() + commands += "swag" + commands += "swags" + +// seemingly no way to tell if a mob is wagging it's tail! VB +/datum/emote/tail/wag/stop/available(var/mob/user) + var/mob/living/carbon/human/H = user + if(!H.species.bodyflags & TAIL_WAGGING && !H.body_accessory) + return "you can't stop wagging a tail you don't have!" + +/datum/emote/tail/wag/stop/doAction(var/mob/user, var/list/params) + var/mob/living/carbon/human/H = user + H.stop_tail_wagging(1) + +/datum/emote/tremble + name = "tremble" + desc = "Makes the mob tremble" + text = "trembles" + selfText = "tremble" + +/datum/emote/tremble/New() + ..() + commands += "tremble" + commands += "trembles" + +/datum/emote/tremble/available(var/mob/user) + if(ishuman(user)) + return 1 + +/datum/emote/twitch_s + name = "twitch_s" + desc = "Makes the mob twitch" + text = "twitches" + selfText = "twitch" + +/datum/emote/twitch_s/New() + ..() + commands += "twitch_s" + commands += "twitches_s" + +/datum/emote/twitch_s/available(var/mob/user) + if(ishuman(user)) + return 1 + if(isrobot(user)) + return 1 + +/datum/emote/twitch_s/twitch + name = "twitch" + desc = "Makes the mob twitch violently" + allowParent = 1 + +/datum/emote/twitch_s/twitch/New() + ..() + commands = new /list() + commands += "twitch" + commands += "twitches" + +/datum/emote/twitch_s/twitch/available(var/mob/user) + if(isslime(user)) + return 1 + if(islarva(user)) + return 1 + return ..() + +/datum/emote/twitch_s/twitch/standardMessage(var/mob/user, var/list/params) + var/message = ..() + message += " violently" + return message + +/datum/emote/vibrate + name = "vibrate" + desc = "Makes the mob vibrate" + text = "vibrates" + selfText = "vibrate" + +/datum/emote/vibrate/New() + ..() + commands += "vibrate" + commands += "vibrates" + +/datum/emote/vibrate/available(var/mob/user) + if(isslime(user)) + return 1 + +/datum/emote/wave + name = "wave" + desc = "Makes the mob wave" + text = "waves" + selfText = "wave" + +/datum/emote/wave/New() + ..() + commands += "wave" + commands += "waves" + +/datum/emote/wave/available(var/mob/user) + if(ishuman(user)) + return 1 + +/datum/emote/whimper + name = "whimper" + desc = "Makes the mob whimper" + text = "whimpers" + selfText = "whimper" + audible = 1 + mimeText = "appears hurt" + mimeSelf = "appear hurt" + muzzleAffected = 1 + +/datum/emote/whimper/New() + ..() + commands += "whimper" + commands += "whimpers" + +/datum/emote/whimper/available(var/mob/user) + if(islarva(user) || isalienadult(user)) + return 1 + if(ishuman(user)) + return 1 + +/datum/emote/whistle + name = "whistle" + desc = "Makes the mob whistle" + text = "whistles" + selfText = "whistle" + audible = 1 + +/datum/emote/whistle/New() + ..() + commands += "whistle" + commands += "whistles" + +/datum/emote/whistle/available(var/mob/user) + if(isbrain(user)) + return 1 + +/datum/emote/wink + name = "wink" + desc = "Makes the mob wink" + text = "winks" + desc = "wink" + +/datum/emote/wink/New() + ..() + commands += "wink" + commands += "winks" + +/datum/emote/wink/available(var/mob/user) + if(ishuman(user)) + return 1 + +/datum/emote/yawn + name = "yawn" + desc = "makes the mob yawn" + text = "yawns" + selfText = "yawn" + audible = 1 + mimeText = "yawns" + muzzleAffected = 1 + +/datum/emote/yawn/New() + ..() + commands += "yawn" + commands += "yawns" + +/datum/emote/yawn/available(var/mob/user) + if(ishuman(user)) + return 1 + +/datum/emote/yes + name = "yes" + desc = "Makes the mob let out an affirmative beep" + text = "lets out an affirmative beep" + selfText = "let out an affirmative beep" + audible = 1 + sound = 'sound/machines/synth_yes.ogg' + canTarget = 1 + targetMob = 1 + +/datum/emote/yes/New() + ..() + commands += "yes" + +/datum/emote/yes/available(var/mob/user) + if(user.is_mechanical()) + return 1 \ No newline at end of file From ac9eac44a183785c4a74690ad0de624fc2e7540d Mon Sep 17 00:00:00 2001 From: VampyrBytes Date: Sat, 28 May 2016 05:55:59 +0100 Subject: [PATCH 015/275] Adds support for holopad emoting --- code/datums/Emote_system/emote.dm | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/code/datums/Emote_system/emote.dm b/code/datums/Emote_system/emote.dm index a8da598c573..3015d42f4ac 100644 --- a/code/datums/Emote_system/emote.dm +++ b/code/datums/Emote_system/emote.dm @@ -264,10 +264,14 @@ VampyrBytes if(doMime(user)) visualOrAudible = 1 + var/location = checkForHolopad(user) + if(!location) + location = user + log_emote("[user.name]/[user.key] : [message]") sendToDead(message) testing(message) - for(var/mob/M in getRecipients(user, visualOrAudible)) + for(var/mob/M in getRecipients(location, visualOrAudible)) var/msg = "" if(M==user) @@ -311,17 +315,27 @@ VampyrBytes continue msg = message - outputMessage(M, msg) + outputMessage(M, msg, user) if(visualOrAudible == 2) handleListeningObjects(user, message) return visualOrAudible -/datum/emote/proc/outputMessage(var/mob/M, var/msg = "") - msg = replaceMobWithYou(M, msg, M) +/datum/emote/proc/outputMessage(var/mob/M, var/msg = "", var/mob/user) + msg = replaceMobWithYou(M, msg, user) to_chat(M, msg) +/datum/emote/proc/checkForHolopad(var/mob/user, var/message) + if(!isAI(user)) + return + var/mob/living/silicon/ai/AI = user + var/obj/machinery/hologram/holopad/T = AI.holo + if(!(T && T.hologram && T.master == AI)) + return + to_chat(AI, "Holopad action relayed, [AI.real_name] [message]") + return T + /datum/emote/proc/handleListeningObjects(var/mob/user, var/message = "") // based on say code var/omsg = replacetext(message, "[user] ", "") @@ -337,10 +351,10 @@ VampyrBytes for(var/obj/O in listening_obj) O.hear_message(user, omsg) -/datum/emote/proc/getRecipients(var/mob/user, var/visualOrAudible) +/datum/emote/proc/getRecipients(var/location, var/visualOrAudible) if(visualOrAudible == 1) - return viewers(user) - return get_mobs_in_view(HEARING_RANGE, user) + return viewers(location) + return get_mobs_in_view(HEARING_RANGE, location) // set up different messages for blind people here. Empty will mean no message for //non-audible emotes and the standard message for audible ones From a547781fb166e02a5471d79ae3515352c1ebe3c5 Mon Sep 17 00:00:00 2001 From: Matt Date: Mon, 30 May 2016 16:55:59 +0200 Subject: [PATCH 016/275] Fixes a grave mistake --- code/game/machinery/recycler.dm | 6 +++--- sound/machines/recycler.ogg | Bin 0 -> 17157 bytes 2 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 sound/machines/recycler.ogg diff --git a/code/game/machinery/recycler.dm b/code/game/machinery/recycler.dm index 9bcf3de53a8..2db551b67fa 100644 --- a/code/game/machinery/recycler.dm +++ b/code/game/machinery/recycler.dm @@ -129,7 +129,7 @@ var/const/SAFETY_COOLDOWN = 100 return if(sound) - playsound(src.loc, 'sound/items/Welder.ogg', 50, 1) + playsound(src.loc, 'sound/machines/recycler.ogg', 100, 0) var/material_amount = materials.get_item_material_amount(I) if(!material_amount) qdel(I) @@ -155,7 +155,7 @@ var/const/SAFETY_COOLDOWN = 100 L.loc = src.loc if(issilicon(L)) - playsound(src.loc, 'sound/items/Welder.ogg', 50, 1) + playsound(src.loc, 'sound/machines/recycler.ogg', 100, 0) else playsound(src.loc, 'sound/effects/splat.ogg', 50, 1) @@ -219,4 +219,4 @@ var/const/SAFETY_COOLDOWN = 100 /obj/item/weapon/paper/recycler name = "paper - 'garbage duty instructions'" - info = "

New Assignment

You have been assigned to collect garbage from trash bins, located around the station. The crewmembers will put their trash into it and you will collect the said trash.

There is a recycling machine near your closet, inside maintenance; use it to recycle the trash for a small chance to get useful minerals. Then deliver these minerals to cargo or engineering. You are our last hope for a clean station, do not screw this up!" \ No newline at end of file + info = "

New Assignment

You have been assigned to collect garbage from trash bins, located around the station. The crewmembers will put their trash into it and you will collect the said trash.

There is a recycling machine near your closet, inside maintenance; use it to recycle the trash for a small chance to get useful minerals. Then deliver these minerals to cargo or engineering. You are our last hope for a clean station, do not screw this up!" diff --git a/sound/machines/recycler.ogg b/sound/machines/recycler.ogg new file mode 100644 index 0000000000000000000000000000000000000000..d4f2c88408741eddd39f7e024456e9656303ab1c GIT binary patch literal 17157 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 Date: Tue, 31 May 2016 12:52:56 +0200 Subject: [PATCH 017/275] Tweaks some things regarding the change notification --- code/modules/client/client procs.dm | 3 ++- code/modules/client/preferences_mysql.dm | 2 ++ code/modules/mob/new_player/login.dm | 4 ---- interface/interface.dm | 4 +--- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm index 768de2a270b..7e180419dc0 100644 --- a/code/modules/client/client procs.dm +++ b/code/modules/client/client procs.dm @@ -316,8 +316,9 @@ send_resources() if(prefs.lastchangelog != changelog_hash) //bolds the changelog button on the interface so we know there are updates. -CP + if(!setup_database_connection()) + return // if we have db problems, don't show anything winset(src, "rpane.changelog", "background-color=#f4aa94;font-style=bold") - prefs.SetChangelog(src,changelog_hash) to_chat(src, "Changelog has changed since your last visit.") if(!void) diff --git a/code/modules/client/preferences_mysql.dm b/code/modules/client/preferences_mysql.dm index 07db4bf7128..15abdab0360 100644 --- a/code/modules/client/preferences_mysql.dm +++ b/code/modules/client/preferences_mysql.dm @@ -456,10 +456,12 @@ /datum/preferences/proc/SetChangelog(client/C,hash) lastchangelog=hash + winset(C, "rpane.changelog", "background-color=none;font-style=") var/DBQuery/query = dbcon.NewQuery("UPDATE [format_table_name("player")] SET lastchangelog='[lastchangelog]' WHERE ckey='[C.ckey]'") if(!query.Execute()) var/err = query.ErrorMsg() log_game("SQL ERROR during lastchangelog updating. Error : \[[err]\]\n") message_admins("SQL ERROR during lastchangelog updating. Error : \[[err]\]\n") + to_chat(C, "Couldn't update your last seen changelog, please try again later.") return return 1 diff --git a/code/modules/mob/new_player/login.dm b/code/modules/mob/new_player/login.dm index bbb0211ce67..f7cff248519 100644 --- a/code/modules/mob/new_player/login.dm +++ b/code/modules/mob/new_player/login.dm @@ -34,10 +34,6 @@ verbs += /client/proc/readmin spawn(40) if(client) - if(client.prefs.lastchangelog != changelog_hash) //bolds the changelog button on the interface so we know there are updates. -CP - winset(client, "rpane.changelog", "background-color=#f4aa94;font-style=bold") - client.prefs.SetChangelog(client,changelog_hash) - to_chat(src, "Changelog has changed since your last visit.") client.playtitlemusic() if(config.player_overflow_cap && config.overflow_server_url) //Overflow rerouting, if set, forces players to be moved to a different server once a player cap is reached. Less rough than a pure kick. diff --git a/interface/interface.dm b/interface/interface.dm index 589a5e0dc86..81fe75b269b 100644 --- a/interface/interface.dm +++ b/interface/interface.dm @@ -40,9 +40,7 @@ src << browse('html/changelog.html', "window=changes;size=675x650") if(prefs.lastchangelog != changelog_hash) //if it's already opened, no need to tell them they have unread changes - prefs.lastchangelog = changelog_hash - prefs.save_preferences() - winset(src, "rpane.changelog", "background-color=none;font-style=") + prefs.SetChangelog(src,changelog_hash) /client/verb/forum() set name = "forum" From 3a2be3d63f63746aa16cf1a82ecb074342871ff5 Mon Sep 17 00:00:00 2001 From: Matt Milosevic Date: Tue, 31 May 2016 23:15:20 +0200 Subject: [PATCH 018/275] Adds a check for twohanded items to cuffing --- code/modules/mob/living/carbon/carbon.dm | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 5a641a81689..25234143cc4 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -863,6 +863,12 @@ var/list/ventcrawl_machinery = list(/obj/machinery/atmospherics/unary/vent_pump, //called when we get cuffed/uncuffed /mob/living/carbon/proc/update_handcuffed() if(handcuffed) + //we don't want problems with nodrop shit if there ever is more than one nodrop twohanded + var/obj/item/I = get_active_hand() + if(istype(I, /obj/item/weapon/twohanded)) + var/obj/item/weapon/twohanded/TH = I //FML + if(TH.wielded) + TH.unwield() drop_r_hand() drop_l_hand() stop_pulling() From bc7cee537fe8365687325dc24f18106286989e5b Mon Sep 17 00:00:00 2001 From: Fox-McCloud Date: Tue, 31 May 2016 22:15:41 -0400 Subject: [PATCH 019/275] Ports 3 New Chaplain Weapons --- .../objects/items/weapons/holy_weapons.dm | 56 ++++++++++++++++++ icons/mob/back.dmi | Bin 90019 -> 90954 bytes icons/mob/inhands/items_lefthand.dmi | Bin 168196 -> 169503 bytes icons/mob/inhands/items_righthand.dmi | Bin 165849 -> 167011 bytes icons/obj/weapons.dmi | Bin 55328 -> 61347 bytes 5 files changed, 56 insertions(+) diff --git a/code/game/objects/items/weapons/holy_weapons.dm b/code/game/objects/items/weapons/holy_weapons.dm index 879e82f9ee0..4b0b1e9ba8d 100644 --- a/code/game/objects/items/weapons/holy_weapons.dm +++ b/code/game/objects/items/weapons/holy_weapons.dm @@ -134,6 +134,13 @@ item_state = "katana" slot_flags = SLOT_BELT | SLOT_BACK +/obj/item/weapon/nullrod/claymore/multiverse + name = "extradimensional blade" + desc = "Once the harbringer of a interdimensional war, now a dormant souvenir. Still sharp though." + icon_state = "multiverse" + item_state = "multiverse" + slot_flags = SLOT_BELT + /obj/item/weapon/nullrod/claymore/saber name = "light energy sword" hitsound = 'sound/weapons/blade1.ogg' @@ -148,6 +155,12 @@ item_state = "swordred" desc = "Woefully ineffective when used on steep terrain." +/obj/item/weapon/nullrod/claymore/saber/pirate + name = "nautical energy sword" + icon_state = "cutlass1" + item_state = "cutlass1" + desc = "Convincing HR that your religion involved piracy was no mean feat." + /obj/item/weapon/nullrod/sord name = "\improper UNREAL SORD" desc = "This thing is so unspeakably HOLY you are having a hard time even holding it." @@ -179,6 +192,49 @@ desc = "Bad references are the DNA of the soul." attack_verb = list("chopped", "sliced", "cut", "zandatsu'd") +/obj/item/weapon/nullrod/scythe/talking + icon_state = "talking_sword" + item_state = "talking_sword" + name = "possessed blade" + desc = "When the station falls into chaos, it's nice to have a friend by your side." + attack_verb = list("chopped", "sliced", "cut") + hitsound = 'sound/weapons/bladeslice.ogg' + var/possessed = FALSE + +/obj/item/weapon/nullrod/scythe/talking/attack_self(mob/living/user) + if(possessed) + return + + to_chat(user, "You attempt to wake the spirit of the blade...") + + possessed = TRUE + + var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as the spirit of [user.real_name]'s blade?", ROLE_PAI, 0, 100) + var/mob/dead/observer/theghost = null + + if(candidates.len) + theghost = pick(candidates) + var/mob/living/simple_animal/shade/S = new(src) + S.real_name = name + S.name = name + S.ckey = theghost.ckey + var/input = stripped_input(S,"What are you named?", ,"", MAX_NAME_LEN) + + if(src && input) + name = input + S.real_name = input + S.name = input + else + to_chat(user, "The blade is dormant. Maybe you can try again later.") + possessed = FALSE + +/obj/item/weapon/nullrod/scythe/talking/Destroy() + for(var/mob/living/simple_animal/shade/S in contents) + to_chat(S, "You were destroyed!") + S.ghostize() + qdel(S) + return ..() + /obj/item/weapon/nullrod/hammmer icon_state = "hammeron" item_state = "hammeron" diff --git a/icons/mob/back.dmi b/icons/mob/back.dmi index ce634c701d968aee8e0f67c90ca3828c6233875e..a4cd1d997a858c83a04164db7e0324613e835f1a 100644 GIT binary patch delta 5401 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

M4r+mc=ifQLf32@>A55s;%* zX<`80VG9${+ER_0ZTj4^m1R>=i-(rJs2rb>$V$`+h5Sn*C03I{QkU6VVJqCc0ntgB zIOy_<=2$((pn|O*Be&e6t`tvo!d+)y{-A?OOA+IRH{pjUB&fcE2yv=*ARnw7hn;Yt zoBXL}DvI2rCRC%T;9icS?-d?PP(C|S@Pq}^?K`Ok=C^74+8mX4FR-#I{4?vzMWU8Y zhhtvST=&-kcMFmdlNKx(gpZjmpIZyqeJwAfs5nz%cqw#FQpyb#JtwHQ!kVq(=d&V>H;89V9$`iQmQBrXNVK}q zGxKf4k>OvC-Y2<{HC)GtMt`pzmuC11ec0}$=Cw6@9X-7;WdVyV#!Gl!7B121Dd(mx z%*|t!a9^&$3j@zjgU1x2tbAGjR~C(#kB@9C|1rt3YO>5*3Y_SLD;?AK$-8hf;3OEK jGpzq6m9p~x^aLX0RM7|J*1Cu&fU+K|s4JH#nFap`8gbQS delta 4459 zcmZWsc{r49)PKgFNQG?K@`hI;6mRz8rIe)@OO{btvTxb4JeHDD4cU@C%fw)8Su>-B zNkYO5$-Wz78)Kd28@$(deb@D!Kc4Hl&wcK*{LZ=WbDr<*tOXd>#J?aXtIo2poa**o z#!mL7V<*>0ee1B14+g)88%N_}ud~!&XMsG@WfJbMZ_|h1kANDlUQRZV09v;E;O!@| zo(ge%NA%9VU`jvgHf`3yE^h@@@V6fzy!>6m85D3t+c5gn+1?K2Qj-%WP6|x-o9Gei zo*5TFOUv-Tie#VuOl`Z^bh2AO=4%!3w;&(hvBS3=ZLRtw7%UqL392F3ub|abn)j&J z&b5iCtPo#_|G$rFuc#+}!2l!EkY3gK+ka62_8d!3o~;}UykkWeixErX>l z40*3XOzzT08XppcQuMf*#|1`2M0~HP@9rsg;(a5aK;#3=E^=+3Gs#?`6Mr)m>1;H= z$`x>ZTi@s+5z6d9`uv#iC2lLl>M`>3_dQoH#QZ(AVmvs)0yNCnEvZL30IkmnNG0-v zf}O|)2V&zq(M=$bo84|L12S&6=S^zz)LBL6BSBn{iG741+-HOn@a;U10#r>YtHM>VP)7Pe7tLMQqN-tUT8KdTpCI5IAhM1R=XxSI2pzPA7d z0Otfo(?P3&THTMqP7&=9UheKEv$L}+wtoQQq@5prLYz;y?5Q<%q*i7g$AMBP0MutC z6c4iIn9tLV|1FIU>TskB7bA)q4)b8c|*6RD*N7fe=Pdac_|}r zY2_L2kT#l?7&8!W`(#y&^gOy4a?{w@&4V3SjKhhvKI*T1!J)=7X^-tm;Ky6(IV6vs zhiBl&h8Ouu^~UpKO%uL&`KU)ydp@k3dg@?*H!@)KBmpS zdJ<_9sEUcGTXz-j%4b+O)gI1N2VO1=x-s zvG?Zqs!ZvK-0dmp-9vNJj4KJCz(1%Ec(FcP@L#DWy;~y)i zvD_1)IqBqtEng<>?uCQeN!}bRf5J%e_>zv2=j*fd>5k;tt@*`fk=Vq-rmJ(aJsku( z)VuWO@2-`tqi}42W*+3Iq#4;SnhOzS@B|a;+LIGF!-Med!L0t(gx3;d*wh6AU3!Co@8Ieor4*)=x8{gK8gY%AR$$8O01cY`L*8hJSoY_dW3!W#eW2Y zg%_7XT0*@o*d~3UgRN?3pt?}(O~a*am|w&tVS0=4&UX5QV(&JO)f4XU<(nKEIqRI^ zVxVwoAjQxa)@A1Eil}lQki+6*w!^in=$l@emo!h4X)R$}DfP^NDK`&RD~KVZ1S%;_ zT;uR$+&RW=@ zH!nY@c(zEYh%**vyb0a3cZK%4lZx6_gJJ_T#Z z-wd1lrs9NxW@l$pK1jy)(T3|GfKF4m%}`Zfaztu`sTxar=HhG|C;}tIi3=#$T?me@ zUEY;Nu$Q{4jCMaa$xYDjI{_s!br3KT;wX&m01n3q$MTZjvp3QdhYR@uXYvXRJPtO^ zy?X9XDtg-o)ot9w%4V=uK=wAZAQJ1gyA>6X8IfEdDbqOgi_i@d{+b$2!RBQ}+&yYn z%D%WXs`4Tj)jPbkRwJ&)9B;AU5VqH_{?c?YH=;efEuevDJdBL0-c5gS-lbaG_49&U z-pCZVGTM^x3gKHrh3eMqraPW5USe>Xy3D_P zd6SiOGi)kZUs&gaEVEhKIGsKA%72QDUV}?0u8z{iiT16cbcd^^raW?Ta_IH4`oZV% zoSAAqulCU9bN8w~Jb1QS%88=Kgny)&xO;i=J3BkeordAYz!3)-Y6^x+!-m)EQg*ie8?taly!?*E&~=zqNgr=Wfc6dmi)0 z0P@t>!f|Wrm#z?Uk|6-Fva*IC?1+l$DkGY0URU!I^B;K?Caa+t4_f|HF=Zbb)GGXe z-$&YXiXwWOX^et^-3<@_6RrsQ6h}!TX*MrM8Z#YGx5xE4YAdJ<52%8SB;Ku{hfxQg z-YEr3Dr-33llpml3Uv~oFUgc?-l*14NsAW2KxL>ht@}52(AeLiCQi3G#&KyoU2+b? zzEK-I`Sl2(8$8}oCPW{J{&x9w1inp}iO3m)a~~Vs8CjlAF%vbh651Y_?V2C0y-qk* zP|A3NdRyFhBkMy|Osp0#iq!;0i|H+3irN9Q>Ic)i>&ivo@|#*gSx=qR=p`G_vkvqp zD;-#Z4}$Tc+`4n;c?s>lphyBG2$`4HI^p2#(y?$hbrI&dy7%y*$qor9Qt2Azvf23^ zNGYzHn>?=x_c^s!dWI0$WWt?C=m&g_iF!Td=Ec)&Djkd=qvci~6A^?I-lisufR$&0 zjF3i2_Oeo`irGDFwjpA$R<7u17@f_hWxZ&95w?`laV_yqIPV)qBNu*pO#A$zum$-H z+M*P!62Vy`YD-U5aEer~7m*r6aBHxQr`|xTg^iR+)<4nQ3hSBcdIXwWgw(S=W>=Pz%W(E*kz; zUHVIJ(lW}php@Y@xt~n$VAz1)i1s+-=eY0%Pxh#t@nDH{l^l0n+tPOlBS4xe%{(J5 zqomO5s}Oy?W*+=CH01Uz9Tztnswu{}5i=h2GAGAJTSdMfN1W2&l@R4C4bw-{?9L=>Ze%Q&s> z7rvlare)}M%Kns8Hx|?FVC!PCBQL*)Y~;=B2BTEMyo6r7wjmk(?CNX%2tg&PMH+OJ zmG|90o89*pf_=;JkuQAbgQy3+n0+g+@}eW#6*W_4Ej-%x+qjjraJ;%zQ14~!dtgrB zOZwZi6G|=1($5uwT&dfNO@rJTcy~gU^ko&5*o+Jzk_Q|c`)cxDImFe)g-r~O<%;pn z0ofKwJCC^{#o#iFc0-bzvlb@9H>^}gD7+}0f6bMoKCf&gFfQ{8IJ^Lb>Pb}Or<$M7 z?m>ogkJD*m@^RQ=B~8_rIypOv!~u+TpdRL@%0QX6=fgnmJfue}S4!7 zn|>INsO%vLRe}k<{Oxt(A!8v#;#3I8__!SJ7xi_@tUjOGBh){$4AXQ012s=c3EAuT z_{*Cx+AL$Rof6kj_uOGMtb>Ei3!L}!&Au_(%Gc@XLLrtw_R~Yfo;PL!*a9;$GZ{9f zsswK+UA&kQ#4+9lZcdSVV38PDN?Mu=AJdGI_V##e+2DLpb+vy-E}jE?`o&1&Zp5G1 zk%=A!+}P*g6TVB{io8Sp_=h$~-yoLL&BLAgs^m-dL6jGG#;ARHL_M?J;v@Qw&JBf1 z%ZVk9e}d}MkJOU~c0EDstBwp*aR{Y#5rX}iU(;ZH9kjJ|qB%#iEThj-taBzCs1${cq6WPi*D zZH}Dy`1o%}Zj7XjmJbi+YKHz=P$>Liyf2@dxhu@Y_Hu2_uTWj|V&)g|%8f2JYsg<` z81ybH*{HH}b2a^#CGd=)AzpUwn~|gP4j&EJXMp>xb+&U+438KT8Usn(=fumJ?*A3; z)66qxli;ZJOgtg5>FA+QEWhr)>ND>&KOKakub(0#c&I&oorPKA;M}boN8rGNGv9eD zc%npnL{FG(MIR~`XwU{6{&rL9#(;L|fs%=zQV;ll3_O61J*v^wpLMxgE>I3lx_@@V z?O*qt9%Vg`(Q)Z=l|C@L)prVjKGC@zr(4hZX^@r$Hkx?uh5EEl@Bj)wsCt&HumdrF zHL26b&S~Xc+GdTta`wu%a@A4wGxrehOF2PHIlZFckQN6-GpM^CQi@tF6bR6wK$6JM zfYmMfrmH+sC%;^@-csifvu1hoh}Lk9NXyOy+;bi9q0rc)kEcP-d9QO;$V2Fyc7Ka0C4(t8Bi?gu+4l52eKTZ+<1a{ z{N!0q@L*rxJ3Rp68`^uvOT$^2a)_pCXCv_b+#99;mUa6}=KpNWFtN4%B}hBO)=K^| zuuF~f@>sI4b*Qy;s+LZiP}EY#-#svNYd4J#phjZemzDJo2w2?(w{O5TeYm0z>@Fw! z11S?I3a(6m8Do9IPUhaRtR*vn=US>P+rLD^I@3B8)V{kMY{gFeTD{^6~ zQEDG@f5cg1NLTu*;eoc4gxklWJ~<{%zz-cuSW`NF7~cP>GL`==vm)7|DDErRX$B-@S zSce&A<~>uN&-eHKm)~{$uj~E4bM-u)=R9Y*@B7^6KKFJC{(KUVckMP)+Mo&9pgB~rL38-6 zEHpWj{&kHU1s#{Gq7l3|bxKi#@y&&RW6r5-fpMQd7}f`V{(6+1mt`aOiw1j6re-#q zhS24E__Nv!+P#FGm*wYfQG35Sr*Kq*LHX45(?eBP8u3I%{ztcJ5=WEc(V`{Dg_&vj z;*D$-eVduqrm3SjPbIE-|hpEc_lRnFtXM@_&Dj^TEW(wY4H?#bai(Vu)_Hz#; z6%ah^1Qeqaf1doH>Q*iIX5uPQvMlC$NGIsTx8eFui!rmtvnC}JRi?Q@ygly=WIjLm z;@-fp6SCDXAw!-aKq3`L@@;a*>K2T#fb5DVs4G}De%(bS?s6Asc7VGs^Zh#RH-z_L zdNs`Ei}VfTTr#OgpURo`N$-mzH%pw*f`~+eih|RU&o7k5yvy?xXzWL**7;^zZgIa3 zf2+WRT2s&`hyvVD4_T6UF@BY4?W^D}r&c|6hNj~obYD6QAbgrwBz*UM7o;zI^C@30 zI_&y(%gI!Asl~$vyImnVvK4EI^mC;#!qlp*96|=GYQbM%^9iBvg?Wp`=9yI#GdiMs zLbMyl*pp+jsHiSbJyej>PNsec0ld&#Fo4!RAqwCtOJ<%Xh{Peu3WA4!TP~2zspFZU zhuQ$I-vFu)0qp$4!MAL@yc(i*egt%F zud(G1WnIdc>JC&gyU>6*Q^dSpCnSdXw7;0p8{Ap+{twA4=u^{I+j9oF9rd0C^I1j; zP(Vn7C6VXW@Z7_I5s58$GbaQ&>^U*6V;JJqIr3YBSvaRZ2;6TrGTan+59Z?KBzFK= zfJDT?sj#z0FXhEd;buFSwn?|EsAvd^_j#Q}p!%zFYf&-G@2)3V{+OYB9sKjPL-GFA z3Kp}E7j|17QgMEKcjx2yI-1gwCbfy-X3NYizlx((|2;I_hkvJrbcR*ZVy?-p0^-ID zuh~-}+G&(V_`m1Ih^Rc?k7+-q^jPF3#I&*WtAptWDZ}+J(`?LJ?^;QG@zH!(wsGK5 zBJahIdK4IN0oUN9|Ex!>c?u0x87inbCrCWUyw!wVn2 zAF8WrN7G>7rM*k>-ik6edP$|(aaHa2B;xaD-nscpJh<3^|99DEL6!^GSOi;GE&v7t zU<&FH4wg0LYY@(DmD%X93)@*=zg}K_cY9LVnbPdPx>r@=sOdL?DhTggjB1#xcSE*d z-Q-GA$Lccp>$-mswcIdKPjX{sYAM(r%CEPa7+MQnBTnx&Y>oLF9fHOkE3)DB2RB!l z8J2Z|L%MIv^Q$GD|EhQQZ@nVlsrkc}T1ZpnVZ!^3&z=JAYdww?=*+irFjtSmH-S8e zO~9*PTZUAc!^e_)t6;kH{U&J^`MmvQ{|;5PT0X8VW$}3IP-$5|JR%|6%D=5LA5DYa zccTm*n=b2d_HLp*Z_Z4xn&D8~Tb44ZG&ey0nSVf^IS{Gn`HNA5XY&QUL)*a`SRs=h z=a|3n0Jcm&yabVx-<|tucWMl$B5Feb^Hop73`pi=A(@$;TxO43OsBhyJJfNkeP4RA$=R83%-Bp=;N~-BXUj0LUSruTIz&f>Uxk z1T>8&uk@zDKta!ebqF2N91J)QlXCw}4+=!dqiOQcC-R{Et|PrS)jvPicxU*xBmca- z>!6-G^!FK}T0eA1`rlfz7XnZG^Zf0^(e;0e_X4T7{(a_V_)j-eM-Km|f4|34<433t zHkEr)1V+}_t-ce|6r!koAPxC=t&X{3 zqkHb)CDB3FSv&l>P2m>*40ij?*>&nVbXhKxC?V;tl%bnnTCe71I>`9Z^q(4|3RF?O z_U^ymWtnA3i7Zj8i2O5{18?Aiy`STnKSN8x-wgbZkla1T`0e80FQ+e?P4tj0l)P5^ z_2SjKe|jvApyEnv#)*#JIsQ+{Veep*+I(#V%iS9-+Qg*)xwDU;vr8QY4Hprx!Gpto9|Ul_@Sd<)?8oOChVm5{gy@63scvA<$FZ@ z1MyKKhjk7iRO;=~4R^Z4e3$UnTa0+u#|RbV?u^T``ugV|Lz8%TRcWSPY~LA!?4LT3 zNy-Wtn4~+>eSa~2Ck}?3V`ju}BHhSh>#Z#%b4rg_rZ-AGXBC6A3VwTa#L2?c^k7l+ zvo8(B{l8C;h^UzHijHG&oc8ezx$*lzUvGJO(TU#t$$*;n5u-X{`HcaBAJSA1wrP_LSVr$I z4`#<~zQJk_W@}|_7AgD~Zc$czbh-Aig7D6|_Y3p7^{L@E{<`0eOg=9?To2}~IfRz| zxF=t23{^F1+?n0ncySe|2Ek=83hII13^cRUI6+sD#xm4k&pMR$9(C*Vmlp&{%!PoVXrZNwNn& z9>xe(tWMS$@vA0e?7Z3A+;kl+vIr5g8*Hs;u^Idty)sfH-`>&T4}Yr-`N)umHda27 zPfB*f7!u@{hR+`uNgMBxE5cd7e)Tx1z?!dUad#Ed1NNHENjUR@H^q?RIMTY($j3@!>IMjbV#piG9(bSz9nkroMvv zoWIA|=V7AmtF&7P)@s~qe9vhUxLc3R?$=WywY@PGCXn~^D_8Jtqi0dRgTL-li_(#)~am4-600Dk)Nv zLm;(eOR!NJ9#BP!gZeY{$&yu)ue*2nW)%M@VvE6ja;K6kd|2!EeI7OV6di8QEwa?) zZ_amf{>XL~_P#a&TK1NUZo>~3NvP1uS6`lMJMhv_?qG{)8$4JB$RqG9(+dzkmwd(K z-r0}LjRGH6-M#~X%gRybD;1;;`2)Fgm3@Nn6Nyk9 zC9!7Zj?`P$s0ZZh?=Dx`o>=GAw3;gs9)CB|GVN(q9tjY%q96#t7LT{coler=VTolu~FiSg9{<==fr2ZC0vC95cw>W>}!PXWm zCW<9v@d3ezch3cV`dyfe{mo;KT)J(T}>ZB2nI6?(+-{ui13(SK4onnV)J{Gi|QOQ z;MRs{H|u~{tUr=?$8Ekh>&(Au(FKN;?vTckCT3TSA&^<(6Xj?du?}vk>lw4 z+JEX@&wFxPw;GajshwezJn~>8?(l!U0jU;bIQk!zi^^Pqik>3tz%P*gKdRgR70nIo z$$oeLf9w6f={)d~r02?4%2f=>&{2Jt{}+J4RHF(fO?~}O13b0dA`0ujjyFb9R8Q{M zT3LN?RW`>w%rcAXcsY`;^g}yZJl&qdEyGIvY}lt4IoAe$Gg48tX2uHXu?;o2i3f{Z zy{e6__Sl*msjYl*{UXq8zX>b`|CBrRsf6dU%o~MAd|K75Pzo%sV8edbSWA7j!|>j> zBz*?oS(KthuduABjH$_f%&Dy&V}a&_6XcDFC!`>W7GmbIhzCYJVDyk~$u2Y$Wj^eT6i~X9)1nK49N6Th+zUMp~ZLl0n$NKu- zW9Qe6Dv;hi0oi)VYmdtsY{C37F2&o65}Xkw9ifwg4J5Blh)FlfJ&D}dcn|eEMZ9=F z&9lGWalCz;jKj% zo$1l;i6St|oXOOkvckg0*VxR2S)#ggHzz(xIz)$t=1G#u?8M#oaBO_Mn(LiEV=MAa zYjpEwj;1+9zme3;mWrKub2J?$-US7*c(t`0J3D;!1^tw&Tg03Dwk6UPLI2(JWP%eyOM1bdFz@`T!!oF@J6 z8+sfq0)zE#G(*Y*Tbr|6ai%v9bAC&lXXDen%*)$n6K|dneNkXV>rx_*-;cbJWsf(Z zl{7)apVD=vqoFuk2Ek?ia+v83I{)R%!+gBJuz|I1FAfZFwY=kzAH31Ragmodcpdc< z*Rr&qCLaQNsm(#FhlO5+4EO3lM;N322PS@tMZCOE53a}IqZpB}98cI>Io9+!b3L!P zHYX?OJKw^8iO-qq^ddM$_Z#lELlOK7e_|Gg8gV#^2onZ^p>`%w^yIiCYo zkc_Tj&RnAE=xD{;woAzbAXcysBGjywA$aGNd5l>ngu6k$>sPgYpdP%{Ft~sjpfXj+ zkPmsZYyKwBaHpl6EI9PWdiV_egXy`V15fX7RAs5QKKHrZ-e~7}SC9tM^g`%3-^+*4 z*?L`V07jQeti#wP3km1$5eqJ0In!~=8siakb%v$uaD&p`#0w)G7Wmb-bi=t&xiwc} zUyCvL5P!=x0R*8RK;{z->=GyMEiuCIQ^KHSEhWf?Ka~gS90sY z0)su-BYFk<9g6IfN^jax&UkuEa(s@eRT0oE$L!o$A5uvVa_04`KZ6b1UadAKxv_82 z&lVVala`g@NEwoJJ%{&39IdIR+*DS|MUSpm@p+E39^RW^s#!h#k4NL~tGf*}LVso) z+^RSI{>vZo@16(tzd(`yyIbmiv*}U&Z})33KGi?2mV~j|iZsgAj=P;i-KftTp)p*v zELq@v3+womH~oRV%us3<#jb07LD>`UT0~6_E4tI4kN}^#>X0hsA<^2Z7K3FyN3b^n5(_Z^MqJ^?>%&{D1s5s%18XEzln7h)Nl4h+^dN@dn=~ z*`=VY73cjc+xBfWu1YaTrg`NAzda}2c#~K10&MhrP_K1{(@3ki-S6JCP64prN?v4( zcm_WWx>e$(=BX&qTud(Tr@PmhpR4Wr0MTRt>IuTg&*EWF{U9(f19oz24&Oo0?|#au zk0rNap>t)H^16Hp{}OyC%c3CHJIcvX3uq z-{E*PppC~%r~rPSecf>FUASf-s~QJZvm z=ZiTY4r9ZjMh@57jaUTmap_U!y!8+xH1NoKmaNvg<9|CRM;Utob35^;XyN|*{Wn(Lg?@>VVkqIlVY zjW022SoPU7hPM>k42CN3rb#j8W;+SEFGmzi7dlNi@uk}%XhM=*YGNPh6O!!VsYcWL z)tRbub8%+3s=XRAT-V^Z6@wYUJ@bnp*@;f)FE#A_6v@DUf`0+m+-?K>@#dEEIZ+jn z=B4s>KLA~va^celRDV&w?JLH!t>mEwV!iQb;`a@?zAq z8>?aB*o;S+e7j7anFgWxX8ZhKJTx z*DgKHK+S11IV$DnT6##$SwoZ=roLi7=V@SI5Fv13X!v*On3PHO;A(JWU7t8d_bntw zK?fR^Jt4z^3mHcBcd(Ia;W||{J`5%2seG?vzs&tv*kO^sy zV7ETQ!jenF%zwK+%BdgdM;1W2SlxPL1<^;(_(#ZO(5X;kGn}#xmeeB-`I(!Wr}AGM zm_e1nOe0i=2~6gs=-kiTB07ChalRzt?_rmwyStN>21WS;c35q^jOU+;NV;Ykf<0%l z&oTIXLR(4MLGrvCjb1el z!RyCP5zg;qys(7B+=cvl2HU*^oF(_m@^JoV{od&{K+XjZvn5V+i41=Fb>oFuQoJ2n z?N~OfBTgg^5G&Ii0q*Uaa~=L{8qYGcDzrdwM@~-8%EnlW+A)9K)PzeMXmyA2`7HPxIIiuK?v9C}&H1!#NRUO%=nJqn`}MJ<9QrI{T4yq4u@0SDjy# zON;^rQjcHo*qGhFLD4o_ac+~;3Vc+ zk^ecy(c2_}6xmiR0}mY;JT_h)W}6_m zw1JI}^S!Fe%R4Om+jOpSC}mlN;FQBe=8IDb_=`uj-QX>qQo89(sUQkU0@{O8!iAdYaqA3Tvz7q5S?l15s8M zRmU$*$-m=YpXuOXyu&|V*}}o@=ruCV2+yso)c;~sp`{Cf3AGb<9MV?)c>0|DX+~NC zvlpo)BKP#^EVbl>LIE5y3|&13cZ-m`HKvw}aoNz0heE*oj3a=GY9$jnZUN_;Jb;YS ziemPS(mYC@p>sGTY_<@9KSuX0x}diA-+6i8t3h_;y)kilPHbN z-^x0hxx%iFGchvs$2{oG6cr9mW3J&CCZlAtaw&T%dz6~q5hzw9N(>@N3M^oH*3nPt zr-{EyjKo)82xn#+nhwb;L^@URLPi&O&yMH0V*|oYpTBPhIUVH^`It$3G~h3oHQjE41H;NVE39$L=U~;hAOokxLDzo zy(vC4xdOV{!*L`HZ;4vLNbl{T2V$Mb*p5CFC`8mLn zvPB*i#u%Dfz4Bsclwiz0%}F#QI$QxI9ll93(C5{%Mb%n-MdNnDz2Cr7&35eAvG$WU zW;h|ITblnS6X<#2@Lq-$T1CXRAG-!QPZt_iqZ;06%l`%vKYcYSw|?5DcOI5poh&(q z5T!8juS|(DS!j_jz3aSuf)~PE3@h98>89128;)aQGeqM47sXuN7pGX@+t6>GYlda- zcXzRIwgZ{0TA7b-Tga-9+KiS7JP96#bPR2i`3A2{$j=?C`yJAfY)Vl!&ab zfZEL^bqm!2?U@!33;C(f-;2nhLlvlti2dT{7~<1c3&B_nS4l{|YK->;%pgk8m&6&g zJie{PWqaUppIfCx_5qZx(FBAv4u!?_jAP*yw!WUS*(L0x@^5`hUNtsic6m+{`cIp@ z2Eh}nLl=gC7DU1#&h+3U&rybTx7lALL^xPzl9)QRf!Xh?M{|z zK@Zoj1&eBNHBqK%xWMF#{cu6C-2%pHv0L%|qJ(ks@D?Ph(_?c1ldo6&^ykUZ3TK4F zczOQVFsF!su2J&Z&TRz+VM)Hv^E(HR4BVbK(g?KbnfddPi5PP7I`L?+Em&;LV3!}D zyb`y7$wofXGBD~MEM34;B&O|nd0Pl??82Q)+Z;j{TdR>s z9qS0!k7D0nxZ6WHdm$zqLX+pjh8!VJ7S9!73COP9E%~$Se7tr03k@@EpU!ILZtbE9 zl;uN=svWAkAw0B|?h3M22DOgvTJp*rY8gJ;k(?C~vz9CpD@U%(81wPW2jcd~3A)xI zi}Jlq|MOCjlsa8mMC3u<78UDul#1p29ca*f0R&hIR z8@qPE`GEDQSIM5f+$w+g+2eM3!OL4@xnGw9MWl<9+eE_Ol%@oNHI==LY>=I?7|e!I z>?M@_uy6HEqb$OEb5c|>QeRns!xK(+XdA2y8siR*DdVm}7=6}n8M4K?nSg*mg`9bB zP_uE%!NR#LD4d=I!pch&WlT+e!M>pCei^1rMb>fXF3u4x7y$Z2J`w#B6za9|S$I-`PLFZ)uGsJi8Y)s;pp%SYc$0mJ%UHI3M!5 zsT)W}N=0hWvWdN`gYUj+?em{%fC{Ua7)Q$?$3!H&sipp1AwJGykVYWs0>*DEBFVk~ zVX@nS8Y3fElo6pBlg=V%Sy+T%xQz00bnB~cH%ugt78h+fJ2t&<-&LsHKk7&t$R1xU z@<@V2qS!O=J+Iy8F?~{ZD!{Iwi!>1eM6x4VhmKO4DQ+)Dovk0o1P-369@L95h7e@^ zKAS-$2@X#;j6-qMXb&ur&JJQw3kbpTK9~#~6e~OV$s^6)S-8@mu9<}5?qeTeG+Zc8?QAI9ltDS_ z9Y1|O)zEpIA7O2~AAccf*Z{ir1XJ9iLfLDwl3bNz#{ne-Te#qP(zqe4<*b$B)AO%^ zm{GmuF!By^rE-SV@62b7>L22k;*U;uzG)A|j5y@o8g`VX3VdP?O!hS9;8WtjPIpkU z#<(>cnOCSCF_O{BDBZF$A&|{V@JTtBHPGWX z$Lx{E1gX1c%}(tV(JHU#0$;+ny0L#e^LHV@IF<7W_JhZ2Jn_|YW-Qs%7?8~{*D~#2 zW51z@(+AO0!zV(Wd z2-rW@+8{$P1T`;&{&A;Fu?jt|xO8er3vYN--YV7UArqxK_BPE06n`q2fMf4S@ zxO8*oO0hP7nez<6KBy77>i)7=wdc+$b>{I8Z?4L?CUzicPd1bfm?r|CN8l7MB|#<7 zI>q~0!i{Wy_8UF>-5RM*uh!>%Fl`w)$JsFJOjcI5oP~oiH36mZZRw3pv6-Y-Sr0Ak z1RlJoKHK;p)APOCxs-Fi@w@g?oaKuUATIT-|M)PdqxZv|v})?nLcG5&Kd!SFVc_HK z-`hLBIo==k;`9@XiUG!K{XUE^n}<%6S<8-=a5q%8>aL*h@r{D>Wj9LMv72-G--)rI zt(!u=9Y|-6rw0MeVkkV)`Aujms;8S~XV=AHRc(#Aob&_tg3zFt$aUDHv07hwE47i(Dlx`gb)~dR(fOxjHe#@{#0696+?>U+h~opDwR2 z++J9gMK&YIM=L_0SK|bbst}wwfa;Vp-Er8kmrB0iJP@O@g&p>kG@r;kQ8feb(LtNh z#Ubeg4JthAfcxL5#UEFBc+=rJZ?%EAFgV(Vpm%t**7`ZO{Sf%AjJXEX2I-KJSKpRG9>Lb!)cg(1`i62(`9P6w2)$XIsp)NGddB$pBXbhF(kKKi zje1HB9gt`GJ(%!tfz<`>!dmgl2Ex)4#pdFLC4{cQA;LcEfOb>GBm-mg!1DPtgGJki zf0Is&xlY%SWj%0XJqnj9claO+q`%^L2?sKu0%VuA`7*r5VlBN%dgu2aXA4$ae3)gK zFtj?m_l^BT?xL1}$|5uKy(%VI4Ky#S@gdAV4w^^sdZ>(Hj7zwpX8>;2q0N^&o(&Rs z#V#&&4I8r8AxHJE1(%0(WU?0cUyA_*J5Lxu)oLaanh_C52qJ9R@s!FKui2Thwi1{g z>H>27R{vp~P9fKZ2SP9RRWI)Oou>sfmi}RvC_V3g&={Zt%gP^Y`6OPdp%$QM4LU& zJ%hfXJn7>VFlDF0Zvbd%7zDb|5bxM`u{J|XAd-?KZ zdv9eX!&tql3UHsK3h=~MC!c&uF|`HdXAXIsrdYw1;Bw~yrVn67zkn4m$bF&nV3O<7)k7ZobH5Jg+j{gs zXp|Ymzkp$J!>j7luc)dH?M&O_g1x(+owD*sp`q@}L#pm4ry-a{f??I3#Y4Wpx3sM2 zqaQ~4FG;s z&-JQ8GtS)|lW$H{uJ9CCAyP{6j1P+RLU7vEGKC>;<*bnC+;!B`zaw*jSdH-I9&!p?GlQ{f@U~_Tzd|1tCq_5sB5w z#RTSHyohm;vLIW3!ZYMcw;b9JsyFqEP#@?b2Dsp-H zy8lcjWmW+(s4iXHcY}d#@#0r!O*T)c>K1uuX_vLk3`iYU`{bVg`tIsZb$l;9F1f4yOMroKVBC~_CS)9 z*-z^3A^Xh0FAD{8y@%;%szO69drt8(GX@r8+t#_9!vik}mr&LA`{M7iA+0PfyeM9D zpqf0^Gf-nzUX?$dU4}qh0f_*yKv%YNIn33uBtKrhh;DEkM@`aQmIs~|?eX&5BR9xe z-D?+S`YWO8-Huxo9#Z0SD|FvKk=3|6ZZ9To5(n!3_I4#%cF2y@WwR4_$*vkYG=`ix zo{VDQgil*t{<;MP47dGedi(6i7NTYgz%e!fs4#8b(o20zjcFYjMn`NzoM$&gO zkaXCI*o~GFg4=gItN-udB?&15S%&ENm=w>lg`yDo_VXZxxN)f`b^4+<@tMFr{_&Nq z&nM*20hhG4$jj^@=Ur#s^>)L0$g}SLBr|c}7d(JMi;%a6;dBijEBLwM7yTF626MpWR<=|J17oU!LhvR!3J0T|v(vJ0gRldyC*Iz>p)zY`nO6H1a zq_I#Hi)_|r*WXEtFkUyjX5hDIihnwH?#{Ln0fvYKi`&}MK^V<)>m5Dv&Q%1ikNoo! zH%#I&M4|TxDXG-0?gKib2OD%EWcRTJzYlW~Wt%E9|+*sh_mauP#(!Tri`<(c4PAR&I)gHOSA$igT z!jKa(9k?jJP?D%uo>0P$h;jFL=z^ZlMsxZi^-^N&YgOT%23s!wLxno&zQwk?0qT)8 z;BJdj>XBfJn6pBwfgrRj{pfYK{(f!$fRg&{=0yS?sO19YY_CGz(AWOS)0e_N`(0zf$Qq=EkpT${sVGEL?8eAje zr_lbFpF)$bdgJ`uw?9d4Rj(!Jw^Zydl(G>06hO98>|dLRy3*!bB)fZ`1AVF>Ob_!+ zbRt-^6Iy$7KmE0Bg5Azij}>r2ZB7Nzw~Ih`#!yL$=^D^XQA=~~4U1+aV5eGjd~`*= zUcMs&5(&y-tK;NPC@gRXQ5p3DsHmtkaU&KpsEbk3ycDn3DdEcAnCl~Asyovm-1>)V z%cotp=6%{bfP~y#D5zO$*WUx?R)5PmF-qIO55Q+tXNt!nhwTlM;{wJ6OLrcipiiUvcm zAcOuXOSNxBSGiexpgo3Bd*To)8y&F8)OaX%)g7Lv33ph9=j_mTr2}4fCQEtg0)Gu| zdEK_8aToZ*H9m@%ISp=?#j^o!xm)n(us|Bi4OMZeZbx(atbu$H7T=(Wn6P#KjbA@C zb#*_A&30#KH+nHf*Zc1?H3F;8JFl54U@9UAH4>sM@4=*740#1rI}JQgR+z0;<<5Rz z$9_-8j28!#9$%=RI7v&`t;NjHD}$BzgbT)AZ+J_}T|BMvnbErmUbg(x2;R)>_DDaD zp~6v9t(=`v_{>h_JLB?t?)(ga4-f1lx?;x63+?fO9uo(kuLUlaV9LP;F1OOd zq=N#;xg@~hFfssvu!atEl^1}({h*IYFeg@ z!$hrB_@fl*JJ&t#-0<17Ec59&_dsZD#<9kHK2Gg$gjvPn zMUR2_g?9B_)KEb|!e5QN2P%3L;P*7;gV=4w8d=&}mqNRtZOf`0)Xgfp?ZZu^jatCHSFDWU>z&u{hgOG{CV}2tIaz*@J!xol+q;cmK_2Nz=-1HVZhv4VKv~C?K-vj9 zAB5i#CF`QqqM*1um}F0b)qJwd;42Aqf+mB^R`(m?+6fsh5ENTFyK&1eO5EjBwpyN^ zi;ckhGNdb4OP3Q*Opy*(POK>@?Ub_am|q2Un!XzGIHLj zl`szwozS)mr=0?#$zrxO8VK5zlAd#n8~Nm+83S%3C<K&!P0py5d=YBbKW6y)KoArc5x_aN$}XPho;YeF$N8 z3RS$>9Gc5O0D}C=KJg_}>u8e;tZ~;nYQW`w{H-nLDq?q^;4Kcn=ic->rn|7bmaK%gkDpNN z)>iFaWhUUGfady#>p+a9)>2mS-;6So4)6ZS>1VM~Ka5q=GVZ0;ROW1q9ykoF14C;vYouP3JT+`*Z1NrxPQ(mc~y1GtMS7W)8+@dnv z)!hwod5+RAnHn37I$`f>0wg^1PHuXN$^CNAJtyQ{?(RSU+H6_uRE+X#LAXA`;Y%g) zlPP&l;WKfy?&RF6&`inM;Bm9lt^m<9R}7PC+-+|X43Cv@t^ zL}F&Pu+M@PBV$#Lht{HQ{aDWhQ@0mzP#8mae1l?oYo~3r@=19Gp`oR|`;I;Kj}}$+Ef&w$(*c3Q#7Ge} z7GQrbn7rItOGRgR zq6L0LS48iJ(F_-mB!)##U+IOY+MlM>!X19dA94Ohtg|=ozayzUhq_BG%47Mfn5al4v*sqG@loDLNEeDe7BfKIzTWTZ5GYV~2 z0L6|CECTwAb8E7PIY59%GeTgwGou~ZA1Y^0B*_lN7kxnHJaX}y2djdKD;G!qN+nGT;3nRWwJwVfnRA)&%7+%Gb z&6{3>kmql^Xu}$Ga@63MV~(Ppo=m~8F*2~C!}G%O?u>PfEc#U)jGhjg_Okv#C8BW> zQ8>JCjwq-9YC6Jzc+Z*YH*Nu|vN@LP!(Q>FjA^3F-=hx@!J~;*0%`>Zu`USd6>R4n zx1U?C!PX0TEDiRROK?hgYO|Pnr}?daKXGLkJ6|tZcvu0pK;F&mC<|~JS$N)F&YhDN zT#h^*Wy0#TBm8?ygg59j8D8=U^6nn+DK_=PcPzOwa)aXov=wfjR^S4cf#}f#$If>A zcb0)INIoF9U>-rJ>ipI=flv;o4+OkYK00%(UdwXEFxpLc3+vZBK(QGvIOItn2F(%n zXokik*HyI^nZAW*uwzFJ8s&= zoqKWGZ1o@oN?tI%RL7$ElJqP5Cs!l<4w?2MqZK{I10r&Ly%JoZ+uRZ!w&CwA9B!d^pZj@z%-uc=NczN%XnKy`I!yDy}O+aGQqHH zk1lh91iKLFjR^B}4fUT?V`JXn6RC+HB{SaTI#I9SSt4? zujpgS{UqFgIn@u4V4Xau6m;%nVXs#o+eAOw_h)o?@G>i2ugpnMl(6#w;F^nT&S*;H zmaw_skP?jL5w{wr7p0u6S_IQ_wap&+6hWr&VZDlko|Dx3t|DLP6?QsZx4_tz!e_42 zuUM2XF=gK(M=M=&pRSSd-d%$8gf=j1(O?Gfz7l_ITM#cxR*-|QI3CrT{_q>gVMkb| z4whdryQ6R9x>uxrYx_)?0`Si{TCI+i0UPkyrE1#NqyL!Kmw&!41P2k2RX@2Z!#R?n za!%-E!)+s4nrV3DPp6;EVG4s^AOiy{5{v0*k@8+QPUb&Nzj^?3ZWPAvM+<|0{b)m? znQXk+XKcvh*V*OLpjklR@ALnOFH%w6yLd~xNVfdm$XrJpdssa``^+nH zUPCYgBWaW8!8Je(g$!3Ah;26V6GO@ZtoY@>5!M%R-Ql*iR4f5Fi7Q<={db9g&1SC- zh?U431-cLT1?&GC$ptTg*Z+hh|9tpAJTo6+8mgdO-tz9vg|`yW_vW+CPj`cx5y2hTfKRQ+xyd#4^`- zYB6`TQij^CmVYXJV?Vl17a<}fN_W-p60dJai$(KW4eM)$mn1&KvW{}8@U}I3%NxYI z41I^NUakuus$L_3-qzDF6MCn}AMkbzQ>6e$|Ui+;cRQf|NP z?D+$pdq4L+_dfT2&hvcSGA86Gxj(~x`6^Zq1g@Su!kdX@eOxV*X;%CAIkrC42dnkGX$bD8sh#In?2{usiDeKi1r(zJ9r{LTy{DH=_hamV~+@ zCu~~F^p0W~Lo>0pM@$E+gEg|RwCthXFSIbCJab=G__F-`qt_s)@k?ZELa}r!NhTW*qO#(EBUAGmKX9HJOTl9XO^Ri(Q3$+*O!(5*VY9jf3?DU%0sQF9S;v zfb+(_iyU3Y#`+xRYgI%Lp^-?q&!1j!pr7Ue<;`6(AFZ896z2|Jy4x5}J0BVfP7G3? z@(`82P31HF%Ca;ib`$&cbyR;--zPh{0S9l7bASp=Dz7}zk$U@{T3Jke?d5sO@ui5- z15DXK5@*9} z&>(##DN?XjbQZ+a9w$K3x?&%M_l6Pei8;LO1ZIqZ-XfbC7$O-?g%Pcws+C;{_I$ff zkjJ$>-f4vyKc`v!3SEJtZ*zX*h!zH@ejb#<#e6#SWZjm6?ZI?B-OM%X0HYlYKIv_- zwX+n3Jc5Hqv)Kqj?Kn@w&US)91M$D7FJ)VGieCHL(V@Cn;qLvTLr~0-Nq{++iLTN# zG1pPDL4?iP2vtuiur~%09m?4=-t7+@^U!_92SjNmj1o?&(l@7nA}S=`@mwgvk;sjD z6EQw2YQ%TpLAadFy02$_=beZue9Azl2Expl3XZlTmad`3E6H#SMqyJ9edtKAZjJ@|3;mjBKEAO`+VdrIf}X&;S4c delta 18768 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 diff --git a/icons/mob/inhands/items_righthand.dmi b/icons/mob/inhands/items_righthand.dmi index 1462d26e344b5b2752620de69a84b6806259a9f8..8fd47551efa53b56996e31c69bed9b8cbc78af5a 100644 GIT binary patch delta 18557 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 T`GZ!_00000NkvXXu0mjfBL4;# delta 17396 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�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 diff --git a/icons/obj/weapons.dmi b/icons/obj/weapons.dmi index a10524fb9b94f045c2f1e2e079cd20a15a7f3dde..416b1c8b93224de915caa3643a15e118fabb7464 100644 GIT binary patch delta 19448 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 S=FlJn9(!xYLr-z%V*Ur+m-CPS delta 13382 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 Date: Tue, 31 May 2016 22:41:07 -0400 Subject: [PATCH 020/275] fix --- code/game/objects/items/weapons/holy_weapons.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/objects/items/weapons/holy_weapons.dm b/code/game/objects/items/weapons/holy_weapons.dm index 4b0b1e9ba8d..f501ef8e002 100644 --- a/code/game/objects/items/weapons/holy_weapons.dm +++ b/code/game/objects/items/weapons/holy_weapons.dm @@ -90,7 +90,7 @@ item_state = "claymore" name = "holy claymore" desc = "A weapon fit for a crusade!" - w_class = 5 + w_class = 4 slot_flags = SLOT_BACK|SLOT_BELT sharp = 1 edge = 1 From 95d7a68a7501518b80d70f86babfc41404b3e7e6 Mon Sep 17 00:00:00 2001 From: TheDZD Date: Fri, 27 May 2016 19:14:04 -0400 Subject: [PATCH 021/275] Here we go again... --- code/__DEFINES/combat.dm | 9 + code/__DEFINES/misc.dm | 2 +- .../game/objects/items/weapons/power_cells.dm | 13 +- code/game/sound.dm | 44 +- code/modules/projectiles/ammunition.dm | 159 ++--- .../projectiles/ammunition/ammo_casings.dm | 211 ++++--- code/modules/projectiles/ammunition/boxes.dm | 23 +- code/modules/projectiles/ammunition/energy.dm | 224 +++++++ .../projectiles/ammunition/magazines.dm | 268 ++++---- .../modules/projectiles/ammunition/special.dm | 49 ++ code/modules/projectiles/firing.dm | 95 +++ code/modules/projectiles/gun.dm | 572 ++++++++--------- code/modules/projectiles/guns/alien.dm | 2 +- code/modules/projectiles/guns/energy.dm | 171 +++-- .../projectiles/guns/energy/advtaser.dm | 40 -- .../projectiles/guns/energy/blueshield.dm | 37 -- code/modules/projectiles/guns/energy/hos.dm | 45 -- code/modules/projectiles/guns/energy/laser.dm | 116 ++-- .../projectiles/guns/energy/nuclear.dm | 240 +++---- code/modules/projectiles/guns/energy/pulse.dm | 84 +-- .../projectiles/guns/energy/special.dm | 592 ++++++++++++------ code/modules/projectiles/guns/energy/stun.dm | 89 ++- .../projectiles/guns/energy/telegun.dm | 6 +- .../projectiles/guns/energy/temperature.dm | 182 ------ .../projectiles/guns/grenade_launcher.dm | 54 ++ code/modules/projectiles/guns/magic.dm | 50 +- code/modules/projectiles/guns/magic/staff.dm | 32 +- code/modules/projectiles/guns/magic/wand.dm | 131 ++-- .../modules/projectiles/{ => guns}/medbeam.dm | 8 +- code/modules/projectiles/guns/mounted.dm | 24 +- code/modules/projectiles/guns/projectile.dm | 98 +-- .../projectiles/guns/projectile/automatic.dm | 127 ++-- .../projectiles/guns/projectile/crossbow.dm | 323 ---------- .../guns/projectile/grenade launcher.dm | 91 --- .../projectiles/guns/projectile/launchers.dm | 91 +-- .../projectiles/guns/projectile/pistol.dm | 24 +- .../projectiles/guns/projectile/revolver.dm | 319 ++++++---- .../projectiles/guns/projectile/rocket.dm | 20 +- .../projectiles/guns/projectile/saw.dm | 179 ++++++ .../projectiles/guns/projectile/shotgun.dm | 225 ++++--- .../projectiles/guns/projectile/sniper.dm | 26 +- .../projectiles/guns/projectile/toy.dm | 71 ++- code/modules/projectiles/guns/syringe_gun.dm | 41 +- code/modules/projectiles/projectile.dm | 495 ++++++--------- code/modules/projectiles/projectile/beams.dm | 127 ++-- .../modules/projectiles/projectile/bullets.dm | 190 +++--- code/modules/projectiles/projectile/energy.dm | 58 +- code/modules/projectiles/projectile/force.dm | 23 +- code/modules/projectiles/projectile/magic.dm | 119 ++-- .../projectiles/projectile/reusable.dm | 65 +- .../modules/projectiles/projectile/special.dm | 287 ++++----- code/modules/reagents/grenade_launcher.dm | 111 ---- .../reagents/reagent_containers/syringes.dm | 3 +- paradise.dme | 14 +- 54 files changed, 3403 insertions(+), 3296 deletions(-) create mode 100644 code/modules/projectiles/ammunition/energy.dm create mode 100644 code/modules/projectiles/ammunition/special.dm create mode 100644 code/modules/projectiles/firing.dm delete mode 100644 code/modules/projectiles/guns/energy/advtaser.dm delete mode 100644 code/modules/projectiles/guns/energy/blueshield.dm delete mode 100644 code/modules/projectiles/guns/energy/hos.dm delete mode 100644 code/modules/projectiles/guns/energy/temperature.dm create mode 100644 code/modules/projectiles/guns/grenade_launcher.dm rename code/modules/projectiles/{ => guns}/medbeam.dm (93%) delete mode 100644 code/modules/projectiles/guns/projectile/crossbow.dm delete mode 100644 code/modules/projectiles/guns/projectile/grenade launcher.dm create mode 100644 code/modules/projectiles/guns/projectile/saw.dm delete mode 100644 code/modules/reagents/grenade_launcher.dm diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm index bf620aad6e3..c137c320390 100644 --- a/code/__DEFINES/combat.dm +++ b/code/__DEFINES/combat.dm @@ -53,3 +53,12 @@ #define AI_ON 1 #define AI_IDLE 2 #define AI_OFF 3 + +//Gun Stuff + #define SAWN_INTACT 0 + #define SAWN_OFF 1 + #define SAWN_SAWING -1 + + #define WEAPON_LIGHT 0 + #define WEAPON_MEDIUM 1 + #define WEAPON_HEAVY 2 diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index d375f63dd39..99918eccbc8 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -250,4 +250,4 @@ #define MATRIX_GREYSCALE list(0.3, 0.3, 0.3, 0,\ 0.3, 0.3, 0.3, 0,\ 0.3, 0.3, 0.3, 0,\ - 0, 0, 0, 1) + 0, 0, 0, 1)//Gun trigger guards #define TRIGGER_GUARD_ALLOW_ALL -1 #define TRIGGER_GUARD_NONE 0 #define TRIGGER_GUARD_NORMAL 1 \ No newline at end of file diff --git a/code/game/objects/items/weapons/power_cells.dm b/code/game/objects/items/weapons/power_cells.dm index 62f60a91bc4..a34a8328457 100644 --- a/code/game/objects/items/weapons/power_cells.dm +++ b/code/game/objects/items/weapons/power_cells.dm @@ -128,7 +128,6 @@ materials = list() minor_fault = 1 - /obj/item/weapon/stock_parts/cell/high/slime name = "charged slime core" desc = "A yellow slime core infused with plasma, it crackles with power." @@ -138,6 +137,12 @@ materials = list() self_recharge = 1 // Infused slime cores self-recharge, over time +/obj/item/weapon/stock_parts/cell/pulse //200 pulse shots + name = "pulse rifle power cell" + maxcharge = 40000 + rating = 3 + chargerate = 1500 + /obj/item/weapon/stock_parts/cell/pulse/carbine name = "pulse carbine power cell" maxcharge = 4000 @@ -168,9 +173,3 @@ /obj/item/weapon/stock_parts/cell/emproof/corrupt() return - -/obj/item/weapon/stock_parts/cell/temperaturegun - name = "temperature gun cell" - desc = "A specially designed power cell for heating and cooling projectiles" - icon_state = "icell" - maxcharge = 900 \ No newline at end of file diff --git a/code/game/sound.dm b/code/game/sound.dm index cecf12c657d..1890a0966e7 100644 --- a/code/game/sound.dm +++ b/code/game/sound.dm @@ -9,8 +9,9 @@ var/list/jackboot_sound = list('sound/effects/jackboot1.ogg','sound/effects/jack var/list/swing_hit_sound = list('sound/weapons/genhit1.ogg', 'sound/weapons/genhit2.ogg', 'sound/weapons/genhit3.ogg') var/list/hiss_sound = list('sound/voice/hiss1.ogg','sound/voice/hiss2.ogg','sound/voice/hiss3.ogg','sound/voice/hiss4.ogg') var/list/page_sound = list('sound/effects/pageturn1.ogg', 'sound/effects/pageturn2.ogg','sound/effects/pageturn3.ogg') -//var/list/gun_sound = list('sound/weapons/Gunshot.ogg', 'sound/weapons/Gunshot2.ogg','sound/weapons/Gunshot3.ogg','sound/weapons/Gunshot4.ogg') +var/list/gun_sound = list('sound/weapons/Gunshot.ogg', 'sound/weapons/Gunshot2.ogg','sound/weapons/Gunshot3.ogg','sound/weapons/Gunshot4.ogg') var/list/computer_ambience = list('sound/goonstation/machines/ambicomp1.ogg', 'sound/goonstation/machines/ambicomp2.ogg', 'sound/goonstation/machines/ambicomp3.ogg') +var/list/ricochet = list('sound/weapons/effects/ric1.ogg', 'sound/weapons/effects/ric2.ogg','sound/weapons/effects/ric3.ogg','sound/weapons/effects/ric4.ogg','sound/weapons/effects/ric5.ogg') /proc/playsound(var/atom/source, soundin, vol as num, vary, extrarange as num, falloff, var/is_global, var/pitch) @@ -107,17 +108,32 @@ var/const/FALLOFF_SOUNDS = 0.5 /proc/get_sfx(soundin) if(istext(soundin)) switch(soundin) - if ("shatter") soundin = pick(shatter_sound) - if ("explosion") soundin = pick(explosion_sound) - if ("sparks") soundin = pick(spark_sound) - if ("rustle") soundin = pick(rustle_sound) - if ("bodyfall") soundin = pick(bodyfall_sound) - if ("punch") soundin = pick(punch_sound) - if ("clownstep") soundin = pick(clown_sound) - if ("jackboot") soundin = pick(jackboot_sound) - if ("swing_hit") soundin = pick(swing_hit_sound) - if ("hiss") soundin = pick(hiss_sound) - if ("pageturn") soundin = pick(page_sound) - //if ("gunshot") soundin = pick(gun_sound) - if("computer_ambience") soundin = pick(computer_ambience) + if ("shatter") + soundin = pick(shatter_sound) + if ("explosion") + soundin = pick(explosion_sound) + if ("sparks") + soundin = pick(spark_sound) + if ("rustle") + soundin = pick(rustle_sound) + if ("bodyfall") + soundin = pick(bodyfall_sound) + if ("punch") + soundin = pick(punch_sound) + if ("clownstep") + soundin = pick(clown_sound) + if ("jackboot") + soundin = pick(jackboot_sound) + if ("swing_hit") + soundin = pick(swing_hit_sound) + if ("hiss") + soundin = pick(hiss_sound) + if ("pageturn") + soundin = pick(page_sound) + if ("gunshot") + soundin = pick(gun_sound) + if("computer_ambience") + soundin = pick(computer_ambience) + if("ricochet") + soundin = pick(ricochet) return soundin diff --git a/code/modules/projectiles/ammunition.dm b/code/modules/projectiles/ammunition.dm index 45670ec70b0..169e968afaf 100644 --- a/code/modules/projectiles/ammunition.dm +++ b/code/modules/projectiles/ammunition.dm @@ -6,13 +6,16 @@ flags = CONDUCT slot_flags = SLOT_BELT throwforce = 1 - w_class = 1.0 - var/caliber = "" //Which kind of guns it can be loaded into - var/projectile_type = ""//The bullet type to create when New() is called + w_class = 1 + var/fire_sound = null //What sound should play when this ammo is fired + var/caliber = null //Which kind of guns it can be loaded into + var/projectile_type = null //The bullet type to create when New() is called var/obj/item/projectile/BB = null //The loaded bullet - var/pellets = 1 - var/deviation = 0 - var/spread = 0 + var/pellets = 1 //Pellets for spreadshot + var/variance = 0 //Variance for inaccuracy fundamental to the casing + var/delay = 0 //Delay for energy weapons + var/randomspread = 0 //Randomspread for automatics + var/click_cooldown_override = 0 //Override this to make your gun have a faster fire rate, in tenths of a second. 4 is the default gun cooldown. /obj/item/ammo_casing/New() @@ -29,54 +32,45 @@ icon_state = "[initial(icon_state)][BB ? "-live" : ""]" desc = "[initial(desc)][BB ? "" : " This one is spent"]" - - -/obj/item/ammo_casing/attackby(obj/item/weapon/W as obj, mob/user as mob, params) - if(istype(W, /obj/item/weapon/screwdriver)) - if(BB) - if(initial(BB.name) == "bullet") - var/tmp_label = "" - var/label_text = sanitize(input(user, "Inscribe some text into \the [initial(BB.name)]","Inscription",tmp_label)) - if(length(label_text) > 20) - to_chat(user, "The inscription can be at most 20 characters long.") +/obj/item/ammo_casing/attackby(obj/item/I as obj, mob/user as mob, params) + if(istype(I, /obj/item/ammo_box)) + var/obj/item/ammo_box/box = I + if(isturf(loc)) + var/boolets = 0 + for(var/obj/item/ammo_casing/bullet in loc) + if (box.stored_ammo.len >= box.max_ammo) + break + if (bullet.BB) + if (box.give_round(bullet, 0)) + boolets++ else - if(label_text == "") - to_chat(user, "You scratch the inscription off of [initial(BB)].") - BB.name = initial(BB.name) + continue + if (boolets > 0) + box.update_icon() + to_chat(user, "You collect [boolets] shell\s. [box] now contains [box.stored_ammo.len] shell\s.") + else + to_chat(user, "You fail to collect anything!") + else + if(istype(I, /obj/item/weapon/screwdriver)) + if(BB) + if(initial(BB.name) == "bullet") + var/tmp_label = "" + var/label_text = sanitize(input(user, "Inscribe some text into \the [initial(BB.name)]","Inscription",tmp_label)) + if(length(label_text) > 20) + to_chat(user, "The inscription can be at most 20 characters long.") else - to_chat(user, "You inscribe \"[label_text]\" into \the [initial(BB.name)].") - BB.name = "[initial(BB.name)] \"[label_text]\"" + if(label_text == "") + to_chat(user, "You scratch the inscription off of [initial(BB)].") + BB.name = initial(BB.name) + else + to_chat(user, "You inscribe \"[label_text]\" into \the [initial(BB.name)].") + BB.name = "[initial(BB.name)] \"[label_text]\"" + else + to_chat(user, "You can only inscribe a metal bullet.")//because inscribing beanbags is silly + else - to_chat(user, "You can only inscribe a metal bullet.")//because inscribing beanbags is silly - - else - to_chat(user, "There is no bullet in the casing to inscribe anything into.") - -/obj/item/ammo_casing/attackby(obj/item/ammo_box/box as obj, mob/user as mob, params) - if(!istype(box, /obj/item/ammo_box)) - return - if(isturf(loc)) - var/boolets = 0 - for(var/obj/item/ammo_casing/pew in loc) - if(box.stored_ammo.len >= box.max_ammo) - break - if(pew.BB) - if(box.give_round(pew)) - boolets++ - else - continue - if(boolets > 0) - box.update_icon() - to_chat(user, "You collect [boolets] shell\s. [box] now contains [box.stored_ammo.len] shell\s.") - else - to_chat(user, "You fail to collect anything.") - -/obj/item/ammo_casing/proc/newshot() //For energy weapons, shotgun shells and wands (!). - if(!BB) - BB = new projectile_type(src) - return - - + to_chat(user, "There is no bullet in the casing to inscribe anything into.") + ..() //Boxes of ammo /obj/item/ammo_box @@ -89,53 +83,78 @@ item_state = "syringe_kit" materials = list(MAT_METAL=30000) throwforce = 2 - w_class = 1.0 + w_class = 1 throw_speed = 4 throw_range = 10 var/list/stored_ammo = list() - var/obj/item/ammo_casing/ammo_type + var/ammo_type = /obj/item/ammo_casing var/max_ammo = 7 var/multiple_sprites = 0 - var/caliber = "" + var/caliber var/multiload = 1 + /obj/item/ammo_box/New() - for(var/i = 1, i <= max_ammo, i++) + for(var/i in 1 to max_ammo) stored_ammo += new ammo_type(src) update_icon() -/obj/item/ammo_box/proc/get_round(var/keep = 0) - if(!stored_ammo.len) +/obj/item/ammo_box/Destroy() + for(atom/A in stored_ammo) + qdel(A) + stored_ammo = null + return ..() + +/obj/item/ammo_box/proc/get_round(keep = 0) + if (!stored_ammo.len) return null else var/b = stored_ammo[stored_ammo.len] stored_ammo -= b - if(keep) + if (keep) stored_ammo.Insert(1,b) return b -/obj/item/ammo_box/proc/give_round(var/obj/item/ammo_casing/r) - var/obj/item/ammo_casing/rb = r - if(rb) - if(stored_ammo.len < max_ammo && rb.caliber == caliber) - stored_ammo += rb - rb.loc = src - return 1 +/obj/item/ammo_box/proc/give_round(obj/item/ammo_casing/R, replace_spent = 0) + // Boxes don't have a caliber type, magazines do. Not sure if it's intended or not, but if we fail to find a caliber, then we fall back to ammo_type. + if(!R || (caliber && R.caliber != caliber) || (!caliber && R.type != ammo_type)) + return 0 + + if (stored_ammo.len < max_ammo) + stored_ammo += R + R.loc = src + return 1 + //for accessibles magazines (e.g internal ones) when full, start replacing spent ammo + else if(replace_spent) + for(var/obj/item/ammo_casing/AC in stored_ammo) + if(!AC.BB)//found a spent ammo + stored_ammo -= AC + AC.loc = get_turf(src.loc) + + stored_ammo += R + R.loc = src + return 1 + return 0 -/obj/item/ammo_box/attackby(var/obj/item/A as obj, mob/user as mob, var/silent = 0, params) +/obj/item/ammo_box/proc/can_load(mob/user) + return 1 + +/obj/item/ammo_box/attackby(obj/item/A, mob/user, params, silent = 0, replace_spent = 0) var/num_loaded = 0 + if(!can_load(user)) + return if(istype(A, /obj/item/ammo_box)) var/obj/item/ammo_box/AM = A for(var/obj/item/ammo_casing/AC in AM.stored_ammo) - var/didload = give_round(AC) - if(didload) + var/did_load = give_round(AC, replace_spent) + if(did_load) AM.stored_ammo -= AC num_loaded++ - if(!multiload || !didload) + if(!multiload || !did_load) break if(istype(A, /obj/item/ammo_casing)) var/obj/item/ammo_casing/AC = A - if(give_round(AC)) + if(give_round(AC, replace_spent)) user.drop_item() AC.loc = src num_loaded++ diff --git a/code/modules/projectiles/ammunition/ammo_casings.dm b/code/modules/projectiles/ammunition/ammo_casings.dm index 44189411515..010ce185acb 100644 --- a/code/modules/projectiles/ammunition/ammo_casings.dm +++ b/code/modules/projectiles/ammunition/ammo_casings.dm @@ -1,128 +1,137 @@ /obj/item/ammo_casing/a357 desc = "A .357 bullet casing." caliber = "357" - projectile_type = "/obj/item/projectile/bullet" + projectile_type = /obj/item/projectile/bullet + +/obj/item/ammo_casing/a762 + desc = "A 7.62mm bullet casing." + icon_state = "762-casing" + caliber = "a762" + projectile_type = /obj/item/projectile/bullet + +/obj/item/ammo_casing/a762/enchanted + projectile_type = /obj/item/projectile/bullet/weakbullet3 /obj/item/ammo_casing/a50 desc = "A .50AE bullet casing." caliber = ".50" - projectile_type = "/obj/item/projectile/bullet" + projectile_type = /obj/item/projectile/bullet /obj/item/ammo_casing/c38 desc = "A .38 bullet casing." caliber = "38" - projectile_type = "/obj/item/projectile/bullet/weakbullet2/rubber" - -/obj/item/ammo_casing/cap - desc = "A cap for children toys." - caliber = "caps" - projectile_type = "/obj/item/projectile/bullet/cap" - + projectile_type = /obj/item/projectile/bullet/weakbullet2/rubber /obj/item/ammo_casing/c10mm desc = "A 10mm bullet casing." caliber = "10mm" - projectile_type = "/obj/item/projectile/bullet/midbullet3" - + projectile_type = /obj/item/projectile/bullet/midbullet3 /obj/item/ammo_casing/c9mm desc = "A 9mm bullet casing." caliber = "9mm" - projectile_type = "/obj/item/projectile/bullet/weakbullet3" + projectile_type = /obj/item/projectile/bullet/weakbullet3 /obj/item/ammo_casing/c9mmap desc = "A 9mm bullet casing." caliber = "9mm" - projectile_type = "/obj/item/projectile/bullet/armourpiercing" + projectile_type = /obj/item/projectile/bullet/armourpiercing /obj/item/ammo_casing/c9mmtox desc = "A 9mm bullet casing." caliber = "9mm" - projectile_type = "/obj/item/projectile/bullet/toxinbullet" + projectile_type = /obj/item/projectile/bullet/toxinbullet /obj/item/ammo_casing/c9mminc desc = "A 9mm bullet casing." caliber = "9mm" - projectile_type = "/obj/item/projectile/bullet/incendiary/firebullet" + projectile_type = /obj/item/projectile/bullet/incendiary/firebullet /obj/item/ammo_casing/c46x30mm desc = "A 4.6x30mm bullet casing." caliber = "4.6x30mm" - projectile_type = "/obj/item/projectile/bullet/weakbullet3" + projectile_type = /obj/item/projectile/bullet/weakbullet3 /obj/item/ammo_casing/c46x30mmap desc = "A 4.6x30mm bullet casing." caliber = "4.6x30mm" - projectile_type = "/obj/item/projectile/bullet/armourpiercing" + projectile_type = /obj/item/projectile/bullet/armourpiercing /obj/item/ammo_casing/c46x30mmtox desc = "A 4.6x30mm bullet casing." caliber = "4.6x30mm" - projectile_type = "/obj/item/projectile/bullet/toxinbullet" + projectile_type = /obj/item/projectile/bullet/toxinbullet /obj/item/ammo_casing/c46x30mminc desc = "A 4.6x30mm bullet casing." caliber = "4.6x30mm" - projectile_type = "/obj/item/projectile/bullet/incendiary/firebullet" + projectile_type = /obj/item/projectile/bullet/incendiary/firebullet /obj/item/ammo_casing/c45 desc = "A .45 bullet casing." caliber = ".45" - projectile_type = "/obj/item/projectile/bullet/midbullet" + projectile_type = /obj/item/projectile/bullet/midbullet +/obj/item/ammo_casing/c45nostamina + desc = "A .45 bullet casing." + caliber = ".45" + projectile_type = /obj/item/projectile/bullet/midbullet3 + +/obj/item/ammo_casing/n762 + desc = "A 7.62x38mmR bullet casing." + caliber = "n762" + projectile_type = /obj/item/projectile/bullet /obj/item/ammo_casing/shotgun name = "shotgun slug" desc = "A 12 gauge lead slug." icon_state = "blshell" caliber = "shotgun" - projectile_type = "/obj/item/projectile/bullet" + projectile_type = /obj/item/projectile/bullet materials = list(MAT_METAL=4000) - /obj/item/ammo_casing/shotgun/buckshot name = "buckshot shell" desc = "A 12 gauge buckshot shell." icon_state = "gshell" - projectile_type = "/obj/item/projectile/bullet/pellet" + projectile_type = /obj/item/projectile/bullet/pellet pellets = 6 - deviation = 30 + deviation = 25 /obj/item/ammo_casing/shotgun/rubbershot name = "rubber shot" desc = "A shotgun casing filled with densely-packed rubber balls, used to incapacitate crowds from a distance." icon_state = "bshell" - projectile_type = "/obj/item/projectile/bullet/rpellet" + projectile_type = /obj/item/projectile/bullet/rpellet pellets = 6 - deviation = 30 + deviation = 25 materials = list(MAT_METAL=4000) /obj/item/ammo_casing/shotgun/beanbag name = "beanbag slug" desc = "A weak beanbag slug for riot control." icon_state = "bshell" - projectile_type = "/obj/item/projectile/bullet/weakbullet/rubber" + projectile_type = /obj/item/projectile/bullet/weakbullet/rubber materials = list(MAT_METAL=250) - /obj/item/ammo_casing/shotgun/improvised name = "improvised shell" desc = "An extremely weak shotgun shell with multiple small pellets made out of metal shards." icon_state = "gshell" - projectile_type = "/obj/item/projectile/bullet/pellet/weak" + projectile_type = /obj/item/projectile/bullet/pellet/weak materials = list(MAT_METAL=250) pellets = 5 - deviation = 30 + deviation = 25 /obj/item/ammo_casing/shotgun/improvised/overload name = "overloaded improvised shell" desc = "An extremely weak shotgun shell with multiple small pellets made out of metal shards. This one has been packed with even more \ propellant. It's like playing russian roulette, with a shotgun." icon_state = "improvshell" - projectile_type = "/obj/item/projectile/bullet/pellet/random" + projectile_type = /obj/item/projectile/bullet/pellet/random materials = list(MAT_METAL=250) pellets = 5 - deviation = 30 + deviation = 40 /obj/item/ammo_casing/shotgun/improvised/overload/New() ..() @@ -132,15 +141,20 @@ name = "taser slug" desc = "A stunning taser slug." icon_state = "stunshell" - projectile_type = "/obj/item/projectile/bullet/stunshot" + projectile_type = /obj/item/projectile/bullet/stunshot materials = list(MAT_METAL=250) - /obj/item/ammo_casing/shotgun/meteorshot name = "meteorshot shell" desc = "A shotgun shell rigged with CMC technology, which launches a massive slug when fired." icon_state = "mshell" - projectile_type = "/obj/item/projectile/bullet/meteorshot" + projectile_type = /obj/item/projectile/bullet/meteorshot + +/obj/item/ammo_casing/shotgun/breaching + name = "breaching shell" + desc = "An economic version of the meteorshot, utilizing similar technologies. Great for busting down doors." + icon_state = "mshell" + projectile_type = /obj/item/projectile/bullet/meteorshot/weak /obj/item/ammo_casing/shotgun/pulseslug name = "pulse slug" @@ -148,42 +162,42 @@ energy blast. While the heat and power drain limit it to one use, it can still allow an operator to engage targets that ballistic ammunition \ would have difficulty with." icon_state = "pshell" - projectile_type = "/obj/item/projectile/beam/pulse/shot" + projectile_type = /obj/item/projectile/beam/pulse/shot /obj/item/ammo_casing/shotgun/incendiary name = "incendiary slug" desc = "An incendiary-coated shotgun slug." icon_state = "ishell" - projectile_type = "/obj/item/projectile/bullet/incendiary/shell" + projectile_type = /obj/item/projectile/bullet/incendiary/shell /obj/item/ammo_casing/shotgun/frag12 name = "FRAG-12 slug" desc = "A high explosive breaching round for a 12 gauge shotgun." icon_state = "heshell" - projectile_type = "/obj/item/projectile/bullet/frag12" + projectile_type = /obj/item/projectile/bullet/frag12 /obj/item/ammo_casing/shotgun/incendiary/dragonsbreath name = "dragonsbreath shell" desc = "A shotgun shell which fires a spread of incendiary pellets." icon_state = "ishell2" - projectile_type = "/obj/item/projectile/bullet/incendiary/shell/dragonsbreath" + projectile_type = /obj/item/projectile/bullet/incendiary/shell/dragonsbreath pellets = 4 - deviation = 30 + variance = 35 /obj/item/ammo_casing/shotgun/ion name = "ion shell" desc = "An advanced shotgun shell which uses a subspace ansible crystal to produce an effect similar to a standard ion rifle. \ The unique properties of the crystal splot the pulse into a spread of individually weaker bolts." icon_state = "ionshell" - projectile_type = "/obj/item/projectile/ion/weak" + projectile_type = /obj/item/projectile/ion/weak pellets = 4 - deviation = 30 + deviation = 35 /obj/item/ammo_casing/shotgun/laserslug name = "laser slug" desc = "An advanced shotgun shell that uses a micro laser to replicate the effects of a laser weapon in a ballistic package." icon_state = "lshell" - projectile_type = "/obj/item/projectile/beam" + projectile_type = /obj/item/projectile/beam /obj/item/ammo_casing/shotgun/techshell name = "unloaded technological shell" @@ -195,7 +209,7 @@ name = "shotgun dart" desc = "A dart for use in shotguns. Can be injected with up to 30 units of any chemical." icon_state = "cshell" - projectile_type = "/obj/item/projectile/bullet/dart" + projectile_type = /obj/item/projectile/bullet/dart /obj/item/ammo_casing/shotgun/dart/New() ..() @@ -206,93 +220,125 @@ /obj/item/ammo_casing/shotgun/dart/attackby() return +/obj/item/ammo_casing/shotgun/dart/bioterror + desc = "A shotgun dart filled with deadly toxins." + +/obj/item/ammo_casing/shotgun/dart/bioterror/New() + ..() + reagents.add_reagent("neurotoxin", 6) + reagents.add_reagent("spores", 6) + reagents.add_reagent("mutetoxin", 6) //;HELP OPS IN MAINT + reagents.add_reagent("coniine", 6) + reagents.add_reagent("sodium_thiopental", 6) + /obj/item/ammo_casing/shotgun/tranquilizer name = "tranquilizer darts" desc = "A tranquilizer round used to subdue individuals utilizing stimulants." icon_state = "cshell" - projectile_type = "/obj/item/projectile/bullet/dart/syringe/tranquilizer" + projectile_type = /obj/item/projectile/bullet/dart/syringe/tranquilizer materials = list(MAT_METAL=250) -/obj/item/ammo_casing/syringegun - name = "syringe gun spring" - desc = "A high-power spring that throws syringes." - projectile_type = null +/obj/item/ammo_casing/a556 + desc = "A 5.56mm bullet casing." + caliber = "a556" + projectile_type = /obj/item/projectile/bullet/heavybullet /obj/item/ammo_casing/shotgun/fakebeanbag name = "beanbag shell" desc = "A weak beanbag shell." icon_state = "bshell" - projectile_type = "/obj/item/projectile/bullet/weakbullet/booze" - -/obj/item/ammo_casing/a762 - desc = "A 7.62mm bullet casing." - icon_state = "762-casing" - caliber = "a762" - projectile_type = "/obj/item/projectile/bullet" - -/obj/item/ammo_casing/a556 - desc = "A 5.56mm bullet casing." - caliber = "a556" - projectile_type = "/obj/item/projectile/bullet/heavybullet" + projectile_type = /obj/item/projectile/bullet/weakbullet/booze /obj/item/ammo_casing/rocket name = "rocket shell" desc = "A high explosive designed to be fired from a launcher." icon_state = "rocketshell" - projectile_type = "/obj/item/missile" + projectile_type = /obj/item/missile caliber = "rocket" /obj/item/ammo_casing/caseless desc = "A caseless bullet casing." +/obj/item/ammo_casing/caseless/fire(atom/target as mob|obj|turf, mob/living/user as mob|obj, params, distro, quiet) + if (..()) + loc = null + return 1 + else + return 0 + /obj/item/ammo_casing/caseless/a75 desc = "A .75 bullet casing." caliber = "75" - projectile_type = "/obj/item/projectile/bullet/gyro" + projectile_type = /obj/item/projectile/bullet/gyro /obj/item/ammo_casing/a40mm name = "40mm HE shell" desc = "A cased high explosive grenade that can only be activated once fired out of a grenade launcher." caliber = "40mm" icon_state = "40mmHE" - projectile_type = "/obj/item/projectile/bullet/a40mm" + projectile_type = /obj/item/projectile/bullet/a40mm + +/obj/item/ammo_casing/caseless/magspear + name = "magnetic spear" + desc = "A reusable spear that is typically loaded into kinetic spearguns." + projectile_type = /obj/item/projectile/bullet/reusable/magspear + caliber = "speargun" + icon_state = "magspear" + throwforce = 15 //still deadly when thrown + throw_speed = 3 -//FOAM DARTS /obj/item/ammo_casing/caseless/foam_dart name = "foam dart" desc = "It's nerf or nothing! Ages 8 and up." - projectile_type = "/obj/item/projectile/bullet/reusable/foam_dart" + projectile_type = /obj/item/projectile/bullet/reusable/foam_dart caliber = "foam_force" icon = 'icons/obj/toyguns.dmi' icon_state = "foamdart" var/modified = 0 /obj/item/ammo_casing/caseless/foam_dart/update_icon() - if(modified) - icon_state = "foamdart_empty" - -/obj/item/ammo_casing/caseless/foam_dart/attackby(var/obj/item/weapon/A as obj, mob/user as mob, params) ..() - if(istype(A, /obj/item/weapon/screwdriver) && !modified) - modified = 1 - BB.damage_type = BRUTE + if (modified) icon_state = "foamdart_empty" - desc = "It's nerf or nothing! ...Although, this one doesn't look too safe." - to_chat(user, "You pop the safety cap off of [src].") - else if((istype(A, /obj/item/weapon/pen)) && modified && !BB.contents.len) + desc = "Its nerf or nothing! ... Although, this one doesn't look too safe." + if(BB) + BB.icon_state = "foamdart_empty" + else + icon_state = "foamdart" + desc = "Its nerf or nothing! Ages 8 and up." + if(BB) + BB.icon_state = "foamdart_empty" + +/obj/item/ammo_casing/caseless/foam_dart/attackby(obj/item/A, mob/user, params) + ..() + var/obj/item/projectile/bullet/reusable/foam_dart/FD = BB + if (istype(A, /obj/item/weapon/screwdriver) && !modified) + modified = 1 + FD.damage_type = BRUTE + update_icon() + else if ((istype(A, /obj/item/weapon/pen)) && modified && !FD.pen) if(!user.unEquip(A)) return - A.forceMove(BB) - BB.damage = 5 - BB.nodamage = 0 + A.loc = FD + FD.pen = A + FD.damage = 5 + FD.nodamage = 0 to_chat(user, "You insert [A] into [src].") return +/obj/item/ammo_casing/caseless/foam_dart/attack_self(mob/living/user) + var/obj/item/projectile/bullet/reusable/foam_dart/FD = BB + if(FD.pen) + FD.damage = initial(FD.damage) + FD.nodamage = initial(FD.nodamage) + user.put_in_hands(FD.pen) + to_chat(user, "You remove [FD.pen] from [src].") + FD.pen = null /obj/item/ammo_casing/caseless/foam_dart/riot name = "riot foam dart" desc = "Whose smart idea was it to use toys as crowd control? Ages 18 and up." - projectile_type = "/obj/item/projectile/bullet/reusable/foam_dart/riot" + projectile_type = /obj/item/projectile/bullet/reusable/foam_dart/riot icon_state = "foamdart_riot" /obj/item/ammo_casing/shotgun/dart/assassination @@ -301,3 +347,8 @@ /obj/item/ammo_casing/shotgun/dart/assassination/New() ..() reagents.add_reagent("neurotoxin", 6) + +/obj/item/ammo_casing/cap + desc = "A cap for children toys." + caliber = "caps" + projectile_type = /obj/item/projectile/bullet/cap diff --git a/code/modules/projectiles/ammunition/boxes.dm b/code/modules/projectiles/ammunition/boxes.dm index eb998823b6a..3a82a0b625e 100644 --- a/code/modules/projectiles/ammunition/boxes.dm +++ b/code/modules/projectiles/ammunition/boxes.dm @@ -1,5 +1,6 @@ /obj/item/ammo_box/a357 name = "speed loader (.357)" + desc = "Designed to quickly reload revolvers." icon_state = "357" ammo_type = /obj/item/ammo_casing/a357 max_ammo = 7 @@ -7,18 +8,12 @@ /obj/item/ammo_box/c38 name = "speed loader (.38)" + desc = "Designed to quickly reload revolvers." icon_state = "38" ammo_type = /obj/item/ammo_casing/c38 max_ammo = 6 multiple_sprites = 1 -/obj/item/ammo_box/caps - name = "speed loader (caps)" - icon_state = "357" - ammo_type = /obj/item/ammo_casing/cap - max_ammo = 7 - multiple_sprites = 1 - /obj/item/ammo_box/c9mm name = "ammo box (9mm)" icon_state = "9mmbox" @@ -55,6 +50,13 @@ max_ammo = 5 multiple_sprites = 1 +/obj/item/ammo_box/n762 + name = "ammo box (7.62x38mmR)" + icon_state = "10mmbox" + origin_tech = "combat=2" + ammo_type = /obj/item/ammo_casing/n762 + max_ammo = 14 + /obj/item/ammo_box/shotgun name = "Ammunition Box (slug)" icon_state = "9mmbox" @@ -99,3 +101,10 @@ /obj/item/ammo_box/foambox/riot icon_state = "foambox_riot" ammo_type = /obj/item/ammo_casing/caseless/foam_dart/riot + +/obj/item/ammo_box/caps + name = "speed loader (caps)" + icon_state = "357" + ammo_type = /obj/item/ammo_casing/cap + max_ammo = 7 + multiple_sprites = 1 diff --git a/code/modules/projectiles/ammunition/energy.dm b/code/modules/projectiles/ammunition/energy.dm new file mode 100644 index 00000000000..b4e979e3b20 --- /dev/null +++ b/code/modules/projectiles/ammunition/energy.dm @@ -0,0 +1,224 @@ +/obj/item/ammo_casing/energy + name = "energy weapon lens" + desc = "The part of the gun that makes the laser go pew" + caliber = "energy" + projectile_type = /obj/item/projectile/energy + var/e_cost = 1000 //The amount of energy a cell needs to expend to create this shot. + var/select_name = "energy" + fire_sound = 'sound/weapons/Laser.ogg' + +/obj/item/ammo_casing/energy/laser + projectile_type = /obj/item/projectile/beam/laser + select_name = "kill" + +/obj/item/ammo_casing/energy/lasergun + projectile_type = /obj/item/projectile/beam/laser + e_cost = 830 + select_name = "kill" + +/obj/item/ammo_casing/energy/laser/hos //allows balancing of HoS and blueshit guns seperately from other energy weapons + +/obj/item/ammo_casing/energy/laser/practice + projectile_type = /obj/item/projectile/beam/practice + select_name = "practice" + +/obj/item/ammo_casing/energy/laser/scatter + projectile_type = /obj/item/projectile/beam/scatter + pellets = 5 + variance = 0.8 + select_name = "scatter" + +/obj/item/ammo_casing/energy/laser/heavy + projectile_type = /obj/item/projectile/beam/laser/heavylaser + select_name = "anti-vehicle" + fire_sound = 'sound/weapons/lasercannonfire.ogg' + +/obj/item/ammo_casing/energy/laser/pulse + projectile_type = /obj/item/projectile/beam/pulse + e_cost = 200 + select_name = "DESTROY" + fire_sound = 'sound/weapons/pulse.ogg' + +/obj/item/ammo_casing/energy/laser/scatter/pulse + projectile_type = /obj/item/projectile/beam/pulse + e_cost = 2000 + select_name = "ANNIHILATE" + fire_sound = 'sound/weapons/pulse.ogg' + +/obj/item/ammo_casing/energy/laser/bluetag + projectile_type = /obj/item/projectile/beam/lasertag/bluetag + select_name = "bluetag" + +/obj/item/ammo_casing/energy/laser/redtag + projectile_type = /obj/item/projectile/beam/lasertag/redtag + select_name = "redtag" + +/obj/item/ammo_casing/energy/xray + projectile_type = /obj/item/projectile/beam/xray + e_cost = 500 + fire_sound = 'sound/weapons/laser3.ogg' + +/obj/item/ammo_casing/energy/immolator + projectile_type = /obj/item/projectile/beam/immolator + fire_sound = 'sound/weapons/laser3.ogg' + e_cost = 1250 + +/obj/item/ammo_casing/energy/electrode + projectile_type = /obj/item/projectile/energy/electrode + select_name = "stun" + fire_sound = 'sound/weapons/taser.ogg' + e_cost = 2000 + delay = 15 + +/obj/item/ammo_casing/energy/electrode/gun + fire_sound = 'sound/weapons/gunshot.ogg' + e_cost = 1000 + +/obj/item/ammo_casing/energy/electrode/hos //allows balancing of HoS and blueshit guns seperately from other energy weapons + +/obj/item/ammo_casing/energy/ion + projectile_type = /obj/item/projectile/ion + select_name = "ion" + fire_sound = 'sound/weapons/IonRifle.ogg' + +/obj/item/ammo_casing/energy/declone + projectile_type = /obj/item/projectile/energy/declone + select_name = "declone" + fire_sound = 'sound/weapons/pulse3.ogg' + +/obj/item/ammo_casing/energy/mindflayer + projectile_type = /obj/item/projectile/beam/mindflayer + select_name = "MINDFUCK" + fire_sound = 'sound/weapons/Laser.ogg' + +/obj/item/ammo_casing/energy/flora + fire_sound = 'sound/effects/stealthoff.ogg' + +/obj/item/ammo_casing/energy/flora/yield + projectile_type = /obj/item/projectile/energy/florayield + select_name = "yield" + +/obj/item/ammo_casing/energy/flora/mut + projectile_type = /obj/item/projectile/energy/floramut + select_name = "mutation" + +/obj/item/ammo_casing/energy/temp + projectile_type = /obj/item/projectile/temp + var/temperature = 300 + e_cost = 100 + fire_sound = 'sound/weapons/pulse3.ogg' + +/obj/item/ammo_casing/energy/temp/newshot() + ..() + BB.temperature = temperature + +/obj/item/ammo_casing/energy/meteor + projectile_type = /obj/item/projectile/meteor + select_name = "goddamn meteor" + +/obj/item/ammo_casing/energy/kinetic + projectile_type = /obj/item/projectile/kinetic + select_name = "kinetic" + e_cost = 5000 + fire_sound = 'sound/weapons/Kenetic_accel.ogg' // fine spelling there chap + +/obj/item/ammo_casing/energy/kinetic/super + projectile_type = /obj/item/projectile/kinetic/super + +/obj/item/ammo_casing/energy/kinetic/hyper + projectile_type = /obj/item/projectile/kinetic/hyper + +/obj/item/ammo_casing/energy/disabler + projectile_type = /obj/item/projectile/beam/disabler + select_name = "disable" + e_cost = 500 + fire_sound = "sound/weapons/taser2.ogg" + +/obj/item/ammo_casing/energy/plasma + projectile_type = /obj/item/projectile/plasma + select_name = "plasma burst" + fire_sound = 'sound/weapons/plasma_cutter.ogg' + delay = 15 + e_cost = 250 + +/obj/item/ammo_casing/energy/plasma/adv + projectile_type = /obj/item/projectile/plasma/adv + delay = 10 + e_cost = 100 + +/obj/item/ammo_casing/energy/wormhole + projectile_type = /obj/item/projectile/beam/wormhole + e_cost = 0 + fire_sound = "sound/weapons/pulse3.ogg" + var/obj/item/weapon/gun/energy/wormhole_projector/gun = null + select_name = "blue" + +/obj/item/ammo_casing/energy/wormhole/New(var/obj/item/weapon/gun/energy/wormhole_projector/wh) + gun = wh + +/obj/item/ammo_casing/energy/wormhole/orange + projectile_type = /obj/item/projectile/beam/wormhole/orange + select_name = "orange" + +/obj/item/ammo_casing/energy/bolt + projectile_type = /obj/item/projectile/energy/bolt + select_name = "bolt" + e_cost = 5000 + fire_sound = 'sound/weapons/Genhit.ogg' + +/obj/item/ammo_casing/energy/bolt/large + projectile_type = /obj/item/projectile/energy/bolt/large + select_name = "heavy bolt" + +/obj/item/ammo_casing/energy/dart + projectile_type = /obj/item/projectile/energy/dart + fire_sound = 'sound/weapons/Genhit.ogg' + e_cost = 5000 + select_name = "toxic dart" + +/obj/item/ammo_casing/energy/instakill + projectile_type = /obj/item/projectile/beam/instakill + e_cost = 0 + select_name = "DESTROY" + +/obj/item/ammo_casing/energy/instakill/blue + projectile_type = /obj/item/projectile/beam/instakill/blue + +/obj/item/ammo_casing/energy/instakill/red + projectile_type = /obj/item/projectile/beam/instakill/red + +/obj/item/ammo_casing/energy/plasma + projectile_type = /obj/item/projectile/plasma + select_name = "plasma burst" + fire_sound = 'sound/weapons/pulse.ogg' + +/obj/item/ammo_casing/energy/plasma/adv + projectile_type = /obj/item/projectile/plasma/adv + +/obj/item/ammo_casing/energy/shock_revolver + fire_sound = 'sound/magic/lightningbolt.ogg' + e_cost = 2000 + select_name = "lightning beam" + projectile_type = /obj/item/projectile/energy/shock_revolver + +/obj/item/ammo_casing/energy/toxplasma + projectile_type = /obj/item/projectile/toxplasma + fire_sound = "sound/weapons/taser2.ogg" + select_name = "plasma dart" + +/obj/item/ammo_casing/energy/clown + projectile_type = /obj/item/projectile/clown + fire_sound = 'sound/weapons/Gunshot_smg.ogg' + select_name = "clown" + +/obj/item/ammo_casing/energy/sniper + projectile_type = /obj/item/projectile/beam/sniper + fire_sound = 'sound/weapons/marauder.ogg' + fire_delay = 50 + select_name = "sniper beam" + +/obj/item/ammo_casing/energy/teleport + projectile_type = /obj/item/projectile/energy/teleport + fire_sound = 'sound/weapons/wave.ogg' + e_cost = 1250 + select_name = "teleport beam" diff --git a/code/modules/projectiles/ammunition/magazines.dm b/code/modules/projectiles/ammunition/magazines.dm index 32c3eeaa37c..e26e536e62a 100644 --- a/code/modules/projectiles/ammunition/magazines.dm +++ b/code/modules/projectiles/ammunition/magazines.dm @@ -1,113 +1,160 @@ ////////////////INTERNAL MAGAZINES////////////////////// +/obj/item/ammo_box/magazine/internal + desc = "Oh god, this shouldn't be here" + +//internals magazines are accessible, so replace spent ammo if full when trying to put a live one in +/obj/item/ammo_box/magazine/internal/give_round(obj/item/ammo_casing/R) + return ..(R,1) + +// Revolver internal mags /obj/item/ammo_box/magazine/internal/cylinder name = "revolver cylinder" - desc = "Oh god, this shouldn't be here" - ammo_type = "/obj/item/ammo_casing/a357" + ammo_type = /obj/item/ammo_casing/a357 caliber = "357" max_ammo = 7 -/obj/item/ammo_box/magazine/internal/cylinder/ammo_count(var/countempties = 1) - if (!countempties) - var/boolets = 0 - for (var/i = 1, i <= stored_ammo.len, i++) - var/obj/item/ammo_casing/bullet = stored_ammo[i] - if (bullet.BB) - boolets++ - return boolets - else - return stored_ammo.len +/obj/item/ammo_box/magazine/internal/cylinder/ammo_count(countempties = 1) + var/boolets = 0 + for(var/obj/item/ammo_casing/bullet in stored_ammo) + if(bullet && (bullet.BB || countempties)) + boolets++ + return boolets -/obj/item/ammo_box/magazine/internal/cylinder/rus357 - name = "russian revolver cylinder" - desc = "Oh god, this shouldn't be here" - ammo_type = "/obj/item/ammo_casing/a357" - caliber = "357" - max_ammo = 6 - multiload = 0 +/obj/item/ammo_box/magazine/internal/cylinder/get_round(keep = 0) + rotate() -/obj/item/ammo_box/magazine/internal/cylinder/rus357/New() - stored_ammo += new ammo_type(src) + var/b = stored_ammo[1] + if(!keep) + stored_ammo[1] = null + + return b + +/obj/item/ammo_box/magazine/internal/cylinder/proc/rotate() + var/b = stored_ammo[1] + stored_ammo.Cut(1,2) + stored_ammo.Insert(0, b) + +/obj/item/ammo_box/magazine/internal/cylinder/proc/spin() + for(var/i in 1 to rand(0, max_ammo*2)) + rotate() + +/obj/item/ammo_box/magazine/internal/cylinder/give_round(obj/item/ammo_casing/R, replace_spent = 0) + if(!R || (caliber && R.caliber != caliber) || (!caliber && R.type != ammo_type)) + return 0 + + for(var/i in 1 to stored_ammo.len) + var/obj/item/ammo_casing/bullet = stored_ammo[i] + if(!bullet || !bullet.BB) // found a spent ammo + stored_ammo[i] = R + R.loc = src + + if(bullet) + bullet.loc = get_turf(src.loc) + return 1 + + return 0 /obj/item/ammo_box/magazine/internal/cylinder/rev38 - name = "d-tiv revolver cylinder" - desc = "Oh god, this shouldn't be here" - ammo_type = "/obj/item/ammo_casing/c38" + name = "detective revolver cylinder" + ammo_type = /obj/item/ammo_casing/c38 caliber = "38" max_ammo = 6 +/obj/item/ammo_box/magazine/internal/cylinder/rev762 + name = "nagant revolver cylinder" + ammo_type = /obj/item/ammo_casing/n762 + caliber = "n762" + max_ammo = 7 + /obj/item/ammo_box/magazine/internal/cylinder/cap name = "cap gun revolver cylinder" desc = "Oh god, this shouldn't be here" - ammo_type = "/obj/item/ammo_casing/cap" + ammo_type = /obj/item/ammo_casing/cap caliber = "cap" max_ammo = 7 +// Shotgun internal mags /obj/item/ammo_box/magazine/internal/shot name = "shotgun internal magazine" - desc = "Oh god, this shouldn't be here" - ammo_type = "/obj/item/ammo_casing/shotgun/beanbag" + ammo_type = /obj/item/ammo_casing/shotgun/beanbag caliber = "shotgun" max_ammo = 4 multiload = 0 -/obj/item/ammo_box/magazine/internal/shotcom +/obj/item/ammo_box/magazine/internal/shot/lethal + ammo_type = /obj/item/ammo_casing/shotgun/buckshot + +/obj/item/ammo_box/magazine/internal/shot/ammo_count(countempties = 1) + if (!countempties) + var/boolets = 0 + for(var/obj/item/ammo_casing/bullet in stored_ammo) + if(bullet.BB) + boolets++ + return boolets + else + return ..() + +/obj/item/ammo_box/magazine/internal/shot/com name = "combat shotgun internal magazine" - desc = "Oh god, this shouldn't be here" - ammo_type = "/obj/item/ammo_casing/shotgun/buckshot" - caliber = "shotgun" + ammo_type = /obj/item/ammo_casing/shotgun/buckshot max_ammo = 6 - multiload = 0 -/obj/item/ammo_box/magazine/internal/cylinder/dualshot +/obj/item/ammo_box/magazine/internal/shot/dual name = "double-barrel shotgun internal magazine" - desc = "This doesn't even exist" - ammo_type = "/obj/item/ammo_casing/shotgun/beanbag" - caliber = "shotgun" max_ammo = 2 - multiload = 0 -/obj/item/ammo_box/magazine/internal/cylinder/improvised +/obj/item/ammo_box/magazine/internal/shot/improvised name = "improvised shotgun internal magazine" - desc = "This doesn't even exist" - ammo_type = "/obj/item/ammo_casing/shotgun/improvised" - caliber = "shotgun" + ammo_type = /obj/item/ammo_casing/shotgun/improvised max_ammo = 1 - multiload = 0 -/obj/item/ammo_box/magazine/internal/shotriot +/obj/item/ammo_box/magazine/internal/shot/riot name = "riot shotgun internal magazine" - desc = "Oh god, this shouldn't be here" - ammo_type = "/obj/item/ammo_casing/shotgun/rubbershot" - caliber = "shotgun" + ammo_type = /obj/item/ammo_casing/shotgun/rubbershot max_ammo = 6 - multiload = 0 -/obj/item/ammo_box/magazine/internal/cylinder/grenadelauncher +/obj/item/ammo_box/magazine/internal/grenadelauncher name = "grenade launcher internal magazine" - ammo_type = "/obj/item/ammo_casing/a40mm" + ammo_type = /obj/item/ammo_casing/a40mm caliber = "40mm" max_ammo = 1 /obj/item/ammo_box/magazine/internal/cylinder/grenadelauncher/multi - ammo_type = "/obj/item/ammo_casing/a40mm" + ammo_type = /obj/item/ammo_casing/a40mm max_ammo = 6 +/obj/item/ammo_box/magazine/internal/speargun + name = "speargun internal magazine" + ammo_type = /obj/item/ammo_casing/caseless/magspear + caliber = "speargun" + max_ammo = 1 + +/obj/item/ammo_box/magazine/internal/rus357 + name = "russian revolver cylinder" + ammo_type = /obj/item/ammo_casing/a357 + caliber = "357" + max_ammo = 6 + multiload = 0 + +/obj/item/ammo_box/magazine/internal/rus357/New() + stored_ammo += new ammo_type(src) + /obj/item/ammo_box/magazine/internal/boltaction name = "bolt action rifle internal magazine" desc = "Oh god, this shouldn't be here" - ammo_type = "/obj/item/ammo_casing/a762" + ammo_type = /obj/item/ammo_casing/a762 caliber = "a762" max_ammo = 5 multiload = 1 /obj/item/ammo_box/magazine/internal/shot/toy - ammo_type = "/obj/item/ammo_casing/caseless/foam_dart" + ammo_type = /obj/item/ammo_casing/caseless/foam_dart caliber = "foam_force" max_ammo = 4 /obj/item/ammo_box/magazine/internal/shot/toy/crossbow - max_ammo = 5 + max_ammo = 5 /obj/item/ammo_box/magazine/internal/shot/toy/tommygun max_ammo = 10 @@ -116,17 +163,18 @@ /obj/item/ammo_box/magazine/m10mm name = "pistol magazine (10mm)" + desc = "A gun magazine." icon_state = "9x19p" origin_tech = "combat=2" - ammo_type = "/obj/item/ammo_casing/c10mm" + ammo_type = /obj/item/ammo_casing/c10mm caliber = "10mm" max_ammo = 8 multiple_sprites = 2 /obj/item/ammo_box/magazine/m45 name = "handgun magazine (.45)" - icon_state = "45" - ammo_type = "/obj/item/ammo_casing/c45" + icon_state = "45-8" + ammo_type = /obj/item/ammo_casing/c45 caliber = ".45" max_ammo = 8 @@ -137,7 +185,7 @@ /obj/item/ammo_box/magazine/wt550m9 name = "wt550 magazine (4.6x30mm)" icon_state = "46x30mmt-20" - ammo_type = "/obj/item/ammo_casing/c46x30mm" + ammo_type = /obj/item/ammo_casing/c46x30mm caliber = "4.6x30mm" max_ammo = 20 @@ -147,20 +195,20 @@ /obj/item/ammo_box/magazine/wt550m9/wtap name = "wt550 magazine (Armour Piercing 4.6x30mm)" - ammo_type = "/obj/item/ammo_casing/c46x30mmap" + ammo_type = /obj/item/ammo_casing/c46x30mmap /obj/item/ammo_box/magazine/wt550m9/wttx name = "wt550 magazine (Toxin Tipped 4.6x30mm)" - ammo_type = "/obj/item/ammo_casing/c46x30mmtox" + ammo_type = /obj/item/ammo_casing/c46x30mmtox /obj/item/ammo_box/magazine/wt550m9/wtic name = "wt550 magazine (Incindiary 4.6x30mm)" - ammo_type = "/obj/item/ammo_casing/c46x30mminc" + ammo_type = /obj/item/ammo_casing/c46x30mminc /obj/item/ammo_box/magazine/uzim9mm name = "uzi magazine (9mm)" icon_state = "uzi9mm-32" - ammo_type = "/obj/item/ammo_casing/c9mm" + ammo_type = /obj/item/ammo_casing/c9mm caliber = "9mm" max_ammo = 32 @@ -171,35 +219,46 @@ /obj/item/ammo_box/magazine/smgm9mm name = "SMG magazine (9mm)" icon_state = "smg9mm" - ammo_type = "/obj/item/ammo_casing/c9mm" + ammo_type = /obj/item/ammo_casing/c9mm caliber = "9mm" max_ammo = 30 materials = list(MAT_METAL = 2000) /obj/item/ammo_box/magazine/smgm9mm/ap name = "SMG magazine (Armour Piercing 9mm)" - ammo_type = "/obj/item/ammo_casing/c9mmap" + ammo_type = /obj/item/ammo_casing/c9mmap materials = list(MAT_METAL = 3000) /obj/item/ammo_box/magazine/smgm9mm/toxin name = "SMG magazine (Toxin Tipped 9mm)" - ammo_type = "/obj/item/ammo_casing/c9mmtox" + ammo_type = /obj/item/ammo_casing/c9mmtox materials = list(MAT_METAL = 3000) /obj/item/ammo_box/magazine/smgm9mm/fire name = "SMG Magazine (Incendiary 9mm)" - ammo_type = "/obj/item/ammo_casing/c9mminc" + ammo_type = /obj/item/ammo_casing/c9mminc materials = list(MAT_METAL = 3000) /obj/item/ammo_box/magazine/smgm9mm/update_icon() ..() icon_state = "[initial(icon_state)]-[round(ammo_count(),6)]" +/obj/item/ammo_box/magazine/pistolm9mm + name = "pistol magazine (9mm)" + icon_state = "9x19p-8" + ammo_type = /obj/item/ammo_casing/c9mm + caliber = "9mm" + max_ammo = 15 + +/obj/item/ammo_box/magazine/pistolm9mm/update_icon() + ..() + icon_state = "9x19p-[ammo_count() ? "8" : "0"]" + /obj/item/ammo_box/magazine/smgm45 name = "SMG magazine (.45)" icon_state = "c20r45" origin_tech = "combat=2" - ammo_type = "/obj/item/ammo_casing/c45" + ammo_type = /obj/item/ammo_casing/c45 caliber = ".45" max_ammo = 20 @@ -210,7 +269,7 @@ /obj/item/ammo_box/magazine/tommygunm45 name = "drum magazine (.45)" icon_state = "drum45" - ammo_type = "/obj/item/ammo_casing/c45" + ammo_type = /obj/item/ammo_casing/c45 caliber = ".45" max_ammo = 50 @@ -218,7 +277,7 @@ name = "handgun magazine (.50ae)" icon_state = "50ae" origin_tech = "combat=2" - ammo_type = "/obj/item/ammo_casing/a50" + ammo_type = /obj/item/ammo_casing/a50 caliber = ".50" max_ammo = 7 multiple_sprites = 1 @@ -226,7 +285,7 @@ /obj/item/ammo_box/magazine/m75 name = "specialized magazine (.75)" icon_state = "75" - ammo_type = "/obj/item/ammo_casing/caseless/a75" + ammo_type = /obj/item/ammo_casing/caseless/a75 caliber = "75" multiple_sprites = 2 max_ammo = 8 @@ -235,50 +294,16 @@ name = "toploader magazine (5.56mm)" icon_state = "5.56m" origin_tech = "combat=5;syndicate=1" - ammo_type = "/obj/item/ammo_casing/a556" + ammo_type = /obj/item/ammo_casing/a556 caliber = "a556" max_ammo = 30 multiple_sprites = 2 -/obj/item/ammo_box/magazine/m762 - name = "box magazine (7.62mm)" - icon_state = "a762" - origin_tech = "combat=2" - ammo_type = "/obj/item/ammo_casing/a762" - caliber = "a762" - max_ammo = 50 - -/obj/item/ammo_box/magazine/m762/update_icon() - ..() - icon_state = "[initial(icon_state)]-[round(ammo_count(),10)]" - -/obj/item/ammo_box/magazine/m762/buckshot - name = "box magazine (buckshot)" - icon_state = "b762" - caliber = "shotgun" - ammo_type ="/obj/item/ammo_casing/shotgun/buckshot" - max_ammo = 300 // Bluespace! - -/obj/item/ammo_box/magazine/m762/buckshot/update_icon() - ..() - icon_state = "[initial(icon_state)]-[round(ammo_count(),60)]" - -/obj/item/ammo_box/magazine/m762/he // I am not a good person. - name = "box magazine (40mm high explosive)" - icon_state = "g762" - caliber = "40mm" - ammo_type ="/obj/item/ammo_casing/a40mm" - max_ammo = 25 - -/obj/item/ammo_box/magazine/m762/he/update_icon() - ..() - icon_state = "[initial(icon_state)]-[round(ammo_count(),5)]" - - /obj/item/ammo_box/magazine/m12g name = "shotgun magazine (12g slugs)" + desc = "A drum magazine." icon_state = "m12gb" - ammo_type = "/obj/item/ammo_casing/shotgun" + ammo_type = /obj/item/ammo_casing/shotgun origin_tech = "combat=3;syndicate=1" caliber = "shotgun" max_ammo = 8 @@ -290,36 +315,45 @@ /obj/item/ammo_box/magazine/m12g/buckshot name = "shotgun magazine (12g buckshot slugs)" icon_state = "m12gb" - ammo_type = "/obj/item/ammo_casing/shotgun/buckshot" + ammo_type = /obj/item/ammo_casing/shotgun/buckshot /obj/item/ammo_box/magazine/m12g/stun name = "shotgun magazine (12g taser slugs)" icon_state = "m12gs" - ammo_type = "/obj/item/ammo_casing/shotgun/stunslug" + ammo_type = /obj/item/ammo_casing/shotgun/stunslug /obj/item/ammo_box/magazine/m12g/dragon name = "shotgun magazine (12g dragon's breath)" icon_state = "m12gf" - ammo_type = "/obj/item/ammo_casing/shotgun/incendiary/dragonsbreath" + ammo_type = /obj/item/ammo_casing/shotgun/incendiary/dragonsbreath + +/obj/item/ammo_box/magazine/m12g/bioterror + name = "shotgun magazine (12g bioterror)" + icon_state = "m12gt" + ammo_type = /obj/item/ammo_casing/shotgun/dart/bioterror + +/obj/item/ammo_box/magazine/m12g/breach + name = "shotgun magazine (12g breacher slugs)" + icon_state = "m12gbc" + ammo_type = /obj/item/ammo_casing/shotgun/breaching -//FOAM DARTS /obj/item/ammo_box/magazine/toy name = "foam force META magazine" - ammo_type = "/obj/item/ammo_casing/caseless/foam_dart" + ammo_type = /obj/item/ammo_casing/caseless/foam_dart caliber = "foam_force" /obj/item/ammo_box/magazine/toy/smg name = "foam force SMG magazine" icon_state = "smg9mm-30" - max_ammo = 30 + max_ammo = 20 /obj/item/ammo_box/magazine/toy/smg/update_icon() ..() - icon_state = "smg9mm-[round(ammo_count(),6)]" + icon_state = "smg9mm-[round(ammo_count(),5)]" /obj/item/ammo_box/magazine/toy/smg/riot - ammo_type = "/obj/item/ammo_casing/caseless/foam_dart/riot" + ammo_type = /obj/item/ammo_casing/caseless/foam_dart/riot /obj/item/ammo_box/magazine/toy/pistol name = "foam force pistol magazine" @@ -328,11 +362,11 @@ multiple_sprites = 2 /obj/item/ammo_box/magazine/toy/pistol/riot - ammo_type = "/obj/item/ammo_casing/caseless/foam_dart/riot" + ammo_type = /obj/item/ammo_casing/caseless/foam_dart/riot /obj/item/ammo_box/magazine/toy/smgm45 name = "donksoft SMG magazine" - ammo_type = "/obj/item/ammo_casing/caseless/foam_dart/riot" + ammo_type = /obj/item/ammo_casing/caseless/foam_dart/riot max_ammo = 20 /obj/item/ammo_box/magazine/toy/smgm45/update_icon() @@ -341,7 +375,7 @@ /obj/item/ammo_box/magazine/toy/m762 name = "donksoft box magazine" - ammo_type = "/obj/item/ammo_casing/caseless/foam_dart/riot" + ammo_type = /obj/item/ammo_casing/caseless/foam_dart/riot max_ammo = 50 /obj/item/ammo_box/magazine/toy/m762/update_icon() diff --git a/code/modules/projectiles/ammunition/special.dm b/code/modules/projectiles/ammunition/special.dm new file mode 100644 index 00000000000..d17d65e9fc8 --- /dev/null +++ b/code/modules/projectiles/ammunition/special.dm @@ -0,0 +1,49 @@ +/obj/item/ammo_casing/magic + name = "magic casing" + desc = "I didn't even know magic needed ammo..." + projectile_type = /obj/item/projectile/magic + +/obj/item/ammo_casing/magic/change + projectile_type = /obj/item/projectile/magic/change + +/obj/item/ammo_casing/magic/animate + projectile_type = /obj/item/projectile/magic/animate + +/obj/item/ammo_casing/magic/heal + projectile_type = /obj/item/projectile/magic/resurrection + +/obj/item/ammo_casing/magic/death + projectile_type = /obj/item/projectile/magic/death + +/obj/item/ammo_casing/magic/teleport + projectile_type = /obj/item/projectile/magic/teleport + +/obj/item/ammo_casing/magic/door + projectile_type = /obj/item/projectile/magic/door + +/obj/item/ammo_casing/magic/fireball + projectile_type = /obj/item/projectile/magic/fireball + +/obj/item/ammo_casing/magic/chaos + projectile_type = /obj/item/projectile/magic + +/obj/item/ammo_casing/magic/chaos/newshot() + projectile_type = pick(typesof(/obj/item/projectile/magic)) + ..() + +/obj/item/ammo_casing/magic/honk + projectile_type = /obj/item/projectile/bullet/honker + +/obj/item/ammo_casing/forcebolt + projectile_type = /obj/item/projectile/forcebolt + +/obj/item/ammo_casing/syringegun + name = "syringe gun spring" + desc = "A high-power spring that throws syringes." + projectile_type = null + +/obj/item/ammo_casing/energy/c3dbullet + projectile_type = /obj/item/projectile/bullet/midbullet3 + select_name = "spraydown" + fire_sound = 'sound/weapons/gunshot_smg.ogg' + e_cost = 200 diff --git a/code/modules/projectiles/firing.dm b/code/modules/projectiles/firing.dm new file mode 100644 index 00000000000..9642923c452 --- /dev/null +++ b/code/modules/projectiles/firing.dm @@ -0,0 +1,95 @@ +/obj/item/ammo_casing/proc/fire(atom/target as mob|obj|turf, mob/living/user as mob|obj, params, distro, quiet, zone_override = "",spread) + distro += variance + for (var/i = max(1, pellets), i > 0, i--) + var/targloc = get_turf(target) + ready_proj(target, user, quiet, zone_override) + if(distro) //We have to spread a pixel-precision bullet. throw_proj was called before so angles should exist by now... + if(randomspread) + spread = round((rand() - 0.5) * distro) + else //Smart spread + spread = round((i / pellets - 0.5) * distro) + if(!throw_proj(target, targloc, user, params, spread)) + return 0 + if(i > 1) + newshot() + if(click_cooldown_override) + user.changeNext_move(click_cooldown_override) + else + user.changeNext_move(CLICK_CD_RANGE) + user.newtonian_move(get_dir(target, user)) + update_icon() + return 1 + +/obj/item/ammo_casing/proc/ready_proj(atom/target, mob/living/user, quiet, zone_override = "") + if (!BB) + return + BB.original = target + BB.firer = user + if (zone_override) + BB.def_zone = zone_override + else + BB.def_zone = user.zone_selected + BB.suppressed = quiet + + if(reagents && BB.reagents) + reagents.trans_to(BB, reagents.total_volume) //For chemical darts/bullets + qdel(reagents) + +/obj/item/ammo_casing/proc/throw_proj(atom/target, turf/targloc, mob/living/user, params, spread) + var/turf/curloc = get_turf(user) + if (!istype(targloc) || !istype(curloc) || !BB) + return 0 + BB.ammo_casing = src + if(targloc == curloc) + if(target) //if the target is right on our location we go straight to bullet_act() + target.bullet_act(BB, BB.def_zone) + qdel(BB) + BB = null + return 1 + + BB.preparePixelProjectile(target, targloc, user, params, spread) + if(BB) + BB.fire() + BB = null + return 1 + +/obj/item/ammo_casing/proc/spread(turf/target, turf/current, distro) + var/dx = abs(target.x - current.x) + var/dy = abs(target.y - current.y) + return locate(target.x + round(gaussian(0, distro) * (dy+2)/8, 1), target.y + round(gaussian(0, distro) * (dx+2)/8, 1), target.z) + +/obj/item/projectile/proc/preparePixelProjectile(atom/target, var/turf/targloc, mob/living/user, params, spread) + var/turf/curloc = get_turf(user) + src.loc = get_turf(user) + src.starting = get_turf(user) + src.current = curloc + src.yo = targloc.y - curloc.y + src.xo = targloc.x - curloc.x + + if(params) + var/list/mouse_control = params2list(params) + if(mouse_control["icon-x"]) + src.p_x = text2num(mouse_control["icon-x"]) + if(mouse_control["icon-y"]) + src.p_y = text2num(mouse_control["icon-y"]) + if(mouse_control["screen-loc"]) + //Split screen-loc up into X+Pixel_X and Y+Pixel_Y + var/list/screen_loc_params = splittext(mouse_control["screen-loc"], ",") + + //Split X+Pixel_X up into list(X, Pixel_X) + var/list/screen_loc_X = splittext(screen_loc_params[1],":") + + //Split Y+Pixel_Y up into list(Y, Pixel_Y) + var/list/screen_loc_Y = splittext(screen_loc_params[2],":") + var/x = text2num(screen_loc_X[1]) * 32 + text2num(screen_loc_X[2]) - 32 + var/y = text2num(screen_loc_Y[1]) * 32 + text2num(screen_loc_Y[2]) - 32 + + //Calculate the "resolution" of screen based on client's view and world's icon size. This will work if the user can view more tiles than average. + var/screenview = (user.client.view * 2 + 1) * world.icon_size //Refer to http://www.byond.com/docs/ref/info.html#/client/var/view for mad maths + + var/ox = round(screenview/2) //"origin" x + var/oy = round(screenview/2) //"origin" y + var/angle = Atan2(y - oy, x - ox) + src.Angle = angle + if(spread) + src.Angle += spread diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index cb4f21d2497..7d72082b297 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -1,54 +1,59 @@ - #define SAWN_INTACT 0 - #define SAWN_OFF 1 - #define SAWN_SAWING -1 - /obj/item/weapon/gun name = "gun" - desc = "Its a gun. It's pretty terrible, though." + desc = "It's a gun. It's pretty terrible, though." icon = 'icons/obj/gun.dmi' icon_state = "detective" item_state = "gun" - lefthand_file = 'icons/mob/inhands/guns_lefthand.dmi' - righthand_file = 'icons/mob/inhands/guns_righthand.dmi' flags = CONDUCT slot_flags = SLOT_BELT materials = list(MAT_METAL=2000) - w_class = 3.0 + w_class = 3 throwforce = 5 - throw_speed = 4 + throw_speed = 3 throw_range = 5 - force = 5.0 + force = 5 origin_tech = "combat=1" needs_permit = 1 attack_verb = list("struck", "hit", "bashed") - var/fire_sound = 'sound/weapons/Gunshot.ogg' - var/obj/item/projectile/in_chamber = null - var/silenced = 0 - var/ghettomodded = 0 - var/recoil = 0 + var/fire_sound = "gunshot" + var/fire_sound_text = "gunshot" //the fire sound that shows in chat messages: laser blast, gunshot, etc. + var/suppressed = 0 //whether or not a message is displayed when fired var/can_suppress = 0 var/can_unsuppress = 1 + var/recoil = 0 //boom boom shake the room var/clumsy_check = 1 - var/sawn_desc = null + var/obj/item/ammo_casing/chambered = null + var/trigger_guard = TRIGGER_GUARD_NORMAL //trigger guard on the weapon, hulks can't fire them with their big meaty fingers + var/sawn_desc = null //description change if weapon is sawn-off var/sawn_state = SAWN_INTACT - var/obj/item/ammo_casing/chambered = null // The round (not bullet) that is in the chamber. THIS MISPLACED ITEM BROUGHT TO YOU BY HACKY BUCKSHOT. - var/tmp/list/mob/living/target //List of who yer targeting. - var/tmp/lock_time = -100 - var/tmp/mouthshoot = 0 ///To stop people from suiciding twice... >.> - var/automatic = 0 //Used to determine if you can target multiple people. - var/tmp/mob/living/last_moved_mob //Used to fire faster at more than one person. - var/tmp/told_cant_shoot = 0 //So that it doesn't spam them with the fact they cannot hit them. - var/firerate = 1 // 0 for one bullet after tarrget moves and aim is lowered, - //1 for keep shooting until aim is lowered - var/fire_delay = 0 - var/last_fired = 0 + var/burst_size = 1 //how large a burst is + var/fire_delay = 0 //rate of fire for burst firing and semi auto + var/firing_burst = 0 //Prevent the weapon from firing again while already firing + var/semicd = 0 //cooldown handler + var/weapon_weight = WEAPON_LIGHT + + var/spread = 0 + var/randomspread = 1 + + var/unique_rename = 0 //allows renaming with a pen + var/unique_reskin = 0 //allows one-time reskinning + var/reskinned = 0 //whether or not the gun has been reskinned + var/current_skin = null + var/list/options = list() + + lefthand_file = 'icons/mob/inhands/guns_lefthand.dmi' + righthand_file = 'icons/mob/inhands/guns_righthand.dmi' + var/obj/item/device/flashlight/F = null var/can_flashlight = 0 - var/heavy_weapon = 0 - var/randomspread = 0 - var/burst_size = 1 + var/list/upgrades = list() + + var/ammo_x_offset = 0 //used for positioning ammo count overlay on sprite + var/ammo_y_offset = 0 + var/flight_x_offset = 0 + var/flight_y_offset = 0 //Zooming var/zoomable = FALSE //whether the gun generates a Zoom action on creation @@ -56,312 +61,196 @@ var/zoom_amt = 3 //Distance in TURFs to move the user's screen forward (the "zoom" effect) var/datum/action/toggle_scope_zoom/azoom - proc/ready_to_fire() - if(world.time >= last_fired + fire_delay) - last_fired = world.time - return 1 - else - return 0 - - proc/process_chambered() - return 0 - - - proc/special_check(var/mob/M) //Placeholder for any special checks, like detective's revolver. - if(!M.IsAdvancedToolUser()) - return 0 - return 1 - - - proc/shoot_with_empty_chamber(mob/living/user as mob|obj) - to_chat(user, "*click*") - playsound(user, 'sound/weapons/emptyclick.ogg', 40, 1) - return - - emp_act(severity) - for(var/obj/O in contents) - O.emp_act(severity) - - proc/prepare_shot(var/obj/item/projectile/proj) //Transfer properties from the gun to the bullet - proj.shot_from = src - proj.silenced = silenced - return - /obj/item/weapon/gun/New() + ..() + if(F) + verbs += /obj/item/weapon/gun/proc/toggle_gunlight + new /datum/action/item_action/toggle_gunlight(src) build_zooming() + +/obj/item/weapon/gun/examine(mob/user) ..() + if(unique_reskin && !reskinned) + to_chat(user, "Alt-click it to reskin it.") + if(unique_rename) + to_chat(user, "Use a pen on it to rename it.") -/obj/item/weapon/gun/pickup(mob/user) +/obj/item/weapon/gun/proc/process_chamber() + return 0 - if(azoom) - azoom.Grant(user) - ..() - -/obj/item/weapon/gun/dropped(mob/user) - - zoom(user,FALSE) - if(azoom) - azoom.Remove(user) - ..() - -/obj/item/weapon/gun/afterattack(atom/A as mob|obj|turf|area, mob/living/user as mob|obj, flag, params) - if(flag) return //we're placing gun on a table or in backpack - if(istype(target, /obj/machinery/recharger) && istype(src, /obj/item/weapon/gun/energy)) return//Shouldnt flag take care of this? - Fire(A,user,params) //Otherwise, fire normally. - -/obj/item/weapon/gun/proc/isHandgun() +//check if there's enough ammo/energy/whatever to shoot one time +//i.e if clicking would make it shoot +/obj/item/weapon/gun/proc/can_shoot() return 1 -/obj/item/weapon/gun/proc/Fire(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, params, reflex = 0)//TODO: go over this +/obj/item/weapon/gun/proc/shoot_with_empty_chamber(mob/living/user as mob|obj) + to_chat(user, "*click*") + playsound(user, 'sound/weapons/empty.ogg', 100, 1) + +/obj/item/weapon/gun/proc/shoot_live_shot(mob/living/user as mob|obj, pointblank = 0, mob/pbtarget = null, message = 1) + if(recoil) + shake_camera(user, recoil + 1, recoil) + + if(suppressed) + playsound(user, fire_sound, 10, 1) + else + playsound(user, fire_sound, 50, 1) + if(!message) + return + if(pointblank) + user.visible_message("[user] fires [src] point blank at [pbtarget]!", "You fire [src] point blank at [pbtarget]!", "You hear \a [fire_sound_text]!") + else + user.visible_message("[user] fires [src]!", "You fire [src]!", "You hear \a [fire_sound_text]!") + + if(weapon_weight >= WEAPON_MEDIUM) + if(user.get_inactive_hand()) + if(prob(15)) + if(user.drop_item()) + user.visible_message("[src] flies out of [user]'s hands!", "[src] kicks out of your grip!") + +/obj/item/weapon/gun/emp_act(severity) + for(var/obj/O in contents) + O.emp_act(severity) + +/obj/item/weapon/gun/afterattack(atom/target, mob/living/user, flag, params) + if(firing_burst) + return + if(flag) //It's adjacent, is the user, or is on the user's person + if(target in user.contents) //can't shoot stuff inside us. + return + if(!ismob(target) || user.a_intent == "harm") //melee attack + return + if(target == user && user.zone_selected != "mouth") //so we can't shoot ourselves (unless mouth selected) + return + + if(istype(user))//Check if the user can use the gun, if the user isn't alive(turrets) assume it can. + var/mob/living/L = user + if(!can_trigger_gun(L)) + return + + if(!can_shoot()) //Just because you can pull the trigger doesn't mean it can't shoot. + shoot_with_empty_chamber(user) + return + + if(flag) + if(user.zone_selected == "mouth") + handle_suicide(user, target, params) + return + + //Exclude lasertag guns from the CLUMSY check. if(clumsy_check) - if(istype(user, /mob/living)) - var/mob/living/M = user - if ((CLUMSY in M.mutations) && prob(50)) - to_chat(M, "[src] blows up in your face.") - M.take_organ_damage(0,20) - M.drop_item() - qdel(src) + if(istype(user)) + if (user.disabilities & CLUMSY && prob(40)) + to_chat(user, "You shoot yourself in the foot with \the [src]!") + var/shot_leg = pick("l_foot", "r_foot") + process_fire(user, user, 0, params, zone_override = shot_leg) + user.drop_item() return - if (!user.IsAdvancedToolUser() || istype(user, /mob/living/simple_animal/diona)) - to_chat(user, "You don't have the dexterity to do this!") + if(weapon_weight == WEAPON_HEAVY && user.get_inactive_hand()) + to_chat(user, "You need both hands free to fire \the [src]!") return - if(istype(user, /mob/living)) - var/mob/living/M = user - if (HULK in M.mutations) - to_chat(M, "Your meaty finger is much too large for the trigger guard!") - return - if(ishuman(user)) - var/mob/living/carbon/human/H = user - if(H.species.flags & NOGUNS) - to_chat(user, "Your fingers don't fit in the trigger guard!") - return - if(H.martial_art && H.martial_art.name == "The Sleeping Carp") //great dishonor to famiry - to_chat(user, "Use of ranged weaponry would bring dishonor to the clan.") - return + process_fire(target,user,1,params) + +/obj/item/weapon/gun/proc/can_trigger_gun(var/mob/living/user) + if(!user.can_use_guns(src)) + return 0 + return 1 + +obj/item/weapon/gun/proc/newshot() + return + +/obj/item/weapon/gun/proc/process_fire(atom/target as mob|obj|turf, mob/living/user as mob|obj, message = 1, params, zone_override) add_fingerprint(user) - if(!special_check(user)) + if(semicd) return - if (!ready_to_fire()) - if (world.time % 3) //to prevent spam - to_chat(user, "[src] is not ready to fire again!") - return - - if(heavy_weapon) + if(weapon_weight) if(user.get_inactive_hand()) recoil = 4 //one-handed kick else recoil = initial(recoil) - if (istype(in_chamber, /obj/item/projectile/bullet/blank)) // A hacky way of making blank shotgun shells work again. Honk. - in_chamber.delete() - in_chamber = null - return - - user.changeNext_move(CLICK_CD_RANGE) - - var/spread = 0 - var/turf/targloc = get_turf(target) - for(var/f = 1 to burst_size) - if(!process_chambered()) //CHECK - return click_empty(user) - - if(chambered) - for (var/i = max(1, chambered.pellets), i > 0, i--) //Previous way of doing it fucked up math for spreading. This way, even the first projectile is part of the spread code. - if(i != max(1, chambered.pellets)) //Have we fired the initial chambered bullet yet? - in_chamber = new chambered.projectile_type() - ready_projectile(target, user) - prepare_shot(in_chamber) - if(chambered.deviation) - if(randomspread) //Random spread - spread = (rand() - 0.5) * chambered.deviation - else //Smart spread - spread = (i / chambered.pellets - 0.5) * chambered.deviation - if(!process_projectile(targloc, user, params, spread)) - return 0 - else - ready_projectile(target, user) - prepare_shot(in_chamber) - if(!process_projectile(targloc, user, params, spread)) - return 0 - - if(recoil) - spawn() - shake_camera(user, recoil + 1, recoil) - - if(silenced) - playsound(user, fire_sound, 10, 1) - else - playsound(user, fire_sound, 50, 1) - if(f == 1) // Only print this once - user.visible_message("[user] fires [src][reflex ? " by reflex":""]!", \ - "You fire [src][reflex ? "by reflex":""]!", \ - "You hear a [istype(in_chamber, /obj/item/projectile/beam) ? "laser blast" : "gunshot"]!") - - if(heavy_weapon) - if(user.get_inactive_hand()) - if(prob(15)) - user.visible_message("[src] flies out of [user]'s hands!", "[src] kicks out of your grip!") - user.drop_item() + if(burst_size > 1) + firing_burst = 1 + for(var/i = 1 to burst_size) + if(!user) + break + if(!issilicon(user)) + if( i>1 && !(src in get_both_hands(user))) //for burst firing break - - user.newtonian_move(get_dir(target, user)) + if(chambered) + if(!chambered.fire(target, user, params, , suppressed, zone_override)) + shoot_with_empty_chamber(user) + break + else + if(get_dist(user, target) <= 1) //Making sure whether the target is in vicinity for the pointblank shot + shoot_live_shot(user, 1, target, message) + else + shoot_live_shot(user, 0, target, message) + else + shoot_with_empty_chamber(user) + break + process_chamber() + update_icon() + sleep(fire_delay) + firing_burst = 0 + else + if(chambered) + if(!chambered.fire(target, user, params, , suppressed, zone_override, spread)) + shoot_with_empty_chamber(user) + return + else + if(get_dist(user, target) <= 1) //Making sure whether the target is in vicinity for the pointblank shot + shoot_live_shot(user, 1, target, message) + else + shoot_live_shot(user, 0, target, message) + else + shoot_with_empty_chamber(user) + return + process_chamber() update_icon() + semicd = 1 + spawn(fire_delay) + semicd = 0 + + if(user) if(user.hand) user.update_inv_l_hand() else user.update_inv_r_hand() - sleep(fire_delay) + feedback_add_details("gun_fired","[src.type]") -/obj/item/weapon/gun/proc/ready_projectile(atom/target as mob|obj|turf, mob/living/user) - in_chamber.firer = user - in_chamber.def_zone = user.zone_sel.selecting - in_chamber.original = target - return - -/obj/item/weapon/gun/proc/process_projectile(var/turf/targloc, mob/living/user as mob|obj, params, spread) - var/turf/curloc = user.loc - if (!istype(targloc) || !istype(curloc) || !in_chamber) - return 0 - if(targloc == curloc) //Fire the projectile - user.bullet_act(in_chamber) - del(in_chamber) - update_icon() - return 1 - - in_chamber.loc = get_turf(user) - in_chamber.starting = get_turf(user) - in_chamber.current = curloc - in_chamber.OnFired() - in_chamber.yo = targloc.y - curloc.y - in_chamber.xo = targloc.x - curloc.x - if(params) - var/list/mouse_control = params2list(params) - if(mouse_control["icon-x"]) - in_chamber.p_x = text2num(mouse_control["icon-x"]) - if(mouse_control["icon-y"]) - in_chamber.p_y = text2num(mouse_control["icon-y"]) - if(mouse_control["screen-loc"]) - //Split screen-loc up into X+Pixel_X and Y+Pixel_Y - var/list/screen_loc_params = splittext(mouse_control["screen-loc"], ",") - - //Split X+Pixel_X up into list(X, Pixel_X) - var/list/screen_loc_X = splittext(screen_loc_params[1],":") - - //Split Y+Pixel_Y up into list(Y, Pixel_Y) - var/list/screen_loc_Y = splittext(screen_loc_params[2],":") - - var/x = text2num(screen_loc_X[1]) * 32 + text2num(screen_loc_X[2]) - 32 - var/y = text2num(screen_loc_Y[1]) * 32 + text2num(screen_loc_Y[2]) - 32 - var/ox = round(480/2) //"origin" x - Basically center of the screen. This is a bad way of doing it because if you are able to view MORE than 17 tiles at a time your aim will get fucked. - var/oy = round(480/2) //"origin" y - Basically center of the screen. - - var/angle = Atan2(y - oy, x - ox) - - in_chamber.Angle = angle - - if(istype(user, /mob/living/carbon)) //Increase spread based on shock - var/mob/living/carbon/mob = user - if(mob.shock_stage > 120) - spread += rand(-5,5) - else if(mob.shock_stage > 70) - spread += rand(-2,2) - - if(spread) - in_chamber.Angle += spread - if(in_chamber) - in_chamber.process() - in_chamber = null - return 1 - -/obj/item/weapon/gun/proc/can_fire() - return process_chambered() - -/obj/item/weapon/gun/proc/can_hit(var/mob/living/target as mob, var/mob/living/user as mob) - return in_chamber.check_fire(target,user) - -/obj/item/weapon/gun/proc/click_empty(mob/user = null) - if (user) - user.visible_message("*click click*", "\red *click*") - playsound(user, 'sound/weapons/empty.ogg', 100, 1) +/obj/item/weapon/gun/attack(mob/M as mob, mob/user) + if(user.a_intent == "harm") //Flogging + ..() else - src.visible_message("*click click*") - playsound(src.loc, 'sound/weapons/empty.ogg', 100, 1) + return -/obj/item/weapon/gun/attack(mob/living/M as mob, mob/living/user as mob, def_zone) - //Suicide handling. - if (M == user && user.zone_sel.selecting == "mouth" && !mouthshoot) - mouthshoot = 1 - M.visible_message(" [user] sticks their gun in their mouth, ready to pull the trigger...") - if(!do_after(user, 40, target = M)) - M.visible_message(" [user] decided life was worth living") - mouthshoot = 0 - return - if (process_chambered()) - user.visible_message("[user] pulls the trigger.") - if(silenced) - playsound(user, fire_sound, 10, 1) - else - playsound(user, fire_sound, 50, 1) - if(istype(in_chamber, /obj/item/projectile/lasertag) || istype(in_chamber, /obj/item/projectile/bullet/reusable/foam_dart)) - user.show_message("You feel rather silly, trying to commit suicide with a toy.") - mouthshoot = 0 - return - in_chamber.on_hit(M) - if (in_chamber.damage_type != STAMINA) - user.apply_damage(in_chamber.damage*2.5, in_chamber.damage_type, "head", used_weapon = "Point blank shot in the mouth with \a [in_chamber]", sharp=1) - user.death() - else - to_chat(user, "Ow...") - user.apply_effect(110,STAMINA,0) - del(in_chamber) - mouthshoot = 0 - return - else - click_empty(user) - mouthshoot = 0 - return - - if (src.process_chambered()) - //Point blank shooting if on harm intent or target we were targeting. - if(user.a_intent == I_HARM) - user.visible_message("\red \The [user] fires \the [src] point blank at [M]!") - if(istype(in_chamber)) in_chamber.damage *= 1.3 - Fire(M,user,0,0,1) - return - else if(target && M in target) - Fire(M,user,0,0,1) ///Otherwise, shoot! - return - else - return ..() //Pistolwhippin' - - -/obj/item/weapon/gun/attackby(var/obj/item/A as obj, mob/user as mob, params) - if(istype(A, /obj/item/device/flashlight/seclite)) - var/obj/item/device/flashlight/seclite/S = A +/obj/item/weapon/gun/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/device/flashlight/seclite)) + var/obj/item/device/flashlight/seclite/S = I if(can_flashlight) if(!F) - if(user.l_hand != src && user.r_hand != src) - to_chat(user, "You'll need [src] in your hands to do that.") + if(!user.unEquip(I)) return - user.drop_item() to_chat(user, "You click [S] into place on [src].") if(S.on) - set_light(0) + SetLuminosity(0) F = S - A.loc = src + I.loc = src update_icon() update_gunlight(user) verbs += /obj/item/weapon/gun/proc/toggle_gunlight - if(istype(A, /obj/item/weapon/screwdriver)) - if(F) - if(user.l_hand != src && user.r_hand != src) - to_chat(user, "You'll need [src] in your hands to do that.") - return + var/datum/action/A = new /datum/action/item_action/toggle_gunlight(src) + if(loc == user) + A.Grant(user) + + if(istype(I, /obj/item/weapon/screwdriver)) + if(F && can_flashlight) for(var/obj/item/device/flashlight/seclite/S in src) to_chat(user, "You unscrew the seclite from [src].") F = null @@ -370,8 +259,13 @@ S.update_brightness(user) update_icon() verbs -= /obj/item/weapon/gun/proc/toggle_gunlight + for(var/datum/action/item_action/toggle_gunlight/TGL in actions) + qdel(TGL) + + if(unique_rename) + if(istype(I, /obj/item/weapon/pen)) + rename_gun(user) ..() - return /obj/item/weapon/gun/proc/toggle_gunlight() set name = "Toggle Gunlight" @@ -383,15 +277,14 @@ var/mob/living/carbon/human/user = usr if(!isturf(user.loc)) - to_chat(user, "You cannot turn the light on while in this [user.loc].") + to_chat(user, "You cannot turn the light on while in this [user.loc]!") F.on = !F.on to_chat(user, "You toggle the gunlight [F.on ? "on":"off"].") playsound(user, 'sound/weapons/empty.ogg', 100, 1) update_gunlight(user) - return -/obj/item/weapon/gun/proc/update_gunlight(var/mob/user = null) +/obj/item/weapon/gun/proc/update_gunlight(mob/user = null) if(F) if(F.on) set_light(F.brightness_on) @@ -400,7 +293,84 @@ update_icon() else set_light(0) + +/obj/item/weapon/gun/pickup(mob/user) + ..() + if(azoom) + azoom.Grant(user) + +/obj/item/weapon/gun/dropped(mob/user) + ..() + zoom(user,FALSE) + if(azoom) + azoom.Remove(user) + +/obj/item/weapon/gun/AltClick(mob/user) + ..() + if(user.incapacitated()) + to_chat(user, "You can't do that right now!") return + if(unique_reskin && !reskinned && loc == user) + reskin_gun(user) + +/obj/item/weapon/gun/proc/reskin_gun(mob/M) + var/choice = input(M,"Warning, you can only reskin your weapon once!","Reskin Gun") in options + + if(src && choice && !M.stat && in_range(M,src) && !M.restrained() && M.canmove) + if(options[choice] == null) + return + if(sawn_state == SAWN_OFF) + icon_state = options[choice] + "-sawn" + else + icon_state = options[choice] + current_skin = icon_state + to_chat(M, "Your gun is now skinned as [choice]. Say hello to your new friend.") + reskinned = 1 + +/obj/item/weapon/gun/proc/rename_gun(mob/M) + var/input = stripped_input(M,"What do you want to name the gun?", ,"", MAX_NAME_LEN) + + if(src && input && !M.stat && in_range(M,src) && !M.restrained() && M.canmove) + name = input + to_chat(M, "You name the gun [input]. Say hello to your new friend.") + return + +/obj/item/weapon/gun/proc/handle_suicide(mob/living/carbon/human/user, mob/living/carbon/human/target, params) + if(!ishuman(user) || !ishuman(target)) + return + + if(semicd) + return + + if(user == target) + target.visible_message("[user] sticks [src] in their mouth, ready to pull the trigger...", \ + "You stick [src] in your mouth, ready to pull the trigger...") + else + target.visible_message("[user] points [src] at [target]'s head, ready to pull the trigger...", \ + "[user] points [src] at your head, ready to pull the trigger...") + + semicd = 1 + + if(!do_mob(user, target, 120) || user.zone_selected != "mouth") + if(user) + if(user == target) + user.visible_message("[user] decided life was worth living.") + else if(target && target.Adjacent(user)) + target.visible_message("[user] has decided to spare [target]'s life.", "[user] has decided to spare your life!") + semicd = 0 + return + + semicd = 0 + + target.visible_message("[user] pulls the trigger!", "[user] pulls the trigger!") + + if(chambered && chambered.BB) + chambered.BB.damage *= 5 + + process_fire(target, user, 1, params) + +/obj/item/weapon/gun/proc/isHandgun() + return 1 ///////////// // ZOOMING // @@ -424,8 +394,6 @@ gun.zoom(L, FALSE) ..() - - /obj/item/weapon/gun/proc/zoom(mob/living/user, forced_zoom) if(!user || !user.client) return diff --git a/code/modules/projectiles/guns/alien.dm b/code/modules/projectiles/guns/alien.dm index eeb775be84a..56852f4e1ae 100644 --- a/code/modules/projectiles/guns/alien.dm +++ b/code/modules/projectiles/guns/alien.dm @@ -115,4 +115,4 @@ /obj/item/projectile/energy/sonic/proc/split() //TODO: create two more projectiles to either side of this one, fire at targets to the side of target turf. - return \ No newline at end of file + return diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm index 8add6a5caf9..df023424265 100644 --- a/code/modules/projectiles/guns/energy.dm +++ b/code/modules/projectiles/guns/energy.dm @@ -5,22 +5,22 @@ fire_sound = 'sound/weapons/Taser.ogg' var/obj/item/weapon/stock_parts/cell/power_supply //What type of power cell this uses - var/charge_cost = 1000 //How much energy is needed to fire. - var/cell_type = "/obj/item/weapon/stock_parts/cell" - var/projectile_type = "/obj/item/projectile/beam" - var/modifystate + var/cell_type = /obj/item/weapon/stock_parts/cell + var/modifystate = 0 + var/list/ammo_type = list(/obj/item/ammo_casing/energy) + var/select = 1 //The state of the select fire switch. Determines from the ammo_type list what kind of shot is fired next. var/can_charge = 1 - - //self-recharging - var/self_recharge = 0 //if set, the weapon will recharge itself + var/charge_sections = 4 + ammo_x_offset = 2 + var/shaded_charge = 0 //if this gun uses a stateful charge bar for more detail + var/selfcharge = 0 var/use_external_power = 0 //if set, the weapon will look for an external power source to draw from, otherwise it recharges magically - var/recharge_time = 4 var/charge_tick = 0 + var/charge_delay = 4 /obj/item/weapon/gun/energy/emp_act(severity) - if(power_supply) - power_supply.use(round(power_supply.charge / severity)) - update_icon() + power_supply.use(round(power_supply.charge / severity)) + update_icon() /obj/item/weapon/gun/energy/New() ..() @@ -29,7 +29,15 @@ else power_supply = new(src) power_supply.give(power_supply.maxcharge) - if(self_recharge) + var/obj/item/ammo_casing/energy/shot + for (var/i = 1, i <= ammo_type.len, i++) + var/shottype = ammo_type[i] + shot = new shottype(src) + ammo_type[i] = shot + shot = ammo_type[select] + fire_sound = shot.fire_sound + fire_delay = shot.delay + if(selfcharge) processing_objects.Add(src) update_icon() @@ -39,27 +47,122 @@ return ..() /obj/item/weapon/gun/energy/process() - if(self_recharge) //Every [recharge_time] ticks, recharge a shot for the cyborg + if(selfcharge) //Every [recharge_time] ticks, recharge a shot for the cyborg charge_tick++ - if(charge_tick < recharge_time) return 0 + if(charge_tick < recharge_time) + return 0 charge_tick = 0 - - if(!power_supply || power_supply.charge >= power_supply.maxcharge) + if(!power_supply) return 0 // check if we actually need to recharge - if(use_external_power) var/obj/item/weapon/stock_parts/cell/external = get_external_power_supply() if(!external || !external.use(charge_cost/10)) //Take power from the borg... return 0 //Note, uses /10 because of shitty mods to the cell system - power_supply.give(charge_cost) //... to recharge the shot update_icon() - return 1 -/obj/item/weapon/gun/energy/proc/get_external_power_supply() +/obj/item/weapon/gun/energy/attack_self(mob/living/user as mob) + if(ammo_type.len > 1) + select_fire(user) + update_icon() + +/obj/item/weapon/gun/energy/afterattack(atom/target as mob|obj|turf, mob/living/user as mob|obj, params) + newshot() //prepare a new shot + ..() + +/obj/item/weapon/gun/energy/can_shoot() + var/obj/item/ammo_casing/energy/shot = ammo_type[select] + return power_supply.charge >= shot.e_cost + +/obj/item/weapon/gun/energy/newshot() + if (!ammo_type || !power_supply) + return + var/obj/item/ammo_casing/energy/shot = ammo_type[select] + if(power_supply.charge >= shot.e_cost) //if there's enough power in the power_supply cell... + chambered = shot //...prepare a new shot based on the current ammo type selected + chambered.newshot() + return + +/obj/item/weapon/gun/energy/process_chamber() + if(chambered && !chambered.BB) //if BB is null, i.e the shot has been fired... + var/obj/item/ammo_casing/energy/shot = chambered + power_supply.use(shot.e_cost)//... drain the power_supply cell + chambered = null //either way, released the prepared shot + return + +/obj/item/weapon/gun/energy/proc/select_fire(mob/living/user) + select++ + if (select > ammo_type.len) + select = 1 + var/obj/item/ammo_casing/energy/shot = ammo_type[select] + fire_sound = shot.fire_sound + fire_delay = shot.delay + if (shot.select_name) + to_chat(user, "[src] is now set to [shot.select_name].") + update_icon() + return + +/obj/item/weapon/gun/energy/update_icon() + overlays.Cut() + var/ratio = Ceiling((power_supply.charge / power_supply.maxcharge) * charge_sections) + var/obj/item/ammo_casing/energy/shot = ammo_type[select] + var/iconState = "[icon_state]_charge" + var/itemState = null + if(!initial(item_state)) + itemState = icon_state + if (modifystate) + overlays += "[icon_state]_[shot.select_name]" + iconState += "_[shot.select_name]" + if(itemState) + itemState += "[shot.select_name]" + if(power_supply.charge < shot.e_cost) + overlays += "[icon_state]_empty" + else + if(!shaded_charge) + for(var/i = ratio, i >= 1, i--) + overlays += image(icon = icon, icon_state = iconState, pixel_x = ammo_x_offset * (i -1)) + else + overlays += image(icon = icon, icon_state = "[icon_state]_charge[ratio]") + if(F && can_flashlight) + var/iconF = "flight" + if(F.on) + iconF = "flight_on" + overlays += image(icon = icon, icon_state = iconF, pixel_x = flight_x_offset, pixel_y = flight_y_offset) + if(itemState) + itemState += "[ratio]" + item_state = itemState + +/obj/item/weapon/gun/energy/ui_action_click() + toggle_gunlight() + +/obj/item/weapon/gun/energy/suicide_act(mob/user) + if (src.can_shoot()) + user.visible_message("[user] is putting the barrel of the [src.name] in \his mouth. It looks like \he's trying to commit suicide.") + sleep(25) + if(user.l_hand == src || user.r_hand == src) + user.visible_message("[user] melts \his face off with the [src.name]!") + playsound(loc, fire_sound, 50, 1, -1) + var/obj/item/ammo_casing/energy/shot = ammo_type[select] + power_supply.use(shot.e_cost) + update_icon() + return(FIRELOSS) + else + user.visible_message("[user] panics and starts choking to death!") + return(OXYLOSS) + else + user.visible_message("[user] is pretending to blow \his brains out with the [src.name]! It looks like \he's trying to commit suicide!") + playsound(loc, 'sound/weapons/empty.ogg', 50, 1, -1) + return (OXYLOSS) + +/obj/item/weapon/gun/energy/proc/robocharge() if(isrobot(src.loc)) var/mob/living/silicon/robot/R = src.loc - return R.cell + if(R && R.cell) + var/obj/item/ammo_casing/energy/shot = ammo_type[select] //Necessary to find cost of shot + if(R.cell.use(shot.e_cost/10)) //Take power from the borg... //Divided by 10 because cells and charge costs are fucked. + power_supply.give(shot.e_cost) //... to recharge the shot + +/obj/item/weapon/gun/energy/proc/get_external_power_supply() if(istype(src.loc, /obj/item/rig_module)) var/obj/item/rig_module/module = src.loc if(module.holder && module.holder.wearer) @@ -69,29 +172,3 @@ if(istype(suit)) return suit.cell return null - -/obj/item/weapon/gun/energy/process_chambered() - if(in_chamber) return 1 - if(!power_supply) return 0 - if(!power_supply.use(charge_cost)) return 0 - if(!projectile_type) return 0 - in_chamber = new projectile_type(src) - return 1 - -/obj/item/weapon/gun/energy/update_icon() - if(cell_type) - var/ratio = power_supply.charge / power_supply.maxcharge - ratio = round(ratio, 0.25) * 100 - if(modifystate) - icon_state = "[modifystate][ratio]" - else - icon_state = "[initial(icon_state)][ratio]" - else - icon_state = "energy0" - overlays.Cut() - if(F) - if(F.on) - overlays += "flight-on" - else - overlays += "flight" - return diff --git a/code/modules/projectiles/guns/energy/advtaser.dm b/code/modules/projectiles/guns/energy/advtaser.dm deleted file mode 100644 index 8601a388f15..00000000000 --- a/code/modules/projectiles/guns/energy/advtaser.dm +++ /dev/null @@ -1,40 +0,0 @@ -/obj/item/weapon/gun/energy/advtaser - name = "hybrid taser" - desc = "A hybrid taser designed to fire both short-range high-power electrodes and long-range disabler beams." - icon_state = "advtaser" - item_state = null //so the human update icon uses the icon_state instead. - cell_type = "/obj/item/weapon/stock_parts/cell" - origin_tech = null - fire_sound = 'sound/weapons/Taser.ogg' - projectile_type = "/obj/item/projectile/energy/electrode" - charge_cost = 2000 - modifystate = "advtaserstun" - can_flashlight = 1 - fire_delay = 15 - - var/mode = 0 //0 = stun, 1 = disable - - - attack_self(mob/living/user as mob) - switch(mode) - if(0) - mode = 1 - charge_cost = 500 - fire_sound = 'sound/weapons/taser2.ogg' - to_chat(user, "\red [src.name] is now set to Disable.") - projectile_type = "/obj/item/projectile/beam/disabler" - modifystate = "advtaserdisable" - fire_delay = 0 - if(1) - mode = 0 - charge_cost = 2000 - fire_sound = 'sound/weapons/Taser.ogg' - to_chat(user, "\red [src.name] is now set to stun.") - projectile_type = "/obj/item/projectile/energy/electrode" - modifystate = "advtaserstun" - fire_delay = 15 - update_icon() - if(user.l_hand == src) - user.update_inv_l_hand() - else - user.update_inv_r_hand() diff --git a/code/modules/projectiles/guns/energy/blueshield.dm b/code/modules/projectiles/guns/energy/blueshield.dm deleted file mode 100644 index 914b92e19a6..00000000000 --- a/code/modules/projectiles/guns/energy/blueshield.dm +++ /dev/null @@ -1,37 +0,0 @@ -/obj/item/weapon/gun/energy/blueshield - name = "advanced stun revolver" - desc = "An advanced stun revolver with the capacity to shoot both electrodes and lasers." - icon_state = "bsgun_stun" - item_state = "gun" - modifystate = "bsgun_stun" - force = 7 - fire_sound = 'sound/weapons/gunshot.ogg' - charge_cost = 2000 - projectile_type = "/obj/item/projectile/energy/electrode" - fire_delay = 15 - var/fire_mode = 0 // 0 for taser, 1 for lethals. - -/obj/item/weapon/gun/energy/blueshield/attack_self(mob/living/user as mob) - if(!fire_mode) - fire_mode = 1 - charge_cost = 1000 - fire_delay = 0 - - fire_sound = 'sound/weapons/Laser.ogg' - projectile_type = "/obj/item/projectile/beam" - modifystate = "bsgun_kill" - - to_chat(user, "You adjust [src.name] to the kill setting.") - - else - fire_mode = 0 - charge_cost = 2000 - fire_delay = 15 - - projectile_type = "/obj/item/projectile/energy/electrode" - fire_sound = 'sound/weapons/gunshot.ogg' - modifystate = "bsgun_stun" - - to_chat(user, "You adjust [src.name] to the stun setting.") - - update_icon() \ No newline at end of file diff --git a/code/modules/projectiles/guns/energy/hos.dm b/code/modules/projectiles/guns/energy/hos.dm deleted file mode 100644 index 6bcb9992c4d..00000000000 --- a/code/modules/projectiles/guns/energy/hos.dm +++ /dev/null @@ -1,45 +0,0 @@ -/obj/item/weapon/gun/energy/hos - name = "head of security's energy gun" - desc = "This is a modern recreation of the captain's antique laser gun. This gun has several unique fire modes, but lacks the ability to recharge over time." - icon_state = "hoslaser" - item_state = null //so the human update icon uses the icon_state instead. - force = 10 - fire_sound = 'sound/weapons/Taser.ogg' - origin_tech = "combat=3;magnets=2" - charge_cost = 2000 - modifystate = "hoslaserstun" - projectile_type = "/obj/item/projectile/energy/electrode" - fire_delay = 15 - var/mode = 2 - - attack_self(mob/living/user as mob) - switch(mode) - if(2) - mode = 0 - charge_cost = 1000 - fire_sound = 'sound/weapons/Laser.ogg' - to_chat(user, "\red [src.name] is now set to kill.") - projectile_type = "/obj/item/projectile/beam" - modifystate = "hoslaserkill" - fire_delay = 0 - if(0) - mode = 1 - charge_cost = 500 - fire_sound = 'sound/weapons/taser2.ogg' - to_chat(user, "\red [src.name] is now set to disable.") - projectile_type = "/obj/item/projectile/beam/disabler" - modifystate = "hoslaserdisable" - fire_delay = 0 - if(1) - mode = 2 - charge_cost = 2000 - fire_sound = 'sound/weapons/taser.ogg' - to_chat(user, "\red [src.name] is now set to stun.") - projectile_type = "/obj/item/projectile/energy/electrode" - modifystate = "hoslaserstun" - fire_delay = 15 - update_icon() - if(user.l_hand == src) - user.update_inv_l_hand() - else - user.update_inv_r_hand() \ No newline at end of file diff --git a/code/modules/projectiles/guns/energy/laser.dm b/code/modules/projectiles/guns/energy/laser.dm index 330d9136f17..46add8b8b47 100644 --- a/code/modules/projectiles/guns/energy/laser.dm +++ b/code/modules/projectiles/guns/energy/laser.dm @@ -1,59 +1,63 @@ /obj/item/weapon/gun/energy/laser name = "laser gun" - desc = "a basic weapon designed kill with concentrated energy bolts" + desc = "A basic energy-based laser gun that fires concentrated beams of light which pass through glass and thin metal." icon_state = "laser" item_state = "laser" - fire_sound = 'sound/weapons/Laser.ogg' - charge_cost = 760 - w_class = 3.0 + w_class = 3 materials = list(MAT_METAL=2000) origin_tech = "combat=3;magnets=2" - projectile_type = "/obj/item/projectile/beam" + ammo_type = list(/obj/item/ammo_casing/energy/lasergun) + ammo_x_offset = 1 + shaded_charge = 1 /obj/item/weapon/gun/energy/laser/practice name = "practice laser gun" desc = "A modified version of the basic laser gun, this one fires less concentrated energy bolts designed for target practice." - projectile_type = "/obj/item/projectile/practice" + ammo_type = list(/obj/item/ammo_casing/energy/laser/practice) clumsy_check = 0 needs_permit = 0 -obj/item/weapon/gun/energy/laser/retro +/obj/item/weapon/gun/energy/laser/retro name ="retro laser gun" icon_state = "retro" - desc = "An older model of the basic lasergun, no longer used by Nanotrasen's security or military forces. Nevertheless, it is still quite deadly and easy to maintain, making it a favorite amongst pirates and other outlaws." - + desc = "An older model of the basic lasergun, no longer used by Nanotrasen's private security or military forces. Nevertheless, it is still quite deadly and easy to maintain, making it a favorite amongst pirates and other outlaws." + ammo_x_offset = 3 /obj/item/weapon/gun/energy/laser/captain - name = "captain's antique laser gun" + name = "antique laser gun" icon_state = "caplaser" item_state = "caplaser" desc = "This is an antique laser gun. All craftsmanship is of the highest quality. It is decorated with assistant leather and chrome. The object menaces with spikes of energy. On the item is an image of Space Station 13. The station is exploding." force = 10 origin_tech = null + ammo_x_offset = 3 + selfcharge = 1 - self_recharge = 1 - +/obj/item/weapon/gun/energy/laser/captain/scattershot + name = "scatter shot laser rifle" + icon_state = "lasercannon" + item_state = "laser" + desc = "An industrial-grade heavy-duty laser rifle with a modified laser lense to scatter its shot into multiple smaller lasers. The inner-core can self-charge for theorically infinite use." + origin_tech = "combat=5;materials=4;powerstorage=4" + ammo_type = list(/obj/item/ammo_casing/energy/laser/scatter, /obj/item/ammo_casing/energy/laser) /obj/item/weapon/gun/energy/laser/cyborg + can_charge = 0 desc = "An energy-based laser gun that draws power from the cyborg's internal energy cell directly. So this is what freedom looks like?" -/obj/item/weapon/gun/energy/laser/cyborg/process() - return 1 - -/obj/item/weapon/gun/energy/laser/cyborg/process_chambered() - if(in_chamber) - return 1 - if(isrobot(src.loc)) - var/mob/living/silicon/robot/R = src.loc - if(R && R.cell && R.cell.charge >= 83) - R.cell.use(83) - in_chamber = new/obj/item/projectile/beam(src) - return 1 - return 0 +/obj/item/weapon/gun/energy/laser/cyborg/newshot() + ..() + robocharge() /obj/item/weapon/gun/energy/laser/cyborg/emp_act() return +/obj/item/weapon/gun/energy/laser/scatter + name = "scatter laser gun" + desc = "A laser gun equipped with a refraction kit that spreads bolts." + ammo_type = list(/obj/item/ammo_casing/energy/laser/scatter, /obj/item/ammo_casing/energy/laser) + +///Laser Cannon /obj/item/weapon/gun/energy/lasercannon name = "accelerator laser cannon" @@ -64,13 +68,18 @@ obj/item/weapon/gun/energy/laser/retro force = 10 flags = CONDUCT slot_flags = SLOT_BACK - fire_sound = 'sound/weapons/lasercannonfire.ogg' origin_tech = "combat=4;materials=3;powerstorage=3" - projectile_type = "/obj/item/projectile/beam/laser/accelerator" + ammo_type = list(/obj/item/ammo_casing/energy/laser/accelerator) + ammo_x_offset = 3 /obj/item/weapon/gun/energy/lasercannon/isHandgun() return 0 +/obj/item/ammo_casing/energy/laser/accelerator + projectile_type = /obj/item/projectile/beam/laser/accelerator + select_name = "accelerator" + fire_sound = 'sound/weapons/lasercannonfire.ogg' + /obj/item/projectile/beam/laser/accelerator name = "accelerator laser" icon_state = "heavylaser" @@ -87,36 +96,29 @@ obj/item/weapon/gun/energy/laser/retro use_external_power = 1 recharge_time = 10 +/obj/item/weapon/gun/energy/lasercannon/cyborg -/obj/item/weapon/gun/energy/lasercannon/cyborg/process_chambered() - if(in_chamber) - return 1 - if(isrobot(src.loc)) - var/mob/living/silicon/robot/R = src.loc - if(R && R.cell) - R.cell.use(250) - in_chamber = new/obj/item/projectile/beam(src) - return 1 - return 0 +/obj/item/weapon/gun/energy/lasercannon/cyborg/newshot() + ..() + robocharge() + +/obj/item/weapon/gun/energy/lasercannon/cyborg/emp_act() + return /obj/item/weapon/gun/energy/xray name = "xray laser gun" desc = "A high-power laser gun capable of expelling concentrated xray blasts." icon_state = "xray" - fire_sound = 'sound/weapons/laser3.ogg' origin_tech = "combat=5;materials=3;magnets=2;syndicate=2" - projectile_type = "/obj/item/projectile/beam/xray" - charge_cost = 500 + ammo_type = list(/obj/item/ammo_casing/energy/xray) /obj/item/weapon/gun/energy/immolator name = "Immolator laser gun" desc = "A modified laser gun, shooting highly concetrated beams with higher intensity that ignites the target, for the cost of draining more power per shot" icon_state = "immolator" item_state = "laser" - fire_sound = 'sound/weapons/laser3.ogg' - projectile_type = "/obj/item/projectile/beam/immolator" + ammo_type = list(/obj/item/ammo_casing/energy/immolator) origin_tech = "combat=4;materials=4;magnets=3;plasmatech=2" - charge_cost = 1250 ////////Laser Tag//////////////////// @@ -124,34 +126,22 @@ obj/item/weapon/gun/energy/laser/retro name = "laser tag gun" icon_state = "bluetag" desc = "Standard issue weapon of the Imperial Guard" - projectile_type = "/obj/item/projectile/lasertag/blue" + ammo_type = list(/obj/item/ammo_casing/energy/laser/bluetag) origin_tech = "combat=1;magnets=2" clumsy_check = 0 needs_permit = 0 - - self_recharge = 1 - -/obj/item/weapon/gun/energy/laser/bluetag/special_check(var/mob/living/carbon/human/M) - if(ishuman(M)) - if(istype(M.wear_suit, /obj/item/clothing/suit/bluetag)) - return 1 - to_chat(M, "\red You need to be wearing your laser tag vest!") - return 0 + pin = /obj/item/device/firing_pin/tag/blue + ammo_x_offset = 2 + selfcharge = 1 /obj/item/weapon/gun/energy/laser/redtag name = "laser tag gun" icon_state = "redtag" desc = "Standard issue weapon of the Imperial Guard" - projectile_type = "/obj/item/projectile/lasertag/red" + ammo_type = list(/obj/item/ammo_casing/energy/laser/redtag) origin_tech = "combat=1;magnets=2" clumsy_check = 0 needs_permit = 0 - - self_recharge = 1 - -/obj/item/weapon/gun/energy/laser/redtag/special_check(var/mob/living/carbon/human/M) - if(ishuman(M)) - if(istype(M.wear_suit, /obj/item/clothing/suit/redtag)) - return 1 - to_chat(M, "\red You need to be wearing your laser tag vest!") - return 0 \ No newline at end of file + pin = /obj/item/device/firing_pin/tag/red + ammo_x_offset = 2 + selfcharge = 1 diff --git a/code/modules/projectiles/guns/energy/nuclear.dm b/code/modules/projectiles/guns/energy/nuclear.dm index 5bc164c5d76..b53563c81a7 100644 --- a/code/modules/projectiles/guns/energy/nuclear.dm +++ b/code/modules/projectiles/guns/energy/nuclear.dm @@ -3,169 +3,133 @@ desc = "A basic energy-based gun with two settings: kill and disable." icon_state = "energystun100" item_state = null //so the human update icon uses the icon_state instead. - fire_sound = 'sound/weapons/Taser2.ogg' - charge_cost = 500 - projectile_type = "/obj/item/projectile/beam/disabler" + ammo_type = list(/obj/item/ammo_casing/energy/disabler, /obj/item/ammo_casing/energy/laser) origin_tech = "combat=3;magnets=2" - modifystate = "energystun" + modifystate = 2 can_flashlight = 1 - - var/mode = 0 //0 = disable, 1 = kill - - - attack_self(mob/living/user as mob) - switch(mode) - if(0) - mode = 1 - charge_cost = 1000 - fire_sound = 'sound/weapons/Laser.ogg' - to_chat(user, "\red [src.name] is now set to kill.") - projectile_type = "/obj/item/projectile/beam" - modifystate = "energykill" - if(1) - mode = 0 - charge_cost = 500 - fire_sound = 'sound/weapons/Taser2.ogg' - to_chat(user, "\red [src.name] is now set to disable.") - projectile_type = "/obj/item/projectile/beam/disabler" - modifystate = "energystun" - update_icon() - if(user.l_hand == src) - user.update_inv_l_hand() - else - user.update_inv_r_hand() + ammo_x_offset = 3 + flight_x_offset = 15 + flight_y_offset = 10 /obj/item/weapon/gun/energy/gun/cyborg desc = "An energy-based laser gun that draws power from the cyborg's internal energy cell directly. So this is what freedom looks like?" -/obj/item/weapon/gun/energy/gun/cyborg/process() - return 1 - -/obj/item/weapon/gun/energy/gun/cyborg/process_chambered() - if(in_chamber) - return 1 - if(isrobot(src.loc)) - var/mob/living/silicon/robot/R = src.loc - if(R && R.cell && R.cell.charge >= 83) - R.cell.use(83) - in_chamber = new projectile_type(src) - return 1 - return 0 +/obj/item/weapon/gun/energy/gun/cyborg/newshot() + ..() + robocharge() +/obj/item/weapon/gun/energy/gun/cyborg/emp_act() + return /obj/item/weapon/gun/energy/gun/mounted name = "mounted energy gun" self_recharge = 1 use_external_power = 1 -/obj/item/weapon/gun/energy/gun/nuclear - name = "Advanced Energy Gun" - desc = "An energy gun with an experimental miniaturized reactor." - icon_state = "nucgun" - origin_tech = "combat=3;materials=5;powerstorage=3" - var/lightfail = 0 - can_flashlight = 0 - can_charge = 0 +/obj/item/weapon/gun/energy/gun/mini + name = "miniature energy gun" + desc = "A small, pistol-sized energy gun with a built-in flashlight. It has two settings: stun and kill." + icon_state = "mini" + item_state = "gun" + w_class = 2 + ammo_x_offset = 2 + charge_sections = 3 + can_flashlight = 0 // Can't attach or detach the flashlight, and override it's icon update - New() - ..() - processing_objects.Add(src) +/obj/item/weapon/gun/energy/gun/mini/New() + cell.maxcharge = 6000 + cell.charge = 6000 + F = new /obj/item/device/flashlight/seclite(src) + ..() +/obj/item/weapon/gun/energy/gun/mini/update_icon() + ..() + if(F && F.on) + overlays += "mini-light" - Destroy() - processing_objects.Remove(src) - return ..() - - - process() - charge_tick++ - if(charge_tick < 4) return 0 - charge_tick = 0 - if(!power_supply) return 0 - if((power_supply.charge / power_supply.maxcharge) != 1) - if(!failcheck()) return 0 - power_supply.give(1000) - update_icon() - return 1 - - - proc - failcheck() - lightfail = 0 - if (prob(src.reliability)) return 1 //No failure - if (prob(src.reliability)) - for (var/mob/living/M in range(0,src)) //Only a minor failure, enjoy your radiation if you're in the same tile or carrying it - if (src in M.contents) - to_chat(M, "\red Your gun feels pleasantly warm for a moment.") - else - to_chat(M, "\red You feel a warm sensation.") - M.apply_effect(rand(3,120), IRRADIATE) - lightfail = 1 - else - for (var/mob/living/M in range(rand(1,4),src)) //Big failure, TIME FOR RADIATION BITCHES - if (src in M.contents) - to_chat(M, "\red Your gun's reactor overloads!") - to_chat(M, "\red You feel a wave of heat wash over you.") - M.apply_effect(300, IRRADIATE) - crit_fail = 1 //break the gun so it stops recharging - processing_objects.Remove(src) - update_icon() - return 0 - - - update_charge() - if (crit_fail) - overlays += "nucgun-whee" - return - var/ratio = power_supply.charge / power_supply.maxcharge - ratio = round(ratio, 0.25) * 100 - overlays += "nucgun-[ratio]" - - - update_reactor() - if(crit_fail) - overlays += "nucgun-crit" - return - if(lightfail) - overlays += "nucgun-medium" - else if ((power_supply.charge/power_supply.maxcharge) <= 0.5) - overlays += "nucgun-light" - else - overlays += "nucgun-clean" - - - update_mode() - if (mode == 0) - overlays += "nucgun-stun" - else if (mode == 1) - overlays += "nucgun-kill" - - emp_act(severity) - ..() - reliability = max(reliability - round(15/severity), 0) //Do not allow it to go negative! - - - update_icon() - overlays.Cut() - update_charge() - update_reactor() - update_mode() +/obj/item/weapon/gun/energy/gun/hos + name = "\improper X-01 MultiPhase Energy Gun" + desc = "This is a expensive, modern recreation of a antique laser gun. This gun has several unique firemodes, but lacks the ability to recharge over time." + icon_state = "hoslaser" + force = 10 + ammo_type = list(/obj/item/ammo_casing/energy/electrode/hos, /obj/item/ammo_casing/energy/laser/hos, /obj/item/ammo_casing/energy/disabler) + ammo_x_offset = 4 +/obj/item/weapon/gun/energy/blueshield + name = "advanced stun revolver" + desc = "An advanced stun revolver with the capacity to shoot both electrodes and lasers." + icon_state = "bsgun_stun" + force = 7 + ammo_type = list(/obj/item/ammo_casing/energy/electrode/hos, /obj/item/ammo_casing/energy/laser/hos) + ammo_x_offset = 4 /obj/item/weapon/gun/energy/gun/turret name = "hybrid turret gun" desc = "A heavy hybrid energy cannon with two settings: Stun and kill." icon_state = "turretlaser" + item_state = "turretlaser" slot_flags = null w_class = 5 - heavy_weapon = 1 + ammo_type = list(/obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/laser) + weapon_weight = WEAPON_MEDIUM can_flashlight = 0 - projectile_type = /obj/item/projectile/energy/electrode - charge_cost = 1000 - fire_delay = 15 + trigger_guard = TRIGGER_GUARD_NONE + ammo_x_offset = 2 -/obj/item/weapon/gun/energy/gun/turret/update_icon() - icon_state = initial(icon_state) +/obj/item/weapon/gun/energy/gun/nuclear + name = "advanced energy gun" + desc = "An energy gun with an experimental miniaturized nuclear reactor that automatically charges the internal power cell." + icon_state = "nucgun" + item_state = "nucgun" + origin_tech = "combat=3;materials=5;powerstorage=3" + var/fail_tick = 0 + charge_delay = 5 + pin = null + can_charge = 0 + ammo_x_offset = 1 + ammo_type = list(/obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/laser, /obj/item/ammo_casing/energy/disabler) + selfcharge = 1 + +/obj/item/weapon/gun/energy/gun/nuclear/process() + if(fail_tick > 0) + fail_tick-- + ..() + +/obj/item/weapon/gun/energy/gun/nuclear/shoot_live_shot() + failcheck() + update_icon() + ..() + +/obj/item/weapon/gun/energy/gun/nuclear/proc/failcheck() + if(!prob(reliability) && istype(loc, /mob/living)) + var/mob/living/M = loc + switch(fail_tick) + if(0 to 200) + fail_tick += (2*(100-reliability)) + M.rad_act(40) + to_chat(M, "Your [name] feels warmer.") + if(201 to INFINITY) + SSobj.processing.Remove(src) + M.rad_act(80) + crit_fail = 1 + to_chat(M, "Your [name]'s reactor overloads!") + +/obj/item/weapon/gun/energy/gun/nuclear/emp_act(severity) + ..() + reliability = max(reliability - round(15/severity), 0) //Do not allow it to go negative! + +/obj/item/weapon/gun/energy/gun/nuclear/update_icon() + ..() + if(crit_fail) + overlays += "[icon_state]_fail_3" + else + switch(fail_tick) + if(0) + overlays += "[icon_state]_fail_0" + if(1 to 150) + overlays += "[icon_state]_fail_1" + if(151 to INFINITY) + overlays += "[icon_state]_fail_2" /obj/item/weapon/gun/energy/gun/turret/attack_self(mob/living/user as mob) switch(mode) @@ -189,4 +153,4 @@ if(user.l_hand == src) user.update_inv_l_hand() else - user.update_inv_r_hand() \ No newline at end of file + user.update_inv_r_hand() diff --git a/code/modules/projectiles/guns/energy/pulse.dm b/code/modules/projectiles/guns/energy/pulse.dm index bc7704d709a..b8ddc12a691 100644 --- a/code/modules/projectiles/guns/energy/pulse.dm +++ b/code/modules/projectiles/guns/energy/pulse.dm @@ -1,64 +1,26 @@ -/obj/item/weapon/gun/energy/pulse_rifle +/obj/item/weapon/gun/energy/pulse name = "pulse rifle" - desc = "A heavy-duty, pulse-based energy weapon, preferred by front-line combat personnel." + desc = "A heavy-duty, multifaceted energy rifle with three modes. Preferred by front-line combat personnel." icon_state = "pulse" - item_state = null //so the human update icon uses the icon_state instead. + item_state = null + w_class = 4 force = 10 - fire_sound = 'sound/weapons/pulse.ogg' - charge_cost = 200 - projectile_type = "/obj/item/projectile/beam/pulse" - cell_type = "/obj/item/weapon/stock_parts/cell/super" - var/mode = 2 + flags = CONDUCT slot_flags = SLOT_BACK - w_class = 4.0 + ammo_type = list(/obj/item/ammo_casing/energy/laser/pulse, /obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/laser) + cell_type = "/obj/item/weapon/stock_parts/cell/pulse" - emp_act() - return +/obj/item/weapon/gun/energy/pulse/emp_act(severity) + return - attack_self(mob/living/user as mob) - switch(mode) - if(2) - mode = 0 - charge_cost = 50 - fire_sound = 'sound/weapons/Taser.ogg' - to_chat(user, "\red [src.name] is now set to stun.") - projectile_type = "/obj/item/projectile/energy/electrode" - if(0) - mode = 1 - charge_cost = 100 - fire_sound = 'sound/weapons/Laser.ogg' - to_chat(user, "\red [src.name] is now set to kill.") - projectile_type = "/obj/item/projectile/beam" - if(1) - mode = 2 - charge_cost = 200 - fire_sound = 'sound/weapons/pulse.ogg' - to_chat(user, "\red [src.name] is now set to DESTROY.") - projectile_type = "/obj/item/projectile/beam/pulse" - return - - isHandgun() - return 0 - -/obj/item/weapon/gun/energy/pulse_rifle/cyborg/process_chambered() - if(in_chamber) - return 1 - if(isrobot(src.loc)) - var/mob/living/silicon/robot/R = src.loc - if(R && R.cell) - R.cell.use(charge_cost) - in_chamber = new projectile_type(src) - return 1 +/obj/item/weapon/gun/energy/pulse/isHandgun() return 0 +/obj/item/weapon/gun/energy/pulse_rifle/cyborg -/obj/item/weapon/gun/energy/pulse_rifle/destroyer - name = "pulse destroyer" - desc = "A heavy-duty, pulse-based energy weapon." - cell_type = "/obj/item/weapon/stock_parts/cell/infinite" - - attack_self(mob/living/user as mob) - to_chat(user, "\red [src.name] has three settings, and they are all DESTROY.") +/obj/item/weapon/gun/energy/pulse_rifle/cyborg/newshot() + ..() + robocharge() /obj/item/weapon/gun/energy/pulse_rifle/carbine name = "pulse carbine" @@ -69,7 +31,8 @@ item_state = "pulse" cell_type = "/obj/item/weapon/stock_parts/cell/pulse/carbine" can_flashlight = 1 - + flight_x_offset = 18 + flight_y_offset = 12 /obj/item/weapon/gun/energy/pulse_rifle/pistol name = "pulse pistol" @@ -79,15 +42,26 @@ icon_state = "pulse_pistol" item_state = "gun" cell_type = "/obj/item/weapon/stock_parts/cell/pulse/pistol" + can_charge = 0 /obj/item/weapon/gun/energy/pulse_rifle/pistol/isHandgun() return 1 +/obj/item/weapon/gun/energy/pulse_rifle/destroyer + name = "pulse destroyer" + desc = "A heavy-duty, pulse-based energy weapon." + cell_type = "/obj/item/weapon/stock_parts/cell/infinite" + ammo_type = list(/obj/item/ammo_casing/energy/laser/pulse) + +/obj/item/weapon/gun/energy/pulse/destroyer/attack_self(mob/living/user) + to_chat(user, "[src.name] has three settings, and they are all DESTROY.") + /obj/item/weapon/gun/energy/pulse_rifle/pistol/m1911 name = "\improper M1911-P" desc = "A compact pulse core in a classic handgun frame for Nanotrasen officers. It's not the size of the gun, it's the size of the hole it puts through people." icon_state = "m1911-p" item_state = "gun" cell_type = "/obj/item/weapon/stock_parts/cell/infinite" - isHandgun() - return 1 \ No newline at end of file + +/obj/item/weapon/gun/energy/pulse_rifle/pistol/m1911/isHandgun() + return 1 diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm index d6c0ad7beb3..e0328ac6058 100644 --- a/code/modules/projectiles/guns/energy/special.dm +++ b/code/modules/projectiles/guns/energy/special.dm @@ -8,7 +8,10 @@ w_class = 5.0 flags = CONDUCT slot_flags = SLOT_BACK - projectile_type = "/obj/item/projectile/ion" + ammo_type = list(/obj/item/ammo_casing/energy/ion) + ammo_x_offset = 3 + flight_x_offset = 17 + flight_y_offset = 9 /obj/item/weapon/gun/energy/ionrifle/emp_act(severity) return @@ -17,10 +20,12 @@ name = "ion carbine" desc = "The MK.II Prototype Ion Projector is a lightweight carbine version of the larger ion rifle, built to be ergonomic and efficient." icon_state = "ioncarbine" - item_state = "ioncarbine" origin_tech = "combat=4;magnets=4;materials=4" w_class = 3 slot_flags = SLOT_BELT + ammo_x_offset = 2 + flight_x_offset = 18 + flight_y_offset = 11 /obj/item/weapon/gun/energy/decloner name = "biological demolecularisor" @@ -28,8 +33,14 @@ icon_state = "decloner" fire_sound = 'sound/weapons/pulse3.ogg' origin_tech = "combat=5;materials=4;powerstorage=3" - projectile_type = "/obj/item/projectile/energy/declone" + ammo_type = list(/obj/item/ammo_casing/energy/declone) + ammo_x_offset = 1 +/obj/item/weapon/gun/energy/decloner/update_icon() + ..() + var/obj/item/ammo_casing/energy/shot = ammo_type[select] + if(power_supply.charge > shot.e_cost) + overlays += "decloner_spin" /obj/item/weapon/gun/energy/floragun name = "floral somatoray" @@ -37,42 +48,11 @@ icon_state = "floramut100" item_state = "gun" fire_sound = 'sound/effects/stealthoff.ogg' - projectile_type = "/obj/item/projectile/energy/floramut" + ammo_type = list(/obj/item/ammo_casing/energy/flora/yield, /obj/item/ammo_casing/energy/flora/mut) origin_tech = "materials=2;biotech=3;powerstorage=3" - modifystate = "floramut" - var/mode = 0 //0 = mutate, 1 = yield boost - needs_permit = 0 - - self_recharge = 1 - -/obj/item/weapon/gun/energy/floragun/attack_self(mob/living/user as mob) - switch(mode) - if(0) - mode = 1 - charge_cost = 1000 - to_chat(user, "\red The [src.name] is now set to increase yield.") - projectile_type = "/obj/item/projectile/energy/florayield" - modifystate = "florayield" - if(1) - mode = 0 - charge_cost = 1000 - to_chat(user, "\red The [src.name] is now set to induce mutations.") - projectile_type = "/obj/item/projectile/energy/floramut" - modifystate = "floramut" - update_icon() - return - - -/obj/item/weapon/gun/energy/floragun/afterattack(obj/target, mob/user, flag) - - if(flag && istype(target,/obj/machinery/portable_atmospherics/hydroponics)) - var/obj/machinery/portable_atmospherics/hydroponics/tray = target - if(process_chambered()) - user.visible_message("\red \The [user] fires \the [src] into \the [tray]!") - Fire(target,user) - return - - ..() + modifystate = 1 + ammo_x_offset = 1 + selfcharge = 1 /obj/item/weapon/gun/energy/meteorgun name = "meteor gun" @@ -80,13 +60,10 @@ icon_state = "riotgun" item_state = "c20r" w_class = 4 - projectile_type = "/obj/item/projectile/meteor" + ammo_type = list(/obj/item/ammo_casing/energy/meteor) cell_type = "/obj/item/weapon/stock_parts/cell/potato" clumsy_check = 0 //Admin spawn only, might as well let clowns use it. - - self_recharge = 1 - recharge_time = 5 //Time it takes for shots to recharge (in ticks) - + selfcharge = 1 /obj/item/weapon/gun/energy/meteorgun/pen name = "meteor pen" @@ -98,119 +75,115 @@ righthand_file = 'icons/mob/inhands/items_righthand.dmi' w_class = 1 - /obj/item/weapon/gun/energy/mindflayer name = "\improper Mind Flayer" desc = "A prototype weapon recovered from the ruins of Research-Station Epsilon." icon_state = "xray" - projectile_type = "/obj/item/projectile/beam/mindflayer" - fire_sound = 'sound/weapons/Laser.ogg' - -obj/item/weapon/gun/energy/staff/focus - name = "mental focus" - desc = "An artefact that channels the will of the user into destructive bolts of force. If you aren't careful with it, you might poke someone's brain out." - icon = 'icons/obj/wizard.dmi' - icon_state = "focus" - item_state = "focus" - projectile_type = "/obj/item/projectile/forcebolt" - /* - attack_self(mob/living/user as mob) - if(projectile_type == "/obj/item/projectile/forcebolt") - charge_cost = 200 - to_chat(user, "\red The [src.name] will now strike a small area.") - projectile_type = "/obj/item/projectile/forcebolt/strong" - else - charge_cost = 100 - to_chat(user, "\red The [src.name] will now strike only a single person.") - projectile_type = "/obj/item/projectile/forcebolt" - */ - -/obj/item/weapon/gun/energy/clown - name = "HONK Rifle" - desc = "Clown Planet's finest." - icon_state = "energy" - projectile_type = "/obj/item/projectile/clown" - fire_sound = 'sound/weapons/Gunshot_smg.ogg' - clumsy_check = 0 - - - -/obj/item/weapon/gun/energy/toxgun - name = "plasma pistol" - desc = "A specialized firearm designed to fire lethal bolts of toxins." - icon_state = "toxgun" - fire_sound = 'sound/effects/stealthoff.ogg' - w_class = 3.0 - origin_tech = "combat=4;plasmatech=3" - projectile_type = "/obj/item/projectile/energy/plasma" - -/obj/item/weapon/gun/energy/sniperrifle - name = "L.W.A.P. Sniper Rifle" - desc = "A rifle constructed of lightweight materials, fitted with a SMART aiming-system scope." - icon = 'icons/obj/gun.dmi' - icon_state = "esniper" - fire_sound = 'sound/weapons/marauder.ogg' - origin_tech = "combat=6;materials=5;powerstorage=4" - projectile_type = "/obj/item/projectile/beam/sniper" - slot_flags = SLOT_BACK - charge_cost = 2500 - fire_delay = 50 - w_class = 4.0 - zoomable = TRUE - zoom_amt = 7 //Long range, enough to see in front of you, but no tiles behind you. - + item_state = null + ammo_type = list(/obj/item/ammo_casing/energy/mindflayer) + ammo_x_offset = 2 /obj/item/weapon/gun/energy/kinetic_accelerator name = "proto-kinetic accelerator" desc = "According to Nanotrasen accounting, this is mining equipment. It's been modified for extreme power output to crush rocks, but often serves as a miner's first defense against hostile alien life; it's not very powerful unless used in a low pressure environment." icon_state = "kineticgun" item_state = "kineticgun" - projectile_type = "/obj/item/projectile/kinetic" - fire_sound = 'sound/weapons/Kenetic_accel.ogg' - charge_cost = 5000 - cell_type = "/obj/item/weapon/stock_parts/cell/emproof" - needs_permit = 0 // Aparently these are safe to carry? I'm sure Golliaths would disagree. - fire_delay = 16 //Because guncode is bad and you can bug the reload for rapid fire otherwise. - var/recently_fired = 0 + ammo_type = list(/obj/item/ammo_casing/energy/kinetic) + cell_type = /obj/item/weapon/stock_parts/cell/emproof + // Apparently these are safe to carry? I'm sure goliaths would disagree. + var/overheat_time = 16 + unique_rename = 1 + weapon_weight = WEAPON_LIGHT + origin_tech = "combat=2;powerstorage=1" + var/holds_charge = FALSE + var/unique_frequency = FALSE // modified by KA modkits + + var/overheat = FALSE /obj/item/weapon/gun/energy/kinetic_accelerator/super name = "super-kinetic accelerator" desc = "An upgraded, superior version of the proto-kinetic accelerator." icon_state = "kineticgun_u" - projectile_type = "/obj/item/projectile/kinetic/super" - fire_delay = 15 + ammo_type = list(/obj/item/ammo_casing/energy/kinetic/super) + overheat_time = 15 origin_tech = "combat=3;powerstorage=2" /obj/item/weapon/gun/energy/kinetic_accelerator/hyper name = "hyper-kinetic accelerator" desc = "An upgraded, even more superior version of the proto-kinetic accelerator." icon_state = "kineticgun_h" - projectile_type = "/obj/item/projectile/kinetic/hyper" - fire_delay = 13 + ammo_type = list(/obj/item/ammo_casing/energy/kinetic/hyper) + overheat_time = 14 origin_tech = "combat=4;powerstorage=3" /obj/item/weapon/gun/energy/kinetic_accelerator/cyborg - flags = NODROP + holds_charge = TRUE + unique_frequency = TRUE -/obj/item/weapon/gun/energy/kinetic_accelerator/Fire() - if(!recently_fired) - recently_fired = 1 - spawn(fire_delay) - reload(usr) - ..() +/obj/item/weapon/gun/energy/kinetic_accelerator/New() + . = ..() + if(!holds_charge) + empty() + +/obj/item/weapon/gun/energy/kinetic_accelerator/shoot_live_shot() + . = ..() + attempt_reload() + +/obj/item/weapon/gun/energy/kinetic_accelerator/equipped(mob/user) + . = ..() + if(!can_shoot()) + attempt_reload() + +/obj/item/weapon/gun/energy/kinetic_accelerator/dropped() + . = ..() + if(!holds_charge) + // Put it on a delay because moving item from slot to hand + // calls dropped(). + spawn(1) + if(!ismob(loc)) + empty() + +/obj/item/weapon/gun/energy/kinetic_accelerator/proc/empty() + power_supply.use(5000) + update_icon() + +/obj/item/weapon/gun/energy/kinetic_accelerator/proc/attempt_reload() + if(overheat) + return + overheat = TRUE + + var/carried = 0 + if(!unique_frequency) + for(var/obj/item/weapon/gun/energy/kinetic_accelerator/K in \ + loc.GetAllContents()) + + carried++ + + carried = max(carried, 1) + else + carried = 1 + + spawn(overheat_time * carried) + reload() + overheat = FALSE /obj/item/weapon/gun/energy/kinetic_accelerator/emp_act(severity) return -/obj/item/weapon/gun/energy/kinetic_accelerator/proc/reload(mob/living/user) +/obj/item/weapon/gun/energy/kinetic_accelerator/proc/reload() power_supply.give(5000) - if(!silenced) + if(!suppressed) playsound(src.loc, 'sound/weapons/kenetic_reload.ogg', 60, 1) - else if(user) - to_chat(usr, "You silently charge [src].") - recently_fired = 0 + else + to_chat(loc, "[src] silently charges up.") update_icon() +/obj/item/weapon/gun/energy/kinetic_accelerator/update_icon() + if(!can_shoot()) + icon_state = "[initial(icon_state)]_empty" + else + icon_state = initial(icon_state) + /obj/item/weapon/gun/energy/kinetic_accelerator/crossbow name = "mini energy crossbow" desc = "A weapon favored by syndicate stealth specialists." @@ -219,10 +192,13 @@ obj/item/weapon/gun/energy/staff/focus w_class = 2 materials = list(MAT_METAL=2000) origin_tech = "combat=2;magnets=2;syndicate=5" - silenced = 1 - projectile_type = "/obj/item/projectile/energy/bolt" - fire_sound = 'sound/weapons/Genhit.ogg' - fire_delay = 20 + suppressed = 1 + ammo_type = list(/obj/item/ammo_casing/energy/bolt) + weapon_weight = WEAPON_LIGHT + unique_rename = 0 + overheat_time = 20 + holds_charge = TRUE + unique_frequency = TRUE /obj/item/weapon/gun/energy/kinetic_accelerator/crossbow/large name = "energy crossbow" @@ -231,8 +207,8 @@ obj/item/weapon/gun/energy/staff/focus w_class = 3 materials = list(MAT_METAL=4000) origin_tech = "combat=2;magnets=2;syndicate=3" //can be further researched for more syndie tech - silenced = 0 - projectile_type = "/obj/item/projectile/energy/bolt/large" + suppressed = 0 + ammo_type = list(/obj/item/ammo_casing/energy/bolt/large) /obj/item/weapon/gun/energy/kinetic_accelerator/crossbow/large/cyborg desc = "One and done!" @@ -240,27 +216,35 @@ obj/item/weapon/gun/energy/staff/focus origin_tech = null materials = list() +/obj/item/weapon/gun/energy/kinetic_accelerator/suicide_act(mob/user) + if(!suppressed) + playsound(src.loc, 'sound/weapons/kenetic_reload.ogg', 60, 1) + user.visible_message("[user] cocks the [src.name] and pretends to blow \his brains out! It looks like \he's trying to commit suicide!") + shoot_live_shot() + return (OXYLOSS) + /obj/item/weapon/gun/energy/plasmacutter name = "plasma cutter" desc = "A mining tool capable of expelling concentrated plasma bursts. You could use it to cut limbs off of xenos! Or, you know, mine stuff." icon_state = "plasmacutter" item_state = "plasmacutter" - modifystate = "plasmacutter" + modifystate = -1 origin_tech = "combat=1;materials=3;magnets=2;plasmatech=2;engineering=1" - projectile_type = /obj/item/projectile/plasma + ammo_type = list(/obj/item/ammo_casing/energy/plasma) fire_sound = 'sound/weapons/laser.ogg' flags = CONDUCT | OPENCONTAINER attack_verb = list("attacked", "slashed", "cut", "sliced") - charge_cost = 250 - fire_delay = 15 + force = 12 + sharpness = IS_SHARP can_charge = 0 + heat = 3800 /obj/item/weapon/gun/energy/plasmacutter/examine(mob/user) - ..(user) + ..() if(power_supply) to_chat(user, "[src] is [round(power_supply.percent())]% charged.") -/obj/item/weapon/gun/energy/plasmacutter/attackby(var/obj/item/A, var/mob/user) +/obj/item/weapon/gun/energy/plasmacutter/attackby(obj/item/A, mob/user) if(istype(A, /obj/item/stack/sheet/mineral/plasma)) var/obj/item/stack/sheet/S = A S.use(1) @@ -273,92 +257,34 @@ obj/item/weapon/gun/energy/staff/focus else ..() +/obj/item/weapon/gun/energy/plasmacutter/update_icon() + return + /obj/item/weapon/gun/energy/plasmacutter/adv name = "advanced plasma cutter" icon_state = "adv_plasmacutter" modifystate = "adv_plasmacutter" origin_tech = "combat=3;materials=4;magnets=3;plasmatech=3;engineering=2" - projectile_type = /obj/item/projectile/plasma/adv - fire_delay = 10 - charge_cost = 100 - -/obj/item/weapon/gun/energy/disabler - name = "disabler" - desc = "A self-defense weapon that exhausts organic targets, weakening them until they collapse." - icon_state = "disabler" - item_state = null - projectile_type = /obj/item/projectile/beam/disabler - fire_sound = 'sound/weapons/taser2.ogg' - cell_type = "/obj/item/weapon/stock_parts/cell" - charge_cost = 500 - -/obj/item/weapon/gun/energy/disabler/cyborg - name = "cyborg disabler" - desc = "An integrated disabler that draws from a cyborg's power cell. This weapon contains a limiter to prevent the cyborg's power cell from overheating." - - self_recharge = 1 - use_external_power = 1 - recharge_time = 2.5 - - -/* 3d printer 'pseudo guns' for borgs */ - -/obj/item/weapon/gun/energy/printer - name = "cyborg lmg" - desc = "A machinegun that fires 3d-printed flachettes slowly regenerated using a cyborg's internal power source." - icon_state = "l6closed0" - icon = 'icons/obj/gun.dmi' - fire_sound = 'sound/weapons/Gunshot_smg.ogg' - cell_type = "/obj/item/weapon/stock_parts/cell/secborg" - projectile_type = "/obj/item/projectile/bullet/midbullet3" - charge_cost = 200 //Yeah, let's NOT give them a 300 round clip that recharges, 20 is more reasonable and will actually hurt the borg's battery for overuse. - - self_recharge = 1 - use_external_power = 1 - recharge_time = 5 - -/obj/item/weapon/gun/energy/printer/update_icon() - return + ammo_type = list(/obj/item/ammo_casing/energy/plasma/adv) /obj/item/weapon/gun/energy/wormhole_projector name = "bluespace wormhole projector" desc = "A projector that emits high density quantum-coupled bluespace beams." - projectile_type = "/obj/item/projectile/beam/wormhole" - charge_cost = 0 - fire_sound = "sound/weapons/pulse3.ogg" + ammo_type = list(/obj/item/ammo_casing/energy/wormhole, /obj/item/ammo_casing/energy/wormhole/orange) item_state = null icon_state = "wormhole_projector100" - modifystate = "wormhole_projector" var/obj/effect/portal/blue var/obj/effect/portal/orange - var/mode = 0 //0 = blue 1 = orange +/obj/item/weapon/gun/energy/wormhole_projector/update_icon() + icon_state = "[initial(icon_state)][select]" + item_state = icon_state + return -/obj/item/weapon/gun/energy/wormhole_projector/attack_self(mob/living/user as mob) - switch_modes() - -/obj/item/weapon/gun/energy/wormhole_projector/proc/switch_modes(mob/living/user as mob) - switch(mode) - if(0) - mode = 1 - to_chat(user, "[name] is now set to orange.") - projectile_type = "/obj/item/projectile/beam/wormhole/orange" - modifystate = "wormhole_projector_orange" - if(1) - mode = 0 - to_chat(user, "[name] is now set to blue.") - projectile_type = "/obj/item/projectile/beam/wormhole" - modifystate = "wormhole_projector" - update_icon() - if(user.hand) - user.update_inv_l_hand() - else - user.update_inv_r_hand() - -/obj/item/weapon/gun/energy/wormhole_projector/Fire(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, params, reflex = 0) +/obj/item/weapon/gun/energy/wormhole_projector/process_chamber() ..() - switch_modes(user) + select_fire() /obj/item/weapon/gun/energy/wormhole_projector/proc/portal_destroyed(obj/effect/portal/P) if(P.icon_state == "portal") @@ -373,7 +299,6 @@ obj/item/weapon/gun/energy/staff/focus /obj/item/weapon/gun/energy/wormhole_projector/proc/create_portal(obj/item/projectile/beam/wormhole/W) var/obj/effect/portal/P = new /obj/effect/portal(get_turf(W), null, src) P.precision = 0 - P.failchance = 0 if(W.name == "bluespace beam") qdel(blue) blue = P @@ -383,4 +308,259 @@ obj/item/weapon/gun/energy/staff/focus orange = P if(orange && blue) blue.target = get_turf(orange) - orange.target = get_turf(blue) \ No newline at end of file + orange.target = get_turf(blue) + +/* 3d printer 'pseudo guns' for borgs */ + +/obj/item/weapon/gun/energy/printer + name = "cyborg lmg" + desc = "A machinegun that fires 3d-printed flachettes slowly regenerated using a cyborg's internal power source." + icon_state = "l6closed0" + icon = 'icons/obj/gun.dmi' + cell_type = "/obj/item/weapon/stock_parts/cell/secborg" + ammo_type = list(/obj/item/ammo_casing/energy/c3dbullet) + can_charge = 0 + +/obj/item/weapon/gun/energy/printer/update_icon() + return + +/obj/item/weapon/gun/energy/printer/emp_act() + return + +/obj/item/weapon/gun/energy/printer/newshot() + ..() + robocharge() + +/obj/item/weapon/gun/energy/laser/instakill + name = "instakill rifle" + icon_state = "instagib" + item_state = "instagib" + desc = "A specialized ASMD laser-rifle, capable of flat-out disintegrating most targets in a single hit." + ammo_type = list(/obj/item/ammo_casing/energy/instakill) + force = 60 + origin_tech = null + +/obj/item/weapon/gun/energy/laser/instakill/emp_act() //implying you could stop the instagib + return + +/obj/item/weapon/gun/energy/laser/instakill/red + desc = "A specialized ASMD laser-rifle, capable of flat-out disintegrating most targets in a single hit. This one has a red design." + icon_state = "instagibred" + item_state = "instagibred" + ammo_type = list(/obj/item/ammo_casing/energy/instakill/red) + +/obj/item/weapon/gun/energy/laser/instakill/blue + desc = "A specialized ASMD laser-rifle, capable of flat-out disintegrating most targets in a single hit. This one has a blue design." + icon_state = "instagibblue" + item_state = "instagibblue" + ammo_type = list(/obj/item/ammo_casing/energy/instakill/blue) + +obj/item/weapon/gun/energy/staff/focus + name = "mental focus" + desc = "An artefact that channels the will of the user into destructive bolts of force. If you aren't careful with it, you might poke someone's brain out." + icon = 'icons/obj/wizard.dmi' + icon_state = "focus" + item_state = "focus" + ammo_type = list(/obj/item/ammo_casing/forcebolt) + +/obj/item/weapon/gun/energy/clown + name = "HONK Rifle" + desc = "Clown Planet's finest." + icon_state = "energy" + ammo_type = list(/obj/item/ammo_casing/energy/clown) + clumsy_check = 0 + +/obj/item/weapon/gun/energy/toxgun + name = "plasma pistol" + desc = "A specialized firearm designed to fire lethal bolts of toxins." + icon_state = "toxgun" + fire_sound = 'sound/effects/stealthoff.ogg' + w_class = 3 + origin_tech = "combat=4;plasmatech=3" + ammo_type = list(/obj/item/ammo_casing/energy/toxplasma) + +/obj/item/weapon/gun/energy/sniperrifle + name = "L.W.A.P. Sniper Rifle" + desc = "A rifle constructed of lightweight materials, fitted with a SMART aiming-system scope." + icon = 'icons/obj/gun.dmi' + icon_state = "esniper" + fire_sound = 'sound/weapons/marauder.ogg' + origin_tech = "combat=6;materials=5;powerstorage=4" + ammo_type = list(/obj/item/ammo_casing/energy/sniper) + slot_flags = SLOT_BACK + w_class = 4 + zoomable = TRUE + zoom_amt = 7 //Long range, enough to see in front of you, but no tiles behind you. + +/obj/item/weapon/gun/energy/temperature + name = "temperature gun" + icon = 'icons/obj/gun_temperature.dmi' + icon_state = "tempgun_4" + item_state = "tempgun_4" + slot_flags = SLOT_BACK + w_class = 4 + fire_sound = 'sound/weapons/pulse3.ogg' + desc = "A gun that changes the body temperature of its targets." + var/temperature = 300 + var/target_temperature = 300 + var/e_cost = 1000 + origin_tech = "combat=3;materials=4;powerstorage=3;magnets=2" + + ammo_type = /obj/item/ammo_casing/energy/temp + cell_type = "/obj/item/weapon/stock_parts/cell" + + var/powercost = "" + var/powercostcolor = "" + + var/emagged = 0 //ups the temperature cap from 500 to 1000, targets hit by beams over 500 Kelvin will burst into flames + var/dat = "" + +/obj/item/weapon/gun/energy/temperature/New() + ..() + update_icon() + processing_objects.Add(src) + + +/obj/item/weapon/gun/energy/temperature/Destroy() + processing_objects.Remove(src) + return ..() + +/obj/item/weapon/gun/energy/temperature/newshot() + ..() + chambered.temperature = temperature + chambered.e_cost = e_cost + +/obj/item/weapon/gun/energy/temperature/attack_self(mob/living/user as mob) + user.set_machine(src) + update_dat() + user << browse("Temperature Gun Configuration


[dat]", "window=tempgun;size=510x120") + onclose(user, "tempgun") + +/obj/item/weapon/gun/energy/temperature/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W, /obj/item/weapon/card/emag) && !emagged) + emagged = 1 + to_chat(user, "You double the gun's temperature cap! Targets hit by searing beams will burst into flames!") + desc = "A gun that changes the body temperature of its targets. Its temperature cap has been hacked." + +/obj/item/weapon/gun/energy/temperature/Topic(href, href_list) + if (..()) + return + usr.set_machine(src) + add_fingerprint(usr) + + if(href_list["temp"]) + var/amount = text2num(href_list["temp"]) + if(amount > 0) + target_temperature = min((500 + 500*emagged), target_temperature+amount) + else + target_temperature = max(0, target_temperature+amount) + if (istype(loc, /mob)) + attack_self(loc) + add_fingerprint(usr) + return + + +/obj/item/weapon/gun/energy/temperature/process() + switch(temperature) + if(0 to 100) + charge_cost = 3000 + powercost = "High" + if(100 to 250) + charge_cost = 2000 + powercost = "Medium" + if(251 to 300) + charge_cost = 1000 + powercost = "Low" + if(301 to 400) + charge_cost = 2000 + powercost = "Medium" + if(401 to 1000) + charge_cost = 3000 + powercost = "High" + switch(powercost) + if("High") powercostcolor = "orange" + if("Medium") powercostcolor = "green" + else powercostcolor = "blue" + if(target_temperature != temperature) + var/difference = abs(target_temperature - temperature) + if(difference >= (10 + 40*emagged)) //so emagged temp guns adjust their temperature much more quickly + if(target_temperature < temperature) + temperature -= (10 + 40*emagged) + else + temperature += (10 + 40*emagged) + else + temperature = target_temperature + update_icon() + + if (istype(loc, /mob/living/carbon)) + var /mob/living/carbon/M = loc + if (src == M.machine) + update_dat() + M << browse("Temperature Gun Configuration
[dat]", "window=tempgun;size=510x102") + return + +/obj/item/weapon/gun/energy/temperature/proc/update_dat() + dat = "" + dat += "Current output temperature: " + if(temperature > 500) + dat += "[temperature] ([round(temperature-T0C)]°C)" + dat += " SEARING!" + else if(temperature > (T0C + 50)) + dat += "[temperature] ([round(temperature-T0C)]°C)" + else if(temperature > (T0C - 50)) + dat += "[temperature] ([round(temperature-T0C)]°C)" + else + dat += "[temperature] ([round(temperature-T0C)]°C)" + dat += "
" + dat += "Target output temperature: " //might be string idiocy, but at least it's easy to read + dat += "- " + dat += "- " + dat += "- " + dat += "[target_temperature] " + dat += "+ " + dat += "+ " + dat += "+" + dat += "
" + dat += "Power cost: " + dat += "[powercost]" + +/obj/item/weapon/gun/energy/temperature/proc/update_temperature() + switch(temperature) + if(501 to INFINITY) + item_state = "tempgun_8" + if(400 to 500) + item_state = "tempgun_7" + if(360 to 400) + item_state = "tempgun_6" + if(335 to 360) + item_state = "tempgun_5" + if(295 to 335) + item_state = "tempgun_4" + if(260 to 295) + item_state = "tempgun_3" + if(200 to 260) + item_state = "tempgun_2" + if(120 to 260) + item_state = "tempgun_1" + if(-INFINITY to 120) + item_state = "tempgun_0" + icon_state = item_state + +/obj/item/weapon/gun/energy/temperature/update_icon() + overlays = 0 + update_temperature() + update_charge() + +/obj/item/weapon/gun/energy/temperature/proc/update_charge() + var/charge = power_supply.charge + switch(charge) + if(9000 to INFINITY) overlays += "900" + if(8000 to 9000) overlays += "800" + if(7000 to 8000) overlays += "700" + if(6000 to 7000) overlays += "600" + if(5000 to 6000) overlays += "500" + if(4000 to 5000) overlays += "400" + if(3000 to 4000) overlays += "300" + if(2000 to 3000) overlays += "200" + if(1000 to 2002) overlays += "100" + if(-INFINITY to 1000) overlays += "0" diff --git a/code/modules/projectiles/guns/energy/stun.dm b/code/modules/projectiles/guns/energy/stun.dm index 37d22ba5ff7..5e3f5128d6f 100644 --- a/code/modules/projectiles/guns/energy/stun.dm +++ b/code/modules/projectiles/guns/energy/stun.dm @@ -1,13 +1,11 @@ - /obj/item/weapon/gun/energy/taser name = "taser gun" desc = "A small, low capacity gun used for non-lethal takedowns." icon_state = "taser" item_state = null //so the human update icon uses the icon_state instead. - fire_sound = 'sound/weapons/Taser.ogg' - projectile_type = "/obj/item/projectile/energy/electrode" + ammo_type = list(/obj/item/ammo_casing/energy/electrode) + ammo_x_offset = 3 cell_type = "/obj/item/weapon/stock_parts/cell/crap" - fire_delay = 15 /obj/item/weapon/gun/energy/taser/mounted name = "mounted taser gun" @@ -18,67 +16,52 @@ name = "taser gun" desc = "A small, low capacity gun used for non-lethal takedowns." icon_state = "taser" - fire_sound = 'sound/weapons/Taser.ogg' - projectile_type = "/obj/item/projectile/energy/electrode" cell_type = "/obj/item/weapon/stock_parts/cell/secborg" - self_recharge = 1 - recharge_time = 10 //Time it takes for shots to recharge (in ticks) +/obj/item/weapon/gun/energy/disabler/cyborg/newshot() + ..() + robocharge() /obj/item/weapon/gun/energy/shock_revolver name = "tesla revolver" desc = "A high-tech revolver that fires internal, reusable shock cartridges in a revolving cylinder. The cartridges can be recharged using conventional rechargers." icon_state = "stunrevolver" item_state = "gun" - fire_sound = 'sound/weapons/gunshot.ogg' - projectile_type = "/obj/item/projectile/energy/shock_revolver" - charge_cost = 2000 + ammo_type = list(/obj/item/ammo_casing/energy/shock_revolver) can_flashlight = 0 + shaded_charge = 1 -/obj/item/projectile/energy/shock_revolver - name = "shock bolt" - icon_state = "purple_laser" - var/chain +/obj/item/weapon/gun/energy/gun/advtaser + name = "hybrid taser" + desc = "A dual-mode taser designed to fire both short-range high-power electrodes and long-range disabler beams." + icon_state = "advtaser" + ammo_type = list(/obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/disabler) + origin_tech = null + ammo_x_offset = 2 -/obj/item/projectile/energy/shock_revolver/OnFired() - spawn(1) - chain = Beam(firer, icon_state="purple_lightning", icon = 'icons/effects/effects.dmi',time=1000, maxdistance = 30) +/obj/item/weapon/gun/energy/gun/advtaser/cyborg + name = "cyborg taser" + desc = "An integrated hybrid taser that draws directly from a cyborg's power cell. The weapon contains a limiter to prevent the cyborg's power cell from overheating." + can_flashlight = 0 + can_charge = 0 -/obj/item/projectile/energy/shock_revolver/on_hit(atom/target) - . = ..() - if(isliving(target)) - tesla_zap(src, 3, 10000) - qdel(chain) +/obj/item/weapon/gun/energy/gun/advtaser/cyborg/newshot() + ..() + robocharge() -/obj/item/weapon/gun/energy/crossbow - name = "mini energy-crossbow" - desc = "A weapon favored by many of the syndicates stealth specialists." - icon_state = "crossbow" - w_class = 2.0 - item_state = "crossbow" - materials = list(MAT_METAL=2000) - origin_tech = "combat=2;magnets=2;syndicate=5" - silenced = 1 - fire_sound = 'sound/weapons/Genhit.ogg' - projectile_type = "/obj/item/projectile/energy/bolt" - cell_type = "/obj/item/weapon/stock_parts/cell/crap" - - self_recharge = 1 - -/obj/item/weapon/gun/energy/crossbow/ninja - name = "energy dart thrower" - projectile_type = /obj/item/projectile/energy/dart - -/obj/item/weapon/gun/energy/crossbow/update_icon() - return -/obj/item/weapon/gun/energy/crossbow/largecrossbow - name = "Energy Crossbow" - desc = "A weapon favored by syndicate carp hunters." - icon_state = "crossbowlarge" - silenced = 0 - w_class = 3.0 - force = 10 - materials = list(MAT_METAL=4000) - projectile_type = "/obj/item/projectile/energy/bolt/large" +/obj/item/weapon/gun/energy/disabler + name = "disabler" + desc = "A self-defense weapon that exhausts organic targets, weakening them until they collapse." + icon_state = "disabler" + item_state = null + ammo_type = list(/obj/item/ammo_casing/energy/disabler) + ammo_x_offset = 3 +/obj/item/weapon/gun/energy/disabler/cyborg + name = "cyborg disabler" + desc = "An integrated disabler that draws from a cyborg's power cell. This weapon contains a limiter to prevent the cyborg's power cell from overheating." + can_charge = 0 +/obj/item/weapon/gun/energy/disabler/cyborg/newshot() + ..() + robocharge() diff --git a/code/modules/projectiles/guns/energy/telegun.dm b/code/modules/projectiles/guns/energy/telegun.dm index 3819401e031..e145e2ccbcf 100644 --- a/code/modules/projectiles/guns/energy/telegun.dm +++ b/code/modules/projectiles/guns/energy/telegun.dm @@ -5,11 +5,9 @@ desc = "An extremely high-tech bluespace energy gun capable of teleporting targets to far off locations." icon_state = "telegun" item_state = "ionrifle" - fire_sound = 'sound/weapons/wave.ogg' origin_tech = "combat=6;materials=7;powerstorage=5;bluespace=5;syndicate=4" cell_type = "/obj/item/weapon/stock_parts/cell/crap" - projectile_type = "/obj/item/projectile/energy/teleport" - charge_cost = 1250 + ammo_type = list(/obj/item/ammo_casing/energy/teleport) var/teleport_target = null /obj/item/weapon/gun/energy/telegun/Destroy() @@ -36,4 +34,4 @@ L[tmpname] = R var/desc = input("Please select a location to lock in.", "Telegun Target Interface") in L - teleport_target = L[desc] \ No newline at end of file + teleport_target = L[desc] diff --git a/code/modules/projectiles/guns/energy/temperature.dm b/code/modules/projectiles/guns/energy/temperature.dm deleted file mode 100644 index 1e0eb5d08c2..00000000000 --- a/code/modules/projectiles/guns/energy/temperature.dm +++ /dev/null @@ -1,182 +0,0 @@ -/obj/item/weapon/gun/energy/temperature - name = "temperature gun" - icon = 'icons/obj/gun_temperature.dmi' - icon_state = "tempgun_4" - item_state = "tempgun_4" - slot_flags = SLOT_BACK - w_class = 4.0 - fire_sound = 'sound/weapons/pulse3.ogg' - desc = "A gun that changes the body temperature of its targets." - var/temperature = 300 - var/target_temperature = 300 - charge_cost = 90 - origin_tech = "combat=3;materials=4;powerstorage=3;magnets=2" - - projectile_type = /obj/item/projectile/temp - cell_type = /obj/item/weapon/stock_parts/cell/temperaturegun - - var/powercost = "" - var/powercostcolor = "" - - var/emagged = 0 //ups the temperature cap from 500 to 1000, targets hit by beams over 500 Kelvin will burst into flames - var/dat = "" - -/obj/item/weapon/gun/energy/temperature/New() - ..() - update_icon() - processing_objects.Add(src) - - -/obj/item/weapon/gun/energy/temperature/Destroy() - processing_objects.Remove(src) - return ..() - - -/obj/item/weapon/gun/energy/temperature/attack_self(mob/living/user as mob) - user.set_machine(src) - update_dat() - - user << browse("Temperature Gun Configuration
[dat]", "window=tempgun;size=510x120") - onclose(user, "tempgun") - -/obj/item/weapon/gun/energy/temperature/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(istype(W, /obj/item/weapon/card/emag) && !emagged) - emagged = 1 - to_chat(user, "You double the gun's temperature cap! Targets hit by searing beams will burst into flames!") - desc = "A gun that changes the body temperature of its targets. Its temperature cap has been hacked." - -/obj/item/weapon/gun/energy/temperature/Topic(href, href_list) - if (..()) - return - usr.set_machine(src) - add_fingerprint(usr) - - if(href_list["temp"]) - var/amount = text2num(href_list["temp"]) - if(amount > 0) - target_temperature = min((500 + 500*emagged), target_temperature+amount) - else - target_temperature = max(0, target_temperature+amount) - if (istype(loc, /mob)) - attack_self(loc) - add_fingerprint(usr) - return - - -/obj/item/weapon/gun/energy/temperature/process() - switch(temperature) - if(0 to 100) - charge_cost = 300 - powercost = "High" - if(100 to 250) - charge_cost = 180 - powercost = "Medium" - if(251 to 300) - charge_cost = 90 - powercost = "Low" - if(301 to 400) - charge_cost = 180 - powercost = "Medium" - if(401 to 1000) - charge_cost = 300 - powercost = "High" - switch(powercost) - if("High") powercostcolor = "orange" - if("Medium") powercostcolor = "green" - else powercostcolor = "blue" - if(target_temperature != temperature) - var/difference = abs(target_temperature - temperature) - if(difference >= (10 + 40*emagged)) //so emagged temp guns adjust their temperature much more quickly - if(target_temperature < temperature) - temperature -= (10 + 40*emagged) - else - temperature += (10 + 40*emagged) - else - temperature = target_temperature - update_icon() - - if (istype(loc, /mob/living/carbon)) - var /mob/living/carbon/M = loc - if (src == M.machine) - update_dat() - M << browse("Temperature Gun Configuration
[dat]", "window=tempgun;size=510x102") - - - if(power_supply) - power_supply.give(50) - update_icon() - return - -/obj/item/weapon/gun/energy/temperature/proc/update_dat() - dat = "" - dat += "Current output temperature: " - if(temperature > 500) - dat += "[temperature] ([round(temperature-T0C)]°C)" - dat += " SEARING!" - else if(temperature > (T0C + 50)) - dat += "[temperature] ([round(temperature-T0C)]°C)" - else if(temperature > (T0C - 50)) - dat += "[temperature] ([round(temperature-T0C)]°C)" - else - dat += "[temperature] ([round(temperature-T0C)]°C)" - dat += "
" - dat += "Target output temperature: " //might be string idiocy, but at least it's easy to read - dat += "- " - dat += "- " - dat += "- " - dat += "[target_temperature] " - dat += "+ " - dat += "+ " - dat += "+" - dat += "
" - dat += "Power cost: " - dat += "[powercost]" - -/obj/item/weapon/gun/energy/temperature/proc/update_temperature() - switch(temperature) - if(501 to INFINITY) - item_state = "tempgun_8" - if(400 to 500) - item_state = "tempgun_7" - if(360 to 400) - item_state = "tempgun_6" - if(335 to 360) - item_state = "tempgun_5" - if(295 to 335) - item_state = "tempgun_4" - if(260 to 295) - item_state = "tempgun_3" - if(200 to 260) - item_state = "tempgun_2" - if(120 to 260) - item_state = "tempgun_1" - if(-INFINITY to 120) - item_state = "tempgun_0" - icon_state = item_state - -/obj/item/weapon/gun/energy/temperature/proc/update_charge() - var/charge = power_supply.charge - switch(charge) - if(900 to INFINITY) overlays += "900" - if(800 to 900) overlays += "800" - if(700 to 800) overlays += "700" - if(600 to 700) overlays += "600" - if(500 to 600) overlays += "500" - if(400 to 500) overlays += "400" - if(300 to 400) overlays += "300" - if(200 to 300) overlays += "200" - if(100 to 200) overlays += "100" - if(-INFINITY to 100) overlays += "0" - -/obj/item/weapon/gun/energy/temperature/proc/update_user() - if (istype(loc,/mob/living/carbon)) - var/mob/living/carbon/M = loc - M.update_inv_back() - M.update_inv_l_hand() - M.update_inv_r_hand() - -/obj/item/weapon/gun/energy/temperature/update_icon() - overlays = 0 - update_temperature() - update_user() - update_charge() \ No newline at end of file diff --git a/code/modules/projectiles/guns/grenade_launcher.dm b/code/modules/projectiles/guns/grenade_launcher.dm new file mode 100644 index 00000000000..5d883ac7de7 --- /dev/null +++ b/code/modules/projectiles/guns/grenade_launcher.dm @@ -0,0 +1,54 @@ +/obj/item/weapon/gun/grenadelauncher + name = "grenade launcher" + desc = "a terrible, terrible thing. it's really awful!" + icon = 'icons/obj/gun.dmi' + icon_state = "riotgun" + item_state = "riotgun" + w_class = 4 + throw_speed = 2 + throw_range = 10 + force = 5 + var/list/grenades = new/list() + var/max_grenades = 3 + + materials = list(MAT_METAL=2000) + +/obj/item/weapon/gun/grenadelauncher/examine(mob/user) + if(..(user, 2)) + to_chat(user, "[grenades.len] / [max_grenades] [ammo_name]s.") + +/obj/item/weapon/gun/grenadelauncher/attackby(obj/item/I as obj, mob/user as mob, params) + if((istype(I, /obj/item/weapon/grenade))) + if(grenades.len < max_grenades) + if(!user.unEquip(I)) + return + I.loc = src + grenades += I + to_chat(user, "You put the [ammo_name] in the [name].") + to_chat(user, "[grenades.len] / [max_grenades] [ammo_name]s.") + else + to_chat(user, "The grenade launcher cannot hold more [ammo_name]s.") + +/obj/item/weapon/gun/grenadelauncher/afterattack(obj/target, mob/user , flag) + if(target == user) + return + + if(grenades.len) + fire_grenade(target,user) + else + to_chat(user, "The grenade launcher is empty.") + +/obj/item/weapon/gun/grenadelauncher/proc/fire_grenade(atom/target, mob/user) + user.visible_message("[user] fired a grenade!", \ + "You fire the grenade launcher!") + var/obj/item/weapon/grenade/chem_grenade/F = grenades[1] //Now with less copypasta! + grenades -= F + F.loc = user.loc + F.throw_at(target, 30, 2, user) + message_admins("[key_name_admin(user)] fired a grenade ([F.name]) from a grenade launcher ([src.name]).") + log_game("[key_name(user)] fired a grenade ([F.name]) from a grenade launcher ([src.name]).") + F.active = 1 + F.icon_state = initial(icon_state) + "_active" + playsound(user.loc, 'sound/weapons/armbomb.ogg', 75, 1, -3) + spawn(15) + F.prime() diff --git a/code/modules/projectiles/guns/magic.dm b/code/modules/projectiles/guns/magic.dm index 13f92bcef86..b64fb37e1c6 100644 --- a/code/modules/projectiles/guns/magic.dm +++ b/code/modules/projectiles/guns/magic.dm @@ -4,32 +4,25 @@ icon = 'icons/obj/gun.dmi' icon_state = "staffofnothing" item_state = "staff" - lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' //not really a gun and some toys use these inhands - righthand_file = 'icons/mob/inhands/items_righthand.dmi' fire_sound = 'sound/weapons/emitter.ogg' flags = CONDUCT w_class = 5 - var/projectile_type = /obj/item/projectile/magic var/max_charges = 6 var/charges = 0 var/recharge_rate = 4 var/charge_tick = 0 var/can_charge = 1 + var/ammo_type var/no_den_usage origin_tech = null clumsy_check = 0 + trigger_guard = TRIGGER_GUARD_ALLOW_ALL // Has no trigger at all, uses magic instead -/obj/item/weapon/gun/magic/emp_act(severity) - return + lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' //not really a gun and some toys use these inhands + righthand_file = 'icons/mob/inhands/items_righthand.dmi' -/obj/item/weapon/gun/magic/process_chambered() - if(in_chamber) return 1 - if(!charges) return 0 - if(!projectile_type) return 0 - in_chamber = new projectile_type(src) - return 1 - -/obj/item/weapon/gun/magic/afterattack(atom/target as mob, mob/living/user as mob, flag) +/obj/item/weapon/gun/magic/afterattack(atom/target, mob/living/user, flag) + newshot() if(no_den_usage) var/area/A = get_area(user) if(istype(A, /area/wizard_station)) @@ -38,22 +31,38 @@ else no_den_usage = 0 ..() - if(charges && !in_chamber && !flag) charges-- + +/obj/item/weapon/gun/magic/can_shoot() + return charges + +/obj/item/weapon/gun/magic/newshot() + if (charges && chambered) + chambered.newshot() + return + +/obj/item/weapon/gun/magic/process_chamber() + if(chambered && !chambered.BB) //if BB is null, i.e the shot has been fired... + charges--//... drain a charge + return /obj/item/weapon/gun/magic/New() ..() charges = max_charges - if(can_charge) processing_objects.Add(src) + chambered = new ammo_type(src) + if(can_charge) + processing_objects.Add(src) /obj/item/weapon/gun/magic/Destroy() - if(can_charge) processing_objects.Remove(src) + if(can_charge) + processing_objects.Remove(src) return ..() /obj/item/weapon/gun/magic/process() charge_tick++ - if(charge_tick < recharge_rate || charges >= max_charges) return 0 + if(charge_tick < recharge_rate || charges >= max_charges) + return 0 charge_tick = 0 charges++ return 1 @@ -63,4 +72,9 @@ /obj/item/weapon/gun/magic/shoot_with_empty_chamber(mob/living/user as mob|obj) to_chat(user, "The [name] whizzles quietly.") - return \ No newline at end of file + return + +/obj/item/weapon/gun/magic/suicide_act(mob/user) + user.visible_message("[user] is twisting the [src.name] above \his head, releasing a magical blast! It looks like \he's trying to commit suicide.") + playsound(loc, fire_sound, 50, 1, -1) + return FIRELOSS diff --git a/code/modules/projectiles/guns/magic/staff.dm b/code/modules/projectiles/guns/magic/staff.dm index 16fc552397d..0a52f5ba416 100644 --- a/code/modules/projectiles/guns/magic/staff.dm +++ b/code/modules/projectiles/guns/magic/staff.dm @@ -4,49 +4,53 @@ obj/item/weapon/gun/magic/staff/ obj/item/weapon/gun/magic/staff/change name = "staff of change" desc = "An artefact that spits bolts of coruscating energy which cause the target's very form to reshape itself" - projectile_type = "/obj/item/projectile/magic/change" + ammo_type = /obj/item/ammo_casing/magic/change icon_state = "staffofchange" item_state = "staffofchange" obj/item/weapon/gun/magic/staff/animate name = "staff of animation" desc = "An artefact that spits bolts of life-force which causes objects which are hit by it to animate and come to life! This magic doesn't affect machines." - projectile_type = "/obj/item/projectile/magic/animate" + ammo_type = /obj/item/ammo_casing/magic/animate icon_state = "staffofanimation" item_state = "staffofanimation" obj/item/weapon/gun/magic/staff/healing name = "staff of healing" desc = "An artefact that spits bolts of restoring magic which can remove ailments of all kinds and even raise the dead." - projectile_type = "/obj/item/projectile/magic/resurrection" + ammo_type = /obj/item/ammo_casing/magic/heal icon_state = "staffofhealing" item_state = "staffofhealing" +/obj/item/weapon/gun/magic/staff/healing/handle_suicide() //Stops people trying to commit suicide to heal themselves + return + obj/item/weapon/gun/magic/staff/chaos name = "staff of chaos" desc = "An artefact that spits bolts of chaotic magic that can potentially do anything." - projectile_type = "/obj/item/projectile/magic" + ammo_type = /obj/item/ammo_casing/magic/chaos icon_state = "staffofchaos" item_state = "staffofchaos" max_charges = 10 recharge_rate = 2 - no_den_usage = 1 -/obj/item/weapon/gun/magic/staff/chaos/process_chambered() //Snowflake proc, because this uses projectile_type instead of ammo_casing for whatever reason. - projectile_type = pick(typesof(/obj/item/projectile/magic)) - if(in_chamber) return 1 - if(!charges) return 0 - in_chamber = new projectile_type(src) - return 1 - obj/item/weapon/gun/magic/staff/door name = "staff of door creation" desc = "An artefact that spits bolts of transformative magic that can create doors in walls." - projectile_type = "/obj/item/projectile/magic/door" + ammo_type = /obj/item/ammo_casing/magic/door icon_state = "staffofdoor" item_state = "staffofdoor" max_charges = 10 recharge_rate = 2 + no_den_usage = 1 - no_den_usage = 1 \ No newline at end of file +/obj/item/weapon/gun/magic/staff/honk + name = "staff of the honkmother" + desc = "Honk" + fire_sound = "sound/items/airhorn.ogg" + ammo_type = /obj/item/ammo_casing/magic/honk + icon_state = "honker" + item_state = "honker" + max_charges = 4 + recharge_rate = 8 diff --git a/code/modules/projectiles/guns/magic/wand.dm b/code/modules/projectiles/guns/magic/wand.dm index 6be4e0936bf..c6d5bc4bf00 100644 --- a/code/modules/projectiles/guns/magic/wand.dm +++ b/code/modules/projectiles/guns/magic/wand.dm @@ -1,21 +1,13 @@ /obj/item/weapon/gun/magic/wand/ name = "wand of nothing" desc = "It's not just a stick, it's a MAGIC stick!" - projectile_type = "/obj/item/projectile/magic" + ammo_type = /obj/item/ammo_casing/magic icon_state = "nothingwand" item_state = "wand" w_class = 2 can_charge = 0 max_charges = 100 //100, 50, 50, 34 (max charge distribution by 25%ths) var/variable_charges = 1 - var/drained = 0 - -/obj/item/projectile/magic/fireball/Range() - var/mob/living/L = locate(/mob/living) in (range(src, 1) - firer) - if(L && L.stat != DEAD) - Bump(L) //Magic Bullet #teachthecontroversy - return - ..() /obj/item/weapon/gun/magic/wand/New() if(prob(75) && variable_charges) //25% chance of listed max charges, 50% chance of 1/2 max charges, 25% chance of 1/3 max charges @@ -26,26 +18,21 @@ ..() /obj/item/weapon/gun/magic/wand/examine(mob/user) - ..(user) + ..() to_chat(user, "Has [charges] charge\s remaining.") -/obj/item/weapon/gun/magic/wand/attack_self(mob/living/user as mob) - if(charges) - zap_self(user) - else - to_chat(user, "The [name] whizzles quietly.") - ..() +/obj/item/weapon/gun/magic/wand/update_icon() + icon_state = "[initial(icon_state)][charges ? "" : "-drained"]" -/obj/item/weapon/gun/magic/wand/attack(atom/target as mob, mob/living/user as mob) + +/obj/item/weapon/gun/magic/wand/attack(atom/target, mob/living/user) if(target == user) return ..() -/obj/item/weapon/gun/magic/wand/afterattack(atom/target as mob, mob/living/user as mob) - if(!charges && !drained) - to_chat(user, "The [name] whizzles quietly.") - icon_state = "[icon_state]-drained" - drained = 1 +/obj/item/weapon/gun/magic/wand/afterattack(atom/target, mob/living/user) + if(!charges) + shoot_with_empty_chamber(user) return if(target == user) if(no_den_usage) @@ -53,74 +40,83 @@ if(istype(A, /area/wizard_station)) to_chat(user, "You know better than to violate the security of The Den, best wait until you leave to use [src].") return - else - no_den_usage = 0 + else + no_den_usage = 0 zap_self(user) else ..() + update_icon() -/obj/item/weapon/gun/magic/wand/proc/zap_self(mob/living/user as mob) - user.visible_message("[user] zaps \himself with [src]!") +/obj/item/weapon/gun/magic/wand/proc/zap_self(mob/living/user) + user.visible_message("[user] zaps \himself with [src].") + playsound(user, fire_sound, 50, 1) + user.attack_log += "\[[time_stamp()]\] [user]/[user.ckey] zapped \himself with a [src]" + +///////////////////////////////////// +//WAND OF DEATH +///////////////////////////////////// /obj/item/weapon/gun/magic/wand/death name = "wand of death" desc = "This deadly wand overwhelms the victim's body with pure energy, slaying them without fail." - projectile_type = "/obj/item/projectile/magic/death" + ammo_type = /obj/item/ammo_casing/magic/death icon_state = "deathwand" max_charges = 3 //3, 2, 2, 1 -/obj/item/weapon/gun/magic/wand/death/zap_self(mob/living/user as mob) - if(alert(user, "You really want to zap yourself with the wand of death?",, "Yes", "No") == "Yes" && charges && user.get_active_hand() == src && isliving(user)) - var/message ="You irradiate yourself with pure energy! " - message += pick("Do not pass go. Do not collect 200 zorkmids.","You feel more confident in your spell casting skills.","You Die...","Do you want your possessions identified?") - to_chat(user, message) - user.adjustOxyLoss(500) - charges-- - ..() +/obj/item/weapon/gun/magic/wand/death/zap_self(mob/living/user) + var/message ="You irradiate yourself with pure energy! " + message += pick("Do not pass go. Do not collect 200 zorkmids.","You feel more confident in your spell casting skills.","You Die...","Do you want your possessions identified?") + to_chat(user, message) + user.adjustBurnLoss(500) + charges-- + ..() + +///////////////////////////////////// +//WAND OF HEALING +///////////////////////////////////// /obj/item/weapon/gun/magic/wand/resurrection name = "wand of resurrection" desc = "This wand uses healing magics to heal and revive. They are rarely utilized within the Wizard Federation for some reason." - projectile_type = "/obj/item/projectile/magic/resurrection" + ammo_type = /obj/item/ammo_casing/magic/heal icon_state = "revivewand" max_charges = 3 //3, 2, 2, 1 -/obj/item/weapon/gun/magic/wand/resurrection/zap_self(mob/living/user as mob) - user.setToxLoss(0) - user.setOxyLoss(0) - user.setCloneLoss(0) - user.SetParalysis(0) - user.SetStunned(0) - user.SetWeakened(0) - user.radiation = 0 - user.heal_overall_damage(user.getBruteLoss(), user.getFireLoss()) - user.reagents.clear_reagents() +/obj/item/weapon/gun/magic/wand/resurrection/zap_self(mob/living/user) + user.revive() to_chat(user, "You feel great!") charges-- ..() +///////////////////////////////////// +//WAND OF POLYMORPH +///////////////////////////////////// + /obj/item/weapon/gun/magic/wand/polymorph name = "wand of polymorph" desc = "This wand is attuned to chaos and will radically alter the victim's form." - projectile_type = "/obj/item/projectile/magic/change" + ammo_type = /obj/item/ammo_casing/magic/change icon_state = "polywand" max_charges = 10 //10, 5, 5, 4 -/obj/item/weapon/gun/magic/wand/polymorph/zap_self(mob/living/user as mob) - if(alert(user, "Your new form might not have arms to zap with... Continue?",, "Yes", "No") == "Yes" && charges && user.get_active_hand() == src && isliving(user)) - ..() //because the user mob ceases to exists by the time wabbajack fully resolves - wabbajack(user) - charges-- +/obj/item/weapon/gun/magic/wand/polymorph/zap_self(mob/living/user) + ..() //because the user mob ceases to exists by the time wabbajack fully resolves + wabbajack(user) + charges-- + +///////////////////////////////////// +//WAND OF TELEPORTATION +///////////////////////////////////// /obj/item/weapon/gun/magic/wand/teleport name = "wand of teleportation" desc = "This wand will wrench targets through space and time to move them somewhere else." - projectile_type = "/obj/item/projectile/magic/teleport" + ammo_type = /obj/item/ammo_casing/magic/teleport icon_state = "telewand" max_charges = 10 //10, 5, 5, 4 no_den_usage = 1 -/obj/item/weapon/gun/magic/wand/teleport/zap_self(mob/living/user as mob) +/obj/item/weapon/gun/magic/wand/teleport/zap_self(mob/living/user) do_teleport(user, user, 10) var/datum/effect/system/harmless_smoke_spread/smoke = new /datum/effect/system/harmless_smoke_spread() smoke.set_up(10, 0, user.loc) @@ -128,25 +124,34 @@ charges-- ..() +///////////////////////////////////// +//WAND OF DOOR CREATION +///////////////////////////////////// + /obj/item/weapon/gun/magic/wand/door name = "wand of door creation" desc = "This particular wand can create doors in any wall for the unscrupulous wizard who shuns teleportation magics." - projectile_type = "/obj/item/projectile/magic/door" + ammo_type = /obj/item/ammo_casing/magic/door icon_state = "doorwand" max_charges = 20 //20, 10, 10, 7 -/obj/item/weapon/gun/magic/wand/door/zap_self() - return +/obj/item/weapon/gun/magic/wand/door/zap_self(mob/living/user) + to_chat(user, "You feel vaguely more open with your feelings.") + charges-- + ..() + +///////////////////////////////////// +//WAND OF FIREBALL +///////////////////////////////////// /obj/item/weapon/gun/magic/wand/fireball name = "wand of fireball" desc = "This wand shoots scorching balls of fire that explode into destructive flames." - projectile_type = "/obj/item/projectile/magic/fireball" + ammo_type = /obj/item/ammo_casing/magic/fireball icon_state = "firewand" max_charges = 8 //8, 4, 4, 3 -/obj/item/weapon/gun/magic/wand/fireball/zap_self(mob/living/user as mob) - if(alert(user, "Zapping yourself with a wand of fireball is probably a bad idea, do it anyway?",, "Yes", "No") == "Yes" && charges && user.get_active_hand() == src && isliving(user)) - explosion(user.loc, -1, 0, 2, 3, 0, flame_range = 2) - charges-- - ..() \ No newline at end of file +/obj/item/weapon/gun/magic/wand/fireball/zap_self(mob/living/user) + explosion(user.loc, -1, 0, 2, 3, 0, flame_range = 2) + charges-- + ..() diff --git a/code/modules/projectiles/medbeam.dm b/code/modules/projectiles/guns/medbeam.dm similarity index 93% rename from code/modules/projectiles/medbeam.dm rename to code/modules/projectiles/guns/medbeam.dm index 04f8f4b6862..bd5f6b23e79 100644 --- a/code/modules/projectiles/medbeam.dm +++ b/code/modules/projectiles/guns/medbeam.dm @@ -4,7 +4,7 @@ icon = 'icons/obj/chronos.dmi' icon_state = "chronogun" item_state = "chronogun" - w_class = 3.0 + w_class = 3 var/mob/living/current_target var/last_check = 0 @@ -13,7 +13,7 @@ var/active = 0 var/datum/beam/current_beam = null - heavy_weapon = 1 + weapon_weight = WEAPON_MEDIUM /obj/item/weapon/gun/medbeam/New() ..() @@ -34,7 +34,7 @@ on_beam_release(current_target) current_target = null -/obj/item/weapon/gun/medbeam/Fire(atom/target as mob|obj|turf, mob/living/user as mob|obj, params) +/obj/item/weapon/gun/medbeam/process_fire(atom/target as mob|obj|turf, mob/living/user as mob|obj, message = 1, params, zone_override) add_fingerprint(user) if(current_target) @@ -113,4 +113,4 @@ return /obj/effect/ebeam/medical - name = "medical beam" \ No newline at end of file + name = "medical beam" diff --git a/code/modules/projectiles/guns/mounted.dm b/code/modules/projectiles/guns/mounted.dm index d0e023d0d6d..b200dd1d6a7 100644 --- a/code/modules/projectiles/guns/mounted.dm +++ b/code/modules/projectiles/guns/mounted.dm @@ -1,31 +1,31 @@ /obj/item/weapon/gun/energy/advtaser/mounted name = "mounted taser" desc = "An arm mounted dual-mode weapon that fires electrodes and disabler shots." - icon_state = "armcannonstun" - item_state = "armcannonstun" - modifystate = "armcannonstun" + icon_state = "taser" + item_state = "armcannonstun4" force = 5 - self_recharge = 1 flags = NODROP - slot_flags = null - w_class = 5.0 + w_class = 5 can_flashlight = 0 + selfcharge = 1 + trigger_guard = TRIGGER_GUARD_ALLOW_ALL // Has no trigger at all, uses neural signals instead /obj/item/weapon/gun/energy/advtaser/mounted/dropped()//if somebody manages to drop this somehow... + ..() src.loc = null//send it to nullspace to get retrieved by the implant later on. gotta cover those edge cases. /obj/item/weapon/gun/energy/laser/mounted name = "mounted laser" desc = "An arm mounted cannon that fires lethal lasers. Doesn't come with a charge beam." - icon_state = "armcannonlase" + icon_state = "laser" item_state = "armcannonlase" - modifystate = "armcannonlase" force = 5 - self_recharge = 1 flags = NODROP - slot_flags = null - w_class = 5.0 + w_class = 5 materials = null + selfcharge = 1 + trigger_guard = TRIGGER_GUARD_ALLOW_ALL /obj/item/weapon/gun/energy/laser/mounted/dropped() - src.loc = null \ No newline at end of file + ..() + src.loc = null diff --git a/code/modules/projectiles/guns/projectile.dm b/code/modules/projectiles/guns/projectile.dm index ffe2cd269b5..fddc09349ab 100644 --- a/code/modules/projectiles/guns/projectile.dm +++ b/code/modules/projectiles/guns/projectile.dm @@ -1,22 +1,13 @@ -#define SPEEDLOADER 0 -#define FROM_BOX 1 -#define MAGAZINE 2 - /obj/item/weapon/gun/projectile desc = "Now comes in flavors like GUN. Uses 10mm ammo, for some reason" name = "projectile gun" icon_state = "pistol" origin_tech = "combat=2;materials=2" - w_class = 3.0 + w_class = 3 materials = list(MAT_METAL=1000) -// recoil = 1 - var/mag_type = "/obj/item/ammo_box/magazine/m10mm" //Removes the need for max_ammo and caliber info + + var/mag_type = /obj/item/ammo_box/magazine/m10mm //Removes the need for max_ammo and caliber info var/obj/item/ammo_box/magazine/magazine -// var/obj/item/ammo_casing/chambered = null // The round (not bullet) that is in the chamber. - - - - /obj/item/weapon/gun/projectile/New() ..() @@ -26,31 +17,26 @@ update_icon() return +/obj/item/weapon/gun/projectile/update_icon() + ..() + if(reskinned && current_skin) + icon_state = "[current_skin][suppressed ? "-suppressed" : ""]" + else + icon_state = "[initial(icon_state)][suppressed ? "-suppressed" : ""]" -/obj/item/weapon/gun/projectile/process_chambered(var/eject_casing = 1, var/empty_chamber = 1 ) -// if(in_chamber) -// return 1 - +/obj/item/weapon/gun/projectile/process_chamber(eject_casing = 1, empty_chamber = 1) var/obj/item/ammo_casing/AC = chambered //Find chambered round if(isnull(AC) || !istype(AC)) - return 0 + chamber_round() + return if(eject_casing) AC.loc = get_turf(src) //Eject casing onto ground. AC.SpinAnimation(10, 1) //next gen special effects + if(empty_chamber) chambered = null chamber_round() - if(AC.BB) - if(AC.reagents && AC.BB.reagents) - var/datum/reagents/casting_reagents = AC.reagents - casting_reagents.trans_to(AC.BB, casting_reagents.total_volume) //For chemical darts/bullets - qdel(casting_reagents) - in_chamber = AC.BB //Load projectile into chamber. - AC.BB.loc = src //Set projectile loc to gun. - AC.update_icon() - return 1 - return 0 - + return /obj/item/weapon/gun/projectile/proc/chamber_round() if(chambered || !magazine) @@ -60,12 +46,16 @@ chambered.loc = src return +/obj/item/weapon/gun/projectile/can_shoot() + if(!magazine || !magazine.ammo_count(0)) + return 0 + return 1 /obj/item/weapon/gun/projectile/attackby(var/obj/item/A as obj, mob/user as mob, params) ..() if(istype(A, /obj/item/ammo_box/magazine)) var/obj/item/ammo_box/magazine/AM = A - if(!magazine && istype(AM, text2path(mag_type))) + if(!magazine && istype(AM, mag_type)) user.remove_from_mob(AM) magazine = AM magazine.loc = src @@ -79,13 +69,11 @@ if(istype(A, /obj/item/weapon/suppressor)) var/obj/item/weapon/suppressor/S = A if(can_suppress) - if(!silenced) - if(user.l_hand != src && user.r_hand != src) - to_chat(user, "You'll need [src] in your hands to do that.") + if(!suppressed) + if(!user.unEquip(A)) return - user.drop_item() to_chat(user, "You screw [S] onto [src].") - silenced = A + suppressed = A S.oldsound = fire_sound S.initial_w_class = w_class fire_sound = 'sound/weapons/Gunshot_silenced.ogg' @@ -101,39 +89,45 @@ return return 0 -/obj/item/weapon/gun/projectile/attack_hand(mob/user as mob) +/obj/item/weapon/gun/projectile/attack_hand(mob/user) if(loc == user) - if(silenced && can_unsuppress) - var/obj/item/weapon/suppressor/S = silenced + if(suppressed && can_unsuppress) + var/obj/item/weapon/suppressor/S = suppressed if(user.l_hand != src && user.r_hand != src) ..() return to_chat(user, "You unscrew [silenced] from [src].") - user.put_in_hands(silenced) + user.put_in_hands(suppressed) fire_sound = S.oldsound w_class = S.initial_w_class - silenced = 0 + suppressed = 0 update_icon() return ..() /obj/item/weapon/gun/projectile/attack_self(mob/living/user as mob) + var/obj/item/ammo_casing/AC = chambered //Find chambered round if(magazine) magazine.loc = get_turf(src.loc) user.put_in_hands(magazine) magazine.update_icon() magazine = null to_chat(user, "You pull the magazine out of \the [src]!") + else if(chambered) + AC.loc = get_turf(src) + AC.SpinAnimation(10, 1) + chambered = null + to_chat(user, "You unload the round from \the [src]'s chamber.") else to_chat(user, "There's no magazine in \the [src].") update_icon() return /obj/item/weapon/gun/projectile/examine(mob/user) - ..(user) + ..() to_chat(user, "Has [get_ammo()] round\s remaining.") -/obj/item/weapon/gun/projectile/proc/get_ammo(var/countchambered = 1) +/obj/item/weapon/gun/projectile/proc/get_ammo(countchambered = 1) var/boolets = 0 //mature var names for mature people if(chambered && countchambered) boolets++ @@ -141,6 +135,22 @@ boolets += magazine.ammo_count() return boolets +/obj/item/weapon/gun/projectile/suicide_act(mob/user) + if (src.chambered && src.chambered.BB && !src.chambered.BB.nodamage) + user.visible_message("[user] is putting the barrel of the [src.name] in \his mouth. It looks like \he's trying to commit suicide.") + sleep(25) + if(user.l_hand == src || user.r_hand == src) + process_fire(user, user, 0, zone_override = "head") + user.visible_message("[user] blows \his brains out with the [src.name]!") + return(BRUTELOSS) + else + user.visible_message("[user] panics and starts choking to death!") + return(OXYLOSS) + else + user.visible_message("[user] is pretending to blow \his brains out with the [src.name]! It looks like \he's trying to commit suicide!") + playsound(loc, 'sound/weapons/empty.ogg', 50, 1, -1) + return (OXYLOSS) + /obj/item/weapon/suppressor name = "suppressor" desc = "A universal syndicate small-arms suppressor for maximum espionage." @@ -150,3 +160,9 @@ w_class = 2 var/oldsound = null var/initial_w_class = null + +/obj/item/weapon/suppressor/specialoffer + name = "cheap suppressor" + desc = "A foreign knock-off suppressor, it feels flimsy, cheap, and brittle. Still fits all weapons." + icon = 'icons/obj/gun.dmi' + icon_state = "suppressor" diff --git a/code/modules/projectiles/guns/projectile/automatic.dm b/code/modules/projectiles/guns/projectile/automatic.dm index b56344ce039..8e700c92cc0 100644 --- a/code/modules/projectiles/guns/projectile/automatic.dm +++ b/code/modules/projectiles/guns/projectile/automatic.dm @@ -15,7 +15,7 @@ name = "\improper Nanotrasen Saber SMG" desc = "A prototype three-round burst 9mm submachine gun, designated 'SABR'. Has a threaded barrel for suppressors." icon_state = "saber" - mag_type = "/obj/item/ammo_box/magazine/smgm9mm" + mag_type = /obj/item/ammo_box/magazine/smgm9mm origin_tech = "combat=4;materials=2" /obj/item/weapon/gun/projectile/automatic/update_icon() @@ -25,7 +25,7 @@ overlays += "[initial(icon_state)]semi" if(select == 1) overlays += "[initial(icon_state)]burst" - icon_state = "[initial(icon_state)][magazine ? "-[magazine.max_ammo]" : ""][chambered ? "" : "-e"][silenced ? "-suppressed" : ""]" + icon_state = "[initial(icon_state)][magazine ? "-[magazine.max_ammo]" : ""][chambered ? "" : "-e"][suppressed ? "-suppressed" : ""]" return /obj/item/weapon/gun/projectile/automatic/attackby(var/obj/item/A as obj, mob/user as mob, params) @@ -34,7 +34,7 @@ return if(istype(A, /obj/item/ammo_box/magazine)) var/obj/item/ammo_box/magazine/AM = A - if(istype(AM, text2path(mag_type))) + if(istype(AM, mag_type)) if(magazine) to_chat(user, "You perform a tactical reload on \the [src], replacing the magazine.") magazine.forceMove(get_turf(src.loc)) @@ -69,6 +69,9 @@ update_icon() return +/obj/item/weapon/gun/projectile/automatic/can_shoot() + return get_ammo() + /obj/item/weapon/gun/projectile/automatic/proc/empty_alarm() if(!chambered && !get_ammo() && !alarmed) playsound(src.loc, 'sound/weapons/smg_empty_alarm.ogg', 40, 1) @@ -82,7 +85,7 @@ icon_state = "c20r" item_state = "c20r" origin_tech = "combat=5;materials=2;syndicate=8" - mag_type = "/obj/item/ammo_box/magazine/smgm45" + mag_type = /obj/item/ammo_box/magazine/smgm45 fire_sound = 'sound/weapons/Gunshot_smg.ogg' fire_delay = 2 burst_size = 2 @@ -99,7 +102,7 @@ /obj/item/weapon/gun/projectile/automatic/c20r/update_icon() ..() - icon_state = "c20r[magazine ? "-[Ceiling(get_ammo(0)/4)*4]" : ""][chambered ? "" : "-e"][silenced ? "-suppressed" : ""]" + icon_state = "c20r[magazine ? "-[Ceiling(get_ammo(0)/4)*4]" : ""][chambered ? "" : "-e"][suppressed ? "-suppressed" : ""]" return /obj/item/weapon/gun/projectile/automatic/wt550 @@ -107,7 +110,7 @@ desc = "An outdated personal defense weapon utilized by law enforcement. The WT-550 Automatic Rifle fires 4.6x30mm rounds." icon_state = "wt550" item_state = "arg" - mag_type = "/obj/item/ammo_box/magazine/wt550m9" + mag_type = /obj/item/ammo_box/magazine/wt550m9 fire_delay = 2 can_suppress = 0 burst_size = 1 @@ -123,80 +126,16 @@ desc = "A lightweight, burst-fire submachine gun, for when you really want someone dead. Uses 9mm rounds." icon_state = "mini-uzi" origin_tech = "combat=5;materials=2;syndicate=8" - mag_type = "/obj/item/ammo_box/magazine/uzim9mm" + mag_type = /obj/item/ammo_box/magazine/uzim9mm burst_size = 2 -/obj/item/weapon/gun/projectile/automatic/l6_saw - name = "\improper L6 SAW" - desc = "A heavily modified 7.62 light machine gun, designated 'L6 SAW'. Has 'Aussec Armoury - 2531' engraved on the reciever below the designation." - icon_state = "l6closed100" - item_state = "l6closedmag" - w_class = 5 - slot_flags = 0 - origin_tech = "combat=5;materials=1;syndicate=2" - mag_type = "/obj/item/ammo_box/magazine/m762" - fire_sound = 'sound/weapons/Gunshot3.ogg' - - var/cover_open = 0 - can_suppress = 0 - burst_size = 5 - fire_delay = 3 - -/obj/item/weapon/gun/projectile/automatic/l6_saw/attack_self(mob/user as mob) - cover_open = !cover_open - to_chat(user, "You [cover_open ? "open" : "close"] [src]'s cover.") - update_icon() - -/obj/item/weapon/gun/projectile/automatic/l6_saw/update_icon() - if(magazine && magazine.caliber != "a762") - icon_state = "l6[cover_open ? "open" : "closed"]0" - icon_state = "l6[cover_open ? "open" : "closed"][magazine ? round(magazine.ammo_count() * 2, 25) : "-empty"]" - -/obj/item/weapon/gun/projectile/automatic/l6_saw/afterattack(atom/target as mob|obj|turf, mob/living/user as mob|obj, flag, params) //what I tried to do here is just add a check to see if the cover is open or not and add an icon_state change because I can't figure out how c-20rs do it with overlays - if(cover_open) - to_chat(user, "[src]'s cover is open! Close it before firing!") - else - ..() - update_icon() - -/obj/item/weapon/gun/projectile/automatic/l6_saw/attack_hand(mob/user as mob) - if(loc != user) - ..() - return //let them pick it up - if(!cover_open || (cover_open && !magazine)) - ..() - else if(cover_open && magazine) - //drop the mag - magazine.update_icon() - magazine.loc = get_turf(src.loc) - user.put_in_hands(magazine) - magazine = null - update_icon() - to_chat(user, "You remove the magazine from [src].") - - -/obj/item/weapon/gun/projectile/automatic/l6_saw/attackby(var/obj/item/A as obj, mob/user as mob, params) - if(!cover_open && istype(A, mag_type)) - to_chat(user, "[src]'s cover is closed! You can't insert a new mag!") - return - ..() - -/obj/item/weapon/gun/projectile/automatic/l6_saw/devastator - name = "\improper 'Devastator' LMG" - desc = "A heavily modified L6 SAW designed to chamber wide rounds. Commonly used by elite Syndicate boarding teams " - mag_type = "/obj/item/ammo_box/magazine/m762/buckshot" - -/obj/item/weapon/gun/projectile/automatic/l6_saw/devastator/New() - ..() - mag_type = "/obj/item/ammo_box/magazine/m762" // Workaround so it spawns with a shotgun mag loaded, but can still load regular LMG mags. - /obj/item/weapon/gun/projectile/automatic/m90 name = "\improper M-90gl Carbine" desc = "A three-round burst 5.56 toploading carbine, designated 'M-90gl'. Has an attached underbarrel grenade launcher which can be toggled on and off." icon_state = "m90" item_state = "m90" origin_tech = "combat=5;materials=2;syndicate=8" - mag_type = "/obj/item/ammo_box/magazine/m556" + mag_type = /obj/item/ammo_box/magazine/m556 fire_sound = 'sound/weapons/Gunshot_smg.ogg' action_button_name = "Toggle Grenade Launcher" can_suppress = 0 @@ -215,12 +154,11 @@ underbarrel.afterattack(target, user, flag, params) else ..() - empty_alarm() return /obj/item/weapon/gun/projectile/automatic/m90/attackby(var/obj/item/A, mob/user, params) if(istype(A, /obj/item/ammo_casing)) - if(istype(A, text2path(underbarrel.magazine.ammo_type))) + if(istype(A, underbarrel.magazine.ammo_type)) underbarrel.attack_self() underbarrel.attackby(A, user, params) else @@ -267,11 +205,11 @@ w_class = 5 slot_flags = 0 origin_tech = "combat=5;materials=1;syndicate=2" - mag_type = "/obj/item/ammo_box/magazine/tommygunm45" + mag_type = /obj/item/ammo_box/magazine/tommygunm45 fire_sound = 'sound/weapons/Gunshot_smg.ogg' can_suppress = 0 - fire_delay = 1 burst_size = 4 + fire_delay = 1 /obj/item/weapon/gun/projectile/automatic/ar name = "ARG" @@ -285,3 +223,40 @@ can_suppress = 0 burst_size = 3 fire_delay = 1 + +// Bulldog shotgun // +/obj/item/weapon/gun/projectile/automatic/shotgun/bulldog + name = "\improper 'Bulldog' Shotgun" + desc = "A compact, mag-fed semi-automatic shotgun for combat in narrow corridors, nicknamed 'Bulldog' by boarding parties. Compatible only with specialized 8-round drum magazines." + icon_state = "bulldog" + item_state = "bulldog" + w_class = 3.0 + origin_tech = "combat=5;materials=4;syndicate=6" + mag_type = /obj/item/ammo_box/magazine/m12g + fire_sound = 'sound/weapons/Gunshot4.ogg' + can_suppress = 0 + burst_size = 1 + fire_delay = 0 + action_button_name = null + +/obj/item/weapon/gun/projectile/automatic/shotgun/bulldog/New() + ..() + update_icon() + return + +/obj/item/weapon/gun/projectile/automatic/shotgun/bulldog/proc/update_magazine() + if(magazine) + overlays.Cut() + overlays += "[magazine.icon_state]" + return + +/obj/item/weapon/gun/projectile/automatic/shotgun/bulldog/update_icon() + overlays.Cut() + update_magazine() + icon_state = "bulldog[chambered ? "" : "-e"]" + return + +/obj/item/weapon/gun/projectile/automatic/shotgun/bulldog/afterattack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, flag) + ..() + empty_alarm() + return diff --git a/code/modules/projectiles/guns/projectile/crossbow.dm b/code/modules/projectiles/guns/projectile/crossbow.dm deleted file mode 100644 index 3645899ab21..00000000000 --- a/code/modules/projectiles/guns/projectile/crossbow.dm +++ /dev/null @@ -1,323 +0,0 @@ -//AMMUNITION - -/obj/item/weapon/arrow - - name = "bolt" - desc = "It's got a tip for you - get the point?" - icon = 'icons/obj/weapons.dmi' - icon_state = "bolt" - item_state = "bolt" - throwforce = 8 - w_class = 3.0 - sharp = 1 - edge = 0 - -/obj/item/weapon/arrow/proc/removed() //Helper for metal rods falling apart. - return - -/obj/item/weapon/spike - name = "alloy spike" - desc = "It's about a foot of weird silver metal with a wicked point." - sharp = 1 - edge = 0 - throwforce = 5 - w_class = 2 - icon = 'icons/obj/weapons.dmi' - icon_state = "metal-rod" - item_state = "bolt" - -/obj/item/weapon/arrow/quill - - name = "vox quill" - desc = "A wickedly barbed quill from some bizarre animal." - icon_state = "quill" - item_state = "quill" - throwforce = 5 - -/obj/item/weapon/arrow/rod - - name = "metal rod" - desc = "Don't cry for me, Orithena." - icon_state = "metal-rod" - -/obj/item/weapon/arrow/rod/removed(mob/user) - if(throwforce == 15) // The rod has been superheated - we don't want it to be useable when removed from the bow. - to_chat(user, "[src] shatters into a scattering of overstressed metal shards as it leaves the crossbow.") - var/obj/item/weapon/shard/shrapnel/S = new() - S.loc = get_turf(src) - qdel(src) - -/obj/item/weapon/arrow/baguette - name = "baguette" - desc = "Still warm from the oven!" - icon = 'icons/obj/weapons.dmi' - icon_state = "baguette" - item_state = "baguette" - throwforce = 3 - sharp = 0 - -/obj/item/weapon/gun/launcher/crossbow - - name = "powered crossbow" - desc = "A 2557AD twist on an old classic. Pick up that can." - icon_state = "crossbow" - item_state = "crossbow-solid" - fire_sound = 'sound/weapons/punchmiss.ogg' // TODO: Decent THWOK noise. -// ejectshell = 0 // No spent shells. - mouthshoot = 1 // No suiciding with this weapon, causes runtimes. - fire_sound_text = "a solid thunk" - fire_delay = 25 - - var/tension = 0 // Current draw on the bow. - var/max_tension = 5 // Highest possible tension. - var/release_speed = 5 // Speed per unit of tension. - var/obj/item/weapon/stock_parts/cell/cell = null // Used for firing superheated rods. - var/current_user // Used to check if the crossbow has changed hands since being drawn. - -/obj/item/weapon/gun/launcher/crossbow/emp_act(severity) - if(cell && severity) - cell.use(100*severity) - -/obj/item/weapon/gun/launcher/crossbow/special_check(user) - if(tension <= 0) - to_chat(user, "\red \The [src] is not drawn back!") - return 0 - return 1 - -/obj/item/weapon/gun/launcher/crossbow/update_release_force() - release_force = tension*release_speed - -/obj/item/weapon/gun/launcher/crossbow/Fire(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, params, reflex = 0) - - if(!..()) return //Only do this on a successful shot. - icon_state = "crossbow" - tension = 0 - -/obj/item/weapon/gun/launcher/crossbow/attack_self(mob/living/user as mob) - if(tension) - if(in_chamber && in_chamber.loc == src) //Just in case they click it the tick after firing. - user.visible_message("[user] relaxes the tension on [src]'s string and removes [in_chamber].","You relax the tension on [src]'s string and remove [in_chamber].") - in_chamber.loc = get_turf(src) - var/obj/item/weapon/arrow/A = in_chamber - in_chamber = null - A.removed(user) - else - user.visible_message("[user] relaxes the tension on [src]'s string.","You relax the tension on [src]'s string.") - tension = 0 - icon_state = "crossbow" - else - draw(user) - -/obj/item/weapon/gun/launcher/crossbow/proc/draw(var/mob/user as mob) - - if(!in_chamber) - to_chat(user, "You don't have anything nocked to [src].") - return - - if(user.restrained()) - return - - current_user = user - user.visible_message("[user] begins to draw back the string of [src].","You begin to draw back the string of [src].") - tension = 1 - spawn(25) increase_tension(user) //TODO: This needs to be changed to something less shit. - -/obj/item/weapon/gun/launcher/crossbow/proc/increase_tension(var/mob/user as mob) - - if(!in_chamber || !tension || current_user != user) //Arrow has been fired, bow has been relaxed or user has changed. - return - - tension++ - icon_state = "crossbow-drawn" - - if(tension>=max_tension) - tension = max_tension - to_chat(usr, "[src] clunks as you draw the string to its maximum tension!") - else - user.visible_message("[usr] draws back the string of [src]!","You continue drawing back the string of [src]!") - spawn(25) increase_tension(user) - -/obj/item/weapon/gun/launcher/crossbow/attackby(obj/item/W as obj, mob/user as mob, params) - if(!in_chamber) - if (istype(W,/obj/item/weapon/arrow)) - user.drop_item() - in_chamber = W - in_chamber.loc = src - user.visible_message("[user] slides [in_chamber] into [src].","You slide [in_chamber] into [src].") - icon_state = "crossbow-nocked" - return - else if(istype(W,/obj/item/stack/rods)) - var/obj/item/stack/rods/R = W - R.use(1) - in_chamber = new /obj/item/weapon/arrow/rod(src) - in_chamber.fingerprintslast = src.fingerprintslast - in_chamber.loc = src - icon_state = "crossbow-nocked" - user.visible_message("[user] jams [in_chamber] into [src].","You jam [in_chamber] into [src].") - superheat_rod(user) - return - - if(istype(W, /obj/item/weapon/stock_parts/cell)) - if(!cell) - user.drop_item() - W.loc = src - cell = W - to_chat(user, "You jam [cell] into [src] and wire it to the firing coil.") - superheat_rod(user) - else - to_chat(user, "[src] already has a cell installed.") - - else if(istype(W, /obj/item/weapon/screwdriver)) - if(cell) - var/obj/item/C = cell - C.loc = get_turf(user) - to_chat(user, "You jimmy [cell] out of [src] with [W].") - cell = null - else - to_chat(user, "[src] doesn't have a cell installed.") - - else - ..() - -/obj/item/weapon/gun/launcher/crossbow/proc/superheat_rod(var/mob/user) - - if(!user || !cell || !in_chamber) return - if(cell.charge < 500) return - if(in_chamber.throwforce >= 15) return - if(!istype(in_chamber,/obj/item/weapon/arrow/rod)) return - - to_chat(user, "[in_chamber] plinks and crackles as it begins to glow red-hot.") - in_chamber.throwforce = 15 - in_chamber.icon_state = "metal-rod-superheated" - cell.use(500) - - -/obj/item/weapon/gun/launcher/crossbow/french - name = "french powered crossbow" - -/obj/item/weapon/gun/launcher/crossbow/french/attackby(obj/item/W as obj, mob/user as mob, params) - if(!in_chamber) - if (istype(W,/obj/item/weapon/reagent_containers/food/snacks/baguette)) - user.drop_item() - in_chamber = W - in_chamber.loc = src - user.visible_message("[user] slides [in_chamber] into [src].","You slide [in_chamber] into [src].") - icon_state = "crossbow-nocked" - return - - else if (istype(W,/obj/item/weapon/arrow)) - user.visible_message("\The [src] only takes baguettes.") - return - else if(istype(W,/obj/item/stack/rods)) - user.visible_message("\The [src] only takes baguettes.") - return - - if(istype(W, /obj/item/weapon/stock_parts/cell)) - user.visible_message("The [src] is too simple be adapted with a cell.") - return - - else - ..() - -/obj/item/weapon/gun/launcher/crossbow/french/increase_tension(var/mob/user as mob) - - if(!in_chamber || !tension || current_user != user) //Arrow has been fired, bow has been relaxed or user has changed. - return - - tension++ - icon_state = "fcrossbow-drawn" - - if(tension>=max_tension) - tension = max_tension - to_chat(usr, "[src] clunks as you draw the string to its maximum tension!") - else - user.visible_message("[usr] draws back the string of [src]!","You continue drawing back the string of [src]!") - spawn(25) increase_tension(user) - -// Crossbow construction. -/obj/item/weapon/crossbowframe - name = "crossbow frame" - desc = "A half-finished crossbow." - icon_state = "crossbowframe0" - item_state = "crossbow-solid" - - var/buildstate = 0 - -/obj/item/weapon/crossbowframe/update_icon() - icon_state = "crossbowframe[buildstate]" - -/obj/item/weapon/crossbowframe/examine(mob/user) - ..(user) - switch(buildstate) - if(1) - to_chat(user, "It has a loose rod frame in place.") - if(2) - to_chat(user, "It has a steel backbone welded in place.") - if(3) - to_chat(user, "It has a steel backbone and a cell mount installed.") - if(4) - to_chat(user, "It has a steel backbone, plastic lath and a cell mount installed.") - if(5) - to_chat(user, "It has a steel cable loosely strung across the lath.") - -/obj/item/weapon/crossbowframe/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W,/obj/item/stack/rods)) - if(buildstate == 0) - var/obj/item/stack/rods/R = W - if(R.amount >= 3) - R.use(3) - to_chat(user, "\blue You assemble a backbone of rods around the wooden stock.") - buildstate++ - update_icon() - else - to_chat(user, "\blue You need at least three rods to complete this task.") - return - else if(istype(W,/obj/item/weapon/weldingtool)) - if(buildstate == 1) - var/obj/item/weapon/weldingtool/T = W - if(T.remove_fuel(0,user)) - if(!src || !T.isOn()) return - playsound(src.loc, 'sound/items/Welder2.ogg', 100, 1) - to_chat(user, "\blue You weld the rods into place.") - buildstate++ - update_icon() - return - else if(istype(W,/obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/C = W - if(buildstate == 2) - if(C.amount >= 5) - C.use(5) - to_chat(user, "\blue You wire a crude cell mount into the top of the crossbow.") - buildstate++ - update_icon() - else - to_chat(user, "\blue You need at least five segments of cable coil to complete this task.") - return - else if(buildstate == 4) - if(C.amount >= 5) - C.use(5) - to_chat(user, "\blue You string a steel cable across the crossbow's lath.") - buildstate++ - update_icon() - else - to_chat(user, "\blue You need at least five segments of cable coil to complete this task.") - return - else if(istype(W,/obj/item/stack/sheet/mineral/plastic)) - if(buildstate == 3) - var/obj/item/stack/sheet/mineral/plastic/P = W - if(P.amount >= 3) - P.use(3) - to_chat(user, "\blue You assemble and install a heavy plastic lath onto the crossbow.") - buildstate++ - update_icon() - else - to_chat(user, "\blue You need at least three plastic sheets to complete this task.") - return - else if(istype(W,/obj/item/weapon/screwdriver)) - if(buildstate == 5) - to_chat(user, "\blue You secure the crossbow's various parts.") - new /obj/item/weapon/gun/launcher/crossbow(get_turf(src)) - qdel(src) - return - else - ..() diff --git a/code/modules/projectiles/guns/projectile/grenade launcher.dm b/code/modules/projectiles/guns/projectile/grenade launcher.dm deleted file mode 100644 index 28dc0c3a4b9..00000000000 --- a/code/modules/projectiles/guns/projectile/grenade launcher.dm +++ /dev/null @@ -1,91 +0,0 @@ -/obj/item/weapon/gun/launcher - - name = "launcher" - desc = "A device that launches things." - icon = 'icons/obj/weapons.dmi' - w_class = 5.0 - flags = CONDUCT - slot_flags = SLOT_BACK - - var/release_force = 0 - var/fire_sound_text = "a launcher firing" - -//Check if we're drawing and if the bow is loaded. -/obj/item/weapon/gun/launcher/proc/chamber_round() - return (!isnull(in_chamber)) - -//This should not fit in a combat belt or holster. -/obj/item/weapon/gun/launcher/isHandgun() - return 0 - -//Launchers are mechanical, no other impact. -/obj/item/weapon/gun/launcher/emp_act(severity) - return - -//This normally uses a proc on projectiles and our ammo is not strictly speaking a projectile. -/obj/item/weapon/gun/launcher/can_hit(var/mob/living/target as mob, var/mob/living/user as mob) - return - -//Override this to avoid a runtime with suicide handling. -/obj/item/weapon/gun/launcher/attack(mob/living/M as mob, mob/living/user as mob, def_zone) - if (M == user && user.zone_sel.selecting == "mouth") - to_chat(user, "\red Shooting yourself with \a [src] is pretty tricky. You can't seem to manage it.") - return - ..() - -/obj/item/weapon/gun/launcher/proc/update_release_force() - return 0 - -/obj/item/weapon/gun/launcher/Fire(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, params, reflex = 0) - - if (!user.IsAdvancedToolUser()) - to_chat(user, "\red You don't have the dexterity to do this!") - return 0 - - add_fingerprint(user) - - //Make sure target turfs both exist. - var/turf/curloc = get_turf(user) - var/turf/targloc = get_turf(target) - if (!istype(targloc) || !istype(curloc)) - return 0 - - if(!special_check(user)) - return 0 - - if (!ready_to_fire()) - if (world.time % 3) //to prevent spam - to_chat(user, "[src] is not ready to fire again!") - return 0 - - if(!chamber_round()) //CHECK - return click_empty(user) - - if(!in_chamber) - return 0 - - update_release_force() - - playsound(user, fire_sound, 50, 1) - user.visible_message("[user] fires [src][reflex ? " by reflex":""]!", \ - "You fire [src][reflex ? "by reflex":""]!", \ - "You hear [fire_sound_text]!") - - in_chamber.loc = get_turf(user) - in_chamber.throw_at(target,10,release_force) - - sleep(1) - - in_chamber = null - - update_icon() - - if(user.hand) - user.update_inv_l_hand() - else - user.update_inv_r_hand() - - return 1 - -/obj/item/weapon/gun/launcher/attack_self(mob/living/user as mob) - return \ No newline at end of file diff --git a/code/modules/projectiles/guns/projectile/launchers.dm b/code/modules/projectiles/guns/projectile/launchers.dm index 6bcca0cdf68..635ab22cc8c 100644 --- a/code/modules/projectiles/guns/projectile/launchers.dm +++ b/code/modules/projectiles/guns/projectile/launchers.dm @@ -1,44 +1,12 @@ //KEEP IN MIND: These are different from gun/grenadelauncher. These are designed to shoot premade rocket and grenade projectiles, not flashbangs or chemistry casings etc. //Put handheld rocket launchers here if someone ever decides to make something so hilarious ~Paprika - -/obj/item/weapon/gun/projectile/automatic/gyropistol - name = "gyrojet pistol" - desc = "A prototype pistol designed to fire self propelled rockets." - icon_state = "gyropistol" - fire_sound = 'sound/effects/Explosion1.ogg' - origin_tech = "combat=3" - mag_type = "/obj/item/ammo_box/magazine/m75" - burst_size = 1 - fire_delay = 0 - -/obj/item/weapon/gun/projectile/automatic/gyropistol/isHandgun() - return 1 - -/obj/item/weapon/gun/projectile/automatic/gyropistol/New() - ..() - update_icon() - return - -/obj/item/weapon/gun/projectile/automatic/gyropistol/afterattack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, flag) - ..() - if(!chambered && !get_ammo() && !alarmed) - playsound(user, 'sound/weapons/smg_empty_alarm.ogg', 40, 1) - update_icon() - alarmed = 1 - return - -/obj/item/weapon/gun/projectile/automatic/gyropistol/update_icon() - ..() - icon_state = "[initial(icon_state)][magazine ? "loaded" : ""]" - return - /obj/item/weapon/gun/projectile/revolver/grenadelauncher//this is only used for underbarrel grenade launchers at the moment, but admins can still spawn it if they feel like being assholes desc = "A break-operated grenade launcher." name = "grenade launcher" icon_state = "dshotgun-sawn" item_state = "gun" - mag_type = "/obj/item/ammo_box/magazine/internal/cylinder/grenadelauncher" + mag_type = /obj/item/ammo_box/magazine/internal/cylinder/grenadelauncher fire_sound = 'sound/weapons/grenadelaunch.ogg' w_class = 3 @@ -52,7 +20,7 @@ name = "multi grenade launcher" icon_state = "bulldog" item_state = "bulldog" - mag_type = "/obj/item/ammo_box/magazine/internal/cylinder/grenadelauncher/multi" + mag_type = /obj/item/ammo_box/magazine/internal/cylinder/grenadelauncher/multi /obj/item/weapon/gun/projectile/revolver/grenadelauncher/multi/cyborg desc = "A 6-shot grenade launcher." @@ -60,4 +28,57 @@ icon_state = "mecha_grenadelnchr" /obj/item/weapon/gun/projectile/revolver/grenadelauncher/multi/cyborg/attack_self() - return \ No newline at end of file + return + +/obj/item/weapon/gun/projectile/automatic/gyropistol + name = "gyrojet pistol" + desc = "A prototype pistol designed to fire self propelled rockets." + icon_state = "gyropistol" + fire_sound = 'sound/effects/Explosion1.ogg' + origin_tech = "combat=3" + mag_type = /obj/item/ammo_box/magazine/m75 + burst_size = 1 + fire_delay = 0 + action_button_name = null + +/obj/item/weapon/gun/projectile/automatic/gyropistol/isHandgun() + return 1 + +/obj/item/weapon/gun/projectile/automatic/gyropistol/process_chamber(eject_casing = 0, empty_chamber = 1) + ..() + +/obj/item/weapon/gun/projectile/automatic/gyropistol/update_icon() + ..() + icon_state = "[initial(icon_state)][magazine ? "loaded" : ""]" + return + +/obj/item/weapon/gun/projectile/automatic/speargun + name = "kinetic speargun" + desc = "A weapon favored by carp hunters. Fires specialized spears using kinetic energy." + icon_state = "speargun" + item_state = "speargun" + w_class = 4 + force = 10 + can_suppress = 0 + mag_type = /obj/item/ammo_box/magazine/internal/speargun + fire_sound = 'sound/weapons/grenadelaunch.ogg' + burst_size = 1 + fire_delay = 0 + select = 0 + action_button_name = null + +/obj/item/weapon/gun/projectile/automatic/speargun/update_icon() + return + +/obj/item/weapon/gun/projectile/automatic/speargun/attack_self() + return + +/obj/item/weapon/gun/projectile/automatic/speargun/process_chamber(eject_casing = 0, empty_chamber = 1) + ..() + +/obj/item/weapon/gun/projectile/automatic/speargun/attackby(obj/item/A, mob/user, params) + var/num_loaded = magazine.attackby(A, user, params, 1) + if(num_loaded) + to_chat(user, "You load [num_loaded] spear\s into \the [src].") + update_icon() + chamber_round() diff --git a/code/modules/projectiles/guns/projectile/pistol.dm b/code/modules/projectiles/guns/projectile/pistol.dm index 99aded2663b..1a37a97ad7a 100644 --- a/code/modules/projectiles/guns/projectile/pistol.dm +++ b/code/modules/projectiles/guns/projectile/pistol.dm @@ -4,18 +4,18 @@ icon_state = "pistol" w_class = 2 origin_tech = "combat=2;materials=2;syndicate=2" - mag_type = "/obj/item/ammo_box/magazine/m10mm" + mag_type = /obj/item/ammo_box/magazine/m10mm can_suppress = 1 burst_size = 1 fire_delay = 0 action_button_name = null - + /obj/item/weapon/gun/projectile/automatic/pistol/isHandgun() return 1 /obj/item/weapon/gun/projectile/automatic/pistol/update_icon() ..() - icon_state = "[initial(icon_state)][chambered ? "" : "-e"][silenced ? "-suppressed" : ""]" + icon_state = "[initial(icon_state)][chambered ? "" : "-e"][suppressed ? "-suppressed" : ""]" return /obj/item/weapon/gun/projectile/automatic/pistol/m2411 @@ -24,7 +24,7 @@ icon_state = "m2411" w_class = 3.0 origin_tech = "combat=3;materials=2" - mag_type = "/obj/item/ammo_box/magazine/m45" + mag_type = /obj/item/ammo_box/magazine/m45 can_suppress = 0 /obj/item/weapon/gun/projectile/automatic/pistol/deagle @@ -32,7 +32,7 @@ desc = "A robust .50 AE handgun." icon_state = "deagle" force = 14.0 - mag_type = "/obj/item/ammo_box/magazine/m50" + mag_type = /obj/item/ammo_box/magazine/m50 can_suppress = 0 /obj/item/weapon/gun/projectile/automatic/pistol/deagle/update_icon() @@ -47,4 +47,16 @@ /obj/item/weapon/gun/projectile/automatic/pistol/deagle/camo desc = "A Deagle brand Deagle for operators operating operationally. Uses .50 AE ammo." icon_state = "deaglecamo" - item_state = "deagleg" \ No newline at end of file + item_state = "deagleg" + +/obj/item/weapon/gun/projectile/automatic/pistol/APS + name = "stechkin APS pistol" + desc = "The original russian version of a widely used Syndicate sidearm. Uses 9mm ammo." + icon_state = "aps" + w_class = 3 + origin_tech = "combat=3;materials=2;syndicate=3" + mag_type = /obj/item/ammo_box/magazine/pistolm9mm + can_suppress = 0 + burst_size = 3 + fire_delay = 2 + action_button_name = "Toggle Firemode" diff --git a/code/modules/projectiles/guns/projectile/revolver.dm b/code/modules/projectiles/guns/projectile/revolver.dm index 2cb3c849e4b..66e404a7de8 100644 --- a/code/modules/projectiles/guns/projectile/revolver.dm +++ b/code/modules/projectiles/guns/projectile/revolver.dm @@ -2,57 +2,80 @@ name = "\improper .357 revolver" desc = "A suspicious revolver. Uses .357 ammo." icon_state = "revolver" - mag_type = "/obj/item/ammo_box/magazine/internal/cylinder" + mag_type = /obj/item/ammo_box/magazine/internal/cylinder -/obj/item/weapon/gun/projectile/revolver/chamber_round() - if (chambered || !magazine) - return - else if (magazine.ammo_count()) +/obj/item/weapon/gun/projectile/revolver/New() + ..() + if(!istype(magazine, /obj/item/ammo_box/magazine/internal/cylinder)) + verbs -= /obj/item/weapon/gun/projectile/revolver/verb/spin + +/obj/item/weapon/gun/projectile/revolver/chamber_round(var/spin = 1) + if(spin) chambered = magazine.get_round(1) + else + chambered = magazine.stored_ammo[1] return -/obj/item/weapon/gun/projectile/revolver/process_chambered() +/obj/item/weapon/gun/projectile/revolver/shoot_with_empty_chamber(mob/living/user as mob|obj) + ..() + chamber_round(1) + +/obj/item/weapon/gun/projectile/revolver/process_chamber() return ..(0, 1) -/obj/item/weapon/gun/projectile/revolver/attackby(var/obj/item/A as obj, mob/user as mob, params) - var/num_loaded = 0 - if(istype(A, /obj/item/ammo_box)) - var/obj/item/ammo_box/AM = A - for(var/obj/item/ammo_casing/AC in AM.stored_ammo) - var/didload = magazine.give_round(AC) - if(didload) - AM.stored_ammo -= AC - num_loaded++ - if(!didload || !magazine.multiload) - break - if(istype(A, /obj/item/ammo_casing)) - var/obj/item/ammo_casing/AC = A - if(magazine.give_round(AC)) - user.drop_item() - AC.loc = src - num_loaded++ +/obj/item/weapon/gun/projectile/revolver/attackby(obj/item/A, mob/user, params) + . = ..() + if(.) + return + var/num_loaded = magazine.attackby(A, user, params, 1) if(num_loaded) - to_chat(user, "You load [num_loaded] shell\s into \the [src]!") + to_chat(user, "You load [num_loaded] shell\s into \the [src].") A.update_icon() update_icon() - chamber_round() + chamber_round(0) -/obj/item/weapon/gun/projectile/revolver/attack_self(mob/living/user as mob) + if(unique_rename) + if(istype(A, /obj/item/weapon/pen)) + rename_gun(user) + +/obj/item/weapon/gun/projectile/revolver/attack_self(mob/living/user) var/num_unloaded = 0 + chambered = null while (get_ammo() > 0) var/obj/item/ammo_casing/CB CB = magazine.get_round(0) - chambered = null - CB.loc = get_turf(src.loc) - CB.SpinAnimation(10, 1) - CB.update_icon() - num_unloaded++ + if(CB) + CB.loc = get_turf(src.loc) + CB.SpinAnimation(10, 1) + CB.update_icon() + num_unloaded++ if (num_unloaded) - to_chat(user, "You unload [num_unloaded] shell\s from [src]!") + to_chat(user, "You unload [num_unloaded] shell\s from [src].") else - to_chat(user, "[src] is empty.") + to_chat(user, "[src] is empty!") -/obj/item/weapon/gun/projectile/revolver/get_ammo(var/countchambered = 0, var/countempties = 1) +/obj/item/weapon/gun/projectile/revolver/verb/spin() + set name = "Spin Chamber" + set category = "Object" + set desc = "Click to spin your revolver's chamber." + + var/mob/M = usr + + if(M.stat || !in_range(M,src)) + return + + if(istype(magazine, /obj/item/ammo_box/magazine/internal/cylinder)) + var/obj/item/ammo_box/magazine/internal/cylinder/C = magazine + C.spin() + chamber_round(0) + usr.visible_message("[usr] spins [src]'s chamber.", "You spin [src]'s chamber.") + else + verbs -= /obj/item/weapon/gun/projectile/revolver/verb/spin + +/obj/item/weapon/gun/projectile/revolver/can_shoot() + return get_ammo(0,0) + +/obj/item/weapon/gun/projectile/revolver/get_ammo(countchambered = 0, countempties = 1) var/boolets = 0 //mature var names for mature people if (chambered && countchambered) boolets++ @@ -61,119 +84,66 @@ return boolets /obj/item/weapon/gun/projectile/revolver/examine(mob/user) - ..(user) + ..() to_chat(user, "[get_ammo(0,0)] of those are live rounds.") -/obj/item/weapon/gun/projectile/revolver/verb/spin_revolver() - set name = "Spin cylinder" - set desc = "Fun when you're bored out of your skull." - set category = "Object" - - usr.visible_message("[usr] spins the chamber of the revolver.", "You spin the revolver's chamber.") - playsound(src.loc, 'sound/weapons/revolver_spin.ogg', 100, 1) - Spin() - -/obj/item/weapon/gun/projectile/revolver/proc/Spin() - chambered = null - var/random = rand(1, magazine.max_ammo) - if(random <= get_ammo(0,0)) - chamber_round() - update_icon() - -/obj/item/weapon/gun/projectile/revolver/capgun - name = "cap gun" - desc = "Looks almost like the real thing! Ages 8 and up." - origin_tech = "combat=1;materials=1" - mag_type = "/obj/item/ammo_box/magazine/internal/cylinder/cap" - /obj/item/weapon/gun/projectile/revolver/detective desc = "A cheap Martian knock-off of a classic law enforcement firearm. Uses .38-special rounds." name = "\improper .38 Mars Special" icon_state = "detective" origin_tech = "combat=2;materials=2" - mag_type = "/obj/item/ammo_box/magazine/internal/cylinder/rev38" + mag_type = /obj/item/ammo_box/magazine/internal/cylinder/rev38 + unique_rename = 1 + unique_reskin = 1 - -/obj/item/weapon/gun/projectile/revolver/detective/special_check(var/mob/living/carbon/human/M) - if(!ghettomodded) - return 1 - if(prob(70 - (magazine.ammo_count() * 10))) //minimum probability of 10, maximum of 60 - to_chat(M, "[src] blows up in your face!") - M.take_organ_damage(0,20) - M.drop_item() - qdel(src) - return 0 - return 1 - -/obj/item/weapon/gun/projectile/revolver/detective/verb/rename_gun() - set name = "Name Gun" - set category = "Object" - set desc = "Click to rename your gun." - - var/mob/M = usr - var/input = stripped_input(M,"What do you want to name the gun?", ,"", MAX_NAME_LEN) - - if(src && input && !M.stat && in_range(M,src)) - name = input - to_chat(M, "You name the gun [input]. Say hello to your new friend.") - return 1 - -/obj/item/weapon/gun/projectile/revolver/detective/verb/reskin_gun() - set name = "Reskin gun" - set category = "Object" - set desc = "Click to reskin your gun." - - var/mob/M = usr - var/list/options = list() +/obj/item/weapon/gun/projectile/revolver/detective/New() + ..() options["The Original"] = "detective" options["Leopard Spots"] = "detective_leopard" options["Black Panther"] = "detective_panther" options["Gold Trim"] = "detective_gold" options["The Peacemaker"] = "detective_peacemaker" - var/choice = input(M,"What do you want to skin the gun to?","Reskin Gun") in options + options["Cancel"] = null - if(src && choice && !M.stat && in_range(M,src)) - icon_state = options[choice] - to_chat(M, "Your gun is now skinned as [choice]. Say hello to your new friend.") - return 1 - -/obj/item/weapon/gun/projectile/revolver/detective/attackby(var/obj/item/A as obj, mob/user as mob, params) +/obj/item/weapon/gun/projectile/revolver/detective/process_fire(atom/target as mob|obj|turf, mob/living/user as mob|obj, message = 1, params, zone_override = "") + if(magazine.caliber != initial(magazine.caliber)) + if(prob(70 - (magazine.ammo_count() * 10))) //minimum probability of 10, maximum of 60 + playsound(user, fire_sound, 50, 1) + to_chat(user, "[src] blows up in your face!") + user.take_organ_damage(0,20) + user.unEquip(src) + return 0 ..() - if(istype(A, /obj/item/weapon/screwdriver) || istype(A, /obj/item/weapon/conversion_kit)) + +/obj/item/weapon/gun/projectile/revolver/detective/attackby(obj/item/A, mob/user, params) + ..() + if(istype(A, /obj/item/weapon/screwdriver)) if(magazine.caliber == "38") - to_chat(user, "You begin to reinforce the barrel of [src].") + to_chat(user, "You begin to reinforce the barrel of [src]...") if(magazine.ammo_count()) afterattack(user, user) //you know the drill - user.visible_message("[src] goes off!", "[src] goes off in your face!") + user.visible_message("[src] goes off!", "[src] goes off in your face!") return - if(do_after(user, 30, target = src)) + if(do_after(user, 30/A.toolspeed, target = src)) if(magazine.ammo_count()) - to_chat(user, "You can't modify it!") + to_chat(user, "You can't modify it!") return - if (istype(A, /obj/item/weapon/conversion_kit)) - ghettomodded = 0 - else - ghettomodded = 1 magazine.caliber = "357" - desc = "[initial(desc)] The barrel and chamber assembly seems to have been modified." - to_chat(user, "You reinforce the barrel of [src]! Now it will fire .357 rounds.") + desc = "The barrel and chamber assembly seems to have been modified." + to_chat(user, "You reinforce the barrel of [src]. Now it will fire .357 rounds.") else - to_chat(user, "You begin to revert the modifications to [src].") + to_chat(user, "You begin to revert the modifications to [src]...") if(magazine.ammo_count()) afterattack(user, user) //and again - user.visible_message("[src] goes off!", "[src] goes off in your face!") + user.visible_message("[src] goes off!", "[src] goes off in your face!") return if(do_after(user, 30, target = src)) if(magazine.ammo_count()) - to_chat(user, "You can't modify it!") + to_chat(user, "You can't modify it!") return - ghettomodded = 0 magazine.caliber = "38" desc = initial(desc) - to_chat(user, "You remove the modifications on [src]! Now it will fire .38 rounds.") - - - + to_chat(user, "You remove the modifications on [src]. Now it will fire .38 rounds.") /obj/item/weapon/gun/projectile/revolver/mateba name = "\improper Unica 6 auto-revolver" @@ -181,6 +151,21 @@ icon_state = "mateba" origin_tech = "combat=2;materials=2" +/obj/item/weapon/gun/projectile/revolver/golden + name = "\improper Golden revolver" + desc = "This ain't no game, ain't never been no show, And I'll gladly gun down the oldest lady you know. Uses .357 ammo." + icon_state = "goldrevolver" + fire_sound = 'sound/weapons/resonator_blast.ogg' + recoil = 8 + +/obj/item/weapon/gun/projectile/revolver/nagant + name = "nagant revolver" + desc = "An old model of revolver that originated in Russia. Able to be suppressed. Uses 7.62x38mmR ammo." + icon_state = "nagant" + origin_tech = "combat=3" + can_suppress = 1 + mag_type = /obj/item/ammo_box/magazine/internal/cylinder/rev762 + // A gun to play Russian Roulette! // You can spin the chamber to randomize the position of the bullet. @@ -188,35 +173,95 @@ name = "\improper Russian Revolver" desc = "A Russian-made revolver for drinking games. Uses .357 ammo, and has a mechanism that spins the chamber before each trigger pull." origin_tech = "combat=2;materials=2" - mag_type = "/obj/item/ammo_box/magazine/internal/cylinder/rus357" + mag_type = /obj/item/ammo_box/magazine/internal/cylinder/rus357 + var/spun = 0 /obj/item/weapon/gun/projectile/revolver/russian/New() ..() Spin() + update_icon() -/obj/item/weapon/gun/projectile/revolver/russian/attack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj) +/obj/item/weapon/gun/projectile/revolver/russian/proc/Spin() + chambered = null + var/random = rand(1, magazine.max_ammo) + if(random <= get_ammo(0,0)) + chamber_round() + spun = 1 - if(!chambered && target == user) - user.visible_message("\red *click*", "\red *click*") - return +/obj/item/weapon/gun/projectile/revolver/russian/attackby(obj/item/A, mob/user, params) + var/num_loaded = ..() + if(num_loaded) + user.visible_message("[user] loads a single bullet into the revolver and spins the chamber.", "You load a single bullet into the chamber and spin it.") + else + user.visible_message("[user] spins the chamber of the revolver.", "You spin the revolver's chamber.") + if(get_ammo() > 0) + Spin() + update_icon() + A.update_icon() + return - if(isliving(target) && isliving(user)) - if(target == user) - var/obj/item/organ/external/affecting = user.zone_sel.selecting - if(affecting == "head") - var/obj/item/ammo_casing/AC = chambered - if(!process_chambered()) - user.visible_message("\red *click*", "\red *click*") - return - if(!in_chamber) - return - var/obj/item/projectile/P = new AC.projectile_type - playsound(user, fire_sound, 50, 1) - user.visible_message("[user.name] fires [src] at \his head!", "You fire [src] at your head!", "You hear a [istype(in_chamber, /obj/item/projectile/beam) ? "laser blast" : "gunshot"]!") - if(!P.nodamage) - user.apply_damage(300, BRUTE, affecting, sharp=1) // You are dead, dead, dead. +/obj/item/weapon/gun/projectile/revolver/russian/attack_self(mob/user) + if(!spun && can_shoot()) + user.visible_message("[user] spins the chamber of the revolver.", "You spin the revolver's chamber.") + Spin() + else + var/num_unloaded = 0 + while (get_ammo() > 0) + var/obj/item/ammo_casing/CB + CB = magazine.get_round() + chambered = null + CB.loc = get_turf(src.loc) + CB.update_icon() + num_unloaded++ + if (num_unloaded) + to_chat(user, "You unload [num_unloaded] shell\s from [src].") + else + to_chat(user, "[src] is empty.") + +/obj/item/weapon/gun/projectile/revolver/russian/afterattack(atom/target as mob|obj|turf, mob/living/user as mob|obj, flag, params) + if(flag) + if(!(target in user.contents) && ismob(target)) + if(user.a_intent == "harm") // Flogging action return - ..() + if(isliving(user)) + if(!can_trigger_gun(user)) + return + if(target != user) + if(ismob(target)) + to_chat(user, "A mechanism prevents you from shooting anyone but yourself!") + return + if(ishuman(user)) + var/mob/living/carbon/human/H = user + if(!spun) + to_chat(user, "You need to spin the revolver's chamber first!") + return + + spun = 0 + + if(chambered) + var/obj/item/ammo_casing/AC = chambered + if(AC.fire(user, user)) + playsound(user, fire_sound, 50, 1) + var/zone = check_zone(user.zone_selected) + var/obj/item/bodypart/affecting = H.get_bodypart(zone) + if(zone == "head" || zone == "eyes" || zone == "mouth") + shoot_self(user, affecting) + else + user.visible_message("[user.name] cowardly fires [src] at \his [affecting.name]!", "You cowardly fire [src] at your [affecting.name]!", "You hear a gunshot!") + return + + user.visible_message("*click*") + playsound(user, 'sound/weapons/empty.ogg', 100, 1) + +/obj/item/weapon/gun/projectile/revolver/russian/proc/shoot_self(mob/living/carbon/human/user, affecting = "head") + user.apply_damage(300, BRUTE, affecting) + user.visible_message("[user.name] fires [src] at \his head!", "You fire [src] at your head!", "You hear a gunshot!") + +/obj/item/weapon/gun/projectile/revolver/capgun + name = "cap gun" + desc = "Looks almost like the real thing! Ages 8 and up." + origin_tech = "combat=1;materials=1" + mag_type = /obj/item/ammo_box/magazine/internal/cylinder/cap diff --git a/code/modules/projectiles/guns/projectile/rocket.dm b/code/modules/projectiles/guns/projectile/rocket.dm index e667d9323ce..a82caddeba1 100644 --- a/code/modules/projectiles/guns/projectile/rocket.dm +++ b/code/modules/projectiles/guns/projectile/rocket.dm @@ -10,7 +10,6 @@ force = 5.0 flags = CONDUCT origin_tech = "combat=8;materials=5" - projectile = /obj/item/missile var/missile_speed = 2 var/missile_range = 30 var/max_rockets = 1 @@ -18,7 +17,12 @@ /obj/item/weapon/gun/rocketlauncher/examine(mob/user) if(..(user, 2)) - to_chat(user, "\blue [rockets.len] / [max_rockets] rockets.") + to_chat(user, "[rockets.len] / [max_rockets] rockets.") + +/obj/item/weapon/gun/rocketlauncher/Destroy() + for(var/datum/D in rockets) + qdel(D) + rockets = null /obj/item/weapon/gun/rocketlauncher/attackby(obj/item/I as obj, mob/user as mob, params) if(istype(I, /obj/item/ammo_casing/rocket)) @@ -26,15 +30,15 @@ user.drop_item() I.loc = src rockets += I - to_chat(user, "\blue You put the rocket in [src].") - to_chat(user, "\blue [rockets.len] / [max_rockets] rockets.") + to_chat(user, "You put the rocket in [src].") + to_chat(user, "[rockets.len] / [max_rockets] rockets.") else - to_chat(usr, "\red [src] cannot hold more rockets.") + to_chat(usr, "[src] cannot hold more rockets.") -/obj/item/weapon/gun/rocketlauncher/can_fire() +/obj/item/weapon/gun/rocketlauncher/can_shoot() return rockets.len -/obj/item/weapon/gun/rocketlauncher/Fire(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, params, reflex = 0) +/obj/item/weapon/gun/rocketlauncher/process_fire(atom/target as mob|obj|turf, mob/living/user as mob|obj, message = 1, params, zone_override = "") if(rockets.len) var/obj/item/ammo_casing/rocket/I = rockets[1] var/obj/item/missile/M = new projectile(user.loc) @@ -47,4 +51,4 @@ qdel(I) return else - to_chat(usr, "\red [src] is empty.") + to_chat(usr, "[src] is empty.") diff --git a/code/modules/projectiles/guns/projectile/saw.dm b/code/modules/projectiles/guns/projectile/saw.dm new file mode 100644 index 00000000000..5c4856d2503 --- /dev/null +++ b/code/modules/projectiles/guns/projectile/saw.dm @@ -0,0 +1,179 @@ +/obj/item/weapon/gun/projectile/automatic/l6_saw + name = "\improper L6 SAW" + desc = "A heavily modified 7.62 light machine gun, designated 'L6 SAW'. Has 'Aussec Armoury - 2531' engraved on the reciever below the designation." + icon_state = "l6closed100" + item_state = "l6closedmag" + w_class = 5 + slot_flags = 0 + origin_tech = "combat=5;materials=1;syndicate=2" + mag_type = /obj/item/ammo_box/magazine/mm556x45 + weapon_weight = WEAPON_MEDIUM + fire_sound = 'sound/weapons/Gunshot3.ogg' + var/cover_open = 0 + can_suppress = 0 + burst_size = 3 + fire_delay = 1 + +/obj/item/weapon/gun/projectile/automatic/l6_saw/attack_self(mob/user) + cover_open = !cover_open + to_chat(user, "You [cover_open ? "open" : "close"] [src]'s cover.") + update_icon() + +/obj/item/weapon/gun/projectile/automatic/l6_saw/update_icon() + icon_state = "l6[cover_open ? "open" : "closed"][magazine ? Ceiling(get_ammo(0)/12.5)*25 : "-empty"][suppressed ? "-suppressed" : ""]" + item_state = "l6[cover_open ? "openmag" : "closedmag"]" + +/obj/item/weapon/gun/projectile/automatic/l6_saw/afterattack(atom/target as mob|obj|turf, mob/living/user as mob|obj, flag, params) //what I tried to do here is just add a check to see if the cover is open or not and add an icon_state change because I can't figure out how c-20rs do it with overlays + if(cover_open) + to_chat(user, "[src]'s cover is open! Close it before firing!") + else + ..() + update_icon() + +/obj/item/weapon/gun/projectile/automatic/l6_saw/attack_hand(mob/user) + if(loc != user) + ..() + return //let them pick it up + if(!cover_open || (cover_open && !magazine)) + ..() + else if(cover_open && magazine) + //drop the mag + magazine.update_icon() + magazine.loc = get_turf(src.loc) + user.put_in_hands(magazine) + magazine = null + update_icon() + to_chat(user, "You remove the magazine from [src].") + + +/obj/item/weapon/gun/projectile/automatic/l6_saw/attackby(obj/item/A, mob/user, params) + . = ..() + if(.) + return + if(!cover_open) + to_chat(user, "[src]'s cover is closed! You can't insert a new mag.") + return + ..() + +//ammo// + +/obj/item/projectile/bullet/saw + damage = 45 + armour_penetration = 5 + +/obj/item/projectile/bullet/saw/bleeding + damage = 20 + armour_penetration = 0 + +/obj/item/projectile/bullet/saw/bleeding/on_hit(atom/target, blocked = 0, hit_zone) + . = ..() + if((blocked != 100) && istype(target, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = target + H.drip(35) + +/obj/item/projectile/bullet/saw/hollow + damage = 60 + armour_penetration = -10 + +/obj/item/projectile/bullet/saw/ap + damage = 40 + armour_penetration = 75 + +/obj/item/projectile/bullet/saw/incen + damage = 7 + armour_penetration = 0 + +obj/item/projectile/bullet/saw/incen/Move() + ..() + var/turf/location = get_turf(src) + if(location) + PoolOrNew(/obj/effect/hotspot, location) + location.hotspot_expose(700, 50, 1) + +/obj/item/projectile/bullet/saw/incen/on_hit(atom/target, blocked = 0) + . = ..() + if(iscarbon(target)) + var/mob/living/carbon/M = target + M.adjust_fire_stacks(3) + M.IgniteMob() + +//magazines// + +/obj/item/ammo_box/magazine/mm556x45 + name = "box magazine (5.56x45mm)" + icon_state = "a762-50" + origin_tech = "combat=2" + ammo_type = /obj/item/ammo_casing/mm556x45 + caliber = "mm55645" + max_ammo = 50 + +/obj/item/ammo_box/magazine/mm556x45/bleeding + name = "box magazine (Bleeding 5.56x45mm)" + origin_tech = "combat=3" + ammo_type = /obj/item/ammo_casing/mm556x45/bleeding + +/obj/item/ammo_box/magazine/mm556x45/hollow + name = "box magazine (Hollow-Point 5.56x45mm)" + origin_tech = "combat=3" + ammo_type = /obj/item/ammo_casing/mm556x45/hollow + +/obj/item/ammo_box/magazine/mm556x45/ap + name = "box magazine (Armor Penetrating 5.56x45mm)" + origin_tech = "combat=4" + ammo_type = /obj/item/ammo_casing/mm556x45/ap + +/obj/item/ammo_box/magazine/mm556x45/incen + name = "box magazine (Incendiary 5.56x45mm)" + origin_tech = "combat=4" + ammo_type = /obj/item/ammo_casing/mm556x45/incen + +/obj/item/ammo_box/magazine/mm556x45/update_icon() + ..() + icon_state = "a762-[round(ammo_count(),10)]" + +/obj/item/ammo_box/magazine/mm556x45/buckshot //adminbus + name = "box magazine (buckshot)" + icon_state = "b762" + caliber = "shotgun" + ammo_type = /obj/item/ammo_casing/shotgun/buckshot + max_ammo = 300 + +/obj/item/ammo_box/magazine/mm556x45/buckshot/update_icon() + ..() + icon_state = "b762-[round(ammo_count(),10)]" + +/obj/item/ammo_box/magazine/m556x45/he // also adminbus + name = "box magazine (40mm high explosive)" + icon_state = "g762" + caliber = "40mm" + ammo_type =/obj/item/ammo_casing/a40mm + max_ammo = 25 + +/obj/item/ammo_box/magazine/mm556x45/he/update_icon() + ..() + icon_state = "g762-[round(ammo_count(),5)]" + +//casings// + +/obj/item/ammo_casing/mm556x45 + desc = "A 556x45mm bullet casing." + icon_state = "762-casing" + caliber = "mm55645" + projectile_type = /obj/item/projectile/bullet/saw + +/obj/item/ammo_casing/mm556x45/bleeding + desc = "A 556x45mm bullet casing with specialized inner-casing, that when it makes contact with a target, release tiny shrapnel to induce internal bleeding." + icon_state = "762-casing" + projectile_type = /obj/item/projectile/bullet/saw/bleeding + +/obj/item/ammo_casing/mm556x45/hollow + desc = "A 556x45mm bullet casing designed to cause more damage to unarmored targets." + projectile_type = /obj/item/projectile/bullet/saw/hollow + +/obj/item/ammo_casing/mm556x45/ap + desc = "A 556x45mm bullet casing designed with a hardened-tipped core to help penetrate armored targets." + projectile_type = /obj/item/projectile/bullet/saw/ap + +/obj/item/ammo_casing/mm556x45/incen + desc = "A 556x45mm bullet casing designed with a chemical-filled capsule on the tip that when bursted, reacts with the atmosphere to produce a fireball, engulfing the target in flames. " + projectile_type = /obj/item/projectile/bullet/saw/incen diff --git a/code/modules/projectiles/guns/projectile/shotgun.dm b/code/modules/projectiles/guns/projectile/shotgun.dm index 7ae951ad908..1119a795505 100644 --- a/code/modules/projectiles/guns/projectile/shotgun.dm +++ b/code/modules/projectiles/guns/projectile/shotgun.dm @@ -8,26 +8,14 @@ flags = CONDUCT slot_flags = SLOT_BACK origin_tech = "combat=4;materials=2" + mag_type = /obj/item/ammo_box/magazine/internal/shot var/recentpump = 0 // to prevent spammage - mag_type = "/obj/item/ammo_box/magazine/internal/shot" -/obj/item/weapon/gun/projectile/shotgun/attackby(var/obj/item/A as obj, mob/user as mob, params) - var/num_loaded = 0 - if(istype(A, /obj/item/ammo_box)) - var/obj/item/ammo_box/AM = A - for(var/obj/item/ammo_casing/AC in AM.stored_ammo) - var/didload = magazine.give_round(AC) - if(didload) - AM.stored_ammo -= AC - num_loaded++ - if(!didload || !magazine.multiload) - break - if(istype(A, /obj/item/ammo_casing)) - var/obj/item/ammo_casing/AC = A - if(magazine && magazine.give_round(AC)) - user.drop_item() - AC.loc = src - num_loaded++ +/obj/item/weapon/gun/projectile/shotgun/attackby(obj/item/A, mob/user, params) + . = ..() + if(.) + return + var/num_loaded = magazine.attackby(A, user, params, 1) if(num_loaded) to_chat(user, "You load [num_loaded] shell\s into \the [src]!") A.update_icon() @@ -40,8 +28,14 @@ /obj/item/weapon/gun/projectile/shotgun/chamber_round() return -/obj/item/weapon/gun/projectile/shotgun/attack_self(mob/living/user as mob) - if(recentpump) return +/obj/item/weapon/gun/projectile/shotgun/can_shoot() + if(!chambered) + return 0 + return (chambered.BB ? 1 : 0) + +/obj/item/weapon/gun/projectile/shotgun/attack_self(mob/living/user) + if(recentpump) + return pump(user) recentpump = 1 spawn(10) @@ -49,7 +43,7 @@ return -/obj/item/weapon/gun/projectile/shotgun/proc/pump(mob/M as mob) +/obj/item/weapon/gun/projectile/shotgun/proc/pump(mob/M) playsound(M, 'sound/weapons/shotgunpump.ogg', 60, 1) pump_unload(M) pump_reload(M) @@ -61,36 +55,41 @@ chambered.loc = get_turf(src)//Eject casing chambered.SpinAnimation(5, 1) chambered = null - if(in_chamber) - in_chamber = null /obj/item/weapon/gun/projectile/shotgun/proc/pump_reload(mob/M) - if(!magazine.ammo_count()) return 0 + if(!magazine.ammo_count()) + return 0 var/obj/item/ammo_casing/AC = magazine.get_round() //load next casing. chambered = AC /obj/item/weapon/gun/projectile/shotgun/examine(mob/user) - ..(user) + ..() if (chambered) to_chat(user, "A [chambered.BB ? "live" : "spent"] one is in the chamber.") /obj/item/weapon/gun/projectile/shotgun/isHandgun() //You cannot, in fact, holster a shotgun. return 0 +/obj/item/weapon/gun/projectile/shotgun/lethal + mag_type = /obj/item/ammo_box/magazine/internal/shot/lethal + // RIOT SHOTGUN // /obj/item/weapon/gun/projectile/shotgun/riot //for spawn in the armory name = "riot shotgun" desc = "A sturdy shotgun with a longer magazine and a fixed tactical stock designed for non-lethal riot control." icon_state = "riotshotgun" - mag_type = "/obj/item/ammo_box/magazine/internal/shotriot" + mag_type = /obj/item/ammo_box/magazine/internal/shotriot sawn_desc = "Come with me if you want to live." -/obj/item/weapon/gun/projectile/shotgun/riot/attackby(var/obj/item/A as obj, mob/user as mob, params) +/obj/item/weapon/gun/projectile/shotgun/riot/attackby(obj/item/A, mob/user, params) ..() - if(istype(A, /obj/item/weapon/circular_saw) || istype(A, /obj/item/weapon/melee/energy) || istype(A, /obj/item/weapon/gun/energy/plasmacutter)) + if(istype(A, /obj/item/weapon/circular_saw) || istype(A, /obj/item/weapon/gun/energy/plasmacutter)) sawoff(user) - + if(istype(A, /obj/item/weapon/melee/energy)) + var/obj/item/weapon/melee/energy/W = A + if(W.active) + sawoff(user) /////////////////////// // BOLT ACTION RIFLE // @@ -102,7 +101,7 @@ icon_state = "moistnugget" item_state = "moistnugget" slot_flags = 0 //no SLOT_BACK sprite, alas - mag_type = "/obj/item/ammo_box/magazine/internal/boltaction" + mag_type = /obj/item/ammo_box/magazine/internal/boltaction var/bolt_open = 0 /obj/item/weapon/gun/projectile/shotgun/boltaction/pump(mob/M) @@ -115,21 +114,21 @@ update_icon() //I.E. fix the desc return 1 -/obj/item/weapon/gun/projectile/shotgun/boltaction/attackby(var/obj/item/A as obj, mob/user as mob) +/obj/item/weapon/gun/projectile/shotgun/boltaction/attackby(obj/item/A, mob/user, params) if(!bolt_open) to_chat(user, "The bolt is closed!") return . = ..() /obj/item/weapon/gun/projectile/shotgun/boltaction/examine(mob/user) - ..(user) + ..() to_chat(user, "The bolt is [bolt_open ? "open" : "closed"].") /obj/item/weapon/gun/projectile/shotgun/boltaction/enchanted name = "enchanted bolt action rifle" desc = "Careful not to lose your head." var/guns_left = 30 - mag_type = "/obj/item/ammo_box/magazine/internal/boltaction/enchanted" + mag_type = /obj/item/ammo_box/magazine/internal/boltaction/enchanted /obj/item/weapon/gun/projectile/shotgun/boltaction/enchanted/New() ..() @@ -137,9 +136,10 @@ pump() /obj/item/weapon/gun/projectile/shotgun/boltaction/enchanted/dropped() + ..() guns_left = 0 -/obj/item/weapon/gun/projectile/shotgun/boltaction/enchanted/Fire(atom/target as mob|obj|turf|area, mob/living/carbon/user as mob|obj, params, reflex = 0) +/obj/item/weapon/gun/projectile/shotgun/boltaction/enchanted/shoot_live_shot(mob/living/user as mob|obj, pointblank = 0, mob/pbtarget = null, message = 1) ..() if(guns_left) var/obj/item/weapon/gun/projectile/shotgun/boltaction/enchanted/GUN = new @@ -156,6 +156,7 @@ /obj/item/ammo_box/magazine/internal/boltaction/enchanted max_ammo =1 + ammo_type = /obj/item/ammo_casing/a762/enchanted ///////////////////////////// // DOUBLE BARRELED SHOTGUN // @@ -166,22 +167,38 @@ desc = "A true classic." icon_state = "dshotgun" item_state = "shotgun" - w_class = 4.0 + w_class = 4 force = 10 flags = CONDUCT slot_flags = SLOT_BACK origin_tech = "combat=3;materials=1" - mag_type = "/obj/item/ammo_box/magazine/internal/cylinder/dualshot" + mag_type = /obj/item/ammo_box/magazine/internal/cylinder/dual sawn_desc = "Omar's coming!" + unique_rename = 1 + unique_reskin = 1 -/obj/item/weapon/gun/projectile/revolver/doublebarrel/attackby(var/obj/item/A as obj, mob/user as mob, params) +/obj/item/weapon/gun/projectile/revolver/doublebarrel/New() + ..() + options["Default"] = "dshotgun" + options["Dark Red Finish"] = "dshotgun-d" + options["Ash"] = "dshotgun-f" + options["Faded Grey"] = "dshotgun-g" + options["Maple"] = "dshotgun-l" + options["Rosewood"] = "dshotgun-p" + options["Cancel"] = null + +/obj/item/weapon/gun/projectile/revolver/doublebarrel/attackby(obj/item/A, mob/user, params) ..() if(istype(A, /obj/item/ammo_box) || istype(A, /obj/item/ammo_casing)) chamber_round() - if(istype(A, /obj/item/weapon/circular_saw) || istype(A, /obj/item/weapon/melee/energy) || istype(A, /obj/item/weapon/gun/energy/plasmacutter)) + if(istype(A, /obj/item/weapon/melee/energy)) + var/obj/item/weapon/melee/energy/W = A + if(W.active) + sawoff(user) + if(istype(A, /obj/item/weapon/circular_saw) || istype(A, /obj/item/weapon/gun/energy/plasmacutter)) sawoff(user) -/obj/item/weapon/gun/projectile/revolver/doublebarrel/attack_self(mob/living/user as mob) +/obj/item/weapon/gun/projectile/revolver/doublebarrel/attack_self(mob/living/user) var/num_unloaded = 0 while (get_ammo() > 0) var/obj/item/ammo_casing/CB @@ -205,14 +222,17 @@ desc = "Essentially a tube that aims shotgun shells." icon_state = "ishotgun" item_state = "shotgun" - w_class = 4.0 + w_class = 4 force = 10 slot_flags = null origin_tech = "combat=2;materials=2" - mag_type = "/obj/item/ammo_box/magazine/internal/cylinder/improvised" + mag_type = /obj/item/ammo_box/magazine/internal/shot/improvised sawn_desc = "I'm just here for the gasoline." + unique_rename = 0 + unique_reskin = 0 + var/slung = 0 -/obj/item/weapon/gun/projectile/revolver/doublebarrel/improvised/attackby(var/obj/item/A as obj, mob/user as mob, params) +/obj/item/weapon/gun/projectile/revolver/doublebarrel/improvised/attackby(obj/item/A, mob/user, params) ..() if(istype(A, /obj/item/stack/cable_coil) && !sawn_state) var/obj/item/stack/cable_coil/C = A @@ -220,21 +240,34 @@ slot_flags = SLOT_BACK icon_state = "ishotgunsling" to_chat(user, "You tie the lengths of cable to the shotgun, making a sling.") + slung = 1 update_icon() else to_chat(user, "You need at least ten lengths of cable if you want to make a sling.") return +/obj/item/weapon/gun/projectile/revolver/doublebarrel/improvised/update_icon() + ..() + if (slung && (slot_flags & SLOT_BELT) ) + slung = 0 + icon_state = "ishotgun-sawn" + // Sawing guns related procs // -/obj/item/weapon/gun/projectile/proc/blow_up(mob/user as mob) - if(get_ammo()) - afterattack(user, user) - playsound(user, fire_sound, 50, 1) - user.visible_message("The [src] goes off!", "The [src] goes off in your face!") - return +/obj/item/weapon/gun/projectile/proc/blow_up(mob/user) + . = 0 + for(var/obj/item/ammo_casing/AC in magazine.stored_ammo) + if(AC.BB) + process_fire(user, user,0) + . = 1 -/obj/item/weapon/gun/projectile/proc/sawoff(mob/user as mob) +/obj/item/weapon/gun/projectile/shotgun/blow_up(mob/user) + . = 0 + if(chambered && chambered.BB) + process_fire(user, user,0) + . = 1 + +/obj/item/weapon/gun/projectile/proc/sawoff(mob/user) if(sawn_state == SAWN_OFF) to_chat(user, "\The [src] is already shortened.") return @@ -256,7 +289,7 @@ name = "sawn-off [src.name]" desc = sawn_desc icon_state = "[icon_state]-sawn" - w_class = 3.0 + w_class = 3 item_state = "gun" slot_flags &= ~SLOT_BACK //you can't sling it on your back slot_flags |= SLOT_BELT //but you can wear it on your belt (poorly concealed under a trenchcoat, ideally) @@ -266,56 +299,64 @@ else sawn_state = SAWN_INTACT -/obj/item/weapon/gun/projectile/automatic/shotgun/bulldog - name = "\improper 'Bulldog' Shotgun" - desc = "A compact, mag-fed semi-automatic shotgun for combat in narrow corridors, nicknamed 'Bulldog' by boarding parties. Compatible only with specialized 8-round drum magazines." - icon_state = "bulldog" - item_state = "bulldog" - w_class = 3.0 - origin_tech = "combat=5;materials=4;syndicate=6" - mag_type = "/obj/item/ammo_box/magazine/m12g" - fire_sound = 'sound/weapons/Gunshot4.ogg' - can_suppress = 0 - burst_size = 1 - fire_delay = 0 - action_button_name = null +// Automatic Shotguns// -/obj/item/weapon/gun/projectile/automatic/shotgun/bulldog/New() +/obj/item/weapon/gun/projectile/shotgun/automatic/shoot_live_shot(mob/living/user as mob|obj) ..() - update_icon() - return - -/obj/item/weapon/gun/projectile/automatic/shotgun/bulldog/proc/update_magazine() - if(magazine) - overlays.Cut() - overlays += "[magazine.icon_state]" - return - -/obj/item/weapon/gun/projectile/automatic/shotgun/bulldog/update_icon() - overlays.Cut() - update_magazine() - icon_state = "bulldog[chambered ? "" : "-e"]" - return - -/obj/item/weapon/gun/projectile/automatic/shotgun/bulldog/afterattack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, flag) - ..() - empty_alarm() - return - -/obj/item/weapon/gun/projectile/shotgun/automatic/Fire(mob/living/user as mob|obj) - ..() - pump(user) - -// COMBAT SHOTGUN // + src.pump(user) /obj/item/weapon/gun/projectile/shotgun/automatic/combat name = "combat shotgun" desc = "A semi automatic shotgun with tactical furniture and a six-shell capacity underneath." icon_state = "cshotgun" origin_tech = "combat=5;materials=2" - mag_type = "/obj/item/ammo_box/magazine/internal/shotcom" + mag_type = /obj/item/ammo_box/magazine/internal/shot/com w_class = 5 +//Dual Feed Shotgun + +/obj/item/weapon/gun/projectile/shotgun/automatic/dual_tube + name = "cycler shotgun" + desc = "An advanced shotgun with two separate magazine tubes, allowing you to quickly toggle between ammo types." + icon_state = "cycler" + origin_tech = "combat=4;materials=2" + mag_type = /obj/item/ammo_box/magazine/internal/shot/tube + w_class = 5 + var/toggled = 0 + var/obj/item/ammo_box/magazine/internal/shot/alternate_magazine + +/obj/item/weapon/gun/projectile/shotgun/automatic/dual_tube/New() + ..() + if (!alternate_magazine) + alternate_magazine = new mag_type(src) + +/obj/item/weapon/gun/projectile/shotgun/automatic/dual_tube/attack_self(mob/living/user) + if(!chambered && magazine.contents.len) + pump() + else + toggle_tube(user) + +/obj/item/weapon/gun/projectile/shotgun/automatic/dual_tube/proc/toggle_tube(mob/living/user) + var/current_mag = magazine + var/alt_mag = alternate_magazine + magazine = alt_mag + alternate_magazine = current_mag + toggled = !toggled + if(toggled) + to_chat(user, "You switch to tube B.") + else + to_chat(user, "You switch to tube A.") + +/obj/item/weapon/gun/projectile/shotgun/automatic/dual_tube/AltClick(mob/living/user) + if(user.incapacitated() || !Adjacent(user) || !istype(user)) + return + pump() + +/obj/item/ammo_box/magazine/internal/shot/tube + name = "dual feed shotgun internal tube" + ammo_type = /obj/item/ammo_casing/shotgun/rubbershot + max_ammo = 4 + //caneshotgun /obj/item/weapon/gun/projectile/revolver/doublebarrel/improvised/cane @@ -330,7 +371,7 @@ can_unsuppress = 0 slot_flags = null origin_tech = "" // NO GIVAWAYS - mag_type = "/obj/item/ammo_box/magazine/internal/cylinder/improvised" + mag_type = /obj/item/ammo_box/magazine/internal/shot/improvised sawn_desc = "I'm sorry, but why did you saw your cane in the first place?" attack_verb = list("bludgeoned", "whacked", "disciplined", "thrashed") fire_sound = 'sound/weapons/Gunshot_silenced.ogg' @@ -354,4 +395,4 @@ to_chat(user, "\icon[src] That's [f_name]") if(desc) - to_chat(user, desc) \ No newline at end of file + to_chat(user, desc) diff --git a/code/modules/projectiles/guns/projectile/sniper.dm b/code/modules/projectiles/guns/projectile/sniper.dm index ee70bb3f80b..5033d0f8a0e 100644 --- a/code/modules/projectiles/guns/projectile/sniper.dm +++ b/code/modules/projectiles/guns/projectile/sniper.dm @@ -5,7 +5,7 @@ item_state = "sniper" recoil = 2 heavy_weapon = 1 - mag_type = "/obj/item/ammo_box/magazine/sniper_rounds" + mag_type = /obj/item/ammo_box/magazine/sniper_rounds fire_delay = 40 origin_tech = "combat=8" can_unsuppress = 1 @@ -35,18 +35,18 @@ max_ammo = 6 caliber = ".50" -/obj/item/ammo_casing/point50 - desc = "A .50 bullet casing." - caliber = ".50" - projectile_type = /obj/item/projectile/bullet/sniper - icon_state = ".50" - /obj/item/ammo_box/magazine/sniper_rounds/update_icon() if(ammo_count()) icon_state = "[initial(icon_state)]-ammo" else icon_state = "[initial(icon_state)]" +/obj/item/ammo_casing/point50 + desc = "A .50 bullet casing." + caliber = ".50" + projectile_type = /obj/item/projectile/bullet/sniper + icon_state = ".50" + /obj/item/projectile/bullet/sniper damage = 70 stun = 5 @@ -69,8 +69,6 @@ origin_tech = "combat=6;syndicate=3" ammo_type = /obj/item/ammo_casing/soporific max_ammo = 3 - caliber = ".50" - icon_state = ".50" /obj/item/ammo_casing/soporific desc = "A .50 bullet casing, specialised in sending the target to sleep, instead of hell." @@ -79,10 +77,10 @@ icon_state = ".50" /obj/item/projectile/bullet/sniper/soporific + armour_penetration = 0 nodamage = 1 stun = 0 weaken = 0 - armour_penetration = 0 breakthings = FALSE /obj/item/projectile/bullet/sniper/soporific/on_hit(atom/target, blocked = 0, hit_zone) @@ -102,8 +100,6 @@ origin_tech = "combat=7;syndicate=5" ammo_type = /obj/item/ammo_casing/haemorrhage max_ammo = 5 - caliber = ".50" - icon_state = ".50" /obj/item/ammo_casing/haemorrhage desc = "A .50 bullet casing, specialised in causing massive bloodloss" @@ -112,16 +108,16 @@ icon_state = ".50" /obj/item/projectile/bullet/sniper/haemorrhage + armour_penetration = 15 damage = 15 stun = 0 weaken = 0 breakthings = FALSE - armour_penetration = 15 /obj/item/projectile/bullet/sniper/haemorrhage/on_hit(atom/target, blocked = 0, hit_zone) if((blocked != 100) && istype(target, /mob/living/carbon/human)) var/mob/living/carbon/human/H = target - H.drip(1000) + H.drip(100) return ..() @@ -145,4 +141,4 @@ forcedodge = 1 stun = 0 weaken = 0 - breakthings = FALSE \ No newline at end of file + breakthings = FALSE diff --git a/code/modules/projectiles/guns/projectile/toy.dm b/code/modules/projectiles/guns/projectile/toy.dm index 7bb290f4f46..e533710b604 100644 --- a/code/modules/projectiles/guns/projectile/toy.dm +++ b/code/modules/projectiles/guns/projectile/toy.dm @@ -4,7 +4,8 @@ icon = 'icons/obj/toyguns.dmi' icon_state = "saber" item_state = "gun" - mag_type = "/obj/item/ammo_box/magazine/toy/smg" + mag_type = /obj/item/ammo_box/magazine/toy/smg + fire_sound = 'sound/weapons/Gunshot_smg.ogg' force = 0 throwforce = 0 burst_size = 3 @@ -12,15 +13,15 @@ clumsy_check = 0 needs_permit = 0 -/obj/item/weapon/gun/projectile/automatic/toy/process_chambered() - return ..(0, 1) +/obj/item/weapon/gun/projectile/automatic/toy/process_chamber(eject_casing = 0, empty_chamber = 1) + ..() /obj/item/weapon/gun/projectile/automatic/toy/pistol name = "foam force pistol" desc = "A small, easily concealable toy handgun. Ages 8 and up." icon_state = "pistol" w_class = 2 - mag_type = "/obj/item/ammo_box/magazine/toy/pistol" + mag_type = /obj/item/ammo_box/magazine/toy/pistol fire_sound = 'sound/weapons/Gunshot.ogg' can_suppress = 0 burst_size = 1 @@ -34,35 +35,12 @@ /obj/item/weapon/gun/projectile/automatic/toy/pistol/riot name = "foam force riot pistol" desc = "RIOT! Ages 8 and up." + mag_type = /obj/item/ammo_box/magazine/toy/pistol/riot /obj/item/weapon/gun/projectile/automatic/toy/pistol/riot/New() magazine = new /obj/item/ammo_box/magazine/toy/pistol/riot(src) ..() -/obj/item/weapon/gun/projectile/automatic/c20r/toy - name = "donksoft SMG" - desc = "A bullpup two-round burst toy SMG, designated 'C-20r'. Ages 8 and up." - icon = 'icons/obj/toyguns.dmi' - can_suppress = 0 - needs_permit = 0 - mag_type = "/obj/item/ammo_box/magazine/toy/smgm45" - -/obj/item/weapon/gun/projectile/automatic/c20r/toy/process_chambered() - return ..(0, 1) - -/obj/item/weapon/gun/projectile/automatic/l6_saw/toy - name = "donksoft LMG" - desc = "A heavily modified toy light machine gun, designated 'L6 SAW'. Ages 8 and up." - icon = 'icons/obj/toyguns.dmi' - can_suppress = 0 - needs_permit = 0 - mag_type = "/obj/item/ammo_box/magazine/toy/m762" - -/obj/item/weapon/gun/projectile/automatic/l6_saw/toy/process_chambered() - return ..(0, 1) - - - /obj/item/weapon/gun/projectile/shotgun/toy name = "foam force shotgun" desc = "A toy shotgun with wood furniture and a four-shell capacity underneath. Ages 8 and up." @@ -70,31 +48,52 @@ force = 0 throwforce = 0 origin_tech = null - mag_type = "/obj/item/ammo_box/magazine/internal/shot/toy" + mag_type = /obj/item/ammo_box/magazine/internal/shot/toy clumsy_check = 0 needs_permit = 0 -/obj/item/weapon/gun/projectile/shotgun/toy/pump_unload() - if(chambered)//We have a shell in the chamber - chambered = null - if(in_chamber) - in_chamber = null +/obj/item/weapon/gun/projectile/shotgun/toy/process_chamber() + ..() + if(chambered && !chambered.BB) + qdel(chambered) /obj/item/weapon/gun/projectile/shotgun/toy/crossbow name = "foam force crossbow" desc = "A weapon favored by many overactive children. Ages 8 and up." icon_state = "crossbow" item_state = "crossbow" - mag_type = "/obj/item/ammo_box/magazine/internal/shot/toy/crossbow" + mag_type = /obj/item/ammo_box/magazine/internal/shot/toy/crossbow fire_sound = 'sound/items/syringeproj.ogg' slot_flags = SLOT_BELT w_class = 2 +/obj/item/weapon/gun/projectile/automatic/c20r/toy + name = "donksoft SMG" + desc = "A bullpup two-round burst toy SMG, designated 'C-20r'. Ages 8 and up." + icon = 'icons/obj/toyguns.dmi' + can_suppress = 0 + needs_permit = 0 + mag_type = /obj/item/ammo_box/magazine/toy/smgm45 + +/obj/item/weapon/gun/projectile/automatic/c20r/toy/process_chamber(eject_casing = 0, empty_chamber = 1) + ..() + +/obj/item/weapon/gun/projectile/automatic/l6_saw/toy + name = "donksoft LMG" + desc = "A heavily modified toy light machine gun, designated 'L6 SAW'. Ages 8 and up." + icon = 'icons/obj/toyguns.dmi' + can_suppress = 0 + needs_permit = 0 + mag_type = /obj/item/ammo_box/magazine/toy/m762 + +/obj/item/weapon/gun/projectile/automatic/l6_saw/toy/process_chamber(eject_casing = 0, empty_chamber = 1) + ..() + /obj/item/weapon/gun/projectile/shotgun/toy/tommygun name = "tommy gun" desc = "Looks almost like the real thing! Great for practicing Drive-bys. Ages 8 and up." icon = 'icons/obj/gun.dmi' icon_state = "tommy" item_state = "shotgun" - mag_type = "/obj/item/ammo_box/magazine/internal/shot/toy/tommygun" + mag_type = /obj/item/ammo_box/magazine/internal/shot/toy/tommygun w_class = 2 diff --git a/code/modules/projectiles/guns/syringe_gun.dm b/code/modules/projectiles/guns/syringe_gun.dm index 0e43d550c85..45a590bd420 100644 --- a/code/modules/projectiles/guns/syringe_gun.dm +++ b/code/modules/projectiles/guns/syringe_gun.dm @@ -13,25 +13,41 @@ var/list/syringes = list() var/max_syringes = 1 -/obj/item/weapon/gun/syringe/process_chambered() - if(!syringes.len) return 0 +/obj/item/weapon/gun/syringe/New() + ..() + chambered = new /obj/item/ammo_casing/syringegun(src) + +/obj/item/weapon/gun/syringe/newshot() + if(!syringes.len) + return var/obj/item/weapon/reagent_containers/syringe/S = syringes[1] - if(!S) return 0 + if(!S) + return + + chambered.BB = new S.projectile_type (src) - in_chamber = new /obj/item/projectile/bullet/dart/syringe(src) S.reagents.trans_to(in_chamber, S.reagents.total_volume) - in_chamber.name = S.name + chambered.BB.name = S.name syringes.Remove(S) qdel(S) - return 1 + return + +/obj/item/weapon/gun/syringe/process_chamber() + return + +/obj/item/weapon/gun/syringe/afterattack(atom/target as mob|obj|turf, mob/living/user as mob|obj, params) + if(target == loc) + return + newshot() + ..() /obj/item/weapon/gun/syringe/examine(mob/user) - ..(user) + ..() to_chat(user, "Can hold [max_syringes] syringe\s. Has [syringes.len] syringe\s remaining.") - + /obj/item/weapon/gun/syringe/attack_self(mob/living/user as mob) if(!syringes.len) to_chat(user, "[src] is empty.") @@ -47,16 +63,17 @@ return 1 -/obj/item/weapon/gun/syringe/attackby(var/obj/item/A as obj, mob/user as mob, var/show_msg = 1, params) +/obj/item/weapon/gun/syringe/attackby(obj/item/A, mob/user, params, show_msg = 1) if(istype(A, /obj/item/weapon/reagent_containers/syringe)) if(syringes.len < max_syringes) - user.drop_item() + if(!user.unEquip(A)) + return to_chat(user, "You load [A] into \the [src]!") syringes.Add(A) A.forceMove(src) return 1 else - to_chat(usr, "[src] cannot hold more syringes.") + to_chat(user, "[src] cannot hold more syringes.") return 0 /obj/item/weapon/gun/syringe/rapidsyringe @@ -64,7 +81,7 @@ desc = "A modification of the syringe gun design, using a rotating cylinder to store up to six syringes." icon_state = "rapidsyringegun" max_syringes = 6 - + /obj/item/weapon/gun/syringe/syndicate name = "dart pistol" desc = "A small spring-loaded sidearm that functions identically to a syringe gun." diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 5a028a1084a..0cba3aa5fbf 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -1,353 +1,224 @@ -/* -#define BRUTE "brute" -#define BURN "burn" -#define TOX "tox" -#define OXY "oxy" -#define CLONE "clone" - -#define ADD "add" -#define SET "set" -*/ - /obj/item/projectile name = "projectile" icon = 'icons/obj/projectiles.dmi' icon_state = "bullet" - density = 1 + density = 0 unacidable = 1 anchored = 1 //There's a reason this is here, Mport. God fucking damn it -Agouri. Find&Fix by Pete. The reason this is here is to stop the curving of emitter shots. + flags = ABSTRACT pass_flags = PASSTABLE mouse_opacity = 0 hitsound = 'sound/weapons/pierce.ogg' - animate_movement = 0 - var/bumped = 0 //Prevents it from hitting more than one guy at once + var/hitsound_wall = "" + pressure_resistance = INFINITY var/def_zone = "" //Aiming at var/mob/firer = null//Who shot it - var/silenced = 0 //Attack message + var/obj/item/ammo_casing/ammo_casing = null + var/suppressed = 0 //Attack message var/yo = null var/xo = null var/current = null - var/obj/shot_from = null // the object which shot us var/atom/original = null // the original target clicked var/turf/starting = null // the projectile's starting turf var/list/permutated = list() // we've passed through these atoms, don't try to hit them again var/paused = FALSE //for suspending the projectile midair - var/p_x = 16 var/p_y = 16 // the pixel location of the tile that the player clicked. Default is the center + var/speed = 1 //Amount of deciseconds it takes for projectile to travel + var/Angle = 0 + var/spread = 0 //amount (in degrees) of projectile spread + var/legacy = 0 //legacy projectile system + animate_movement = 0 var/damage = 10 var/damage_type = BRUTE //BRUTE, BURN, TOX, OXY, CLONE are the only things that should be in here var/nodamage = 0 //Determines if the projectile will skip any damage inflictions var/flag = "bullet" //Defines what armor to use when it hits things. Must be set to bullet, laser, energy,or bomb //Cael - bio and rad are also valid var/projectile_type = "/obj/item/projectile" - var/kill_count = 50 //This will de-increment every process(). When 0, it will delete the projectile. - //Effects + var/range = 50 //This will de-increment every step. When 0, it will delete the projectile. + //Effects var/stun = 0 var/weaken = 0 var/paralyze = 0 var/irradiate = 0 - var/slur = 0 var/stutter = 0 + var/slur = 0 var/eyeblur = 0 var/drowsy = 0 var/stamina = 0 var/jitter = 0 var/embed = 0 // whether or not the projectile can embed itself in the mob - var/forcedodge = 0 + var/forcedodge = 0 //to pass through everything - var/range = 0 - var/proj_hit = 0 +/obj/item/projectile/New() + permutated = list() + return ..() - var/chatlog_attacks = 1 +/obj/item/projectile/proc/Range() + range-- + if(range <= 0 && loc) + on_range() - var/speed = 1 //Amount of deciseconds it takes for projectile to travel. Animation is adjusted accordingly. - var/Angle = 0 //For new projectiles - var/spread = 0 //Amount of degrees by which the projectiles will be spread DURING MOVEMENT. It exists for chaotic types of projectiles, like bees or something. - var/legacy = 0 //Use the legacy projectile system or new pixel movement? +/obj/item/projectile/proc/on_range() //if we want there to be effects when they reach the end of their range + qdel(src) - proc/delete() - // Garbage collect the projectiles - loc = null - - proc/Range() - if(range) - range-- - if(range <= 0) - on_range() - else - return - - proc/on_range() //if we want there to be effects when they reach the end of their range - proj_hit = 1 - del(src) - - proc/on_hit(var/atom/target, var/blocked = 0, var/hit_zone) - if(!isliving(target)) return 0 - if(isanimal(target)) return 0 - var/mob/living/L = target - return L.apply_effects(stun, weaken, paralyze, irradiate, slur, stutter, eyeblur, drowsy, blocked, stamina, jitter) - - proc/OnFired() //if assigned, allows for code when the projectile gets fired - return 1 - - proc/check_fire(var/mob/living/target as mob, var/mob/living/user as mob) //Checks if you can hit them or not. - if(!istype(target) || !istype(user)) - return 0 - var/obj/item/projectile/test/in_chamber = new /obj/item/projectile/test(get_step_to(user,target)) //Making the test.... - in_chamber.target = target - in_chamber.flags = flags //Set the flags... - in_chamber.pass_flags = pass_flags //And the pass flags to that of the real projectile... - in_chamber.firer = user - var/output = in_chamber.process() //Test it! - del(in_chamber) //No need for it anymore - return output //Send it back to the gun! - - Bump(atom/A as mob|obj|turf|area) - if(A == firer) - loc = A.loc - return 0 //cannot shoot yourself - - if(bumped) - return 1 - bumped = 1 - if(firer && istype(A, /mob)) - var/mob/M = A - if(!istype(A, /mob/living)) - loc = A.loc - return 0// nope.avi - - var/reagent_note - if(reagents && reagents.reagent_list) - reagent_note = " REAGENTS:" - for(var/datum/reagent/R in reagents.reagent_list) - reagent_note += R.id + " (" - reagent_note += num2text(R.volume) + ") " - //Lower accurancy/longer range tradeoff. Distance matters a lot here, so at - // close distance, actually RAISE the chance to hit. - var/distance = get_dist(get_turf(A), starting) // Get the distance between the turf shot from and the mob we hit and use that for the calculations. - def_zone = ran_zone(def_zone, max(100-(7*distance), 5)) //Lower accurancy/longer range tradeoff. 7 is a balanced number to use. - /* - if(!def_zone) - visible_message("\blue \The [src] misses [M] narrowly!") - forcedodge = -1 - else - */ - if(ishuman(A)) - var/mob/living/carbon/human/H = A - var/obj/item/organ/external/organ = H.get_organ(check_zone(def_zone)) - if(isnull(organ)) - return - if(silenced) - playsound(loc, hitsound, 5, 1, -1) - to_chat(M, "\red You've been shot in the [parse_zone(def_zone)] by the [src.name]!") - else - playsound(loc, hitsound, 20, 1, -1) - visible_message("\red [A.name] is hit by the [src.name] in the [parse_zone(def_zone)]!")//X has fired Y is now given by the guns so you cant tell who shot you if you could not see the shooter - if(istype(firer, /mob)) - M.attack_log += "\[[time_stamp()]\] [key_name(firer)] shot [M]/[M.ckey] with a [src.type][reagent_note]" - firer.attack_log += "\[[time_stamp()]\] [key_name(firer)] shot [M]/[M.ckey] with a [src.type][reagent_note]" - if(M.ckey && chatlog_attacks) - msg_admin_attack("[key_name_admin(firer)] shot [key_name_admin(M)] with a [src][reagent_note]") //BS12 EDIT ALG - if(!iscarbon(firer)) - M.LAssailant = null - else - M.LAssailant = firer - else - M.attack_log += "\[[time_stamp()]\] UNKNOWN SUBJECT (No longer exists) shot [key_name(M)] with a [src][reagent_note]" - if(M.ckey && chatlog_attacks) - msg_admin_attack("UNKNOWN shot [key_name_admin(M)] with a [src][reagent_note] (JMP)") //BS12 EDIT ALG - - spawn(0) - if(A) - var/turf/new_loc = get_turf(A) - var/permutation = A.bullet_act(src, def_zone) // searches for return value, could be deleted after run so check A isn't null - if(permutation == -1 || forcedodge)// the bullet passes through a dense object! - spawn(1) - bumped = 0 // reset bumped variable... after a delay. We don't want the projectile to hit AGAIN. Fixes deflecting projectiles. - loc = new_loc - permutated.Add(A) - return 0 - if(istype(A,/turf)) - for(var/obj/O in A) - O.bullet_act(src) - for(var/mob/M in A) - M.bullet_act(src, def_zone) - if(!istype(src, /obj/item/projectile/beam/lightning)) - density = 0 - invisibility = 101 - del(src) - return 1 - - - CanPass(atom/movable/mover, turf/target, height=0, air_group=0) - if(air_group || (height==0)) return 1 - - if(istype(mover, /obj/item/projectile)) - return prob(95) - else - return 1 - - - Process_Spacemove(var/movement_dir = 0) - return 1 //Bullets don't drift in space - - - - process(var/setAngle) - if(setAngle) Angle = setAngle - if(!legacy) - spawn() //New projectile system - while(loc) - if(kill_count < 1) - del(src) - return - if(!paused) - kill_count-- - if((!( current ) || loc == current)) - current = locate(Clamp(x+xo,1,world.maxx),Clamp(y+yo,1,world.maxy),z) - - if(!Angle) - Angle=round(Get_Angle(src,current)) -// to_chat(world, "[Angle] angle") - //overlays.Cut() - //var/icon/I=new(initial(icon),icon_state) //using initial(icon) makes sure that the angle for that is reset as well - //I.Turn(Angle) - //I.DrawBox(rgb(255,0,0,50),1,1,32,32) - //icon = I - if(spread) //Chaotic spread - Angle += (rand() - 0.5) * spread - var/matrix/M = new//matrix(transform) - M.Turn(Angle) - transform = M - - var/Pixel_x=round(sin(Angle)+16*sin(Angle)*2) - var/Pixel_y=round(cos(Angle)+16*cos(Angle)*2) - var/pixel_x_offset = pixel_x + Pixel_x - var/pixel_y_offset = pixel_y + Pixel_y - var/new_x = x - var/new_y = y - //Not sure if using whiles for this is good - while(pixel_x_offset > 16) -// to_chat(world, "Pre-adjust coords (x++): xy [pixel_x] xy offset [pixel_x_offset]") - pixel_x_offset -= 32 - pixel_x -= 32 - new_x++// x++ - while(pixel_x_offset < -16) -// to_chat(world, "Pre-adjust coords (x--): xy [pixel_x] xy offset [pixel_x_offset]") - pixel_x_offset += 32 - pixel_x += 32 - new_x-- - - while(pixel_y_offset > 16) -// to_chat(world, "Pre-adjust coords (y++): py [pixel_y] py offset [pixel_y_offset]") - pixel_y_offset -= 32 - pixel_y -= 32 - new_y++ - while(pixel_y_offset < -16) -// to_chat(world, "Pre-adjust coords (y--): py [pixel_y] py offset [pixel_y_offset]") - pixel_y_offset += 32 - pixel_y += 32 - new_y-- - - speed = round(speed) //Just in case. - step_towards(src, locate(new_x, new_y, z)) //Original projectiles stepped towards 'current' - if(speed <= 1) //We should really only animate at speed 2 - pixel_x = pixel_x_offset - pixel_y = pixel_y_offset - else - animate(src, pixel_x = pixel_x_offset, pixel_y = pixel_y_offset, time = max(1, (speed <= 3 ? speed - 1 : speed))) - - /* - var/turf/T = get_turf(src) - if(T) - T.color = "#6666FF" - spawn(10) - T.color = initial(T.color) - */ - - if(!bumped && ((original && original.layer>=2.75) || ismob(original))) - if(loc == get_turf(original)) - if(!(original in permutated)) - Bump(original) - Range() - sleep(max(1, speed)) - else - spawn() //Old projectile system - while(loc) - if(kill_count < 1) - del(src) - return - if(!paused) - kill_count-- - if((!( current ) || loc == current)) - current = locate(Clamp(x+xo,1,world.maxx),Clamp(y+yo,1,world.maxy),z) - if(!Angle) - Angle=round(Get_Angle(src,current)) - var/matrix/M = new//matrix(transform) - M.Turn(Angle) - transform = M //So there's no need to give icons directions again - step_towards(src, current) - if(!bumped && ((original && original.layer>=2.75) || ismob(original))) - if(loc == get_turf(original)) - if(!(original in permutated)) - Bump(original) - Range() - sleep(1) - - proc/dumbfire(var/dir) - current = get_ranged_target_turf(src, dir, world.maxx) //world.maxx is the range. Not sure how to handle this better. - process() - -/obj/item/projectile/test //Used to see if you can hit them. - invisibility = 101 //Nope! Can't see me! - yo = null - xo = null - var/target = null - var/result = 0 //To pass the message back to the gun. - - Bump(atom/A as mob|obj|turf|area) - if(A == firer) - loc = A.loc - return //cannot shoot yourself - if(istype(A, /obj/item/projectile)) - return - if(istype(A, /mob/living) || istype(A, /obj/mecha) || istype(A, /obj/spacepod) || istype(A, /obj/vehicle)) - result = 2 //We hit someone, return 1! - return - result = 1 - return - - process() - var/turf/curloc = get_turf(src) - var/turf/targloc = get_turf(target) - if(!curloc || !targloc) - return 0 - yo = targloc.y - curloc.y - xo = targloc.x - curloc.x - target = targloc - while(src) //Loop on through! - if(result) - return (result - 1) - if((!( target ) || loc == target)) - target = locate(min(max(x + xo, 1), world.maxx), min(max(y + yo, 1), world.maxy), z) //Finding the target turf at map edge - step_towards(src, target) - var/mob/living/M = locate() in get_turf(src) - if(istype(M)) //If there is someting living... - return 1 //Return 1 - else - M = locate() in get_step(src,target) - if(istype(M)) - return 1 - -/proc/check_trajectory(atom/target as mob|obj, atom/firer as mob|obj, var/pass_flags=PASSTABLE|PASSGLASS|PASSGRILLE, flags=null) //Checks if you can hit them or not. - if(!istype(target) || !istype(firer)) +/obj/item/projectile/proc/on_hit(atom/target, blocked = 0, hit_zone) + if(!isliving(target)) return 0 - var/obj/item/projectile/test/trace = new /obj/item/projectile/test(get_turf(firer)) //Making the test.... - trace.target = target - if(!isnull(flags)) - trace.flags = flags //Set the flags... - trace.pass_flags = pass_flags //And the pass flags to that of the real projectile... - var/output = trace.process() //Test it! - qdel(trace) //No need for it anymore - return output //Send it back to the gun! + var/mob/living/L = target + if(blocked < 100) // not completely blocked + var/organ_hit_text = "" + if(L.has_limbs) + organ_hit_text = " in \the [parse_zone(def_zone)]" + if(suppressed) + playsound(loc, hitsound, 5, 1, -1) + to_chat(L, "You're shot by \a [src][organ_hit_text]!") + else + if(hitsound) + var/volume = vol_by_damage() + playsound(loc, hitsound, volume, 1, -1) + L.visible_message("[L] is hit by \a [src][organ_hit_text]!", \ + "[L] is hit by \a [src][organ_hit_text]!") //X has fired Y is now given by the guns so you cant tell who shot you if you could not see the shooter + L.on_hit(type) + + var/reagent_note + if(reagents && reagents.reagent_list) + reagent_note = " REAGENTS:" + for(var/datum/reagent/R in reagents.reagent_list) + reagent_note += R.id + " (" + reagent_note += num2text(R.volume) + ") " + add_logs(L, firer, "shot", src, reagent_note) + return L.apply_effects(stun, weaken, paralyze, irradiate, slur, stutter, eyeblur, drowsy, blocked, stamina, jitter) + +/obj/item/projectile/proc/vol_by_damage() + if(src.damage) + return Clamp((src.damage) * 0.67, 30, 100)// Multiply projectile damage by 0.67, then clamp the value between 30 and 100 + else + return 50 //if the projectile doesn't do damage, play its hitsound at 50% volume + +/obj/item/projectile/Bump(atom/A, yes) + if(!yes) //prevents double bumps. + return + if(firer) + if(A == firer || (A == firer.loc && istype(A, /obj/mecha))) //cannot shoot yourself or your mech + loc = A.loc + return 0 + + var/distance = get_dist(get_turf(A), starting) // Get the distance between the turf shot from and the mob we hit and use that for the calculations. + def_zone = ran_zone(def_zone, max(100-(7*distance), 5)) //Lower accurancy/longer range tradeoff. 7 is a balanced number to use. + + if(ishuman(A)) + var/mob/living/carbon/human/H = A + var/obj/item/organ/external/organ = H.get_organ(check_zone(def_zone)) + if(isnull(organ)) + return + else if(isturf(A) && hitsound_wall) + var/volume = Clamp(vol_by_damage() + 20, 0, 100) + if(suppressed) + volume = 5 + playsound(loc, hitsound_wall, volume, 1, -1) + + var/turf/target_turf = get_turf(A) + + var/permutation = A.bullet_act(src, def_zone) // searches for return value, could be deleted after run so check A isn't null + if(permutation == -1 || forcedodge)// the bullet passes through a dense object! + loc = target_turf + if(A) + permutated.Add(A) + return 0 + else + if(A && A.density && !ismob(A) && !(A.flags & ON_BORDER)) //if we hit a dense non-border obj or dense turf then we also hit one of the mobs on that tile. + var/list/mobs_list = list() + for(var/mob/living/L in target_turf) + mobs_list += L + if(mobs_list.len) + var/mob/living/picked_mob = pick(mobs_list) + picked_mob.bullet_act(src, def_zone) + qdel(src) + +/obj/item/projectile/Process_Spacemove(var/movement_dir = 0) + return 1 //Bullets don't drift in space + +/obj/item/projectile/proc/fire(var/setAngle) + if(setAngle) + Angle = setAngle + if(!legacy) //new projectiles + set waitfor = 0 + while(loc) + if(!paused) + if((!( current ) || loc == current)) + current = locate(Clamp(x+xo,1,world.maxx),Clamp(y+yo,1,world.maxy),z) + + if(!Angle) + Angle=round(Get_Angle(src,current)) + if(spread) + Angle += (rand() - 0.5) * spread + var/matrix/M = new + M.Turn(Angle) + transform = M + + var/Pixel_x=round(sin(Angle)+16*sin(Angle)*2) + var/Pixel_y=round(cos(Angle)+16*cos(Angle)*2) + var/pixel_x_offset = pixel_x + Pixel_x + var/pixel_y_offset = pixel_y + Pixel_y + var/new_x = x + var/new_y = y + + while(pixel_x_offset > 16) + pixel_x_offset -= 32 + pixel_x -= 32 + new_x++// x++ + while(pixel_x_offset < -16) + pixel_x_offset += 32 + pixel_x += 32 + new_x-- + + while(pixel_y_offset > 16) + pixel_y_offset -= 32 + pixel_y -= 32 + new_y++ + while(pixel_y_offset < -16) + pixel_y_offset += 32 + pixel_y += 32 + new_y-- + + speed = round(speed) + step_towards(src, locate(new_x, new_y, z)) + if(speed <= 1) + pixel_x = pixel_x_offset + pixel_y = pixel_y_offset + else + animate(src, pixel_x = pixel_x_offset, pixel_y = pixel_y_offset, time = max(1, (speed <= 3 ? speed - 1 : speed))) + + if(original && (original.layer>=2.75) || ismob(original)) + if(loc == get_turf(original)) + if(!(original in permutated)) + Bump(original, 1) + Range() + sleep(max(1, speed)) + else //old projectile system + set waitfor = 0 + while(loc) + if(!paused) + if((!( current ) || loc == current)) + current = locate(Clamp(x+xo,1,world.maxx),Clamp(y+yo,1,world.maxy),z) + step_towards(src, current) + if(original && (original.layer>=2.75) || ismob(original)) + if(loc == get_turf(original)) + if(!(original in permutated)) + Bump(original, 1) + Range() + sleep(1) + +obj/item/projectile/Crossed(atom/movable/AM) //A mob moving on a tile with a projectile is hit by it. + ..() + if(isliving(AM) && AM.density && !checkpass(PASSMOB)) + Bump(AM, 1) + +/obj/item/projectile/Destroy() + ammo_casing = null + return ..() + +/obj/item/projectile/proc/dumbfire(var/dir) + current = get_ranged_target_turf(src, dir, world.maxx) //world.maxx is the range. Not sure how to handle this better. + fire() diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm index e86d8e7a739..b11c4f66961 100644 --- a/code/modules/projectiles/projectile/beams.dm +++ b/code/modules/projectiles/projectile/beams.dm @@ -5,50 +5,27 @@ damage = 20 damage_type = BURN hitsound = 'sound/weapons/sear.ogg' + hitsound_wall = 'sound/weapons/effects/searwall.ogg' flag = "laser" eyeblur = 2 -/obj/item/projectile/practice - name = "laser" - icon_state = "laser" - pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE +/obj/item/projectile/beam/laser + +/obj/item/projectile/beam/laser/heavylaser + name = "heavy laser" + icon_state = "heavylaser" + damage = 40 + +/obj/item/projectile/beam/practice + name = "practice laser" damage = 0 - hitsound = null - damage_type = BURN - flag = "laser" - eyeblur = 2 - chatlog_attacks = 0 + nodamage = 1 /obj/item/projectile/beam/scatter name = "laser pellet" icon_state = "scatterlaser" damage = 5 - -/obj/item/projectile/beam/heavylaser - name = "heavy laser" - icon_state = "heavylaser" - damage = 40 - -/obj/item/projectile/beam/sniper - name = "sniper beam" - icon_state = "sniperlaser" - damage = 60 - stun = 5 - weaken = 5 - stutter = 5 - - -/obj/item/projectile/beam/immolator - name = "immolation beam" - -/obj/item/projectile/beam/immolator/on_hit(var/atom/target, var/blocked = 0) - . = ..() - if(istype(target, /mob/living/carbon)) - var/mob/living/carbon/M = target - M.adjust_fire_stacks(1) - M.IgniteMob() - /obj/item/projectile/beam/xray name = "xray beam" icon_state = "xray" @@ -82,41 +59,80 @@ name = "emitter beam" icon_state = "emitter" damage = 30 + legacy = 1 + animate_movement = SLIDE_STEPS /obj/item/projectile/beam/emitter/singularity_pull() return //don't want the emitters to miss -/obj/item/projectile/lasertag +/obj/item/projectile/beam/lasertag name = "laser tag beam" icon_state = "omnilaser" hitsound = null damage = 0 damage_type = STAMINA flag = "laser" - pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE var/suit_types = list(/obj/item/clothing/suit/redtag, /obj/item/clothing/suit/bluetag) - chatlog_attacks = 0 -/obj/item/projectile/lasertag/on_hit(var/atom/target, var/blocked = 0) - if(istype(target, /mob/living/carbon/human)) +/obj/item/projectile/beam/lasertag/on_hit(atom/target, blocked = 0) + . = ..() + if(ishuman(target)) var/mob/living/carbon/human/M = target if(istype(M.wear_suit)) if(M.wear_suit.type in suit_types) M.adjustStaminaLoss(34) return 1 -/obj/item/projectile/lasertag/omni +/obj/item/projectile/beam/lasertag/omni name = "laser tag beam" icon_state = "omnilaser" -/obj/item/projectile/lasertag/red +/obj/item/projectile/beam/lasertag/red icon_state = "laser" suit_types = list(/obj/item/clothing/suit/bluetag) -/obj/item/projectile/lasertag/blue +/obj/item/projectile/beam/lasertag/blue icon_state = "bluelaser" suit_types = list(/obj/item/clothing/suit/redtag) +/obj/item/projectile/beam/sniper + name = "sniper beam" + icon_state = "sniperlaser" + damage = 60 + stun = 5 + weaken = 5 + stutter = 5 + +/obj/item/projectile/beam/immolator + name = "immolation beam" + +/obj/item/projectile/beam/immolator/on_hit(var/atom/target, var/blocked = 0) + . = ..() + if(istype(target, /mob/living/carbon)) + var/mob/living/carbon/M = target + M.adjust_fire_stacks(1) + M.IgniteMob() + +/obj/item/projectile/beam/instakill + name = "instagib laser" + icon_state = "purple_laser" + damage = 200 + damage_type = BURN + +/obj/item/projectile/beam/instakill/blue + icon_state = "blue_laser" + +/obj/item/projectile/beam/instakill/red + icon_state = "red_laser" + +/obj/item/projectile/beam/instakill/on_hit(atom/target) + . = ..() + if(iscarbon(target)) + var/mob/living/carbon/M = target + M.visible_message("[M] explodes into a shower of gibs!") + M.gib() + +/* //THIS CODE MAKES ME WANT TO STAB MY EYES OUT /obj/item/projectile/beam/lightning invisibility = 101 name = "lightning" @@ -133,12 +149,12 @@ kill_count = 6 var/bullet_acted = 0 - Bump(var/atom/A) +/obj/item/projectile/beam/lightning/Bump(var/atom/A) if((A != firer) && !bullet_acted) A.bullet_act(src) bullet_acted = 1 - proc/adjustAngle(angle) +/obj/item/projectile/beam/lightning/proc/adjustAngle(angle) angle = round(angle) + 45 if(angle > 180) angle -= 180 @@ -146,14 +162,9 @@ angle += 180 if(!angle) angle = 1 - /*if(angle < 0) - //angle = (round(abs(get_angle(A, user))) + 45) - 90 - angle = round(angle) + 45 + 180 - else - angle = round(angle) + 45*/ return angle - process() +/obj/item/projectile/beam/lightning/process() var/first = 1 //So we don't make the overlay in the same tile as the firer var/broke = 0 var/broken @@ -239,16 +250,11 @@ f.break_tile() f.hotspot_expose(1000,CELL_VOLUME) if((x == 1 || x == world.maxx || y == 1 || y == world.maxy)) -// to_chat(world, "deleting") - //qdel(src) //Delete if it passes the world edge broken = 1 return if(kill_count < 1) -// to_chat(world, "deleting") - //qdel(src) broken = 1 kill_count-- -// to_chat(world, "[x] [y]") if(!bumped && !isturf(original)) if(loc == get_turf(original)) if(!(original in permutated)) @@ -263,7 +269,6 @@ Bump(original) first = 0 if(broken) -// to_chat(world, "breaking") break else last = get_turf(src.loc) @@ -273,13 +278,9 @@ icon_state = "[tang]" qdel(src) return - /*cleanup(reference) //Waits .3 seconds then removes the overlay. -// to_chat(world, "setting invisibility") - sleep(50) - src.invisibility = 101 - return*/ - on_hit(atom/target, blocked = 0) +/obj/item/projectile/beam/lightning/on_hit(atom/target, blocked = 0) if(istype(target, /mob/living)) var/mob/living/M = target M.playsound_local(src, "explosion", 50, 1) - ..() \ No newline at end of file + ..() +*/ diff --git a/code/modules/projectiles/projectile/bullets.dm b/code/modules/projectiles/projectile/bullets.dm index 879bbf7fad0..f3a676d962f 100644 --- a/code/modules/projectiles/projectile/bullets.dm +++ b/code/modules/projectiles/projectile/bullets.dm @@ -3,21 +3,10 @@ icon_state = "bullet" damage = 60 damage_type = BRUTE - nodamage = 0 flag = "bullet" embed = 1 sharp = 1 - -/obj/item/projectile/bullet/cap - name = "cap" - damage = 0 - nodamage = 1 - embed = 0 - sharp = 0 - -/obj/item/projectile/bullet/cap/process() - loc = null - del(src) + hitsound_wall = "ricochet" /obj/item/projectile/bullet/weakbullet //beanbag, heavy stamina damage damage = 5 @@ -27,6 +16,26 @@ embed = 0 sharp = 0 +/obj/item/projectile/bullet/weakbullet/booze + embed = 0 + +/obj/item/projectile/bullet/weakbullet/booze/on_hit(atom/target, blocked = 0) + if(..(target, blocked)) + var/mob/living/M = target + M.dizziness += 20 + M.slurring += 20 + M.confused += 20 + M.eye_blurry += 20 + M.drowsyness += 20 + for(var/datum/reagent/ethanol/A in M.reagents.reagent_list) + M.AdjustParalysis(2) + M.dizziness += 10 + M.slurring += 10 + M.confused += 10 + M.eye_blurry += 10 + M.drowsyness += 10 + A.volume += 5 //Because we can + /obj/item/projectile/bullet/weakbullet2 //detective revolver instastuns, but multiple shots are better for keeping punks down damage = 5 weaken = 3 @@ -44,6 +53,15 @@ damage = 15 damage_type = TOX +/obj/item/projectile/bullet/incendiary + +/obj/item/projectile/bullet/incendiary/on_hit(var/atom/target, var/blocked = 0) + ..() + if(istype(target, /mob/living/carbon)) + var/mob/living/carbon/M = target + M.adjust_fire_stacks(1) + M.IgniteMob() + /obj/item/projectile/bullet/incendiary/firebullet damage = 10 @@ -55,15 +73,34 @@ name = "pellet" damage = 15 -/obj/item/projectile/bullet/blank - name = "blankshot" - nodamage = 1 - /obj/item/projectile/bullet/pellet/weak + damage = 6 + +/obj/item/projectile/bullet/pellet/weak/New() + range = rand(8) + +/obj/item/projectile/bullet/pellet/weak/on_range() + var/datum/effect_system/spark_spread/sparks = new /datum/effect_system/spark_spread + sparks.set_up(1, 1, src) + sparks.start() + ..() + +/obj/item/projectile/bullet/pellet/overload damage = 3 -/obj/item/projectile/bullet/pellet/random/New() - damage = rand(10) +/obj/item/projectile/bullet/pellet/overload/New() + range = rand(10) + +/obj/item/projectile/bullet/pellet/overload/on_hit(atom/target, blocked = 0) + ..() + explosion(target, 0, 0, 2) + +/obj/item/projectile/bullet/pellet/overload/on_range() + explosion(src, 0, 0, 2) + var/datum/effect_system/spark_spread/sparks = new /datum/effect_system/spark_spread + sparks.set_up(3, 3, src) + sparks.start() + ..() /obj/item/projectile/bullet/midbullet damage = 20 @@ -89,39 +126,11 @@ weaken = 5 stutter = 5 jitter = 20 - icon_state = "spark" range = 7 embed = 0 sharp = 0 - - -/obj/item/projectile/bullet/weakbullet/booze - embed = 0 - on_hit(var/atom/target, var/blocked = 0) - if(..(target, blocked)) - var/mob/living/M = target - M.dizziness += 20 - M.slurring += 20 - M.confused += 20 - M.eye_blurry += 20 - M.drowsyness += 20 - for(var/datum/reagent/ethanol/A in M.reagents.reagent_list) - M.AdjustParalysis(2) - M.dizziness += 10 - M.slurring += 10 - M.confused += 10 - M.eye_blurry += 10 - M.drowsyness += 10 - A.volume += 5 //Because we can - -/obj/item/projectile/bullet/incendiary - -/obj/item/projectile/bullet/incendiary/on_hit(var/atom/target, var/blocked = 0) - ..() - if(istype(target, /mob/living/carbon)) - var/mob/living/carbon/M = target - M.adjust_fire_stacks(1) - M.IgniteMob() + icon_state = "spark" + color = "#FFFF00" /obj/item/projectile/bullet/incendiary/shell name = "incendiary slug" @@ -130,8 +139,9 @@ /obj/item/projectile/bullet/incendiary/shell/Move() ..() var/turf/location = get_turf(src) - new /obj/effect/hotspot(location) - location.hotspot_expose(700, 50, 1) + if(location) + new /obj/effect/hotspot(location) + location.hotspot_expose(700, 50, 1) /obj/item/projectile/bullet/incendiary/shell/dragonsbreath name = "dragonsbreath round" @@ -157,7 +167,25 @@ ..() SpinAnimation() +/obj/item/projectile/bullet/meteorshot/weak + damage = 10 + weaken = 4 + stun = 4 +/obj/item/projectile/bullet/honker + damage = 0 + weaken = 5 + stun = 5 + forcedodge = 1 + nodamage = 1 + hitsound = 'sound/items/bikehorn.ogg' + icon = 'icons/obj/hydroponics/harvest.dmi' + icon_state = "banana" + range = 200 + +/obj/item/projectile/bullet/honker/New() + ..() + SpinAnimation() /obj/item/projectile/bullet/mime damage = 0 @@ -168,7 +196,7 @@ /obj/item/projectile/bullet/mime/on_hit(var/atom/target, var/blocked = 0) ..(target, blocked) - if(istype(target, /mob/living/carbon)) + if(iscarbon(target)) var/mob/living/carbon/M = target M.silent = max(M.silent, 10) else if(istype(target, /obj/mecha/combat/honker)) @@ -184,32 +212,38 @@ damage = 6 embed = 0 sharp = 0 + var/piercing = 0 - New() - ..() - flags |= NOREACT - create_reagents(50) +/obj/item/projectile/bullet/dart/New() + ..() + flags |= NOREACT + create_reagents(50) - on_hit(var/atom/target, var/blocked = 0, var/hit_zone) - if(istype(target, /mob/living/carbon)) - var/mob/living/carbon/M = target +/obj/item/projectile/bullet/dart/on_hit(var/atom/target, var/blocked = 0, var/hit_zone) + if(iscarbon(target)) + var/mob/living/carbon/M = target + if(blocked != 100) if(M.can_inject(null,0,hit_zone)) // Pass the hit zone to see if it can inject by whether it hit the head or the body. + ..() reagents.reaction(M, INGEST) reagents.trans_to(M, reagents.total_volume) return 1 else + blocked = 100 target.visible_message("The [name] was deflected!", \ - "You were protected against the [name]!") - flags &= ~NOREACT - reagents.handle_reactions() - return 1 + "You were protected against the [name]!") + ..(target, blocked, hit_zone) + flags &= ~NOREACT + reagents.handle_reactions() + return 1 /obj/item/projectile/bullet/dart/metalfoam - New() - ..() - reagents.add_reagent("aluminum", 15) - reagents.add_reagent("fluorosurfactant", 5) - reagents.add_reagent("sacid", 5) + +/obj/item/projectile/bullet/dart/metalfoam/New() + ..() + reagents.add_reagent("aluminum", 15) + reagents.add_reagent("fluorosurfactant", 5) + reagents.add_reagent("sacid", 5) //This one is for future syringe guns update /obj/item/projectile/bullet/dart/syringe @@ -218,9 +252,9 @@ icon_state = "syringeproj" /obj/item/projectile/bullet/dart/syringe/tranquilizer - New() - ..() - reagents.add_reagent("haloperidol", 15) +/obj/item/projectile/bullet/dart/syringe/tranquilizer/New() + ..() + reagents.add_reagent("haloperidol", 15) /obj/item/projectile/bullet/neurotoxin name = "neurotoxin spit" @@ -231,5 +265,17 @@ /obj/item/projectile/bullet/neurotoxin/on_hit(var/atom/target, var/blocked = 0) if(isalien(target)) - return 0 - ..() // Execute the rest of the code. + weaken = 0 + nodamage = 1 + . = ..() // Execute the rest of the code. + +/obj/item/projectile/bullet/cap + name = "cap" + damage = 0 + nodamage = 1 + embed = 0 + sharp = 0 + +/obj/item/projectile/bullet/cap/fire() + loc = null + qdel(src) diff --git a/code/modules/projectiles/projectile/energy.dm b/code/modules/projectiles/projectile/energy.dm index b5665a6af1c..b023d77de52 100644 --- a/code/modules/projectiles/projectile/energy.dm +++ b/code/modules/projectiles/projectile/energy.dm @@ -5,7 +5,6 @@ damage_type = BURN flag = "energy" - /obj/item/projectile/energy/electrode name = "electrode" icon_state = "spark" @@ -20,26 +19,23 @@ //Damage will be handled on the MOB side, to prevent window shattering. /obj/item/projectile/energy/electrode/on_hit(var/atom/target, var/blocked = 0) - if(!proj_hit) - if(!ismob(target) || blocked >= 2) //Fully blocked by mob or collided with dense object - burst into sparks! - var/datum/effect/system/spark_spread/sparks = new /datum/effect/system/spark_spread - sparks.set_up(1, 1, src) - sparks.start() - proj_hit = 1 - else if(iscarbon(target)) - var/mob/living/carbon/C = target - if(HULK in C.mutations) - C.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" )) - else if(C.status_flags & CANWEAKEN) - C.do_jitter_animation(jitter) - ..() - -/obj/item/projectile/energy/electrode/on_range() //to ensure the bolt sparks when it reaches the end of its range if it didn't hit a target yet - if(!proj_hit) - var/datum/effect/system/spark_spread/sparks = new /datum/effect/system/spark_spread + . = ..() + if(!ismob(target) || blocked >= 100) //Fully blocked by mob or collided with dense object - burst into sparks! + var/datum/effect_system/spark_spread/sparks = new /datum/effect_system/spark_spread sparks.set_up(1, 1, src) sparks.start() - proj_hit = 1 + else if(iscarbon(target)) + var/mob/living/carbon/C = target + if(HULK in C.mutations) + C.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" )) + else if(C.status_flags & CANWEAKEN) + spawn(5) + C.do_jitter_animation(jitter) + +/obj/item/projectile/energy/electrode/on_range() //to ensure the bolt sparks when it reaches the end of its range if it didn't hit a target yet + var/datum/effect_system/spark_spread/sparks = new /datum/effect_system/spark_spread + sparks.set_up(1, 1, src) + sparks.start() ..() /obj/item/projectile/energy/declone @@ -49,7 +45,6 @@ damage_type = CLONE irradiate = 10 - /obj/item/projectile/energy/dart name = "dart" icon_state = "toxin" @@ -63,7 +58,6 @@ icon_state = "toxin" damage = 10 damage_type = TOX - nodamage = 0 weaken = 5 stutter = 5 @@ -77,12 +71,28 @@ stutter = 5 /obj/item/projectile/energy/bolt/large - name = "largebolt" damage = 20 -/obj/item/projectile/energy/plasma +/obj/item/projectile/energy/shock_revolver + name = "shock bolt" + icon_state = "purple_laser" + var/chain + +/obj/item/ammo_casing/energy/shock_revolver/ready_proj(atom/target, mob/living/user, quiet, zone_override = "") + ..() + var/obj/item/projectile/hook/P = BB + spawn(1) + P.chain = P.Beam(user,icon_state="purple_lightning",icon = 'icons/effects/effects.dmi',time=1000, maxdistance = 30) + +/obj/item/projectile/energy/shock_revolver/on_hit(atom/target) + . = ..() + if(isliving(target)) + tesla_zap(src, 3, 10000) + qdel(chain) + +/obj/item/projectile/energy/toxplasma name = "plasma bolt" icon_state = "energy" damage = 20 damage_type = TOX - irradiate = 20 \ No newline at end of file + irradiate = 20 diff --git a/code/modules/projectiles/projectile/force.dm b/code/modules/projectiles/projectile/force.dm index 2fab1a4da18..4e5c6d27700 100644 --- a/code/modules/projectiles/projectile/force.dm +++ b/code/modules/projectiles/projectile/force.dm @@ -10,21 +10,8 @@ name = "force bolt" /obj/item/projectile/forcebolt/on_hit(var/atom/target, var/blocked = 0) - - var/obj/T = target - var/throwdir = get_dir(firer,target) - T.throw_at(get_edge_target_turf(target, throwdir),10,10) - return 1 - -/* -/obj/item/projectile/forcebolt/strong/on_hit(var/atom/target, var/blocked = 0) - - // NONE OF THIS WORKS. DO NOT USE. - var/throwdir = null - - for(var/mob/M in hearers(2, src)) - if(M.loc != src.loc) - throwdir = get_dir(src,target) - M.throw_at(get_edge_target_turf(M, throwdir),15,1) - return ..() -*/ \ No newline at end of file + . = ..() + if(blocked < 100) + var/obj/T = target + var/throwdir = get_dir(firer,target) + T.throw_at(get_edge_target_turf(target, throwdir),10,10) diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm index ecbbfb530f8..74eff57d98d 100644 --- a/code/modules/projectiles/projectile/magic.dm +++ b/code/modules/projectiles/projectile/magic.dm @@ -4,15 +4,14 @@ damage = 0 damage_type = OXY nodamage = 1 + armour_penetration = 100 flag = "magic" /obj/item/projectile/magic/death name = "bolt of death" icon_state = "pulse1_bl" - damage = 9001 - damage_type = OXY - nodamage = 0 - flag = "magic" + damage_type = BURN //OXY does not kill IPCs + damage = 50000 /obj/item/projectile/magic/fireball name = "bolt of fireball" @@ -20,9 +19,22 @@ damage = 10 damage_type = BRUTE nodamage = 0 - flag = "magic" + +/obj/item/projectile/magic/fireball/Range() + var/turf/T1 = get_step(src,turn(dir, -45)) + var/turf/T2 = get_step(src,turn(dir, 45)) + var/mob/living/L = locate(/mob/living) in T1 //if there's a mob alive in our front right diagonal, we hit it. + if(L && L.stat != DEAD) + Bump(L) //Magic Bullet #teachthecontroversy + return + L = locate(/mob/living) in T2 + if(L && L.stat != DEAD) + Bump(L) + return + ..() /obj/item/projectile/magic/fireball/on_hit(var/target) + . = ..() var/turf/T = get_turf(target) explosion(T, -1, 0, 2, 3, 0, flame_range = 2) if(ismob(target)) //multiple flavors of pain @@ -32,35 +44,13 @@ /obj/item/projectile/magic/resurrection name = "bolt of resurrection" icon_state = "ion" - damage = 0 - damage_type = OXY - nodamage = 1 - flag = "magic" /obj/item/projectile/magic/resurrection/on_hit(var/mob/living/carbon/target) - - if(istype(target,/mob)) - var/old_stat = target.stat - if(isanimal(target) && target.stat == DEAD) - var/mob/living/simple_animal/O = target - var/mob/living/simple_animal/A = new O.type(O.loc) - A.real_name = O.real_name - A.name = O.name - if(iscorgi(O)) - var/mob/living/simple_animal/pet/corgi/C = O - if(C.inventory_head) - C.inventory_head.loc = C.loc - if(C.inventory_back) - C.inventory_back.loc = C.loc - if(O.mind) - O.mind.transfer_to(A) - else - A.key = O.key - qdel(O) - target = A - else - target.revive() - target.suiciding = 0 + . = ..() + if(ismob(target)) + var/old_stat = stat + target.suiciding = 0 + target.revive() if(!target.ckey) for(var/mob/dead/observer/ghost in player_list) if(target.real_name == ghost.real_name) @@ -74,14 +64,11 @@ /obj/item/projectile/magic/teleport name = "bolt of teleportation" icon_state = "bluespace" - damage = 0 - damage_type = OXY - nodamage = 1 - flag = "magic" var/inner_tele_radius = 0 var/outer_tele_radius = 6 /obj/item/projectile/magic/teleport/on_hit(var/mob/target) + . = ..() var/teleammount = 0 var/teleloc = target if(!isturf(target)) @@ -97,37 +84,40 @@ /obj/item/projectile/magic/door name = "bolt of door creation" icon_state = "energy" - damage = 0 - damage_type = OXY - nodamage = 1 - flag = "magic" + var/list/door_types = list(/obj/structure/mineral_door/wood,/obj/structure/mineral_door/iron,/obj/structure/mineral_door/silver,\ + /obj/structure/mineral_door/gold,/obj/structure/mineral_door/uranium,/obj/structure/mineral_door/sandstone,/obj/structure/mineral_door/transparent/plasma,\ + /obj/structure/mineral_door/transparent/diamond) /obj/item/projectile/magic/door/on_hit(var/atom/target) + . = ..() var/atom/T = target.loc - if(isturf(target)) - if(target.density) - new /obj/structure/mineral_door/wood(target) - target:ChangeTurf(/turf/simulated/floor/plating) - else if (isturf(T)) - if(T.density) - new /obj/structure/mineral_door/wood(T) - T:ChangeTurf(/turf/simulated/floor/plating) + if(isturf(target) && target.density) + CreateDoor(target) + else if (isturf(T) && T.density) + CreateDoor(T + else if(istype(target, /obj/machinery/door)) + OpenDoor(target)) + +/obj/item/projectile/magic/door/proc/CreateDoor(turf/T) + var/door_type = pick(door_types) + var/obj/structure/mineral_door/D = new door_type(T) + T.ChangeTurf(/turf/open/floor/plating) + D.Open() /obj/item/projectile/magic/change name = "bolt of change" icon_state = "ice_1" - damage = 0 damage_type = BURN - nodamage = 1 - flag = "magic" /obj/item/projectile/magic/change/on_hit(var/atom/change) + . = ..() wabbajack(change) proc/wabbajack(mob/living/M) if(istype(M)) if(istype(M, /mob/living) && M.stat != DEAD) - if(M.notransform) return + if(M.notransform) + return M.notransform = 1 M.canmove = 0 M.icon = null @@ -136,7 +126,9 @@ proc/wabbajack(mob/living/M) if(istype(M, /mob/living/silicon/robot)) var/mob/living/silicon/robot/Robot = M - if(Robot.mmi) qdel(Robot.mmi) + if(Robot.mmi) + qdel(Robot.mmi) + Robot.notify_ai(1) else if(ishuman(M)) var/mob/living/carbon/human/H = M @@ -146,7 +138,8 @@ proc/wabbajack(mob/living/M) for(var/i in H.internal_organs) qdel(i) for(var/obj/item/W in M) - M.unEquip(W) + M.unEquip(W, 1) + qdel(W) var/mob/living/new_mob @@ -166,8 +159,6 @@ proc/wabbajack(mob/living/M) Robot.mmi.transfer_identity(M) //Does not transfer key/client. if("slime") new_mob = new /mob/living/carbon/slime(M.loc) -/* if(prob(50)) - new_mob.is_adult = 1*/ new_mob.universal_speak = 1 if("xeno") if(prob(50)) @@ -175,14 +166,6 @@ proc/wabbajack(mob/living/M) else new_mob = new /mob/living/carbon/alien/humanoid/sentinel(M.loc) new_mob.universal_speak = 1 - - /*var/alien_caste = pick("Hunter","Sentinel","Drone","Larva") - switch(alien_caste) - if("Hunter") new_mob = new /mob/living/carbon/alien/humanoid/hunter(M.loc) - if("Sentinel") new_mob = new /mob/living/carbon/alien/humanoid/sentinel(M.loc) - if("Drone") new_mob = new /mob/living/carbon/alien/humanoid/drone(M.loc) - else new_mob = new /mob/living/carbon/alien/larva(M.loc) - new_mob.universal_speak = 1*/ if("animal") if(prob(50)) var/beast = pick("carp","bear","mushroom","statue", "bat", "goat", "tomato") @@ -225,7 +208,6 @@ proc/wabbajack(mob/living/M) randomize = picked_species var/datum/preferences/A = new() //Randomize appearance for the human A.copy_to(new_mob) - else return @@ -246,13 +228,10 @@ proc/wabbajack(mob/living/M) /obj/item/projectile/magic/animate name = "bolt of animation" icon_state = "red_1" - damage = 0 damage_type = BURN - nodamage = 1 - flag = "magic" /obj/item/projectile/magic/animate/Bump(var/atom/change) - . = ..() + ..() if(istype(change, /obj/item) || istype(change, /obj/structure) && !is_type_in_list(change, protected_objects)) if(istype(change, /obj/structure/closet/statue)) for(var/mob/living/carbon/human/H in change.contents) @@ -273,4 +252,4 @@ proc/wabbajack(mob/living/M) else if(istype(change, /mob/living/simple_animal/hostile/mimic/copy)) // Change our allegiance! var/mob/living/simple_animal/hostile/mimic/copy/C = change - C.ChangeOwner(firer) \ No newline at end of file + C.ChangeOwner(firer) diff --git a/code/modules/projectiles/projectile/reusable.dm b/code/modules/projectiles/projectile/reusable.dm index 81defd55743..9f4e36bc1b4 100644 --- a/code/modules/projectiles/projectile/reusable.dm +++ b/code/modules/projectiles/projectile/reusable.dm @@ -1,42 +1,63 @@ /obj/item/projectile/bullet/reusable name = "reusable bullet" desc = "How do you even reuse a bullet?" - var/obj/item/ammo_casing/caseless/ammo_type = /obj/item/ammo_casing/caseless/ - var/hit = 0 + ammo_type = /obj/item/ammo_casing/caseless/ + var/dropped = 0 -/obj/item/projectile/bullet/reusable/on_hit(var/atom/target, var/blocked = 0) - if(!proj_hit) - proj_hit = 1 - if (src.contents.len) - var/obj/content - for(content in src.contents) - content.loc = src.loc - else - new ammo_type(src.loc) - ..() +/obj/item/projectile/bullet/reusable/on_hit(atom/target, blocked = 0) + . = ..() + handle_drop() /obj/item/projectile/bullet/reusable/on_range() - if(!proj_hit) - if (src.contents.len) - var/obj/content - for(content in src.contents) - content.loc = src.loc - else - new ammo_type(src.loc) + handle_drop() ..() +/obj/item/projectile/bullet/reusable/proc/handle_drop() + if(!dropped) + new ammo_type(src.loc) + dropped = 1 + +/obj/item/projectile/bullet/reusable/magspear + name = "magnetic spear" + desc = "WHITE WHALE, HOLY GRAIL" + damage = 30 //takes 3 spears to kill a mega carp, one to kill a normal carp + icon_state = "magspear" + ammo_type = /obj/item/ammo_casing/caseless/magspear + /obj/item/projectile/bullet/reusable/foam_dart name = "foam dart" desc = "I hope you're wearing eye protection." damage = 0 // It's a damn toy. damage_type = OXY nodamage = 1 - edge = 0 - embed = 0 icon = 'icons/obj/toyguns.dmi' icon_state = "foamdart" ammo_type = /obj/item/ammo_casing/caseless/foam_dart - range = 10 + range = 10 + var/obj/item/weapon/pen/pen = null + edge = 0 + embed = 0 + +/obj/item/projectile/bullet/reusable/foam_dart/handle_drop() + if(dropped) + return + dropped = 1 + var/obj/item/ammo_casing/caseless/foam_dart/newdart = new ammo_type(src.loc) + var/obj/item/ammo_casing/caseless/foam_dart/old_dart = ammo_casing + newdart.modified = old_dart.modified + if(pen) + var/obj/item/projectile/bullet/reusable/foam_dart/newdart_FD = newdart.BB + newdart_FD.pen = pen + pen.loc = newdart_FD + pen = null + newdart.BB.damage = damage + newdart.BB.nodamage = nodamage + newdart.BB.damage_type = damage_type + newdart.update_icon() + +/obj/item/projectile/bullet/reusable/foam_dart/Destroy() + pen = null + return ..() /obj/item/projectile/bullet/reusable/foam_dart/riot name = "riot foam dart" diff --git a/code/modules/projectiles/projectile/special.dm b/code/modules/projectiles/projectile/special.dm index ed64d73df4e..851f23bcdd7 100644 --- a/code/modules/projectiles/projectile/special.dm +++ b/code/modules/projectiles/projectile/special.dm @@ -6,15 +6,15 @@ nodamage = 1 flag = "energy" - - on_hit(var/atom/target, var/blocked = 0) - empulse(target, 1, 1) - return 1 - +/obj/item/projectile/ion/on_hit(var/atom/target, var/blocked = 0) + ..() + empulse(target, 1, 1) + return 1 /obj/item/projectile/ion/weak /obj/item/projectile/ion/weak/on_hit(atom/target, blocked = 0) + ..() empulse(target, 0, 0) return 1 @@ -26,9 +26,10 @@ sharp = 1 edge = 1 - on_hit(var/atom/target, var/blocked = 0) - explosion(target, -1, 0, 2) - return 1 +/obj/item/projectile/bullet/gyro/on_hit(var/atom/target, var/blocked = 0) + ..() + explosion(target, -1, 0, 2) + return 1 /obj/item/projectile/bullet/a40mm name ="40mm grenade" @@ -43,18 +44,17 @@ return 1 /obj/item/projectile/temp - name = "freeze beam" + name = "temperature beam" icon_state = "temp_4" damage = 0 damage_type = BURN nodamage = 1 flag = "energy" - var/temperature = 300 - var/obj/item/weapon/gun/energy/temperature/T = null + var/temperature = null + pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE - OnFired() - T = shot_from - temperature = T.temperature +/obj/item/projectile/temp/New() + spawn(1) switch(temperature) if(501 to INFINITY) name = "searing beam" //if emagged @@ -86,17 +86,19 @@ else name = "temperature beam"//failsafe icon_state = "temp_4" + ..() - on_hit(var/atom/target, var/blocked = 0)//These two could likely check temp protection on the mob - if(istype(target, /mob/living)) - var/mob/living/M = target - M.bodytemperature = temperature - if(temperature > 500)//emagged - M.adjust_fire_stacks(0.5) - M.IgniteMob() - playsound(M.loc, 'sound/effects/bamf.ogg', 50, 0) - return 1 +/obj/item/projectile/temp/on_hit(var/atom/target, var/blocked = 0)//These two could likely check temp protection on the mob + ..() + if(isliving(target)) + var/mob/living/M = target + M.bodytemperature = temperature + if(temperature > 500)//emagged + M.adjust_fire_stacks(0.5) + M.IgniteMob() + playsound(M.loc, 'sound/effects/bamf.ogg', 50, 0) + return 1 /obj/item/projectile/meteor name = "meteor" @@ -107,26 +109,17 @@ nodamage = 1 flag = "bullet" - Bump(atom/A as mob|obj|turf|area) - if(A == firer) - loc = A.loc - return - - sleep(-1) //Might not be important enough for a sleep(-1) but the sleep/spawn itself is necessary thanks to explosions and metoerhits - - if(src)//Do not add to this if() statement, otherwise the meteor won't delete them - if(A) - - A.ex_act(2) - playsound(src.loc, 'sound/effects/meteorimpact.ogg', 40, 1) - - for(var/mob/M in range(10, src)) - if(!M.stat && !istype(M, /mob/living/silicon/ai))\ - shake_camera(M, 3, 1) - qdel(src) - return 1 - else - return 0 +/obj/item/projectile/meteor/Bump(atom/A, yes) + if(yes) + return + if(A == firer) + loc = A.loc + return + playsound(src.loc, 'sound/effects/meteorimpact.ogg', 40, 1) + for(var/mob/M in urange(10, src)) + if(!M.stat) + shake_camera(M, 3, 1) + qdel(src) /obj/item/projectile/energy/floramut name = "alpha somatoray" @@ -136,36 +129,31 @@ nodamage = 1 flag = "energy" - on_hit(var/atom/target, var/blocked = 0) - var/mob/living/M = target - if(ishuman(target)) - var/mob/living/carbon/human/H = M - if((H.species.flags & IS_PLANT) && (M.nutrition < 500)) - if(prob(15)) - M.apply_effect((rand(30,80)),IRRADIATE) - M.Weaken(5) - for (var/mob/V in viewers(src)) - V.show_message("\red [M] writhes in pain as \his vacuoles boil.", 3, "\red You hear the crunching of leaves.", 2) - if(prob(35)) - // for (var/mob/V in viewers(src)) //Public messages commented out to prevent possible metaish genetics experimentation and stuff. - Cheridan - // V.show_message("\red [M] is mutated by the radiation beam.", 3, "\red You hear the snapping of twigs.", 2) - if(prob(80)) - randmutb(M) - domutcheck(M,null) - else - randmutg(M) - domutcheck(M,null) +/obj/item/projectile/energy/floramut/on_hit(var/atom/target, var/blocked = 0) + ..() + var/mob/living/M = target + if(ishuman(target)) + var/mob/living/carbon/human/H = M + if((H.species.flags & IS_PLANT) && (M.nutrition < 500)) + if(prob(15)) + M.apply_effect((rand(30,80)),IRRADIATE) + M.Weaken(5) + for (var/mob/V in viewers(src)) + V.show_message("[M] writhes in pain as \his vacuoles boil.", 3, "You hear the crunching of leaves.", 2) + if(prob(35)) + if(prob(80)) + randmutb(M) + domutcheck(M,null) else - M.adjustFireLoss(rand(5,15)) - M.show_message("\red The radiation beam singes you!") - // for (var/mob/V in viewers(src)) - // V.show_message("\red [M] is singed by the radiation beam.", 3, "\red You hear the crackle of burning leaves.", 2) - else if(istype(target, /mob/living/carbon/)) - // for (var/mob/V in viewers(src)) - // V.show_message("The radiation beam dissipates harmlessly through [M]", 3) - M.show_message("\blue The radiation beam dissipates harmlessly through your body.") - else - return 1 + randmutg(M) + domutcheck(M,null) + else + M.adjustFireLoss(rand(5,15)) + M.show_message("The radiation beam singes you!") + else if(istype(target, /mob/living/carbon/)) + M.show_message("The radiation beam dissipates harmlessly through your body.") + else + return 1 /obj/item/projectile/energy/florayield name = "beta somatoray" @@ -175,40 +163,42 @@ nodamage = 1 flag = "energy" - on_hit(var/atom/target, var/blocked = 0) - var/mob/M = target - if(ishuman(target)) //These rays make plantmen fat. - var/mob/living/carbon/human/H = M - if((H.species.flags & IS_PLANT) && (M.nutrition < 500)) - M.nutrition += 30 - else if (istype(target, /mob/living/carbon/)) - M.show_message("\blue The radiation beam dissipates harmlessly through your body.") - else - return 1 +/obj/item/projectile/energy/florayield/on_hit(var/atom/target, var/blocked = 0) + ..() + var/mob/M = target + if(ishuman(target)) //These rays make plantmen fat. + var/mob/living/carbon/human/H = M + if((H.species.flags & IS_PLANT) && (M.nutrition < 500)) + M.nutrition += 30 + else if (istype(target, /mob/living/carbon/)) + M.show_message("The radiation beam dissipates harmlessly through your body.") + else + return 1 /obj/item/projectile/beam/mindflayer name = "flayer ray" - on_hit(var/atom/target, var/blocked = 0) - if(ishuman(target)) - var/mob/living/carbon/human/M = target - M.adjustBrainLoss(20) - M.hallucination += 20 +/obj/item/projectile/beam/mindflayer/on_hit(var/atom/target, var/blocked = 0) + . = ..() + if(ishuman(target)) + var/mob/living/carbon/human/M = target + M.adjustBrainLoss(20) + M.hallucination += 20 /obj/item/projectile/clown name = "snap-pop" icon = 'icons/obj/toy.dmi' icon_state = "snappop" - Bump(atom/A as mob|obj|turf|area) - var/datum/effect/system/spark_spread/s = new /datum/effect/system/spark_spread - s.set_up(3, 1, src) - s.start() - new /obj/effect/decal/cleanable/ash(src.loc) - src.visible_message("\red The [src.name] explodes!","\red You hear a snap!") - playsound(src, 'sound/effects/snap.ogg', 50, 1) - qdel(src) +/obj/item/projectile/clown/Bump(atom/A as mob|obj|turf|area) + var/datum/effect/system/spark_spread/s = new /datum/effect/system/spark_spread + s.set_up(3, 1, src) + s.start() + new /obj/effect/decal/cleanable/ash(src.loc) + src.visible_message("The [src.name] explodes!","You hear a snap!") + playsound(src, 'sound/effects/snap.ogg', 50, 1) + qdel(src) /obj/item/projectile/kinetic name = "kinetic force" @@ -239,11 +229,9 @@ obj/item/projectile/kinetic/New() damage *= 4 ..() -/obj/item/projectile/kinetic/Range() - range-- - if(range <= 0) - new /obj/item/effect/kinetic_blast(src.loc) - qdel(src) +/obj/item/projectile/kinetic/on_range() + new /obj/effect/kinetic_blast(src.loc) + ..() /obj/item/projectile/kinetic/on_hit(atom/target) . = ..() @@ -268,6 +256,40 @@ obj/item/projectile/kinetic/New() spawn(4) qdel(src) +/obj/item/projectile/beam/wormhole + name = "bluespace beam" + icon_state = "spark" + hitsound = "sparks" + damage = 3 + var/obj/item/weapon/gun/energy/wormhole_projector/gun + color = "#33CCFF" + +/obj/item/projectile/beam/wormhole/orange + name = "orange bluespace beam" + color = "#FF6600" + +/obj/item/projectile/beam/wormhole/New(var/obj/item/ammo_casing/energy/wormhole/casing) + if(casing) + gun = casing.gun + +/obj/item/projectile/beam/wormhole/on_hit(atom/target) + if(ismob(target)) + var/turf/portal_destination = pick(orange(6, src)) + do_teleport(target, portal_destination) + return ..() + if(!gun) + qdel(src) + gun.create_portal(src) + +/obj/item/projectile/beam/wormhole/on_hit(atom/target) + if(ismob(target)) + var/turf/portal_destination = pick(orange(6, src)) + do_teleport(target, portal_destination) + return ..() + if(!gun) + qdel(src) + gun.create_portal(src) + /obj/item/projectile/bullet/frag12 name ="explosive slug" damage = 25 @@ -278,25 +300,6 @@ obj/item/projectile/kinetic/New() explosion(target, -1, 0, 1) return 1 -/obj/item/projectile/energy/teleport - name = "teleportation burst" - icon_state = "bluespace" - damage = 0 - nodamage = 1 - var/obj/item/weapon/gun/energy/telegun/T = null - var/teleport_target = null - - OnFired() - T = shot_from - teleport_target = T.teleport_target - -/obj/item/projectile/energy/teleport/on_hit(var/atom/target, var/blocked = 0) - if(isliving(target)) - if(teleport_target) - do_teleport(target, teleport_target, 0)//teleport what's in the tile to the beacon - else - do_teleport(target, target, 15) //Otherwise it just warps you off somewhere. - /obj/item/projectile/plasma name = "plasma blast" icon_state = "plasmacutter" @@ -321,8 +324,9 @@ obj/item/projectile/kinetic/New() if(istype(target, /turf/simulated/mineral)) var/turf/simulated/mineral/M = target M.gets_drilled(firer) - range = max(range - 1, 1) - return -1 + Range() + if(range > 0) + return -1 /obj/item/projectile/plasma/adv range = 5 @@ -331,29 +335,24 @@ obj/item/projectile/kinetic/New() damage = 10 range = 6 -/obj/item/projectile/beam/wormhole - name = "bluespace beam" - icon_state = "spark" - hitsound = "sparks" - damage = 3 - var/obj/item/weapon/gun/energy/wormhole_projector/gun = null - color = "#33CCFF" +/obj/item/projectile/energy/teleport + name = "teleportation burst" + icon_state = "bluespace" + damage = 0 + nodamage = 1 + var/obj/item/weapon/gun/energy/telegun/T = null + var/teleport_target = null -/obj/item/projectile/beam/wormhole/orange - name = "orange bluespace beam" - color = "#FF6600" + OnFired() + T = shot_from + teleport_target = T.teleport_target -/obj/item/projectile/beam/wormhole/OnFired() - gun = shot_from - -/obj/item/projectile/beam/wormhole/on_hit(atom/target) - if(ismob(target)) - var/turf/portal_destination = pick(orange(6, src)) - do_teleport(target, portal_destination) - return ..() - if(!gun) - qdel(src) - gun.create_portal(src) +/obj/item/projectile/energy/teleport/on_hit(var/atom/target, var/blocked = 0) + if(isliving(target)) + if(teleport_target) + do_teleport(target, teleport_target, 0)//teleport what's in the tile to the beacon + else + do_teleport(target, target, 15) //Otherwise it just warps you off somewhere. /obj/item/projectile/snowball name = "snowball" @@ -363,6 +362,7 @@ obj/item/projectile/kinetic/New() damage_type = BURN /obj/item/projectile/snowball/on_hit(atom/target) //chilling + . = ..() if(istype(target, /mob/living)) var/mob/living/M = target M.bodytemperature = max(0, M.bodytemperature - 50) //each hit will drop your body temp, so don't get surrounded! @@ -380,9 +380,10 @@ obj/item/projectile/kinetic/New() ..() /obj/item/projectile/ornament/on_hit(atom/target) //knockback + ..() if(istype(target, /turf)) return 0 var/obj/T = target var/throwdir = get_dir(firer,target) T.throw_at(get_edge_target_turf(target, throwdir),10,10) - return 1 \ No newline at end of file + return 1 diff --git a/code/modules/reagents/grenade_launcher.dm b/code/modules/reagents/grenade_launcher.dm deleted file mode 100644 index 9b9484ba60e..00000000000 --- a/code/modules/reagents/grenade_launcher.dm +++ /dev/null @@ -1,111 +0,0 @@ - - -/obj/item/weapon/gun/grenadelauncher - name = "grenade launcher" - icon = 'icons/obj/gun.dmi' - icon_state = "riotgun" - item_state = "riotgun" - w_class = 4.0 - throw_speed = 2 - throw_range = 10 - force = 5.0 - var/list/grenades = new/list() - var/max_grenades = 3 - var/ammo_name = "grenade" - var/ammo_type = /obj/item/weapon/grenade - var/unloaded - - materials = list(MAT_METAL=2000) - -/obj/item/weapon/gun/grenadelauncher/examine(mob/user) - if(..(user, 2)) - to_chat(user, "\blue [grenades.len] / [max_grenades] [ammo_name]s.") - -/obj/item/weapon/gun/grenadelauncher/attackby(obj/item/I as obj, mob/user as mob, params) - - if((istype(I, ammo_type))) - if(grenades.len < max_grenades) - user.drop_item() - I.loc = src - grenades += I - to_chat(user, "\blue You put the [ammo_name] in the [name].") - to_chat(user, "\blue [grenades.len] / [max_grenades] [ammo_name]s.") - else - to_chat(usr, "\red The grenade launcher cannot hold more [ammo_name]s.") - -/obj/item/weapon/gun/grenadelauncher/afterattack(obj/target, mob/user , flag) - - if (istype(target, /obj/item/weapon/storage/backpack )) - return - - else if (locate (/obj/structure/table, src.loc)) - return - - else if(target == user) - return - - if(grenades.len) - spawn(0) fire_grenade(target,user) - else - to_chat(usr, "\red The [name] is empty.") - -/obj/item/weapon/gun/grenadelauncher/proc/fire_grenade(atom/target, mob/user) - for(var/mob/O in viewers(world.view, user)) - O.show_message(text("\red [] fired a [ammo_name]!", user), 1) - to_chat(user, "\red You fire the [name]!") - var/obj/item/weapon/grenade/chem_grenade/F = grenades[1] //Now with less copypasta! - grenades -= F - F.loc = user.loc - F.throw_at(target, 30, 2, user) - message_admins("[key_name_admin(user)] fired a [ammo_name] ([F.name]) from a launcher ([name]).") - log_game("[key_name_admin(user)] used a [ammo_name] ([name]).") - F.active = 1 - F.icon_state = initial(icon_state) + "_active" - playsound(user.loc, 'sound/weapons/armbomb.ogg', 75, 1, -3) - spawn(15) - F.prime() - - -/obj/item/weapon/gun/grenadelauncher/piecannon - name = "pie cannon" - icon = 'icons/obj/gun.dmi' - icon_state = "piecannon1" - item_state = "piecannon1" - w_class = 4.0 - throw_speed = 2 - throw_range = 10 - force = 5.0 - clumsy_check = 0 - grenades = new/list() - max_grenades = 5 - ammo_name = "cream pie" - ammo_type = /obj/item/weapon/reagent_containers/food/snacks/pie - -/obj/item/weapon/gun/grenadelauncher/piecannon/New() - ..() - for(var/i=0, i < max_grenades, i++) - var/obj/item/weapon/reagent_containers/food/snacks/pie/P = new /obj/item/weapon/reagent_containers/food/snacks/pie(src) - grenades += P - -/obj/item/weapon/gun/grenadelauncher/piecannon/attackby(obj/item/I as obj, mob/user as mob, params) - if((istype(I, ammo_type))) - if(grenades.len < max_grenades) - user.drop_item() - I.loc = src - grenades += I - to_chat(user, "\blue You put the [ammo_name] in the [name].") - to_chat(user, "\blue [grenades.len] / [max_grenades] [ammo_name]s.") - icon_state = "piecannon1" - else - to_chat(usr, "\red The grenade launcher cannot hold more [ammo_name]s.") - -/obj/item/weapon/gun/grenadelauncher/piecannon/fire_grenade(atom/target, mob/user) - for(var/mob/O in viewers(world.view, user)) - O.show_message(text("\red [] fired a [ammo_name]!", user), 1) - to_chat(user, "\red You fire the [name]!") - var/obj/item/weapon/reagent_containers/food/snacks/pie/P = grenades[1] //Now with less copypasta! - grenades -= P - P.loc = user.loc - P.throw_at(target, 30, 2, user) - if(!grenades.len) - icon_state = "piecannon0" diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm index bd8a22cd06f..332375dae8d 100644 --- a/code/modules/reagents/reagent_containers/syringes.dm +++ b/code/modules/reagents/reagent_containers/syringes.dm @@ -17,6 +17,7 @@ w_class = 1 sharp = 1 var/mode = SYRINGE_DRAW + var/projectile_type = /obj/item/projectile/bullet/dart/syringe /obj/item/weapon/reagent_containers/syringe/on_reagent_change() update_icon() @@ -533,4 +534,4 @@ reagents.add_reagent("capulettium_plus", 5) reagents.add_reagent("sodium_thiopental", 5) mode = SYRINGE_INJECT - update_icon() \ No newline at end of file + update_icon() diff --git a/paradise.dme b/paradise.dme index bf55518d633..97f182a3d3a 100644 --- a/paradise.dme +++ b/paradise.dme @@ -1748,37 +1748,36 @@ #include "code\modules\procedural_mapping\mapGenerators\nature.dm" #include "code\modules\procedural_mapping\mapGenerators\syndicate.dm" #include "code\modules\projectiles\ammunition.dm" +#include "code\modules\projectiles\firing.dm" #include "code\modules\projectiles\gun.dm" -#include "code\modules\projectiles\medbeam.dm" #include "code\modules\projectiles\projectile.dm" #include "code\modules\projectiles\ammunition\ammo_casings.dm" #include "code\modules\projectiles\ammunition\boxes.dm" +#include "code\modules\projectiles\ammunition\energy.dm" #include "code\modules\projectiles\ammunition\magazines.dm" +#include "code\modules\projectiles\ammunition\special.dm" #include "code\modules\projectiles\guns\alien.dm" #include "code\modules\projectiles\guns\energy.dm" +#include "code\modules\projectiles\guns\grenade_launcher.dm" #include "code\modules\projectiles\guns\magic.dm" +#include "code\modules\projectiles\guns\medbeam.dm" #include "code\modules\projectiles\guns\mounted.dm" #include "code\modules\projectiles\guns\projectile.dm" #include "code\modules\projectiles\guns\syringe_gun.dm" -#include "code\modules\projectiles\guns\energy\advtaser.dm" -#include "code\modules\projectiles\guns\energy\blueshield.dm" -#include "code\modules\projectiles\guns\energy\hos.dm" #include "code\modules\projectiles\guns\energy\laser.dm" #include "code\modules\projectiles\guns\energy\nuclear.dm" #include "code\modules\projectiles\guns\energy\pulse.dm" #include "code\modules\projectiles\guns\energy\special.dm" #include "code\modules\projectiles\guns\energy\stun.dm" #include "code\modules\projectiles\guns\energy\telegun.dm" -#include "code\modules\projectiles\guns\energy\temperature.dm" #include "code\modules\projectiles\guns\magic\staff.dm" #include "code\modules\projectiles\guns\magic\wand.dm" #include "code\modules\projectiles\guns\projectile\automatic.dm" -#include "code\modules\projectiles\guns\projectile\crossbow.dm" -#include "code\modules\projectiles\guns\projectile\grenade launcher.dm" #include "code\modules\projectiles\guns\projectile\launchers.dm" #include "code\modules\projectiles\guns\projectile\pistol.dm" #include "code\modules\projectiles\guns\projectile\revolver.dm" #include "code\modules\projectiles\guns\projectile\rocket.dm" +#include "code\modules\projectiles\guns\projectile\saw.dm" #include "code\modules\projectiles\guns\projectile\shotgun.dm" #include "code\modules\projectiles\guns\projectile\sniper.dm" #include "code\modules\projectiles\guns\projectile\toy.dm" @@ -1794,7 +1793,6 @@ #include "code\modules\reagents\Chemistry-Machinery.dm" #include "code\modules\reagents\Chemistry-Readme.dm" #include "code\modules\reagents\dartgun.dm" -#include "code\modules\reagents\grenade_launcher.dm" #include "code\modules\reagents\pandemic.dm" #include "code\modules\reagents\reagent_containers.dm" #include "code\modules\reagents\reagent_dispenser.dm" From a4e950d21a9a366f7785261913c014f4ee75f4ea Mon Sep 17 00:00:00 2001 From: TheDZD Date: Sat, 28 May 2016 14:28:23 -0400 Subject: [PATCH 022/275] =?UTF-8?q?Compiles,=20and=20guns=20work=E2=84=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MetaStation/MetaStation.v41A.II.dmm | 2 +- _maps/map_files/RandomZLevels/beach.dmm | 2 +- _maps/map_files/cyberiad/cyberiad.dmm | 2 +- _maps/map_files/cyberiad/z2.dmm | 4 +- _maps/map_files/cyberiad/z6.dmm | 2 +- code/__DEFINES/misc.dm | 6 +- code/_onclick/click.dm | 56 +------ code/datums/uplink_item.dm | 6 +- .../gamemodes/malfunction/Malf_Modules.dm | 2 +- .../miniantags/abduction/abduction_gear.dm | 6 +- code/game/gamemodes/steal_items.dm | 4 +- code/game/machinery/portable_turret.dm | 8 +- code/game/machinery/rechargestation.dm | 3 +- code/game/machinery/turrets.dm | 6 +- code/game/mecha/equipment/weapons/weapons.dm | 2 +- .../objects/items/robot/robot_upgrades.dm | 6 +- code/game/objects/items/shooting_range.dm | 4 +- .../items/stacks/sheets/sheet_types.dm | 2 +- .../objects/items/weapons/chrono_eraser.dm | 23 ++- .../crates_lockers/closets/secure/security.dm | 2 +- code/modules/admin/verbs/honksquad.dm | 6 +- .../admin/verbs/striketeam_syndicate.dm | 4 +- code/modules/admin/verbs/vox_raiders.dm | 6 +- .../clothing/spacesuits/rig/modules/combat.dm | 4 +- code/modules/mob/living/carbon/human/human.dm | 19 ++- .../mob/living/carbon/human/human_defense.dm | 5 +- .../carbon/human/interactive/functions.dm | 17 +- code/modules/mob/living/living.dm | 6 + code/modules/mob/living/living_defense.dm | 4 - .../mob/living/simple_animal/bot/ed209bot.dm | 10 +- code/modules/mob/living/simple_animal/vox.dm | 8 +- code/modules/projectiles/ammunition.dm | 10 +- .../projectiles/ammunition/ammo_casings.dm | 12 +- code/modules/projectiles/ammunition/energy.dm | 12 +- .../projectiles/ammunition/magazines.dm | 2 +- code/modules/projectiles/firing.dm | 20 +-- code/modules/projectiles/gun.dm | 16 +- code/modules/projectiles/guns/alien.dm | 84 +--------- code/modules/projectiles/guns/energy.dm | 26 +-- code/modules/projectiles/guns/energy/laser.dm | 6 +- .../projectiles/guns/energy/nuclear.dm | 37 +---- code/modules/projectiles/guns/energy/pulse.dm | 2 +- .../projectiles/guns/energy/special.dm | 32 ++-- code/modules/projectiles/guns/energy/stun.dm | 2 +- .../projectiles/guns/energy/telegun.dm | 4 + .../projectiles/guns/grenade_launcher.dm | 12 +- code/modules/projectiles/guns/magic.dm | 3 +- code/modules/projectiles/guns/magic/wand.dm | 2 +- code/modules/projectiles/guns/medbeam.dm | 2 +- code/modules/projectiles/guns/mounted.dm | 4 +- code/modules/projectiles/guns/projectile.dm | 12 +- .../projectiles/guns/projectile/automatic.dm | 7 +- .../projectiles/guns/projectile/revolver.dm | 16 +- .../projectiles/guns/projectile/rocket.dm | 4 +- .../projectiles/guns/projectile/saw.dm | 4 +- .../projectiles/guns/projectile/shotgun.dm | 18 +- .../projectiles/guns/projectile/sniper.dm | 4 +- code/modules/projectiles/guns/syringe_gun.dm | 11 +- code/modules/projectiles/projectile.dm | 18 +- code/modules/projectiles/projectile/beams.dm | 157 +----------------- .../modules/projectiles/projectile/bullets.dm | 6 +- code/modules/projectiles/projectile/energy.dm | 6 +- code/modules/projectiles/projectile/magic.dm | 14 +- .../projectiles/projectile/reusable.dm | 6 +- .../modules/projectiles/projectile/special.dm | 17 +- code/modules/reagents/dartgun.dm | 9 +- .../xenoarchaeology/machinery/coolant.dm | 2 +- code/modules/spacepods/equipment.dm | 2 - sound/weapons/effects/ric1.ogg | Bin 0 -> 7937 bytes sound/weapons/effects/ric2.ogg | Bin 0 -> 6359 bytes sound/weapons/effects/ric3.ogg | Bin 0 -> 7430 bytes sound/weapons/effects/ric4.ogg | Bin 0 -> 7072 bytes sound/weapons/effects/ric5.ogg | Bin 0 -> 8545 bytes sound/weapons/effects/searwall.ogg | Bin 0 -> 7213 bytes 74 files changed, 288 insertions(+), 550 deletions(-) create mode 100644 sound/weapons/effects/ric1.ogg create mode 100644 sound/weapons/effects/ric2.ogg create mode 100644 sound/weapons/effects/ric3.ogg create mode 100644 sound/weapons/effects/ric4.ogg create mode 100644 sound/weapons/effects/ric5.ogg create mode 100644 sound/weapons/effects/searwall.ogg diff --git a/_maps/map_files/MetaStation/MetaStation.v41A.II.dmm b/_maps/map_files/MetaStation/MetaStation.v41A.II.dmm index b50efa38848..61c46bad876 100644 --- a/_maps/map_files/MetaStation/MetaStation.v41A.II.dmm +++ b/_maps/map_files/MetaStation/MetaStation.v41A.II.dmm @@ -583,7 +583,7 @@ "alk" = (/obj/machinery/alarm{pixel_y = 24},/obj/machinery/atmospherics/unary/vent_pump{dir = 8; on = 1},/turf/simulated/floor/plasteel,/area/crew_quarters/fitness{name = "\improper Recreation Area"}) "all" = (/obj/structure/shuttle/engine/propulsion{tag = "icon-propulsion_l"; icon_state = "propulsion_l"},/turf/space,/area/shuttle/vox) "alm" = (/obj/structure/rack,/obj/item/clothing/accessory/storage/black_vest,/obj/item/clothing/suit/space/vox/pressure,/obj/item/clothing/head/helmet/space/vox/pressure,/obj/item/clothing/mask/breath,/turf/simulated/shuttle/floor4/vox,/area/shuttle/vox) -"aln" = (/obj/structure/rack,/obj/item/weapon/gun/launcher/spikethrower,/turf/simulated/shuttle/floor4/vox,/area/shuttle/vox) +"aln" = (/obj/structure/rack,/turf/simulated/shuttle/floor4/vox,/area/shuttle/vox) "alo" = (/obj/machinery/door/poddoor{id_tag = "trash"; name = "disposal bay door"},/turf/simulated/floor/plating,/area/maintenance/disposal) "alp" = (/obj/machinery/mass_driver{dir = 8; id_tag = "trash"},/obj/machinery/light/small{dir = 1},/turf/simulated/floor/plating{icon_state = "warnplate"; dir = 1},/area/maintenance/disposal) "alq" = (/obj/machinery/conveyor_switch/oneway{convdir = -1; id = "garbage"; name = "disposal coveyor"},/turf/simulated/floor/plating,/area/maintenance/disposal) diff --git a/_maps/map_files/RandomZLevels/beach.dmm b/_maps/map_files/RandomZLevels/beach.dmm index 0a5cd703fc0..04512b660bf 100644 --- a/_maps/map_files/RandomZLevels/beach.dmm +++ b/_maps/map_files/RandomZLevels/beach.dmm @@ -19,7 +19,7 @@ "as" = (/obj/structure/boulder,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) "at" = (/obj/item/flag/cult,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) "au" = (/obj/structure/mineral_door/sandstone,/obj/structure/barricade/wooden,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"av" = (/obj/item/weapon/crossbowframe,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"av" = (/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) "aw" = (/obj/item/toy/eight_ball/conch,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) "ax" = (/obj/structure/reagent_dispensers/beerkeg,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) "ay" = (/obj/structure/closet/cabinet,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) diff --git a/_maps/map_files/cyberiad/cyberiad.dmm b/_maps/map_files/cyberiad/cyberiad.dmm index 6eef766fd9b..a2f06af2b25 100644 --- a/_maps/map_files/cyberiad/cyberiad.dmm +++ b/_maps/map_files/cyberiad/cyberiad.dmm @@ -8497,7 +8497,7 @@ "dhv" = (/obj/structure/rack,/obj/item/weapon/pneumatic_cannon,/obj/item/weapon/harpoon,/obj/item/weapon/harpoon,/obj/item/weapon/harpoon,/obj/item/weapon/harpoon,/obj/item/weapon/tank/nitrogen,/obj/machinery/light/spot{tag = "icon-tube1 (EAST)"; icon_state = "tube1"; dir = 4},/turf/simulated/shuttle/floor4/vox,/area/shuttle/vox) "dhw" = (/obj/structure/shuttle/engine/propulsion{tag = "icon-propulsion_l"; icon_state = "propulsion_l"},/turf/space,/area/shuttle/vox) "dhx" = (/obj/structure/rack,/obj/item/clothing/accessory/storage/black_vest,/obj/item/clothing/suit/space/vox/pressure,/obj/item/clothing/head/helmet/space/vox/pressure,/obj/item/clothing/mask/breath,/turf/simulated/shuttle/floor4/vox,/area/shuttle/vox) -"dhy" = (/obj/structure/rack,/obj/item/weapon/gun/launcher/spikethrower,/turf/simulated/shuttle/floor4/vox,/area/shuttle/vox) +"dhy" = (/obj/structure/rack,/turf/simulated/shuttle/floor4/vox,/area/shuttle/vox) "dhz" = (/obj/structure/rack,/obj/item/clothing/accessory/storage/black_vest,/obj/item/clothing/suit/space/vox/stealth,/obj/item/clothing/head/helmet/space/vox/stealth,/obj/item/clothing/mask/breath,/turf/simulated/shuttle/floor4/vox,/area/shuttle/vox) "dhA" = (/obj/structure/grille,/obj/structure/shuttle/window{tag = "icon-window5_end (NORTH)"; icon = 'icons/turf/shuttle.dmi'; icon_state = "window5_end"; dir = 1},/turf/simulated/shuttle/plating/vox,/area/shuttle/vox) "dhB" = (/obj/structure/grille,/obj/structure/shuttle/window{tag = "icon-window5_end"; icon = 'icons/turf/shuttle.dmi'; icon_state = "window5_end"; dir = 2},/turf/simulated/shuttle/plating/vox,/area/shuttle/vox) diff --git a/_maps/map_files/cyberiad/z2.dmm b/_maps/map_files/cyberiad/z2.dmm index 46b1662d8d6..65b00ea2f84 100644 --- a/_maps/map_files/cyberiad/z2.dmm +++ b/_maps/map_files/cyberiad/z2.dmm @@ -1136,7 +1136,7 @@ "EY" = (/turf/unsimulated/floor{tag = "icon-floor"; icon_state = "floor"},/turf/unsimulated/wall{dir = 4; icon = 'icons/turf/shuttle.dmi'; icon_state = "window5"; name = "window"; opacity = 0; tag = "icon-window5 (EAST)"},/area/admin) "EZ" = (/turf/unsimulated/floor{icon_state = "freezerfloor"; dir = 2},/turf/unsimulated/wall{dir = 2; icon = 'icons/turf/shuttle.dmi'; icon_state = "window5"; name = "window"; opacity = 0; tag = "icon-window5"},/area/admin) "Fa" = (/obj/machinery/door/poddoor{density = 0; icon_state = "pdoor0"; id_tag = "ADMINCHEMLOCKDOWN"; name = "Security Doors"; opacity = 0},/obj/machinery/door/airlock/hatch{name = "Chem Lab"; req_access_txt = "0"},/turf/unsimulated/floor{icon_state = "freezerfloor"; dir = 2},/area/admin) -"Fb" = (/obj/structure/rack,/obj/item/ammo_box/magazine/m762,/obj/item/ammo_box/magazine/m762,/obj/item/ammo_box/magazine/m762,/obj/item/ammo_box/magazine/m762,/obj/item/weapon/gun/projectile/automatic/l6_saw,/obj/item/weapon/gun/projectile/automatic/l6_saw{pixel_x = 3; pixel_y = -3},/turf/unsimulated/floor{tag = "icon-floor"; icon_state = "floor"},/area/admin) +"Fb" = (/obj/structure/rack,/obj/item/ammo_box/magazine/mm556x45,/obj/item/ammo_box/magazine/mm556x45,/obj/item/ammo_box/magazine/mm556x45,/obj/item/ammo_box/magazine/mm556x45,/obj/item/weapon/gun/projectile/automatic/l6_saw,/obj/item/weapon/gun/projectile/automatic/l6_saw{pixel_x = 3; pixel_y = -3},/turf/unsimulated/floor{tag = "icon-floor"; icon_state = "floor"},/area/admin) "Fc" = (/obj/structure/rack,/obj/item/weapon/gun/projectile/automatic/proto,/obj/item/weapon/gun/projectile/automatic/proto{pixel_x = 3; pixel_y = -3},/turf/unsimulated/floor{tag = "icon-floor"; icon_state = "floor"},/area/admin) "Fd" = (/obj/structure/rack,/obj/item/ammo_box/magazine/m556,/obj/item/ammo_box/magazine/m556,/obj/item/ammo_box/magazine/m556,/obj/item/ammo_box/magazine/m556,/obj/item/ammo_box/magazine/m556,/obj/item/ammo_box/magazine/m556,/obj/item/ammo_box/a40mm,/obj/item/ammo_box/a40mm,/obj/item/ammo_box/a40mm,/obj/item/ammo_box/a40mm,/obj/item/weapon/gun/projectile/automatic/m90,/obj/item/weapon/gun/projectile/automatic/m90{pixel_x = 3; pixel_y = -3},/turf/unsimulated/floor{tag = "icon-floor"; icon_state = "floor"},/area/admin) "Fe" = (/obj/structure/mirror{dir = 4; pixel_x = 0; pixel_y = -32},/turf/unsimulated/floor{tag = "icon-floor"; icon_state = "floor"},/area/admin) @@ -1174,7 +1174,7 @@ "FL" = (/turf/simulated/wall/r_wall,/area/adminconstruction) "FM" = (/obj/structure/rack,/obj/item/weapon/gun/energy/meteorgun,/obj/item/weapon/gun/energy/meteorgun{pixel_x = 3; pixel_y = -3},/turf/unsimulated/floor{tag = "icon-floor"; icon_state = "floor"},/area/admin) "FN" = (/obj/structure/rack,/obj/item/weapon/twohanded/knighthammer{pixel_x = -3; pixel_y = 3},/obj/item/weapon/twohanded/mjollnir,/obj/item/weapon/twohanded/singularityhammer{pixel_x = 3; pixel_y = -3},/turf/unsimulated/floor{tag = "icon-floor"; icon_state = "floor"},/area/admin) -"FO" = (/obj/structure/rack,/obj/item/weapon/gun/energy/crossbow,/obj/item/weapon/gun/energy/crossbow{pixel_x = 3; pixel_y = -3},/turf/unsimulated/floor{tag = "icon-floor"; icon_state = "floor"},/area/admin) +"FO" = (/obj/structure/rack,/obj/item/weapon/gun/energy/kinetic_accelerator/crossbow,/obj/item/weapon/gun/energy/kinetic_accelerator/crossbow{pixel_x = 3; pixel_y = -3},/turf/unsimulated/floor{tag = "icon-floor"; icon_state = "floor"},/area/admin) "FP" = (/obj/machinery/recharger/wallcharger{pixel_x = 30},/obj/structure/rack,/obj/item/weapon/gun/energy/pulse_rifle,/obj/item/weapon/gun/energy/pulse_rifle{pixel_x = 3; pixel_y = -3},/turf/unsimulated/floor{tag = "icon-floor"; icon_state = "floor"},/area/admin) "FQ" = (/obj/structure/table,/obj/random/tool,/obj/item/clothing/gloves/color/yellow,/obj/item/weapon/pinpointer/advpinpointer,/turf/unsimulated/floor{tag = "icon-dark"; icon_state = "dark"},/area/admin) "FR" = (/obj/structure/table,/obj/item/weapon/grenade/syndieminibomb{pixel_x = 4; pixel_y = 2; pixel_z = 0},/obj/item/weapon/grenade/syndieminibomb{pixel_x = -1},/turf/unsimulated/floor{tag = "icon-dark"; icon_state = "dark"},/area/admin) diff --git a/_maps/map_files/cyberiad/z6.dmm b/_maps/map_files/cyberiad/z6.dmm index 104c590cccc..da5594a4405 100644 --- a/_maps/map_files/cyberiad/z6.dmm +++ b/_maps/map_files/cyberiad/z6.dmm @@ -648,7 +648,7 @@ "mx" = (/obj/structure/table,/turf/simulated/floor/plasteel{icon_state = "dark"},/area/syndicate_depot) "my" = (/obj/structure/window/reinforced{dir = 8},/turf/simulated/floor/engine,/area/syndicate_depot) "mz" = (/turf/simulated/floor/engine,/area/syndicate_depot) -"mA" = (/obj/structure/closet/crate{name = "munitions crate"},/obj/item/weapon/storage/box/donkpockets,/obj/item/ammo_box/magazine/m762,/obj/item/weapon/gun/projectile/automatic/m90,/turf/simulated/floor/plasteel{icon_state = "dark"},/area/syndicate_depot) +"mA" = (/obj/structure/closet/crate{name = "munitions crate"},/obj/item/weapon/storage/box/donkpockets,/obj/item/ammo_box/magazine/mm556x45,/obj/item/weapon/gun/projectile/automatic/m90,/turf/simulated/floor/plasteel{icon_state = "dark"},/area/syndicate_depot) "mB" = (/obj/machinery/door/window{base_state = "right"; dir = 8; icon_state = "right"; name = "Telepad"; req_access_txt = "0"},/turf/simulated/floor/engine,/area/syndicate_depot) "mC" = (/obj/machinery/telepad,/turf/simulated/floor/engine,/area/syndicate_depot) "mD" = (/obj/structure/closet/crate{name = "munitions crate"},/obj/item/ammo_box/magazine/m12g/dragon,/obj/item/ammo_box/magazine/m12g/dragon,/obj/item/weapon/storage/fancy/cigarettes/cigpack_syndicate,/obj/item/weapon/storage/firstaid/tactical,/turf/simulated/floor/plasteel{icon_state = "dark"},/area/syndicate_depot) diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index 99918eccbc8..d36405db5d1 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -250,4 +250,8 @@ #define MATRIX_GREYSCALE list(0.3, 0.3, 0.3, 0,\ 0.3, 0.3, 0.3, 0,\ 0.3, 0.3, 0.3, 0,\ - 0, 0, 0, 1)//Gun trigger guards #define TRIGGER_GUARD_ALLOW_ALL -1 #define TRIGGER_GUARD_NONE 0 #define TRIGGER_GUARD_NORMAL 1 \ No newline at end of file + 0, 0, 0, 1) +//Gun trigger guards +#define TRIGGER_GUARD_ALLOW_ALL -1 +#define TRIGGER_GUARD_NONE 0 +#define TRIGGER_GUARD_NORMAL 1 diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index d135e62ae69..35274d2102d 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -167,8 +167,6 @@ animals lunging, etc. */ /mob/proc/RangedAttack(var/atom/A, var/params) - if(ishuman(src) && (istype(src:gloves, /obj/item/clothing/gloves/color/yellow/power)) && a_intent == I_HARM) - PowerGlove(A) if(!mutations.len) return if((LASER in mutations) && a_intent == I_HARM) LaserEyes(A) // moved into a proc below @@ -322,58 +320,6 @@ spawn(1) LE.process() -/mob/proc/PowerGlove(atom/A) - return - -/mob/living/carbon/human/PowerGlove(atom/A) - var/obj/item/clothing/gloves/color/yellow/power/G = src:gloves - var/time = 100 - var/turf/T = get_turf(src) - var/turf/U = get_turf(A) - var/obj/structure/cable/cable = locate() in T - if(!cable || !istype(cable)) - to_chat(src, "There is no cable here to power the gloves.") - return - if(world.time < G.next_shock) - to_chat(src, "[G] aren't ready to shock again!") - return - src.visible_message("[name] fires an arc of electricity!", \ - "You fire an arc of electricity!", \ - "You hear the loud crackle of electricity!") - var/datum/powernet/PN = cable.get_powernet() - var/available = 0 - var/obj/item/projectile/beam/lightning/L = new /obj/item/projectile/beam/lightning/(get_turf(src)) - if(PN) - available = PN.avail - L.damage = PN.get_electrocute_damage() - if(available >= 5000000) - L.damage = 205 - if(L.damage >= 200) - time = 200 - else if(L.damage >= 100) - time = 150 - var/datum/effect/system/spark_spread/s = new /datum/effect/system/spark_spread - s.set_up(5, 1, src) - s.start() - if(L.damage <= 0) - qdel(L) - if(L) - playsound(get_turf(src), 'sound/effects/eleczap.ogg', 75, 1) - L.tang = L.adjustAngle(get_angle(U,T)) - L.icon = midicon - L.icon_state = "[L.tang]" - L.firer = usr - L.def_zone = get_organ_target() - L.original = src - L.current = U - L.starting = U - L.yo = U.y - T.y - L.xo = U.x - T.x - spawn( 1 ) - L.process() - - next_move = world.time + 12 - G.next_shock = world.time + time // Simple helper to face what you clicked on, in case it should be needed in more than one place /mob/proc/face_atom(var/atom/A) @@ -419,4 +365,4 @@ else var/turf/T = screen_loc2turf(modifiers["screen-loc"], get_turf(usr)) T.Click(location, control, params) - return 1 \ No newline at end of file + return 1 diff --git a/code/datums/uplink_item.dm b/code/datums/uplink_item.dm index 08c418778a3..d9daba46e40 100644 --- a/code/datums/uplink_item.dm +++ b/code/datums/uplink_item.dm @@ -605,10 +605,10 @@ var/list/uplink_items = list() gamemodes = list(/datum/game_mode/nuclear) /datum/uplink_item/ammo/machinegun - name = "Box Magazine - 7.62x51mm" - desc = "A 50-round magazine of 7.62x51mm ammunition for use in the L6 SAW machine gun. By the time you need to use this, you'll already be on a pile of corpses." + name = "Box Magazine - 5.56x45mm" + desc = "A 50-round magazine of 5.56x45mm ammunition for use in the L6 SAW machine gun. By the time you need to use this, you'll already be on a pile of corpses." reference = "762" - item = /obj/item/ammo_box/magazine/m762 + item = /obj/item/ammo_box/magazine/mm556x45 cost = 12 gamemodes = list(/datum/game_mode/nuclear) surplus = 0 diff --git a/code/game/gamemodes/malfunction/Malf_Modules.dm b/code/game/gamemodes/malfunction/Malf_Modules.dm index 803a7ba0d80..3c8b96935ec 100644 --- a/code/game/gamemodes/malfunction/Malf_Modules.dm +++ b/code/game/gamemodes/malfunction/Malf_Modules.dm @@ -72,7 +72,7 @@ rcd light flash thingy on matter drain if(T.z in config.station_levels) // Increase health by 37.5% of original max, decrease delays between shots to 66% turret.health += initial(turret.health) * 3 / 8 - turret.eprojectile = /obj/item/projectile/beam/heavylaser //Once you see it, you will know what it means to FEAR. + turret.eprojectile = /obj/item/projectile/beam/laser/heavylaser //Once you see it, you will know what it means to FEAR. turret.eshot_sound = 'sound/weapons/lasercannonfire.ogg' to_chat(src, "Turrets upgraded.") /datum/AI_Module/large/destroy_rcd diff --git a/code/game/gamemodes/miniantags/abduction/abduction_gear.dm b/code/game/gamemodes/miniantags/abduction/abduction_gear.dm index 5107ff7cab4..d1c5428d093 100644 --- a/code/game/gamemodes/miniantags/abduction/abduction_gear.dm +++ b/code/game/gamemodes/miniantags/abduction/abduction_gear.dm @@ -309,7 +309,9 @@ item_state = "alienpistol" origin_tech = "combat=5;materials=4;powerstorage=3;abductor=3" -/obj/item/weapon/gun/energy/decloner/alien/special_check(user) +/obj/item/weapon/gun/energy/decloner/alien/can_trigger_gun(user) + if(!..()) + return 0 if(istype(user, /mob/living/carbon/human)) var/mob/living/carbon/human/H = user if(H.get_species() != "Abductor") @@ -631,4 +633,4 @@ Congratulations! You are now trained for xenobiology research!"} icon_state = "abductor" icon_closed = "abductor" icon_opened = "abductoropen" - material_drop = /obj/item/stack/sheet/mineral/abductor \ No newline at end of file + material_drop = /obj/item/stack/sheet/mineral/abductor diff --git a/code/game/gamemodes/steal_items.dm b/code/game/gamemodes/steal_items.dm index fd648b05c7d..25d2807432e 100644 --- a/code/game/gamemodes/steal_items.dm +++ b/code/game/gamemodes/steal_items.dm @@ -35,7 +35,7 @@ /datum/theft_objective/hoslaser name = "the head of security's recreated antique laser gun" - typepath = /obj/item/weapon/gun/energy/hos + typepath = /obj/item/weapon/gun/energy/gun/hos protected_jobs = list("Head Of Security") /datum/theft_objective/hand_tele @@ -244,4 +244,4 @@ datum/theft_objective/ai/check_special_completion(var/obj/item/device/aicard/C) typepath = /obj/item/stack/sheet/mineral/uranium min=10 max=30 - step=5 \ No newline at end of file + step=5 diff --git a/code/game/machinery/portable_turret.dm b/code/game/machinery/portable_turret.dm index eab12bb22d5..7378cae2b85 100644 --- a/code/game/machinery/portable_turret.dm +++ b/code/game/machinery/portable_turret.dm @@ -100,10 +100,9 @@ /obj/machinery/porta_turret/proc/setup() var/obj/item/weapon/gun/energy/E = installation //All energy-based weapons are applicable - if(istext(installation)) E = text2path(installation) - //var/obj/item/ammo_casing/shottype = E.projectile_type + var/obj/item/ammo_casing/shottype = E.ammo_type[1] - projectile = initial(E.projectile_type) + projectile = shottype.projectile_type eprojectile = projectile shot_sound = initial(E.fire_sound) eshot_sound = shot_sound @@ -561,9 +560,6 @@ var/list/turret_icons if(get_dist(src, L) > 7) //if it's too far away, why bother? return TURRET_NOT_TARGET - if(!check_trajectory(L, src)) //check if we have true line of sight - return TURRET_NOT_TARGET - if(emagged) // If emagged not even the dead get a rest return L.stat ? TURRET_SECONDARY_TARGET : TURRET_PRIORITY_TARGET diff --git a/code/game/machinery/rechargestation.dm b/code/game/machinery/rechargestation.dm index 92bca16c5b7..a1559c4fb3a 100644 --- a/code/game/machinery/rechargestation.dm +++ b/code/game/machinery/rechargestation.dm @@ -203,7 +203,8 @@ if(istype(O,/obj/item/weapon/gun/energy/disabler/cyborg)) var/obj/item/weapon/gun/energy/disabler/cyborg/D = O if(D.power_supply.charge < D.power_supply.maxcharge) - D.power_supply.give(D.charge_cost) + var/obj/item/ammo_casing/energy/E = D.ammo_type[D.select] + D.power_supply.give(E.e_cost) D.update_icon() else D.charge_tick = 0 diff --git a/code/game/machinery/turrets.dm b/code/game/machinery/turrets.dm index 82060d47bc2..ff24aa01027 100644 --- a/code/game/machinery/turrets.dm +++ b/code/game/machinery/turrets.dm @@ -227,15 +227,15 @@ if(1) A = new /obj/item/projectile/beam(loc) if(2) - A = new /obj/item/projectile/beam/heavylaser(loc) + A = new /obj/item/projectile/beam/laser/heavylaser(loc) if(3) A = new /obj/item/projectile/beam/pulse(loc) if(4) A = new /obj/item/projectile/magic/change(loc) if(5) - A = new /obj/item/projectile/lasertag/blue(loc) + A = new /obj/item/projectile/beam/lasertag/bluetag(loc) if(6) - A = new /obj/item/projectile/lasertag/red(loc) + A = new /obj/item/projectile/beam/lasertag/redtag(loc) A.original = target use_power(500) else diff --git a/code/game/mecha/equipment/weapons/weapons.dm b/code/game/mecha/equipment/weapons/weapons.dm index d95cbaff799..a92e949bafc 100644 --- a/code/game/mecha/equipment/weapons/weapons.dm +++ b/code/game/mecha/equipment/weapons/weapons.dm @@ -56,7 +56,7 @@ name = "CH-LC \"Solaris\" Laser Cannon" icon_state = "mecha_laser" energy_drain = 40 - projectile = /obj/item/projectile/beam/heavylaser + projectile = /obj/item/projectile/beam/laser/heavylaser fire_sound = 'sound/weapons/lasercannonfire.ogg' /obj/item/mecha_parts/mecha_equipment/weapon/energy/ion diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm index bfb90de8cac..512baa30703 100644 --- a/code/game/objects/items/robot/robot_upgrades.dm +++ b/code/game/objects/items/robot/robot_upgrades.dm @@ -125,13 +125,13 @@ to_chat(usr, "This robot has had its disabler removed!") return 0 - if(T.recharge_time <= 2) + if(T.charge_delay <= 2) to_chat(R, "Maximum cooling achieved for this hardpoint!") to_chat(usr, "There's no room for another cooling unit!") return 0 else - T.recharge_time = max(2 , T.recharge_time - 4) + T.charge_delay = max(2 , T.charge_delay - 4) return 1 @@ -214,4 +214,4 @@ return 0 R.emagged = 1 - return 1 \ No newline at end of file + return 1 diff --git a/code/game/objects/items/shooting_range.dm b/code/game/objects/items/shooting_range.dm index c71f2e14182..fd3f5505f4f 100644 --- a/code/game/objects/items/shooting_range.dm +++ b/code/game/objects/items/shooting_range.dm @@ -114,7 +114,7 @@ bmark.pixel_x-- bmark.pixel_y-- - if(Proj.damage >= 20 || istype(Proj, /obj/item/projectile/practice)) + if(Proj.damage >= 20 || istype(Proj, /obj/item/projectile/beam/practice)) bmark.icon_state = "scorch" bmark.dir = pick(NORTH,SOUTH,EAST,WEST) // random scorch design @@ -178,4 +178,4 @@ b2y1 = pixel_y + pick(1,1,1,1,2,2,3,3,4) b2y2 = pixel_y - pick(1,1,1,1,2,2,3,3,4) - Target.bulletholes.Add(src) \ No newline at end of file + Target.bulletholes.Add(src) diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm index 3fe6dc7102f..c4c5d3cea4c 100644 --- a/code/game/objects/items/stacks/sheets/sheet_types.dm +++ b/code/game/objects/items/stacks/sheets/sheet_types.dm @@ -140,7 +140,7 @@ var/global/list/datum/stack_recipe/wood_recipes = list ( \ new/datum/stack_recipe("wooden barricade", /obj/structure/barricade/wooden, 5, time = 50, one_per_turf = 1, on_floor = 1), \ new/datum/stack_recipe("bookcase", /obj/structure/bookcase, 5, time = 50, one_per_turf = 1, on_floor = 1), \ new/datum/stack_recipe("rifle stock", /obj/item/weaponcrafting/stock, 10, time = 40), \ - new/datum/stack_recipe("crossbow frame", /obj/item/weapon/crossbowframe, 5, time = 25, one_per_turf = 0, on_floor = 0), \ + /*new/datum/stack_recipe("crossbow frame", /obj/item/weapon/crossbowframe, 5, time = 25, one_per_turf = 0, on_floor = 0), \ */ new/datum/stack_recipe("wooden door", /obj/structure/mineral_door/wood, 10, time = 20, one_per_turf = 1, on_floor = 1), \ new/datum/stack_recipe("coffin", /obj/structure/closet/coffin, 5, time = 15, one_per_turf = 1, on_floor = 1), \ new/datum/stack_recipe("easel", /obj/structure/easel, 3, one_per_turf = 1, on_floor = 1), \ diff --git a/code/game/objects/items/weapons/chrono_eraser.dm b/code/game/objects/items/weapons/chrono_eraser.dm index e0ac96c4feb..b0a1c45622d 100644 --- a/code/game/objects/items/weapons/chrono_eraser.dm +++ b/code/game/objects/items/weapons/chrono_eraser.dm @@ -41,12 +41,11 @@ icon = 'icons/obj/chronos.dmi' icon_state = "chronogun" item_state = "chronogun" - w_class = 3.0 - projectile_type = "/obj/item/projectile/energy/chrono_beam" - fire_sound = 'sound/weapons/Laser.ogg' - charge_cost = 0 - fire_delay = 50 + w_class = 3 flags = NODROP + ammo_type = list(/obj/item/ammo_casing/energy/chrono_beam) + can_charge = 0 + fire_delay = 50 var/obj/item/weapon/chrono_eraser/TED = null var/obj/effect/chrono_field/field = null var/turf/startpos = null @@ -60,12 +59,13 @@ qdel(src) /obj/item/weapon/gun/energy/chrono_gun/dropped() + ..() qdel(src) /obj/item/weapon/gun/energy/chrono_gun/update_icon() return -/obj/item/weapon/gun/energy/chrono_gun/Fire() +/obj/item/weapon/gun/energy/chrono_gun/process_fire() if(field) field_disconnect(field) ..() @@ -125,18 +125,23 @@ nodamage = 1 var/obj/item/weapon/gun/energy/chrono_gun/gun = null -/obj/item/projectile/energy/chrono_beam/process() +/obj/item/projectile/energy/chrono_beam/fire() gun = firer.get_active_hand() if(istype(gun)) return ..() else return 0 -/obj/item/projectile/energy/chrono_beam/on_hit(var/atom/target) +/obj/item/projectile/energy/chrono_beam/on_hit(atom/target) if(target && gun && isliving(target)) var/obj/effect/chrono_field/F = new(target.loc, target, gun) gun.field_connect(F) +/obj/item/ammo_casing/energy/chrono_beam + name = "eradication beam" + projectile_type = /obj/item/projectile/energy/chrono_beam + icon_state = "chronobolt" + e_cost = 0 /obj/effect/chrono_field name = "eradication field" @@ -256,4 +261,4 @@ #undef CHRONO_BEAM_RANGE -#undef CHRONO_FRAME_COUNT \ No newline at end of file +#undef CHRONO_FRAME_COUNT 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 4a2ced79339..1cadbfbd14a 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm @@ -115,7 +115,7 @@ new /obj/item/weapon/melee/baton/loaded(src) new /obj/item/weapon/storage/belt/security/sec(src) new /obj/item/taperoll/police(src) - new /obj/item/weapon/gun/energy/hos(src) + new /obj/item/weapon/gun/energy/gun/hos(src) new /obj/item/weapon/door_remote/head_of_security(src) diff --git a/code/modules/admin/verbs/honksquad.dm b/code/modules/admin/verbs/honksquad.dm index 75fc6af84d9..4afbeaa4f15 100644 --- a/code/modules/admin/verbs/honksquad.dm +++ b/code/modules/admin/verbs/honksquad.dm @@ -120,8 +120,8 @@ var/global/sent_honksquad = 0 equip_to_slot_or_del(new /obj/item/weapon/reagent_containers/spray/waterflower(src), slot_in_backpack) if(prob(50)) equip_to_slot_or_del(new /obj/item/weapon/gun/energy/clown(src), slot_in_backpack) - else - equip_to_slot_or_del(new /obj/item/weapon/gun/grenadelauncher/piecannon(src), slot_in_backpack) + //else + //equip_to_slot_or_del(new /obj/item/weapon/gun/grenadelauncher/piecannon(src), slot_in_backpack) src.mutations.Add(CLUMSY) @@ -134,4 +134,4 @@ var/global/sent_honksquad = 0 W.registered_name = real_name equip_to_slot_or_del(W, slot_wear_id) - return 1 \ No newline at end of file + return 1 diff --git a/code/modules/admin/verbs/striketeam_syndicate.dm b/code/modules/admin/verbs/striketeam_syndicate.dm index 6131962b881..0b84e6d065a 100644 --- a/code/modules/admin/verbs/striketeam_syndicate.dm +++ b/code/modules/admin/verbs/striketeam_syndicate.dm @@ -144,7 +144,7 @@ var/global/sent_syndicate_strike_team = 0 equip_to_slot_or_del(new /obj/item/clothing/glasses/thermal(src), slot_glasses) equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/security(src), slot_back) - equip_to_slot_or_del(new /obj/item/ammo_box/magazine/m762, slot_in_backpack) + equip_to_slot_or_del(new /obj/item/ammo_box/magazine/mm556x45) equip_to_slot_or_del(new /obj/item/ammo_box/magazine/m45(src), slot_in_backpack) equip_to_slot_or_del(new /obj/item/weapon/reagent_containers/hypospray/combat/nanites(src), slot_in_backpack) @@ -173,4 +173,4 @@ var/global/sent_syndicate_strike_team = 0 W.registered_name = real_name equip_to_slot_or_del(W, slot_wear_id) - return 1 \ No newline at end of file + return 1 diff --git a/code/modules/admin/verbs/vox_raiders.dm b/code/modules/admin/verbs/vox_raiders.dm index c5e370f06f7..0f6f3dda06e 100644 --- a/code/modules/admin/verbs/vox_raiders.dm +++ b/code/modules/admin/verbs/vox_raiders.dm @@ -18,8 +18,8 @@ var/global/vox_tick = 1 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/device/chameleon(src), slot_l_store) - var/obj/item/weapon/gun/launcher/spikethrower/W = new(src) - equip_to_slot_or_del(W, slot_r_hand) + //var/obj/item/weapon/gun/launcher/spikethrower/W = new(src) + //equip_to_slot_or_del(W, slot_r_hand) if(2) // Vox engineer! @@ -77,4 +77,4 @@ var/global/vox_tick = 1 vox_tick++ if (vox_tick > 4) vox_tick = 1 - return 1 \ No newline at end of file + return 1 diff --git a/code/modules/clothing/spacesuits/rig/modules/combat.dm b/code/modules/clothing/spacesuits/rig/modules/combat.dm index 2473fd24eb8..16ddf2bdd69 100644 --- a/code/modules/clothing/spacesuits/rig/modules/combat.dm +++ b/code/modules/clothing/spacesuits/rig/modules/combat.dm @@ -119,7 +119,7 @@ gun.attack_self(holder.wearer) return 1 - gun.Fire(target,holder.wearer) + gun.process_fire(target,holder.wearer) return 1 /obj/item/rig_module/mounted/egun @@ -168,7 +168,7 @@ active_power_cost = 10 passive_power_cost = 0 - gun_type = /obj/item/weapon/gun/energy/crossbow/ninja + gun_type = /obj/item/weapon/gun/energy/kinetic_accelerator/crossbow/ninja /obj/item/rig_module/mounted/energy_blade/process() diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 7775830b4a0..cb067591904 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -1982,4 +1982,21 @@ . |= A.GetAccess() /mob/living/carbon/human/is_mechanical() - return ..() || (species.flags & ALL_RPARTS) != 0 \ No newline at end of file + return ..() || (species.flags & ALL_RPARTS) != 0 + +/mob/living/carbon/human/can_use_guns(var/obj/item/weapon/gun/G) + . = ..() + + if(G.trigger_guard == TRIGGER_GUARD_NORMAL) + if(HULK in mutations) + to_chat(src, "Your meaty finger is much too large for the trigger guard!") + return 0 + if(species.flags & NOGUNS) + to_chat(src, "Your fingers don't fit in the trigger guard!") + return 0 + + if(martial_art && martial_art.name == "The Sleeping Carp") //great dishonor to famiry + to_chat(src, "Use of ranged weaponry would bring dishonor to the clan.") + return 0 + + return . diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 835f741be3d..c30e02f5881 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -46,7 +46,10 @@ emp_act if((P.embed && prob(20 + max(P.damage - armor, -10)))) var/obj/item/weapon/shard/shrapnel/SP = new() (SP.name) = "[P.name] shrapnel" - (SP.desc) = "[SP.desc] It looks like it was fired from [P.shot_from]." + if(P.ammo_casing && P.ammo_casing.caliber) + (SP.desc) = "[SP.desc] It looks like it is a [P.ammo_casing.caliber] caliber round." + else + (SP.desc) = "[SP.desc] The round's caliber is unidentifiable." (SP.loc) = organ organ.embed(SP) diff --git a/code/modules/mob/living/carbon/human/interactive/functions.dm b/code/modules/mob/living/carbon/human/interactive/functions.dm index 0a220accc98..b0806e66241 100644 --- a/code/modules/mob/living/carbon/human/interactive/functions.dm +++ b/code/modules/mob/living/carbon/human/interactive/functions.dm @@ -693,7 +693,7 @@ best = R if(istype(R, /obj/item/weapon/gun)) var/obj/item/weapon/gun/G = R - if(G.can_fire()) + if(G.can_shoot()) best = R break // gun with ammo? screw the rest if(best && best != main_hand) @@ -727,22 +727,23 @@ if(main_hand.force != 0) if(istype(main_hand,/obj/item/weapon/gun)) var/obj/item/weapon/gun/G = main_hand - if(G.special_check(src)) + if(G.can_trigger_gun(src)) var/shouldFire = 1 if(istype(main_hand, /obj/item/weapon/gun/energy)) var/obj/item/weapon/gun/energy/P = main_hand var/stunning = 0 - if(ispath(text2path(P.projectile_type), /obj/item/projectile/energy/electrode)) - stunning = 1 + for(var/A in P.ammo_type) + if(ispath(A,/obj/item/ammo_casing/energy/electrode)) + stunning = 1 var/mob/stunCheck = TARGET if(stunning && stunCheck.stunned) shouldFire = 0 if(shouldFire) - if(!G.process_chambered()) - G.click_empty(src) + if(!G.can_shoot()) + G.shoot_with_empty_chamber(src) npcDrop(G, 1) else - G.Fire(TARGET, src) + G.process_fire(TARGET, src) else if(get_dist(src,TARGET) > 6) if(!walk2derpless(TARGET)) @@ -871,4 +872,4 @@ else A.stamp(S) custom_emote(2, "[pick("gibbers","drools","slobbers","claps wildly","spits")] as they stamp \the [A] with \a [S]!") - return \ No newline at end of file + return diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index f28e884f92f..f3c48bce39a 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -855,3 +855,9 @@ tally += 10 return tally + +/mob/living/proc/can_use_guns(var/obj/item/weapon/gun/G) + if (G.trigger_guard != TRIGGER_GUARD_ALLOW_ALL && !IsAdvancedToolUser()) + to_chat(src, "You don't have the dexterity to do this!") + return 0 + return 1 diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 4d994436489..cc50408148e 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -47,10 +47,6 @@ proj_sharp = 0 proj_edge = 0 - if(istype(P, /obj/item/projectile/beam/lightning)) //Stupid snowflake lightning gun. - if(P.damage >= 200) - src.dust() - if(!P.nodamage) apply_damage(P.damage, P.damage_type, def_zone, armor) return P.on_hit(src, armor, def_zone) diff --git a/code/modules/mob/living/simple_animal/bot/ed209bot.dm b/code/modules/mob/living/simple_animal/bot/ed209bot.dm index 12dd032ba49..23b87eb0c51 100644 --- a/code/modules/mob/living/simple_animal/bot/ed209bot.dm +++ b/code/modules/mob/living/simple_animal/bot/ed209bot.dm @@ -403,7 +403,7 @@ Auto Patrol[]"}, shoot_sound = 'sound/weapons/laser.ogg' if(emagged == 2) if(lasercolor) - projectile = /obj/item/projectile/lasertag + projectile = /obj/item/projectile/beam/lasertag else projectile = /obj/item/projectile/beam else @@ -411,9 +411,9 @@ Auto Patrol[]"}, shoot_sound = 'sound/weapons/Taser.ogg' projectile = /obj/item/projectile/energy/electrode else if(lasercolor == "b") - projectile = /obj/item/projectile/lasertag/blue + projectile = /obj/item/projectile/beam/lasertag/bluetag else if(lasercolor == "r") - projectile = /obj/item/projectile/lasertag/red + projectile = /obj/item/projectile/beam/lasertag/redtag /mob/living/simple_animal/bot/ed209/proc/shootAt(mob/target) if(lastfired && world.time - lastfired < shot_delay) @@ -491,10 +491,10 @@ Auto Patrol[]"}, if(!disabled) var/lasertag_check = 0 if((lasercolor == "b")) - if(istype(Proj, /obj/item/projectile/lasertag/red)) + if(istype(Proj, /obj/item/projectile/beam/lasertag/redtag)) lasertag_check++ else if((lasercolor == "r")) - if(istype(Proj, /obj/item/projectile/lasertag/blue)) + if(istype(Proj, /obj/item/projectile/beam/lasertag/bluetag)) lasertag_check++ if(lasertag_check) icon_state = "[lasercolor]ed2090" diff --git a/code/modules/mob/living/simple_animal/vox.dm b/code/modules/mob/living/simple_animal/vox.dm index 3b34ddaf143..0ca934739d5 100644 --- a/code/modules/mob/living/simple_animal/vox.dm +++ b/code/modules/mob/living/simple_animal/vox.dm @@ -65,9 +65,9 @@ if ((O.client && !( O.blinded ))) to_chat(O, "\red [src] launches a razor-sharp quill at [target]!") - var/obj/item/weapon/arrow/quill/Q = new(loc) - Q.fingerprintslast = src.ckey - Q.throw_at(target,10,30) + //var/obj/item/weapon/arrow/quill/Q = new(loc) + //Q.fingerprintslast = src.ckey + //Q.throw_at(target,10,30) quills-- spawn(100) @@ -155,4 +155,4 @@ desc = "A series of metallic lenses and chains." icon = 'icons/obj/clothing/hats.dmi' icon_state = "amp" - item_state = "amp" \ No newline at end of file + item_state = "amp" diff --git a/code/modules/projectiles/ammunition.dm b/code/modules/projectiles/ammunition.dm index 169e968afaf..ab3c224ac08 100644 --- a/code/modules/projectiles/ammunition.dm +++ b/code/modules/projectiles/ammunition.dm @@ -17,7 +17,6 @@ var/randomspread = 0 //Randomspread for automatics var/click_cooldown_override = 0 //Override this to make your gun have a faster fire rate, in tenths of a second. 4 is the default gun cooldown. - /obj/item/ammo_casing/New() ..() if(projectile_type) @@ -32,6 +31,11 @@ icon_state = "[initial(icon_state)][BB ? "-live" : ""]" desc = "[initial(desc)][BB ? "" : " This one is spent"]" +/obj/item/ammo_casing/proc/newshot() //For energy weapons, shotgun shells and wands (!). + if (!BB) + BB = new projectile_type(src) + return + /obj/item/ammo_casing/attackby(obj/item/I as obj, mob/user as mob, params) if(istype(I, /obj/item/ammo_box)) var/obj/item/ammo_box/box = I @@ -99,7 +103,7 @@ update_icon() /obj/item/ammo_box/Destroy() - for(atom/A in stored_ammo) + for(var/atom/A in stored_ammo) qdel(A) stored_ammo = null return ..() @@ -128,7 +132,7 @@ for(var/obj/item/ammo_casing/AC in stored_ammo) if(!AC.BB)//found a spent ammo stored_ammo -= AC - AC.loc = get_turf(src.loc) + AC.loc = get_turf(loc) stored_ammo += R R.loc = src diff --git a/code/modules/projectiles/ammunition/ammo_casings.dm b/code/modules/projectiles/ammunition/ammo_casings.dm index 010ce185acb..980e9fb16bc 100644 --- a/code/modules/projectiles/ammunition/ammo_casings.dm +++ b/code/modules/projectiles/ammunition/ammo_casings.dm @@ -96,7 +96,7 @@ icon_state = "gshell" projectile_type = /obj/item/projectile/bullet/pellet pellets = 6 - deviation = 25 + variance = 25 /obj/item/ammo_casing/shotgun/rubbershot name = "rubber shot" @@ -104,7 +104,7 @@ icon_state = "bshell" projectile_type = /obj/item/projectile/bullet/rpellet pellets = 6 - deviation = 25 + variance = 25 materials = list(MAT_METAL=4000) /obj/item/ammo_casing/shotgun/beanbag @@ -121,17 +121,17 @@ projectile_type = /obj/item/projectile/bullet/pellet/weak materials = list(MAT_METAL=250) pellets = 5 - deviation = 25 + variance = 25 /obj/item/ammo_casing/shotgun/improvised/overload name = "overloaded improvised shell" desc = "An extremely weak shotgun shell with multiple small pellets made out of metal shards. This one has been packed with even more \ propellant. It's like playing russian roulette, with a shotgun." icon_state = "improvshell" - projectile_type = /obj/item/projectile/bullet/pellet/random + projectile_type = /obj/item/projectile/bullet/pellet/overload materials = list(MAT_METAL=250) pellets = 5 - deviation = 40 + variance = 40 /obj/item/ammo_casing/shotgun/improvised/overload/New() ..() @@ -191,7 +191,7 @@ icon_state = "ionshell" projectile_type = /obj/item/projectile/ion/weak pellets = 4 - deviation = 35 + variance = 35 /obj/item/ammo_casing/shotgun/laserslug name = "laser slug" diff --git a/code/modules/projectiles/ammunition/energy.dm b/code/modules/projectiles/ammunition/energy.dm index b4e979e3b20..4fc5295e8ae 100644 --- a/code/modules/projectiles/ammunition/energy.dm +++ b/code/modules/projectiles/ammunition/energy.dm @@ -110,7 +110,8 @@ /obj/item/ammo_casing/energy/temp/newshot() ..() - BB.temperature = temperature + var/obj/item/projectile/temp/T = BB + T.temperature = temperature /obj/item/ammo_casing/energy/meteor projectile_type = /obj/item/projectile/meteor @@ -202,7 +203,7 @@ projectile_type = /obj/item/projectile/energy/shock_revolver /obj/item/ammo_casing/energy/toxplasma - projectile_type = /obj/item/projectile/toxplasma + projectile_type = /obj/item/projectile/energy/toxplasma fire_sound = "sound/weapons/taser2.ogg" select_name = "plasma dart" @@ -214,7 +215,7 @@ /obj/item/ammo_casing/energy/sniper projectile_type = /obj/item/projectile/beam/sniper fire_sound = 'sound/weapons/marauder.ogg' - fire_delay = 50 + delay = 50 select_name = "sniper beam" /obj/item/ammo_casing/energy/teleport @@ -222,3 +223,8 @@ fire_sound = 'sound/weapons/wave.ogg' e_cost = 1250 select_name = "teleport beam" + var/teleport_target + +/obj/item/ammo_casing/energy/teleport/newshot() + var/obj/item/projectile/energy/teleport/T = BB + T.teleport_target = teleport_target diff --git a/code/modules/projectiles/ammunition/magazines.dm b/code/modules/projectiles/ammunition/magazines.dm index e26e536e62a..367270d0cd1 100644 --- a/code/modules/projectiles/ammunition/magazines.dm +++ b/code/modules/projectiles/ammunition/magazines.dm @@ -50,7 +50,7 @@ R.loc = src if(bullet) - bullet.loc = get_turf(src.loc) + bullet.loc = get_turf(loc) return 1 return 0 diff --git a/code/modules/projectiles/firing.dm b/code/modules/projectiles/firing.dm index 9642923c452..c17a7948fcb 100644 --- a/code/modules/projectiles/firing.dm +++ b/code/modules/projectiles/firing.dm @@ -28,7 +28,7 @@ if (zone_override) BB.def_zone = zone_override else - BB.def_zone = user.zone_selected + BB.def_zone = user.zone_sel.selecting BB.suppressed = quiet if(reagents && BB.reagents) @@ -60,18 +60,18 @@ /obj/item/projectile/proc/preparePixelProjectile(atom/target, var/turf/targloc, mob/living/user, params, spread) var/turf/curloc = get_turf(user) - src.loc = get_turf(user) - src.starting = get_turf(user) - src.current = curloc - src.yo = targloc.y - curloc.y - src.xo = targloc.x - curloc.x + loc = get_turf(user) + starting = get_turf(user) + current = curloc + yo = targloc.y - curloc.y + xo = targloc.x - curloc.x if(params) var/list/mouse_control = params2list(params) if(mouse_control["icon-x"]) - src.p_x = text2num(mouse_control["icon-x"]) + p_x = text2num(mouse_control["icon-x"]) if(mouse_control["icon-y"]) - src.p_y = text2num(mouse_control["icon-y"]) + p_y = text2num(mouse_control["icon-y"]) if(mouse_control["screen-loc"]) //Split screen-loc up into X+Pixel_X and Y+Pixel_Y var/list/screen_loc_params = splittext(mouse_control["screen-loc"], ",") @@ -90,6 +90,6 @@ var/ox = round(screenview/2) //"origin" x var/oy = round(screenview/2) //"origin" y var/angle = Atan2(y - oy, x - ox) - src.Angle = angle + Angle = angle if(spread) - src.Angle += spread + Angle += spread diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 7d72082b297..8233a172af8 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -65,7 +65,6 @@ ..() if(F) verbs += /obj/item/weapon/gun/proc/toggle_gunlight - new /datum/action/item_action/toggle_gunlight(src) build_zooming() /obj/item/weapon/gun/examine(mob/user) @@ -120,7 +119,7 @@ return if(!ismob(target) || user.a_intent == "harm") //melee attack return - if(target == user && user.zone_selected != "mouth") //so we can't shoot ourselves (unless mouth selected) + if(target == user && user.zone_sel.selecting != "mouth") //so we can't shoot ourselves (unless mouth selected) return if(istype(user))//Check if the user can use the gun, if the user isn't alive(turrets) assume it can. @@ -133,7 +132,7 @@ return if(flag) - if(user.zone_selected == "mouth") + if(user.zone_sel.selecting == "mouth") handle_suicide(user, target, params) return @@ -222,7 +221,7 @@ obj/item/weapon/gun/proc/newshot() user.update_inv_l_hand() else user.update_inv_r_hand() - feedback_add_details("gun_fired","[src.type]") + feedback_add_details("gun_fired","[type]") /obj/item/weapon/gun/attack(mob/M as mob, mob/user) if(user.a_intent == "harm") //Flogging @@ -239,15 +238,12 @@ obj/item/weapon/gun/proc/newshot() return to_chat(user, "You click [S] into place on [src].") if(S.on) - SetLuminosity(0) + set_light(0) F = S I.loc = src update_icon() update_gunlight(user) verbs += /obj/item/weapon/gun/proc/toggle_gunlight - var/datum/action/A = new /datum/action/item_action/toggle_gunlight(src) - if(loc == user) - A.Grant(user) if(istype(I, /obj/item/weapon/screwdriver)) if(F && can_flashlight) @@ -259,8 +255,6 @@ obj/item/weapon/gun/proc/newshot() S.update_brightness(user) update_icon() verbs -= /obj/item/weapon/gun/proc/toggle_gunlight - for(var/datum/action/item_action/toggle_gunlight/TGL in actions) - qdel(TGL) if(unique_rename) if(istype(I, /obj/item/weapon/pen)) @@ -351,7 +345,7 @@ obj/item/weapon/gun/proc/newshot() semicd = 1 - if(!do_mob(user, target, 120) || user.zone_selected != "mouth") + if(!do_mob(user, target, 120) || user.zone_sel.selecting != "mouth") if(user) if(user == target) user.visible_message("[user] decided life was worth living.") diff --git a/code/modules/projectiles/guns/alien.dm b/code/modules/projectiles/guns/alien.dm index 56852f4e1ae..a503e92dd6b 100644 --- a/code/modules/projectiles/guns/alien.dm +++ b/code/modules/projectiles/guns/alien.dm @@ -1,71 +1,5 @@ -//Vox pinning weapon. -/obj/item/weapon/gun/launcher/spikethrower - - name = "Vox spike thrower" - desc = "A vicious alien projectile weapon. Parts of it quiver gelatinously, as though the thing is insectile and alive." - - var/last_regen = 0 - var/spike_gen_time = 100 - var/max_spikes = 3 - var/spikes = 3 - release_force = 30 - icon = 'icons/obj/gun.dmi' - icon_state = "spikethrower3" - item_state = "spikethrower" - fire_sound_text = "a strange noise" - fire_sound = 'sound/weapons/bladeslice.ogg' - -/obj/item/weapon/gun/launcher/spikethrower/New() - ..() - processing_objects.Add(src) - last_regen = world.time - -/obj/item/weapon/gun/launcher/spikethrower/Destroy() - processing_objects.Remove(src) - return ..() - -/obj/item/weapon/gun/launcher/spikethrower/process() - - if(spikes < max_spikes && world.time > last_regen + spike_gen_time) - spikes++ - last_regen = world.time - update_icon() - -/obj/item/weapon/gun/launcher/spikethrower/examine(mob/user) - ..(user) - to_chat(user, "It has [spikes] [spikes == 1 ? "spike" : "spikes"] remaining.") - -/obj/item/weapon/gun/launcher/spikethrower/update_icon() - icon_state = "spikethrower[spikes]" - -/obj/item/weapon/gun/launcher/spikethrower/emp_act(severity) - return - -/obj/item/weapon/gun/launcher/spikethrower/special_check(user) - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - if(H.species && H.species.name != "Vox" && H.species.name != "Vox Armalis") - to_chat(user, "\red \The [src] does not respond to you!") - return 0 - return 1 - -/obj/item/weapon/gun/launcher/spikethrower/update_release_force() - return - -/obj/item/weapon/gun/launcher/spikethrower/chamber_round() - if(in_chamber) return 1 - if(spikes < 1) return 0 - - spikes-- - in_chamber = new /obj/item/weapon/spike(src) - return 1 - -/obj/item/weapon/gun/launcher/spikethrower/Fire(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, params, reflex = 0) - if(..()) update_icon() - //This gun only functions for armalis. The on-sprite is too huge to render properly on other sprites. /obj/item/weapon/gun/energy/noisecannon - name = "alien heavy cannon" desc = "It's some kind of enormous alien weapon, as long as a man is tall." @@ -73,15 +7,11 @@ icon_state = "noisecannon" item_state = "noisecannon" recoil = 1 - force = 10 - projectile_type = "/obj/item/projectile/energy/sonic" + ammo_type = "/obj/item/projectile/energy/sonic" cell_type = "/obj/item/weapon/stock_parts/cell/super" - fire_delay = 40 fire_sound = 'sound/effects/basscannon.ogg' - var/mode = 1 - /obj/item/weapon/gun/energy/noisecannon/attack_hand(mob/user as mob) if(loc != user) var/mob/living/carbon/human/H = user @@ -89,16 +19,18 @@ if(H.species.name == "Vox Armalis") ..() return - to_chat(user, "\red \The [src] is far too large for you to pick up.") + to_chat(user, "\The [src] is far too large for you to pick up.") return -/obj/item/weapon/gun/energy/noisecannon/process_chambered() //Does not have ammo. - in_chamber = new projectile_type(src) - return 1 - /obj/item/weapon/gun/energy/noisecannon/update_icon() return +//Casing +/obj/item/ammo_casing/energy/sonic + projectile_type = /obj/item/projectile/energy/sonic + fire_sound = 'sound/effects/basscannon.ogg' + delay = 40 + //Projectile. /obj/item/projectile/energy/sonic name = "distortion" diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm index df023424265..32c3009f312 100644 --- a/code/modules/projectiles/guns/energy.dm +++ b/code/modules/projectiles/guns/energy.dm @@ -3,6 +3,7 @@ name = "energy gun" desc = "A basic energy-based gun." fire_sound = 'sound/weapons/Taser.ogg' + fire_sound_text = "laser blast" var/obj/item/weapon/stock_parts/cell/power_supply //What type of power cell this uses var/cell_type = /obj/item/weapon/stock_parts/cell @@ -42,23 +43,24 @@ update_icon() /obj/item/weapon/gun/energy/Destroy() - if(self_recharge) + if(selfcharge) processing_objects.Remove(src) return ..() /obj/item/weapon/gun/energy/process() if(selfcharge) //Every [recharge_time] ticks, recharge a shot for the cyborg charge_tick++ - if(charge_tick < recharge_time) + if(charge_tick < charge_delay) return 0 charge_tick = 0 if(!power_supply) return 0 // check if we actually need to recharge + var/obj/item/ammo_casing/energy/E = ammo_type[select] if(use_external_power) var/obj/item/weapon/stock_parts/cell/external = get_external_power_supply() - if(!external || !external.use(charge_cost/10)) //Take power from the borg... + if(!external || !external.use((E.e_cost)/10)) //Take power from the borg... return 0 //Note, uses /10 because of shitty mods to the cell system - power_supply.give(charge_cost) //... to recharge the shot + power_supply.give(E.e_cost) //... to recharge the shot update_icon() /obj/item/weapon/gun/energy/attack_self(mob/living/user as mob) @@ -136,11 +138,11 @@ toggle_gunlight() /obj/item/weapon/gun/energy/suicide_act(mob/user) - if (src.can_shoot()) - user.visible_message("[user] is putting the barrel of the [src.name] in \his mouth. It looks like \he's trying to commit suicide.") + if (can_shoot()) + user.visible_message("[user] is putting the barrel of the [name] in \his mouth. It looks like \he's trying to commit suicide.") sleep(25) if(user.l_hand == src || user.r_hand == src) - user.visible_message("[user] melts \his face off with the [src.name]!") + user.visible_message("[user] melts \his face off with the [name]!") playsound(loc, fire_sound, 50, 1, -1) var/obj/item/ammo_casing/energy/shot = ammo_type[select] power_supply.use(shot.e_cost) @@ -150,21 +152,21 @@ user.visible_message("[user] panics and starts choking to death!") return(OXYLOSS) else - user.visible_message("[user] is pretending to blow \his brains out with the [src.name]! It looks like \he's trying to commit suicide!") + user.visible_message("[user] is pretending to blow \his brains out with the [name]! It looks like \he's trying to commit suicide!") playsound(loc, 'sound/weapons/empty.ogg', 50, 1, -1) return (OXYLOSS) /obj/item/weapon/gun/energy/proc/robocharge() - if(isrobot(src.loc)) - var/mob/living/silicon/robot/R = src.loc + if(isrobot(loc)) + var/mob/living/silicon/robot/R = loc if(R && R.cell) var/obj/item/ammo_casing/energy/shot = ammo_type[select] //Necessary to find cost of shot if(R.cell.use(shot.e_cost/10)) //Take power from the borg... //Divided by 10 because cells and charge costs are fucked. power_supply.give(shot.e_cost) //... to recharge the shot /obj/item/weapon/gun/energy/proc/get_external_power_supply() - if(istype(src.loc, /obj/item/rig_module)) - var/obj/item/rig_module/module = src.loc + if(istype(loc, /obj/item/rig_module)) + var/obj/item/rig_module/module = loc if(module.holder && module.holder.wearer) var/mob/living/carbon/human/H = module.holder.wearer if(istype(H) && H.back) diff --git a/code/modules/projectiles/guns/energy/laser.dm b/code/modules/projectiles/guns/energy/laser.dm index 46add8b8b47..952c61aaf00 100644 --- a/code/modules/projectiles/guns/energy/laser.dm +++ b/code/modules/projectiles/guns/energy/laser.dm @@ -92,9 +92,9 @@ /obj/item/weapon/gun/energy/lasercannon/mounted name = "mounted laser cannon" - self_recharge = 1 + selfcharge = 1 use_external_power = 1 - recharge_time = 10 + charge_delay = 10 /obj/item/weapon/gun/energy/lasercannon/cyborg @@ -130,7 +130,6 @@ origin_tech = "combat=1;magnets=2" clumsy_check = 0 needs_permit = 0 - pin = /obj/item/device/firing_pin/tag/blue ammo_x_offset = 2 selfcharge = 1 @@ -142,6 +141,5 @@ origin_tech = "combat=1;magnets=2" clumsy_check = 0 needs_permit = 0 - pin = /obj/item/device/firing_pin/tag/red ammo_x_offset = 2 selfcharge = 1 diff --git a/code/modules/projectiles/guns/energy/nuclear.dm b/code/modules/projectiles/guns/energy/nuclear.dm index b53563c81a7..ff9ef57a322 100644 --- a/code/modules/projectiles/guns/energy/nuclear.dm +++ b/code/modules/projectiles/guns/energy/nuclear.dm @@ -23,7 +23,7 @@ /obj/item/weapon/gun/energy/gun/mounted name = "mounted energy gun" - self_recharge = 1 + selfcharge = 1 use_external_power = 1 /obj/item/weapon/gun/energy/gun/mini @@ -37,8 +37,8 @@ can_flashlight = 0 // Can't attach or detach the flashlight, and override it's icon update /obj/item/weapon/gun/energy/gun/mini/New() - cell.maxcharge = 6000 - cell.charge = 6000 + power_supply.maxcharge = 6000 + power_supply.charge = 6000 F = new /obj/item/device/flashlight/seclite(src) ..() @@ -84,7 +84,6 @@ origin_tech = "combat=3;materials=5;powerstorage=3" var/fail_tick = 0 charge_delay = 5 - pin = null can_charge = 0 ammo_x_offset = 1 ammo_type = list(/obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/laser, /obj/item/ammo_casing/energy/disabler) @@ -106,11 +105,11 @@ switch(fail_tick) if(0 to 200) fail_tick += (2*(100-reliability)) - M.rad_act(40) + M.apply_effect(rand(3,120), IRRADIATE) to_chat(M, "Your [name] feels warmer.") if(201 to INFINITY) - SSobj.processing.Remove(src) - M.rad_act(80) + processing_objects.Remove(src) + M.apply_effect(300, IRRADIATE) crit_fail = 1 to_chat(M, "Your [name]'s reactor overloads!") @@ -130,27 +129,3 @@ overlays += "[icon_state]_fail_1" if(151 to INFINITY) overlays += "[icon_state]_fail_2" - -/obj/item/weapon/gun/energy/gun/turret/attack_self(mob/living/user as mob) - switch(mode) - if(0) - mode = 1 - charge_cost = 500 - fire_sound = 'sound/weapons/Laser.ogg' - to_chat(user, "\red [src.name] is now set to kill.") - projectile_type = /obj/item/projectile/beam - modifystate = "energykill" - fire_delay = 0 - if(1) - mode = 0 - charge_cost = 1000 - fire_sound = 'sound/weapons/Taser.ogg' - to_chat(user, "\red [src.name] is now set to stun.") - projectile_type = /obj/item/projectile/energy/electrode - modifystate = "energystun" - fire_delay = 15 - update_icon() - if(user.l_hand == src) - user.update_inv_l_hand() - else - user.update_inv_r_hand() diff --git a/code/modules/projectiles/guns/energy/pulse.dm b/code/modules/projectiles/guns/energy/pulse.dm index b8ddc12a691..0587040afe4 100644 --- a/code/modules/projectiles/guns/energy/pulse.dm +++ b/code/modules/projectiles/guns/energy/pulse.dm @@ -54,7 +54,7 @@ ammo_type = list(/obj/item/ammo_casing/energy/laser/pulse) /obj/item/weapon/gun/energy/pulse/destroyer/attack_self(mob/living/user) - to_chat(user, "[src.name] has three settings, and they are all DESTROY.") + to_chat(user, "[name] has three settings, and they are all DESTROY.") /obj/item/weapon/gun/energy/pulse_rifle/pistol/m1911 name = "\improper M1911-P" diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm index e0328ac6058..949e0ef7d7d 100644 --- a/code/modules/projectiles/guns/energy/special.dm +++ b/code/modules/projectiles/guns/energy/special.dm @@ -173,7 +173,7 @@ /obj/item/weapon/gun/energy/kinetic_accelerator/proc/reload() power_supply.give(5000) if(!suppressed) - playsound(src.loc, 'sound/weapons/kenetic_reload.ogg', 60, 1) + playsound(loc, 'sound/weapons/kenetic_reload.ogg', 60, 1) else to_chat(loc, "[src] silently charges up.") update_icon() @@ -200,6 +200,10 @@ holds_charge = TRUE unique_frequency = TRUE +/obj/item/weapon/gun/energy/kinetic_accelerator/crossbow/ninja + name = "energy dart thrower" + ammo_type = list(/obj/item/ammo_casing/energy/dart) + /obj/item/weapon/gun/energy/kinetic_accelerator/crossbow/large name = "energy crossbow" desc = "A reverse engineered weapon using syndicate technology." @@ -218,8 +222,8 @@ /obj/item/weapon/gun/energy/kinetic_accelerator/suicide_act(mob/user) if(!suppressed) - playsound(src.loc, 'sound/weapons/kenetic_reload.ogg', 60, 1) - user.visible_message("[user] cocks the [src.name] and pretends to blow \his brains out! It looks like \he's trying to commit suicide!") + playsound(loc, 'sound/weapons/kenetic_reload.ogg', 60, 1) + user.visible_message("[user] cocks the [name] and pretends to blow \his brains out! It looks like \he's trying to commit suicide!") shoot_live_shot() return (OXYLOSS) @@ -235,9 +239,9 @@ flags = CONDUCT | OPENCONTAINER attack_verb = list("attacked", "slashed", "cut", "sliced") force = 12 - sharpness = IS_SHARP + sharp = 1 + edge = 1 can_charge = 0 - heat = 3800 /obj/item/weapon/gun/energy/plasmacutter/examine(mob/user) ..() @@ -406,7 +410,7 @@ obj/item/weapon/gun/energy/staff/focus var/e_cost = 1000 origin_tech = "combat=3;materials=4;powerstorage=3;magnets=2" - ammo_type = /obj/item/ammo_casing/energy/temp + ammo_type = list(/obj/item/ammo_casing/energy/temp) cell_type = "/obj/item/weapon/stock_parts/cell" var/powercost = "" @@ -427,8 +431,9 @@ obj/item/weapon/gun/energy/staff/focus /obj/item/weapon/gun/energy/temperature/newshot() ..() - chambered.temperature = temperature - chambered.e_cost = e_cost + var/obj/item/ammo_casing/energy/temp/T = chambered + T.temperature = temperature + T.e_cost = e_cost /obj/item/weapon/gun/energy/temperature/attack_self(mob/living/user as mob) user.set_machine(src) @@ -459,23 +464,22 @@ obj/item/weapon/gun/energy/staff/focus add_fingerprint(usr) return - /obj/item/weapon/gun/energy/temperature/process() switch(temperature) if(0 to 100) - charge_cost = 3000 + e_cost = 3000 powercost = "High" if(100 to 250) - charge_cost = 2000 + e_cost = 2000 powercost = "Medium" if(251 to 300) - charge_cost = 1000 + e_cost = 1000 powercost = "Low" if(301 to 400) - charge_cost = 2000 + e_cost = 2000 powercost = "Medium" if(401 to 1000) - charge_cost = 3000 + e_cost = 3000 powercost = "High" switch(powercost) if("High") powercostcolor = "orange" diff --git a/code/modules/projectiles/guns/energy/stun.dm b/code/modules/projectiles/guns/energy/stun.dm index 5e3f5128d6f..6fdbe55c171 100644 --- a/code/modules/projectiles/guns/energy/stun.dm +++ b/code/modules/projectiles/guns/energy/stun.dm @@ -9,7 +9,7 @@ /obj/item/weapon/gun/energy/taser/mounted name = "mounted taser gun" - self_recharge = 1 + selfcharge = 1 use_external_power = 1 /obj/item/weapon/gun/energy/taser/cyborg diff --git a/code/modules/projectiles/guns/energy/telegun.dm b/code/modules/projectiles/guns/energy/telegun.dm index e145e2ccbcf..0113f11068c 100644 --- a/code/modules/projectiles/guns/energy/telegun.dm +++ b/code/modules/projectiles/guns/energy/telegun.dm @@ -35,3 +35,7 @@ var/desc = input("Please select a location to lock in.", "Telegun Target Interface") in L teleport_target = L[desc] + +/obj/item/weapon/gun/energy/telegun/newshot() + var/obj/item/ammo_casing/energy/teleport/T = chambered + T.teleport_target = teleport_target diff --git a/code/modules/projectiles/guns/grenade_launcher.dm b/code/modules/projectiles/guns/grenade_launcher.dm index 5d883ac7de7..7af9c18ea7d 100644 --- a/code/modules/projectiles/guns/grenade_launcher.dm +++ b/code/modules/projectiles/guns/grenade_launcher.dm @@ -15,7 +15,7 @@ /obj/item/weapon/gun/grenadelauncher/examine(mob/user) if(..(user, 2)) - to_chat(user, "[grenades.len] / [max_grenades] [ammo_name]s.") + to_chat(user, "[grenades.len] / [max_grenades] grenades.") /obj/item/weapon/gun/grenadelauncher/attackby(obj/item/I as obj, mob/user as mob, params) if((istype(I, /obj/item/weapon/grenade))) @@ -24,10 +24,10 @@ return I.loc = src grenades += I - to_chat(user, "You put the [ammo_name] in the [name].") - to_chat(user, "[grenades.len] / [max_grenades] [ammo_name]s.") + to_chat(user, "You put the grenade in the [name].") + to_chat(user, "[grenades.len] / [max_grenades] grenades.") else - to_chat(user, "The grenade launcher cannot hold more [ammo_name]s.") + to_chat(user, "The grenade launcher cannot hold more grenades.") /obj/item/weapon/gun/grenadelauncher/afterattack(obj/target, mob/user , flag) if(target == user) @@ -45,8 +45,8 @@ grenades -= F F.loc = user.loc F.throw_at(target, 30, 2, user) - message_admins("[key_name_admin(user)] fired a grenade ([F.name]) from a grenade launcher ([src.name]).") - log_game("[key_name(user)] fired a grenade ([F.name]) from a grenade launcher ([src.name]).") + message_admins("[key_name_admin(user)] fired a grenade ([F.name]) from a grenade launcher ([name]).") + log_game("[key_name(user)] fired a grenade ([F.name]) from a grenade launcher ([name]).") F.active = 1 F.icon_state = initial(icon_state) + "_active" playsound(user.loc, 'sound/weapons/armbomb.ogg', 75, 1, -3) diff --git a/code/modules/projectiles/guns/magic.dm b/code/modules/projectiles/guns/magic.dm index b64fb37e1c6..299c87b536f 100644 --- a/code/modules/projectiles/guns/magic.dm +++ b/code/modules/projectiles/guns/magic.dm @@ -5,6 +5,7 @@ icon_state = "staffofnothing" item_state = "staff" fire_sound = 'sound/weapons/emitter.ogg' + fire_sound_text = "energy blast" flags = CONDUCT w_class = 5 var/max_charges = 6 @@ -75,6 +76,6 @@ return /obj/item/weapon/gun/magic/suicide_act(mob/user) - user.visible_message("[user] is twisting the [src.name] above \his head, releasing a magical blast! It looks like \he's trying to commit suicide.") + user.visible_message("[user] is twisting the [name] above \his head, releasing a magical blast! It looks like \he's trying to commit suicide.") playsound(loc, fire_sound, 50, 1, -1) return FIRELOSS diff --git a/code/modules/projectiles/guns/magic/wand.dm b/code/modules/projectiles/guns/magic/wand.dm index c6d5bc4bf00..abde404e87d 100644 --- a/code/modules/projectiles/guns/magic/wand.dm +++ b/code/modules/projectiles/guns/magic/wand.dm @@ -67,7 +67,7 @@ var/message ="You irradiate yourself with pure energy! " message += pick("Do not pass go. Do not collect 200 zorkmids.","You feel more confident in your spell casting skills.","You Die...","Do you want your possessions identified?") to_chat(user, message) - user.adjustBurnLoss(500) + user.adjustFireLoss(500) charges-- ..() diff --git a/code/modules/projectiles/guns/medbeam.dm b/code/modules/projectiles/guns/medbeam.dm index bd5f6b23e79..032874c4918 100644 --- a/code/modules/projectiles/guns/medbeam.dm +++ b/code/modules/projectiles/guns/medbeam.dm @@ -48,7 +48,7 @@ spawn(0) current_beam.Start() - feedback_add_details("gun_fired","[src.type]") + feedback_add_details("gun_fired","[type]") /obj/item/weapon/gun/medbeam/process() var/mob/living/carbon/human/H = loc diff --git a/code/modules/projectiles/guns/mounted.dm b/code/modules/projectiles/guns/mounted.dm index b200dd1d6a7..592908a6b87 100644 --- a/code/modules/projectiles/guns/mounted.dm +++ b/code/modules/projectiles/guns/mounted.dm @@ -12,7 +12,7 @@ /obj/item/weapon/gun/energy/advtaser/mounted/dropped()//if somebody manages to drop this somehow... ..() - src.loc = null//send it to nullspace to get retrieved by the implant later on. gotta cover those edge cases. + loc = null//send it to nullspace to get retrieved by the implant later on. gotta cover those edge cases. /obj/item/weapon/gun/energy/laser/mounted name = "mounted laser" @@ -28,4 +28,4 @@ /obj/item/weapon/gun/energy/laser/mounted/dropped() ..() - src.loc = null + loc = null diff --git a/code/modules/projectiles/guns/projectile.dm b/code/modules/projectiles/guns/projectile.dm index fddc09349ab..3fc01b0a5b5 100644 --- a/code/modules/projectiles/guns/projectile.dm +++ b/code/modules/projectiles/guns/projectile.dm @@ -96,7 +96,7 @@ if(user.l_hand != src && user.r_hand != src) ..() return - to_chat(user, "You unscrew [silenced] from [src].") + to_chat(user, "You unscrew [suppressed] from [src].") user.put_in_hands(suppressed) fire_sound = S.oldsound w_class = S.initial_w_class @@ -108,7 +108,7 @@ /obj/item/weapon/gun/projectile/attack_self(mob/living/user as mob) var/obj/item/ammo_casing/AC = chambered //Find chambered round if(magazine) - magazine.loc = get_turf(src.loc) + magazine.loc = get_turf(loc) user.put_in_hands(magazine) magazine.update_icon() magazine = null @@ -136,18 +136,18 @@ return boolets /obj/item/weapon/gun/projectile/suicide_act(mob/user) - if (src.chambered && src.chambered.BB && !src.chambered.BB.nodamage) - user.visible_message("[user] is putting the barrel of the [src.name] in \his mouth. It looks like \he's trying to commit suicide.") + if (chambered && chambered.BB && !chambered.BB.nodamage) + user.visible_message("[user] is putting the barrel of the [name] in \his mouth. It looks like \he's trying to commit suicide.") sleep(25) if(user.l_hand == src || user.r_hand == src) process_fire(user, user, 0, zone_override = "head") - user.visible_message("[user] blows \his brains out with the [src.name]!") + user.visible_message("[user] blows \his brains out with the [name]!") return(BRUTELOSS) else user.visible_message("[user] panics and starts choking to death!") return(OXYLOSS) else - user.visible_message("[user] is pretending to blow \his brains out with the [src.name]! It looks like \he's trying to commit suicide!") + user.visible_message("[user] is pretending to blow \his brains out with the [name]! It looks like \he's trying to commit suicide!") playsound(loc, 'sound/weapons/empty.ogg', 50, 1, -1) return (OXYLOSS) diff --git a/code/modules/projectiles/guns/projectile/automatic.dm b/code/modules/projectiles/guns/projectile/automatic.dm index 8e700c92cc0..b3d09e049cd 100644 --- a/code/modules/projectiles/guns/projectile/automatic.dm +++ b/code/modules/projectiles/guns/projectile/automatic.dm @@ -1,6 +1,5 @@ /obj/item/weapon/gun/projectile/automatic w_class = 3.0 - automatic = 1 var/alarmed = 0 var/select = 1 can_suppress = 1 @@ -37,14 +36,14 @@ if(istype(AM, mag_type)) if(magazine) to_chat(user, "You perform a tactical reload on \the [src], replacing the magazine.") - magazine.forceMove(get_turf(src.loc)) + magazine.loc = get_turf(loc) magazine.update_icon() magazine = null else to_chat(user, "You insert the magazine into \the [src].") user.remove_from_mob(AM) magazine = AM - magazine.forceMove(src) + magazine.loc = src chamber_round() A.update_icon() update_icon() @@ -74,7 +73,7 @@ /obj/item/weapon/gun/projectile/automatic/proc/empty_alarm() if(!chambered && !get_ammo() && !alarmed) - playsound(src.loc, 'sound/weapons/smg_empty_alarm.ogg', 40, 1) + playsound(loc, 'sound/weapons/smg_empty_alarm.ogg', 40, 1) update_icon() alarmed = 1 return diff --git a/code/modules/projectiles/guns/projectile/revolver.dm b/code/modules/projectiles/guns/projectile/revolver.dm index 66e404a7de8..1a9da6fcf22 100644 --- a/code/modules/projectiles/guns/projectile/revolver.dm +++ b/code/modules/projectiles/guns/projectile/revolver.dm @@ -45,7 +45,7 @@ var/obj/item/ammo_casing/CB CB = magazine.get_round(0) if(CB) - CB.loc = get_turf(src.loc) + CB.loc = get_turf(loc) CB.SpinAnimation(10, 1) CB.update_icon() num_unloaded++ @@ -124,7 +124,7 @@ afterattack(user, user) //you know the drill user.visible_message("[src] goes off!", "[src] goes off in your face!") return - if(do_after(user, 30/A.toolspeed, target = src)) + if(do_after(user, 30, target = src)) if(magazine.ammo_count()) to_chat(user, "You can't modify it!") return @@ -173,7 +173,7 @@ name = "\improper Russian Revolver" desc = "A Russian-made revolver for drinking games. Uses .357 ammo, and has a mechanism that spins the chamber before each trigger pull." origin_tech = "combat=2;materials=2" - mag_type = /obj/item/ammo_box/magazine/internal/cylinder/rus357 + mag_type = /obj/item/ammo_box/magazine/internal/rus357 var/spun = 0 @@ -211,7 +211,7 @@ var/obj/item/ammo_casing/CB CB = magazine.get_round() chambered = null - CB.loc = get_turf(src.loc) + CB.loc = get_turf(loc) CB.update_icon() num_unloaded++ if (num_unloaded) @@ -234,7 +234,6 @@ return if(ishuman(user)) - var/mob/living/carbon/human/H = user if(!spun) to_chat(user, "You need to spin the revolver's chamber first!") return @@ -245,12 +244,11 @@ var/obj/item/ammo_casing/AC = chambered if(AC.fire(user, user)) playsound(user, fire_sound, 50, 1) - var/zone = check_zone(user.zone_selected) - var/obj/item/bodypart/affecting = H.get_bodypart(zone) + var/zone = check_zone(user.zone_sel.selecting) if(zone == "head" || zone == "eyes" || zone == "mouth") - shoot_self(user, affecting) + shoot_self(user, zone) else - user.visible_message("[user.name] cowardly fires [src] at \his [affecting.name]!", "You cowardly fire [src] at your [affecting.name]!", "You hear a gunshot!") + user.visible_message("[user.name] cowardly fires [src] at \his [zone]!", "You cowardly fire [src] at your [zone]!", "You hear a gunshot!") return user.visible_message("*click*") diff --git a/code/modules/projectiles/guns/projectile/rocket.dm b/code/modules/projectiles/guns/projectile/rocket.dm index a82caddeba1..b2d01b303ed 100644 --- a/code/modules/projectiles/guns/projectile/rocket.dm +++ b/code/modules/projectiles/guns/projectile/rocket.dm @@ -45,8 +45,8 @@ playsound(user.loc, 'sound/effects/bang.ogg', 50, 1) M.primed = 1 M.throw_at(target, missile_range, missile_speed,user) - message_admins("[key_name_admin(user)] fired a rocket from a rocket launcher ([src.name]).") - log_game("[key_name_admin(user)] used a rocket launcher ([src.name]).") + message_admins("[key_name_admin(user)] fired a rocket from a rocket launcher ([name]).") + log_game("[key_name_admin(user)] used a rocket launcher ([name]).") rockets -= I qdel(I) return diff --git a/code/modules/projectiles/guns/projectile/saw.dm b/code/modules/projectiles/guns/projectile/saw.dm index 5c4856d2503..4cb8a59b305 100644 --- a/code/modules/projectiles/guns/projectile/saw.dm +++ b/code/modules/projectiles/guns/projectile/saw.dm @@ -39,7 +39,7 @@ else if(cover_open && magazine) //drop the mag magazine.update_icon() - magazine.loc = get_turf(src.loc) + magazine.loc = get_turf(loc) user.put_in_hands(magazine) magazine = null update_icon() @@ -69,7 +69,7 @@ . = ..() if((blocked != 100) && istype(target, /mob/living/carbon/human)) var/mob/living/carbon/human/H = target - H.drip(35) + H.drip(350) /obj/item/projectile/bullet/saw/hollow damage = 60 diff --git a/code/modules/projectiles/guns/projectile/shotgun.dm b/code/modules/projectiles/guns/projectile/shotgun.dm index 1119a795505..47ffa8de488 100644 --- a/code/modules/projectiles/guns/projectile/shotgun.dm +++ b/code/modules/projectiles/guns/projectile/shotgun.dm @@ -22,7 +22,7 @@ update_icon() -/obj/item/weapon/gun/projectile/shotgun/process_chambered() +/obj/item/weapon/gun/projectile/shotgun/process_chamber() return ..(0, 0) /obj/item/weapon/gun/projectile/shotgun/chamber_round() @@ -79,7 +79,7 @@ name = "riot shotgun" desc = "A sturdy shotgun with a longer magazine and a fixed tactical stock designed for non-lethal riot control." icon_state = "riotshotgun" - mag_type = /obj/item/ammo_box/magazine/internal/shotriot + mag_type = /obj/item/ammo_box/magazine/internal/shot/riot sawn_desc = "Come with me if you want to live." /obj/item/weapon/gun/projectile/shotgun/riot/attackby(obj/item/A, mob/user, params) @@ -143,7 +143,7 @@ ..() if(guns_left) var/obj/item/weapon/gun/projectile/shotgun/boltaction/enchanted/GUN = new - GUN.guns_left = src.guns_left - 1 + GUN.guns_left = guns_left - 1 user.drop_item() user.swap_hand() user.put_in_hands(GUN) @@ -172,7 +172,7 @@ flags = CONDUCT slot_flags = SLOT_BACK origin_tech = "combat=3;materials=1" - mag_type = /obj/item/ammo_box/magazine/internal/cylinder/dual + mag_type = /obj/item/ammo_box/magazine/internal/shot/dual sawn_desc = "Omar's coming!" unique_rename = 1 unique_reskin = 1 @@ -204,7 +204,7 @@ var/obj/item/ammo_casing/CB CB = magazine.get_round(0) chambered = null - CB.loc = get_turf(src.loc) + CB.loc = get_turf(loc) CB.update_icon() num_unloaded++ if (num_unloaded) @@ -286,7 +286,7 @@ if(do_after(user, 30, target = src)) user.visible_message("[user] shortens \the [src]!", "You shorten \the [src]!") - name = "sawn-off [src.name]" + name = "sawn-off [name]" desc = sawn_desc icon_state = "[icon_state]-sawn" w_class = 3 @@ -303,7 +303,7 @@ /obj/item/weapon/gun/projectile/shotgun/automatic/shoot_live_shot(mob/living/user as mob|obj) ..() - src.pump(user) + pump(user) /obj/item/weapon/gun/projectile/shotgun/automatic/combat name = "combat shotgun" @@ -375,7 +375,7 @@ sawn_desc = "I'm sorry, but why did you saw your cane in the first place?" attack_verb = list("bludgeoned", "whacked", "disciplined", "thrashed") fire_sound = 'sound/weapons/Gunshot_silenced.ogg' - silenced = 1 + suppressed = 1 needs_permit = 0 //its just a cane beepsky..... /obj/item/weapon/gun/projectile/revolver/doublebarrel/improvised/cane/attackby(obj/item/A, mob/user, params) @@ -385,7 +385,7 @@ /obj/item/weapon/gun/projectile/revolver/doublebarrel/improvised/cane/examine(mob/user) // HAD TO REPEAT EXAMINE CODE BECAUSE GUN CODE DOESNT STEALTH var/f_name = "\a [src]." - if(src.blood_DNA && !istype(src, /obj/effect/decal)) + if(blood_DNA && !istype(src, /obj/effect/decal)) if(gender == PLURAL) f_name = "some " else diff --git a/code/modules/projectiles/guns/projectile/sniper.dm b/code/modules/projectiles/guns/projectile/sniper.dm index 5033d0f8a0e..59bd7fb649b 100644 --- a/code/modules/projectiles/guns/projectile/sniper.dm +++ b/code/modules/projectiles/guns/projectile/sniper.dm @@ -4,7 +4,7 @@ icon_state = "sniper" item_state = "sniper" recoil = 2 - heavy_weapon = 1 + weapon_weight = WEAPON_MEDIUM mag_type = /obj/item/ammo_box/magazine/sniper_rounds fire_delay = 40 origin_tech = "combat=8" @@ -117,7 +117,7 @@ /obj/item/projectile/bullet/sniper/haemorrhage/on_hit(atom/target, blocked = 0, hit_zone) if((blocked != 100) && istype(target, /mob/living/carbon/human)) var/mob/living/carbon/human/H = target - H.drip(100) + H.drip(1000) return ..() diff --git a/code/modules/projectiles/guns/syringe_gun.dm b/code/modules/projectiles/guns/syringe_gun.dm index 45a590bd420..332d279376b 100644 --- a/code/modules/projectiles/guns/syringe_gun.dm +++ b/code/modules/projectiles/guns/syringe_gun.dm @@ -28,7 +28,7 @@ chambered.BB = new S.projectile_type (src) - S.reagents.trans_to(in_chamber, S.reagents.total_volume) + S.reagents.trans_to(chambered.BB, S.reagents.total_volume) chambered.BB.name = S.name syringes.Remove(S) @@ -55,8 +55,9 @@ var/obj/item/weapon/reagent_containers/syringe/S = syringes[syringes.len] - if(!S) return 0 - S.forceMove(user.loc) + if(!S) + return 0 + S.loc = user.loc syringes.Remove(S) to_chat(user, "You unload [S] from \the [src]!") @@ -70,7 +71,7 @@ return to_chat(user, "You load [A] into \the [src]!") syringes.Add(A) - A.forceMove(src) + A.loc = src return 1 else to_chat(user, "[src] cannot hold more syringes.") @@ -90,5 +91,5 @@ w_class = 2 origin_tech = "combat=2;syndicate=2" force = 2 //Also very weak because it's smaller - silenced = 1 //Softer fire sound + suppressed = 1 //Softer fire sound can_unsuppress = 0 //Permanently silenced diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 0cba3aa5fbf..9816fa7a804 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -79,7 +79,7 @@ playsound(loc, hitsound, volume, 1, -1) L.visible_message("[L] is hit by \a [src][organ_hit_text]!", \ "[L] is hit by \a [src][organ_hit_text]!") //X has fired Y is now given by the guns so you cant tell who shot you if you could not see the shooter - L.on_hit(type) + L.apply_effects(stun, weaken, paralyze, irradiate, slur, stutter, eyeblur, drowsy, blocked, stamina, jitter) var/reagent_note if(reagents && reagents.reagent_list) @@ -91,8 +91,8 @@ return L.apply_effects(stun, weaken, paralyze, irradiate, slur, stutter, eyeblur, drowsy, blocked, stamina, jitter) /obj/item/projectile/proc/vol_by_damage() - if(src.damage) - return Clamp((src.damage) * 0.67, 30, 100)// Multiply projectile damage by 0.67, then clamp the value between 30 and 100 + if(damage) + return Clamp((damage) * 0.67, 30, 100)// Multiply projectile damage by 0.67, then clamp the value between 30 and 100 else return 50 //if the projectile doesn't do damage, play its hitsound at 50% volume @@ -113,10 +113,10 @@ if(isnull(organ)) return else if(isturf(A) && hitsound_wall) - var/volume = Clamp(vol_by_damage() + 20, 0, 100) - if(suppressed) - volume = 5 - playsound(loc, hitsound_wall, volume, 1, -1) + var/volume = Clamp(vol_by_damage() + 20, 0, 100) + if(suppressed) + volume = 5 + playsound(loc, hitsound_wall, volume, 1, -1) var/turf/target_turf = get_turf(A) @@ -220,5 +220,5 @@ obj/item/projectile/Crossed(atom/movable/AM) //A mob moving on a tile with a pro return ..() /obj/item/projectile/proc/dumbfire(var/dir) - current = get_ranged_target_turf(src, dir, world.maxx) //world.maxx is the range. Not sure how to handle this better. - fire() + current = get_ranged_target_turf(src, dir, world.maxx) //world.maxx is the range. Not sure how to handle this better. + fire() diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm index b11c4f66961..8c946455a65 100644 --- a/code/modules/projectiles/projectile/beams.dm +++ b/code/modules/projectiles/projectile/beams.dm @@ -87,11 +87,11 @@ name = "laser tag beam" icon_state = "omnilaser" -/obj/item/projectile/beam/lasertag/red +/obj/item/projectile/beam/lasertag/redtag icon_state = "laser" suit_types = list(/obj/item/clothing/suit/bluetag) -/obj/item/projectile/beam/lasertag/blue +/obj/item/projectile/beam/lasertag/bluetag icon_state = "bluelaser" suit_types = list(/obj/item/clothing/suit/redtag) @@ -131,156 +131,3 @@ var/mob/living/carbon/M = target M.visible_message("[M] explodes into a shower of gibs!") M.gib() - -/* //THIS CODE MAKES ME WANT TO STAB MY EYES OUT -/obj/item/projectile/beam/lightning - invisibility = 101 - name = "lightning" - damage = 0 - icon = 'icons/obj/lightning.dmi' - icon_state = "lightning" - stun = 10 - weaken = 10 - stutter = 50 - eyeblur = 50 - var/tang = 0 - layer = 3 - var/turf/last = null - kill_count = 6 - var/bullet_acted = 0 - -/obj/item/projectile/beam/lightning/Bump(var/atom/A) - if((A != firer) && !bullet_acted) - A.bullet_act(src) - bullet_acted = 1 - -/obj/item/projectile/beam/lightning/proc/adjustAngle(angle) - angle = round(angle) + 45 - if(angle > 180) - angle -= 180 - else - angle += 180 - if(!angle) - angle = 1 - return angle - -/obj/item/projectile/beam/lightning/process() - var/first = 1 //So we don't make the overlay in the same tile as the firer - var/broke = 0 - var/broken - var/atom/curr = current - var/Angle=round(Get_Angle(firer,curr)) - var/icon/I=new('icons/obj/zap.dmi',"lightning") - I.Turn(Angle) - var/DX=(32*curr.x+curr.pixel_x)-(32*firer.x+firer.pixel_x) - var/DY=(32*curr.y+curr.pixel_y)-(32*firer.y+firer.pixel_y) - var/N=0 - var/length=round(sqrt((DX)**2+(DY)**2)) - var/count = 0 - for(N,N= kill_count) - break - count++ - var/obj/effect/overlay/beam/X=new(loc) - X.BeamSource=src - if(N+32>length) - var/icon/II=new(icon,icon_state) - II.DrawBox(null,1,(length-N),32,32) - II.Turn(Angle) - X.icon=II - else X.icon=I - var/Pixel_x=round(sin(Angle)+32*sin(Angle)*(N+16)/32) - var/Pixel_y=round(cos(Angle)+32*cos(Angle)*(N+16)/32) - if(DX==0) Pixel_x=0 - if(DY==0) Pixel_y=0 - if(Pixel_x>32) - for(var/a=0, a<=Pixel_x,a+=32) - X.x++ - Pixel_x-=32 - if(Pixel_x<-32) - for(var/a=0, a>=Pixel_x,a-=32) - X.x-- - Pixel_x+=32 - if(Pixel_y>32) - for(var/a=0, a<=Pixel_y,a+=32) - X.y++ - Pixel_y-=32 - if(Pixel_y<-32) - for(var/a=0, a>=Pixel_y,a-=32) - X.y-- - Pixel_y+=32 - X.pixel_x=Pixel_x - X.pixel_y=Pixel_y - var/turf/TT = get_turf(X.loc) - if(TT == firer.loc) - continue - if(TT.density) - qdel(X) - break - for(var/atom/O in TT) - if(istype(O,/mob/living)) - if(O.density) - qdel(X) - broke = 1 - break - if(!O.CanPass(src)) - qdel(X) - broke = 1 - break - if(broke) - if(X) - qdel(X) - break - spawn - while(src) //Move until we hit something - if(first) - icon = midicon - if((!( current ) || loc == current)) //If we pass our target - broken = 1 - icon = endicon - tang = adjustAngle(get_angle(original,current)) - if(tang > 180) - tang -= 180 - else - tang += 180 - icon_state = "[tang]" - var/turf/simulated/floor/f = current - if(f && istype(f)) - if(!bullet_acted) - f.break_tile() - f.hotspot_expose(1000,CELL_VOLUME) - if((x == 1 || x == world.maxx || y == 1 || y == world.maxy)) - broken = 1 - return - if(kill_count < 1) - broken = 1 - kill_count-- - if(!bumped && !isturf(original)) - if(loc == get_turf(original)) - if(!(original in permutated)) - icon = endicon - if(!broken) - tang = adjustAngle(get_angle(original,current)) - if(tang > 180) - tang -= 180 - else - tang += 180 - icon_state = "[tang]" - Bump(original) - first = 0 - if(broken) - break - else - last = get_turf(src.loc) - step_towards(src, current) //Move~ - if(src.loc != current) - tang = adjustAngle(get_angle(src.loc,current)) - icon_state = "[tang]" - qdel(src) - return -/obj/item/projectile/beam/lightning/on_hit(atom/target, blocked = 0) - if(istype(target, /mob/living)) - var/mob/living/M = target - M.playsound_local(src, "explosion", 50, 1) - ..() -*/ diff --git a/code/modules/projectiles/projectile/bullets.dm b/code/modules/projectiles/projectile/bullets.dm index f3a676d962f..38c9a9efd55 100644 --- a/code/modules/projectiles/projectile/bullets.dm +++ b/code/modules/projectiles/projectile/bullets.dm @@ -80,7 +80,7 @@ range = rand(8) /obj/item/projectile/bullet/pellet/weak/on_range() - var/datum/effect_system/spark_spread/sparks = new /datum/effect_system/spark_spread + var/datum/effect/system/spark_spread/sparks = new /datum/effect/system/spark_spread sparks.set_up(1, 1, src) sparks.start() ..() @@ -97,7 +97,7 @@ /obj/item/projectile/bullet/pellet/overload/on_range() explosion(src, 0, 0, 2) - var/datum/effect_system/spark_spread/sparks = new /datum/effect_system/spark_spread + var/datum/effect/system/spark_spread/sparks = new /datum/effect/system/spark_spread sparks.set_up(3, 3, src) sparks.start() ..() @@ -179,7 +179,7 @@ forcedodge = 1 nodamage = 1 hitsound = 'sound/items/bikehorn.ogg' - icon = 'icons/obj/hydroponics/harvest.dmi' + icon = 'icons/obj/harvest.dmi' icon_state = "banana" range = 200 diff --git a/code/modules/projectiles/projectile/energy.dm b/code/modules/projectiles/projectile/energy.dm index b023d77de52..79ffc1d0898 100644 --- a/code/modules/projectiles/projectile/energy.dm +++ b/code/modules/projectiles/projectile/energy.dm @@ -21,7 +21,7 @@ /obj/item/projectile/energy/electrode/on_hit(var/atom/target, var/blocked = 0) . = ..() if(!ismob(target) || blocked >= 100) //Fully blocked by mob or collided with dense object - burst into sparks! - var/datum/effect_system/spark_spread/sparks = new /datum/effect_system/spark_spread + var/datum/effect/system/spark_spread/sparks = new /datum/effect/system/spark_spread sparks.set_up(1, 1, src) sparks.start() else if(iscarbon(target)) @@ -33,7 +33,7 @@ C.do_jitter_animation(jitter) /obj/item/projectile/energy/electrode/on_range() //to ensure the bolt sparks when it reaches the end of its range if it didn't hit a target yet - var/datum/effect_system/spark_spread/sparks = new /datum/effect_system/spark_spread + var/datum/effect/system/spark_spread/sparks = new /datum/effect/system/spark_spread sparks.set_up(1, 1, src) sparks.start() ..() @@ -80,7 +80,7 @@ /obj/item/ammo_casing/energy/shock_revolver/ready_proj(atom/target, mob/living/user, quiet, zone_override = "") ..() - var/obj/item/projectile/hook/P = BB + var/obj/item/projectile/energy/shock_revolver/P = BB spawn(1) P.chain = P.Beam(user,icon_state="purple_lightning",icon = 'icons/effects/effects.dmi',time=1000, maxdistance = 30) diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm index 74eff57d98d..1d1f6dc93a8 100644 --- a/code/modules/projectiles/projectile/magic.dm +++ b/code/modules/projectiles/projectile/magic.dm @@ -48,7 +48,7 @@ /obj/item/projectile/magic/resurrection/on_hit(var/mob/living/carbon/target) . = ..() if(ismob(target)) - var/old_stat = stat + var/old_stat = target.stat target.suiciding = 0 target.revive() if(!target.ckey) @@ -94,16 +94,22 @@ if(isturf(target) && target.density) CreateDoor(target) else if (isturf(T) && T.density) - CreateDoor(T + CreateDoor(T) else if(istype(target, /obj/machinery/door)) - OpenDoor(target)) + OpenDoor(target) /obj/item/projectile/magic/door/proc/CreateDoor(turf/T) var/door_type = pick(door_types) var/obj/structure/mineral_door/D = new door_type(T) - T.ChangeTurf(/turf/open/floor/plating) + T.ChangeTurf(/turf/simulated/floor/plasteel) D.Open() +/obj/item/projectile/magic/door/proc/OpenDoor(var/obj/machinery/door/D) + if(istype(D,/obj/machinery/door/airlock)) + var/obj/machinery/door/airlock/A = D + A.locked = 0 + D.open() + /obj/item/projectile/magic/change name = "bolt of change" icon_state = "ice_1" diff --git a/code/modules/projectiles/projectile/reusable.dm b/code/modules/projectiles/projectile/reusable.dm index 9f4e36bc1b4..466a8ba8e99 100644 --- a/code/modules/projectiles/projectile/reusable.dm +++ b/code/modules/projectiles/projectile/reusable.dm @@ -1,7 +1,7 @@ /obj/item/projectile/bullet/reusable name = "reusable bullet" desc = "How do you even reuse a bullet?" - ammo_type = /obj/item/ammo_casing/caseless/ + var/ammo_type = /obj/item/ammo_casing/caseless/ var/dropped = 0 /obj/item/projectile/bullet/reusable/on_hit(atom/target, blocked = 0) @@ -14,7 +14,7 @@ /obj/item/projectile/bullet/reusable/proc/handle_drop() if(!dropped) - new ammo_type(src.loc) + new ammo_type(loc) dropped = 1 /obj/item/projectile/bullet/reusable/magspear @@ -42,7 +42,7 @@ if(dropped) return dropped = 1 - var/obj/item/ammo_casing/caseless/foam_dart/newdart = new ammo_type(src.loc) + var/obj/item/ammo_casing/caseless/foam_dart/newdart = new ammo_type(loc) var/obj/item/ammo_casing/caseless/foam_dart/old_dart = ammo_casing newdart.modified = old_dart.modified if(pen) diff --git a/code/modules/projectiles/projectile/special.dm b/code/modules/projectiles/projectile/special.dm index 851f23bcdd7..56b4875002b 100644 --- a/code/modules/projectiles/projectile/special.dm +++ b/code/modules/projectiles/projectile/special.dm @@ -115,7 +115,7 @@ if(A == firer) loc = A.loc return - playsound(src.loc, 'sound/effects/meteorimpact.ogg', 40, 1) + playsound(loc, 'sound/effects/meteorimpact.ogg', 40, 1) for(var/mob/M in urange(10, src)) if(!M.stat) shake_camera(M, 3, 1) @@ -195,8 +195,8 @@ var/datum/effect/system/spark_spread/s = new /datum/effect/system/spark_spread s.set_up(3, 1, src) s.start() - new /obj/effect/decal/cleanable/ash(src.loc) - src.visible_message("The [src.name] explodes!","You hear a snap!") + new /obj/effect/decal/cleanable/ash(loc) + visible_message("The [name] explodes!","You hear a snap!") playsound(src, 'sound/effects/snap.ogg', 50, 1) qdel(src) @@ -230,7 +230,7 @@ obj/item/projectile/kinetic/New() ..() /obj/item/projectile/kinetic/on_range() - new /obj/effect/kinetic_blast(src.loc) + new /obj/effect/kinetic_blast(loc) ..() /obj/item/projectile/kinetic/on_hit(atom/target) @@ -240,13 +240,13 @@ obj/item/projectile/kinetic/New() var/turf/simulated/mineral/M = target_turf M.gets_drilled(firer) new /obj/item/effect/kinetic_blast(target_turf) - if(src.splash) + if(splash) for(var/turf/T in range(splash, target_turf)) if(istype(T, /turf/simulated/mineral)) var/turf/simulated/mineral/M = T M.gets_drilled(firer) -/obj/item/effect/kinetic_blast +/obj/effect/kinetic_blast name = "kinetic explosion" icon = 'icons/obj/projectiles.dmi' icon_state = "kinetic_blast" @@ -340,13 +340,8 @@ obj/item/projectile/kinetic/New() icon_state = "bluespace" damage = 0 nodamage = 1 - var/obj/item/weapon/gun/energy/telegun/T = null var/teleport_target = null - OnFired() - T = shot_from - teleport_target = T.teleport_target - /obj/item/projectile/energy/teleport/on_hit(var/atom/target, var/blocked = 0) if(isliving(target)) if(teleport_target) diff --git a/code/modules/reagents/dartgun.dm b/code/modules/reagents/dartgun.dm index 4fb705dbcf9..f08685b3d88 100644 --- a/code/modules/reagents/dartgun.dm +++ b/code/modules/reagents/dartgun.dm @@ -104,7 +104,7 @@ to_chat(user, "