mirror of
https://github.com/vgstation-coders/vgstation13.git
synced 2025-12-10 18:32:03 +00:00
* Adds framework for any gun to be usable by pAI if the can_take_pAI var is changed to TRUE, opening up adminbus options. Adds smart laser gun, which can take pAIs by default. Adds smart laser gun crate to Cargo, for twice the price of a standard weapons crate. It contains four smart laser guns. * Fixes supplypack containername, makes pAIs incapable of firing a gun if they're inside a pipe. * Fixes compile failure.
355 lines
13 KiB
Plaintext
355 lines
13 KiB
Plaintext
/obj/item/weapon/gun
|
|
name = "gun"
|
|
desc = "Its a gun. It's pretty terrible, though."
|
|
icon = 'icons/obj/gun.dmi'
|
|
icon_state = "detective"
|
|
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("<span class='info'>A label is stuck to the trigger, but it is too small to get in the way.</span>", "<span class='warning'>A label firmly sticks the trigger to the guard!</span>")
|
|
ghost_read = 0
|
|
|
|
var/fire_sound = 'sound/weapons/Gunshot.ogg'
|
|
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/recoil = 0
|
|
var/ejectshell = 1
|
|
|
|
var/clumsy_check = 1 //Whether the gun disallows clumsy users from firing it.
|
|
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/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/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
|
|
|
|
/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/afterattack(atom/A as mob|obj|turf|area, mob/living/user as mob|obj, 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, "<span class='warning'>A label sticks the trigger to the trigger guard!</span>")//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 && 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/gun/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/can_Fire(mob/user, var/display_message = 0)
|
|
var/firing_dexterity = 1
|
|
if(advanced_tool_user_check)
|
|
if (!user.IsAdvancedToolUser())
|
|
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, "<span class='warning'>You don't have the dexterity to do this!</span>")
|
|
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, "<span class='warning'>Your meaty finger is much too large for the trigger guard!</span>")
|
|
return 0
|
|
if(ishuman(user))
|
|
var/mob/living/carbon/human/H=user
|
|
if(golem_check)
|
|
if(isgolem(H))
|
|
if(display_message)
|
|
to_chat(user, "<span class='warning'>Your fat fingers don't fit in the trigger guard!</span>")
|
|
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, "<span class='warning'>Your [a_hand] doesn't have the dexterity to do this!</span>")
|
|
return 0
|
|
return 1
|
|
|
|
/obj/item/weapon/gun/proc/Fire(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, params, reflex = 0, struggle = 0, var/use_shooter_turf = FALSE)//TODO: go over this
|
|
//Exclude lasertag guns from the M_CLUMSY check.
|
|
if(clumsy_check)
|
|
if(istype(user, /mob/living))
|
|
var/mob/living/M = user
|
|
if ((M_CLUMSY in M.mutations) && prob(50))
|
|
to_chat(M, "<span class='danger'>[src] blows up in your face.</span>")
|
|
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, "<span class='warning'>[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/laser/redtag) && !istype(src, /obj/item/weapon/gun/energy/laser/bluetag))
|
|
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)
|
|
user.bullet_act(in_chamber)
|
|
qdel(in_chamber)
|
|
in_chamber = null
|
|
update_icon()
|
|
return
|
|
|
|
if(recoil)
|
|
spawn()
|
|
shake_camera(user, recoil + 1, recoil)
|
|
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))
|
|
|
|
if(silenced)
|
|
if(fire_sound)
|
|
playsound(user, fire_sound, fire_volume/5, 1)
|
|
else if (in_chamber.fire_sound)
|
|
playsound(user, in_chamber.fire_sound, fire_volume/5, 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)
|
|
user.visible_message("<span class='warning'>[user] fires [src][reflex ? " by reflex":""]!</span>", \
|
|
"<span class='warning'>You fire [src][reflex ? "by reflex":""]!</span>", \
|
|
"You hear a [istype(in_chamber, /obj/item/projectile/beam) ? "laser blast" : "gunshot"]!")
|
|
|
|
in_chamber.original = target
|
|
in_chamber.forceMove(get_turf(user))
|
|
in_chamber.starting = get_turf(user)
|
|
in_chamber.shot_from = src
|
|
user.delayNextAttack(4) // 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(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/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)
|
|
if(empty_sound)
|
|
user.visible_message("*click click*", "<span class='danger'>*click*</span>")
|
|
playsound(user, empty_sound, 100, 1)
|
|
else
|
|
if(empty_sound)
|
|
src.visible_message("*click click*")
|
|
playsound(get_turf(src), empty_sound, 100, 1)
|
|
|
|
/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)
|
|
if(istype(M.wear_mask, /obj/item/clothing/mask/happy))
|
|
to_chat(M, "<span class='sinister'>BUT WHY? I'M SO HAPPY!</span>")
|
|
return
|
|
mouthshoot = 1
|
|
M.visible_message("<span class='warning'>[user] sticks their gun in their mouth, ready to pull the trigger...</span>")
|
|
if(!do_after(user,src, 40))
|
|
M.visible_message("<span class='notice'>[user] decided life was worth living.</span>")
|
|
mouthshoot = 0
|
|
return
|
|
if (process_chambered())
|
|
user.visible_message("<span class = 'warning'>[user] pulls the trigger.</span>")
|
|
if(silenced)
|
|
if(fire_sound)
|
|
playsound(user, fire_sound, fire_volume/5, 1)
|
|
else if (in_chamber.fire_sound)
|
|
playsound(user, in_chamber.fire_sound, fire_volume/5, 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.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.stat=2 // Just to be sure
|
|
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(get_turf(src), pick(suicidesound), 10, channel = 125)
|
|
log_attack("<font color='red'>[key_name(user)] committed suicide with \the [src].</font>")
|
|
user.attack_log += "\[[time_stamp()]\] <font color='red'> [user.real_name] committed suicide with \the [src]</font>"
|
|
else
|
|
to_chat(user, "<span class = 'notice'>Ow...</span>")
|
|
user.apply_effect(110,AGONY,0)
|
|
qdel(in_chamber)
|
|
in_chamber = null
|
|
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_HURT)
|
|
user.visible_message("<span class='danger'> \The [user] fires \the [src] point blank at [M]!</span>")
|
|
in_chamber.damage *= 1.3
|
|
src.Fire(M,user,0,0,1)
|
|
return
|
|
else if(target && M in target)
|
|
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/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)
|
|
else
|
|
to_chat(user, "<span class='warning'>You can't aim the gun properly from this location!</span>")
|
|
|
|
/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
|
|
return TRUE
|
|
else
|
|
return FALSE |