#define UNCLOWN 1 #define CLOWNABLE 2 #define CLOWNED 3 #define SILENCER_OFFSET_X 1 #define SILENCER_OFFSET_Y 2 /obj/item/weapon/gun name = "gun" desc = "Its a gun. It's pretty terrible, though." icon = 'icons/obj/gun.dmi' icon_state = "detective" var/clowned = UNCLOWN //UNCLOWN, CLOWNABLE, or CLOWNED item_state = "gun" flags = FPRINT siemens_coefficient = 1 slot_flags = SLOT_BELT starting_materials = list(MAT_IRON = 2000) w_type = RECYK_METAL w_class = W_CLASS_MEDIUM throwforce = 5 throw_speed = 4 throw_range = 5 force = 5.0 origin_tech = Tc_COMBAT + "=1" attack_verb = list("strikes", "hits", "bashes") mech_flags = MECH_SCAN_ILLEGAL min_harm_label = 20 harm_label_examine = list("A label is stuck to the trigger, but it is too small to get in the way.", "A label firmly sticks the trigger to the guard!") ghost_read = 0 hitsound = 'sound/weapons/smash.ogg' var/fire_sound = 'sound/weapons/Gunshot.ogg' var/fire_action = "fire" var/empty_sound = 'sound/weapons/empty.ogg' var/fire_volume = 50 //the volume of the fire_sound var/obj/item/projectile/in_chamber = null var/list/caliber //the ammo the gun will accept. Now multiple types (make sure to set them to =1) var/silenced = 0 var/list/silencer_offset = list() //x,y coords to bump silencer overlay to FROM (4,13) (use barrel end pixel position) var/list/gun_part_overlays = list() //holds copy of overlays to allow for sane manipulation var/recoil = 0 var/ejectshell = 1 var/clumsy_check = 1 //Whether the gun disallows clumsy users from firing it. var/honor_check = 1 // Same, but highlanders and bombermen. var/advanced_tool_user_check = 1 //Whether the gun disallows users that cannot use advanced tools from firing it. var/MoMMI_check = 1 //Whether the gun disallows MoMMIs from firing it. var/nymph_check = 1 //Whether the gun disallows diona nymphs from firing it. var/hulk_check = 1 //Whether the gun disallows hulks from firing it. var/golem_check = 1 //Whether the gun disallows golems from firing it. var/manifested_check = 1 //Whether the gun disallows manifested ghosts from firing it. var/tmp/list/mob/living/target //List of who yer targeting. var/tmp/lock_time = -100 var/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 = 2 var/last_fired = 0 var/delay_user = 4 //how much to delay the user's next attack by after firing var/conventional_firearm = 1 //Used to determine whether, when examined, an /obj/item/weapon/gun/projectile will display the amount of rounds remaining. var/jammed = 0 var/projectile_color = null var/projectile_color_shift = null var/pai_safety = TRUE //To allow the pAI to activate or deactivate firing capability // Tells is_honorable() which special_roles to respect. var/honorable = HONORABLE_BOMBERMAN | HONORABLE_HIGHLANDER | HONORABLE_NINJA var/kick_fire_chance = 5 /obj/item/weapon/gun/Destroy() if(in_chamber) qdel(in_chamber) in_chamber = null ..() /obj/item/weapon/gun/proc/ready_to_fire() if(world.time >= last_fired + fire_delay) last_fired = world.time return 1 else return 0 /obj/item/weapon/gun/proc/process_chambered() return 0 /obj/item/weapon/gun/proc/special_check(var/mob/M) //Placeholder for any special checks, like detective's revolver. return 1 /obj/item/weapon/gun/proc/failure_check(var/mob/M) //special_check, but in a different place return 1 /obj/item/weapon/gun/emp_act(severity) for(var/obj/O in contents) O.emp_act(severity) /obj/item/weapon/gun/proc/can_discharge() //because process_chambered() is an atrocity return 0 /obj/item/weapon/gun/afterattack(atom/A, mob/living/user, flag, params, struggle = 0) if(flag) return //we're placing gun on a table or in backpack if(harm_labeled >= min_harm_label) to_chat(user, "A label sticks the trigger to the trigger guard!")//Such a new feature, the player might not know what's wrong if it doesn't tell them. return if(istype(target, /obj/machinery/recharger) && istype(src, /obj/item/weapon/gun/energy)) return//Shouldnt flag take care of this? if (user.is_pacified(VIOLENCE_GUN,A,src)) return if(user && user.client && user.client.gun_mode && !(A in target)) PreFire(A,user,params, "struggle" = struggle) //They're using the new gun system, locate what they're aiming at. else Fire(A,user,params, "struggle" = struggle) //Otherwise, fire normally. /obj/item/weapon/proc/isHandgun() return FALSE //Make this proc return TRUE for handgun-shaped weapons (or in general, small enough weapons I guess) /obj/item/weapon/gun/proc/play_firesound(mob/user, var/reflex) if(istype(silenced, /obj/item/gun_part/silencer)) var/obj/item/gun_part/silencer/A = silenced if(fire_sound) playsound(user, fire_sound, fire_volume/A.volume_mult, 1) else if (in_chamber.fire_sound) playsound(user, in_chamber.fire_sound, fire_volume/A.volume_mult, 1) if(A.volume_mult <= 1) user.visible_message("[user] fires [src][reflex ? " by reflex":""]!", \ "You [fire_action] [src][reflex ? "by reflex":""]!", \ "You hear a [istype(in_chamber, /obj/item/projectile/beam) ? "laser blast" : "gunshot"]!") else if(fire_sound) playsound(user, fire_sound, fire_volume, 1) else if (in_chamber.fire_sound) playsound(user, in_chamber.fire_sound, fire_volume, 1) if(!silenced) user.visible_message("[user] fires [src][reflex ? " by reflex":""]!", \ "You [fire_action] [src][reflex ? "by reflex":""]!", \ "You hear a [istype(in_chamber, /obj/item/projectile/beam) ? "laser blast" : "gunshot"]!") /obj/item/weapon/gun/proc/can_Fire(mob/user, var/display_message = 0) var/firing_dexterity = 1 if(advanced_tool_user_check) if (!user.dexterity_check()) firing_dexterity = 0 if(MoMMI_check) if(isMoMMI(user)) firing_dexterity = 0 if(nymph_check) if(istype(user, /mob/living/carbon/monkey/diona)) firing_dexterity = 0 if(!firing_dexterity) if(display_message) to_chat(user, "You don't have the dexterity to do this!") return 0 if(istype(user, /mob/living)) if(hulk_check) var/mob/living/M = user if (M_HULK in M.mutations) if(display_message) to_chat(M, "Your meaty finger is much too large for the trigger guard!") return 0 if(ishuman(user)) var/mob/living/carbon/human/H=user if(golem_check) if(isgolem(H)) if(display_message) to_chat(user, "Your fat fingers don't fit in the trigger guard!") return 0 if(manifested_check) if(ismanifested(H)) if(display_message) to_chat(user, "It would dishonor the master to use anything but his unholy blade!") return 0 var/datum/organ/external/a_hand = H.get_active_hand_organ() if(!a_hand.can_use_advanced_tools()) if(display_message) to_chat(user, "Your [a_hand] doesn't have the dexterity to do this!") return 0 return 1 /obj/item/weapon/gun/proc/Fire(atom/target, mob/living/user, params, reflex = 0, struggle = 0, var/use_shooter_turf = FALSE) //Exclude lasertag guns from the M_CLUMSY check. var/explode = FALSE var/dehand = FALSE if(istype(user, /mob/living)) var/mob/living/M = user if(clumsy_check && clumsy_check(M) && prob(50)) explode = TRUE if(honor_check && is_honorable(M, honorable)) explode = TRUE dehand = TRUE if(explode) if(dehand) var/limb_index = user.is_holding_item(src) var/datum/organ/external/L = M.find_organ_by_grasp_index(limb_index) visible_message("[src] blows up in [M]'s [L.display_name]!") L.droplimb(1) else to_chat(M, "[src] blows up in your face.") M.take_organ_damage(0,20) M.drop_item(src, force_drop = 1) qdel(src) return if(!can_Fire(user, 1)) return add_fingerprint(user) var/atom/originaltarget = target var/turf/curloc = user.loc if(use_shooter_turf) curloc = get_turf(user) var/turf/targloc = get_turf(target) if (!istype(targloc) || !istype(curloc)) return if(defective) target = get_inaccuracy(originaltarget, 1+recoil) targloc = get_turf(target) if(!special_check(user)) return if (!ready_to_fire()) if (world.time % 3) //to prevent spam to_chat(user, "[src] is not ready to fire again!") return if(!process_chambered() || jammed) //CHECK return click_empty(user) if(!in_chamber) return if(defective) if(!failure_check(user)) return if(!istype(src, /obj/item/weapon/gun/energy/tag)) log_attack("[user.name] ([user.ckey]) fired \the [src] (proj:[in_chamber.name]) at [originaltarget] [ismob(target) ? "([originaltarget:ckey])" : ""] ([originaltarget.x],[originaltarget.y],[originaltarget.z])[struggle ? " due to being disarmed." :""]" ) in_chamber.firer = user if(user.zone_sel) in_chamber.def_zone = user.zone_sel.selecting else in_chamber.def_zone = LIMB_CHEST if(targloc == curloc) target.bullet_act(in_chamber) qdel(in_chamber) in_chamber = null update_icon() play_firesound(user, reflex) return if(recoil) spawn() directional_recoil(user, recoil, get_angle(user, target)) if(user.locked_to && isobj(user.locked_to) && !user.locked_to.anchored ) var/direction = get_dir(user,target) spawn() var/obj/B = user.locked_to var/movementdirection = turn(direction,180) B.Move(get_step(user,movementdirection), movementdirection) sleep(1) B.Move(get_step(user,movementdirection), movementdirection) sleep(1) B.Move(get_step(user,movementdirection), movementdirection) sleep(1) B.Move(get_step(user,movementdirection), movementdirection) sleep(2) B.Move(get_step(user,movementdirection), movementdirection) sleep(2) B.Move(get_step(user,movementdirection), movementdirection) sleep(3) B.Move(get_step(user,movementdirection), movementdirection) sleep(3) B.Move(get_step(user,movementdirection), movementdirection) sleep(3) B.Move(get_step(user,movementdirection), movementdirection) user.apply_inertia(get_dir(target, user)) play_firesound(user, reflex) in_chamber.original = target in_chamber.forceMove(get_turf(user)) in_chamber.starting = get_turf(user) in_chamber.shot_from = src user.delayNextAttack(delay_user) // TODO: Should be delayed per-gun. in_chamber.silenced = silenced in_chamber.current = curloc in_chamber.OnFired() in_chamber.yo = targloc.y - curloc.y in_chamber.xo = targloc.x - curloc.x in_chamber.inaccurate = (istype(user.locked_to, /obj/structure/bed/chair/vehicle)) if(projectile_color) in_chamber.apply_projectile_color(projectile_color) if(projectile_color_shift) in_chamber.apply_projectile_color_shift(projectile_color_shift) 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"]) spawn() if(in_chamber) in_chamber.process() sleep(1) in_chamber = null update_icon() user.update_inv_hand(user.active_hand) if(defective && recoil && prob(3)) var/throwturf = get_ranged_target_turf(user, pick(alldirs), 7) user.drop_item() user.visible_message("\The [src] jumps out of [user]'s hands!","\The [src] jumps out of your hands!") throw_at(throwturf, rand(3, 6), 3) return 1 return 1 /obj/item/weapon/gun/proc/canbe_fired() 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) if(empty_sound) user.visible_message("*click click*", "*click*") playsound(user, empty_sound, 100, 1) else if(empty_sound) src.visible_message("*click click*") playsound(src, empty_sound, 100, 1) /obj/item/weapon/gun/attack(mob/living/M, mob/living/user, def_zone) //Suicide handling. if (M == user && user.zone_sel.selecting == "mouth" && !mouthshoot) if(istype(M.wear_mask, /obj/item/clothing/mask/happy)) to_chat(M, "BUT WHY? I'M SO HAPPY!") return mouthshoot = 1 M.visible_message("[user] sticks their gun in their mouth, ready to pull the trigger...") if(!do_after(user,src, 40)) M.visible_message("[user] decided life was worth living.") mouthshoot = 0 return if (process_chambered()) user.visible_message("[user] pulls the trigger.") if(silenced) var/obj/item/gun_part/silencer/A = silenced if(fire_sound) playsound(user, fire_sound, fire_volume/A.volume_mult, 1) else if (in_chamber.fire_sound) playsound(user, in_chamber.fire_sound, fire_volume/A.volume_mult, 1) else if(fire_sound) playsound(user, fire_sound, fire_volume, 1) else if (in_chamber.fire_sound) playsound(user, in_chamber.fire_sound, fire_volume, 1) in_chamber.firer = M in_chamber.on_hit(M) if (!in_chamber.nodamage) user.apply_damage(in_chamber.damage*2.5, in_chamber.damage_type, LIMB_HEAD, used_weapon = "Point blank shot in the mouth with \a [in_chamber]") user.death() var/suicidesound = pick('sound/misc/suicide/suicide1.ogg','sound/misc/suicide/suicide2.ogg','sound/misc/suicide/suicide3.ogg','sound/misc/suicide/suicide4.ogg','sound/misc/suicide/suicide5.ogg','sound/misc/suicide/suicide6.ogg') playsound(src, pick(suicidesound), 30, channel = 125) log_attack("[key_name(user)] committed suicide with \the [src].") user.attack_log += "\[[time_stamp()]\] [user.real_name] committed suicide with \the [src]" else to_chat(user, "Ow...") user.apply_effect(110,AGONY,0) qdel(in_chamber) in_chamber = null mouthshoot = 0 return else click_empty(user) mouthshoot = 0 return if (can_discharge()) //Need to have something to fire but not load it up yet //Point blank shooting if on harm intent or target we were targeting. if(user.a_intent == I_HURT) if (user.is_pacified()) to_chat(user, "[pick("Hey that's dangerous...wouldn't want hurting people.","You don't feel like firing \the [src] at \the [M].","Peace, my [user.gender == FEMALE ? "girl" : "man"]...")]") return user.visible_message(" \The [user] fires \the [src] point blank at [M]!") if (process_chambered()) //Load whatever it is we fire in_chamber.damage *= 1.3 //Some guns don't work with damage / chambers, like dart guns! src.Fire(M,user,0,0,1) return else if(target && (M in target)) process_chambered() src.Fire(M,user,0,0,1) ///Otherwise, shoot! return else return ..() //Allows a player to choose to melee instead of shoot, by being on help intent. else return ..() //Pistolwhippin' /obj/item/weapon/gun/state_controls_pai(obj/item/device/paicard/P) if(P.pai) to_chat(P.pai, "You have been connected to \a [src].") to_chat(P.pai, "Your controls are:") to_chat(P.pai, "- PageDown / Z(hotkey mode): Connect or disconnect from \the [src]'s firing mechanism.") to_chat(P.pai, "- Click on a target: Fire \the [src] at the target.") /obj/item/weapon/gun/attack_integrated_pai(mob/living/silicon/pai/user) if(!pai_safety) to_chat(user, "You connect to \the [src]'s firing mechanism.") else to_chat(user, "You disconnect from \the [src]'s firing mechanism.") pai_safety = !pai_safety /obj/item/weapon/gun/on_integrated_pai_click(mob/living/silicon/pai/user, var/atom/A) //to allow any gun to be pAI-compatible, on a basic level, just by varediting if(check_pai_can_fire(user)) Fire(A,user,use_shooter_turf = TRUE) /obj/item/weapon/gun/proc/check_pai_can_fire(mob/living/silicon/pai/user) //for various restrictions on when pAIs can fire a gun into which they're integrated if(get_holder_of_type(user, /obj/structure/disposalpipe) || get_holder_of_type(user, /obj/machinery/atmospherics/pipe)) //can't fire the gun from inside pipes or disposal pipes to_chat(user, "You can't aim \the [src] properly from this location!") return FALSE else if(!pai_safety) to_chat(user, "You're not connected to \the [src]'s firing mechanism!") return FALSE else return TRUE /obj/item/weapon/gun/attackby(var/obj/item/A, mob/user) if(istype(A, /obj/item/weapon/gun)) var/obj/item/weapon/gun/G = A if(isHandgun() && G.isHandgun()) var/obj/item/weapon/gun/akimbo/AA = new /obj/item/weapon/gun/akimbo(get_turf(src),src,G) if(user.drop_item(G, AA) && user.drop_item(src, AA)) user.put_in_hands(AA) AA.update_icon(user) else to_chat(user, "You can not combine \the [G] and \the [src].") qdel(AA) if(clowned == CLOWNABLE && istype(A,/obj/item/toy/crayon/rainbow)) to_chat(user, "You begin modifying \the [src].") if(do_after(user, src, 4 SECONDS)) to_chat(user, "You finish modifying \the [src]!") clowned = CLOWNED update_icon() ..() /obj/item/weapon/gun/decontaminate() ..() if(clowned == CLOWNED) clowned = CLOWNABLE update_icon() /obj/item/weapon/gun/update_icon() icon_state = initial(icon_state) + "[clowned == CLOWNED ? "c" : ""]" /obj/item/weapon/gun/proc/bullet_hitting(var/obj/item/projectile/P,var/atom/atarget) return /obj/item/weapon/gun/kick_act(mob/living/carbon/human/H) . = ..() if(prob(kick_fire_chance)) var/list/targets = list() for(var/turf/t in oview(6)) targets += t var/target = pick(targets) src.Fire(target,H,0,0,1)