03f7432511
* knockback component can now be reversed, has projectile and gun handling, and hostile simplemob handling adds signals for hostile mobs attacking, altering projectiles before firing, and for when projectiles successfully hit their target moves knockback handling to a general proc adds ishelpers for guns and projectiles * no more weird projectile handling it can just not apply the effect if the component somehow goes away lifesteal actually works now instead of being a blank file, applies a flat healing effect when you hit something * fixes up comsig stuff adds new components to the fantasy prefix and suffix knockback now handles throwing anchored objects lifesteal now properly heals the target with projectiel weapons adds summoning component to handle mob summoning with item attacking and such adds fired_from variable to handle what a projectile was fired_from, firer would be the mob that fired and fired_from would be the gun, in the case of an autoturret, fired_from and firer would be the same adds shrapnel component, fires projectiles around a fired projectile when it hits adds igniter component to set attacked mobs on fire * no more shrapnel on items that can't use it summoning items now summon at least one mob maximum adds specific weighted projectile types for shrapnel to prevent broken options being picked removes the reverse var from knockback component and instead just handles negative thrown turf
610 lines
19 KiB
Plaintext
610 lines
19 KiB
Plaintext
|
|
#define DUALWIELD_PENALTY_EXTRA_MULTIPLIER 1.4
|
|
|
|
/obj/item/gun
|
|
name = "gun"
|
|
desc = "It's a gun. It's pretty terrible, though."
|
|
icon = 'icons/obj/guns/projectile.dmi'
|
|
icon_state = "detective"
|
|
item_state = "gun"
|
|
flags_1 = CONDUCT_1
|
|
slot_flags = ITEM_SLOT_BELT
|
|
materials = list(MAT_METAL=2000)
|
|
w_class = WEIGHT_CLASS_NORMAL
|
|
throwforce = 5
|
|
throw_speed = 3
|
|
throw_range = 5
|
|
force = 5
|
|
item_flags = NEEDS_PERMIT
|
|
attack_verb = list("struck", "hit", "bashed")
|
|
|
|
var/fire_sound = "gunshot"
|
|
var/vary_fire_sound = TRUE
|
|
var/fire_sound_volume = 50
|
|
var/dry_fire_sound = 'sound/weapons/gun_dry_fire.ogg'
|
|
var/suppressed = null //whether or not a message is displayed when fired
|
|
var/can_suppress = FALSE
|
|
var/suppressed_sound = 'sound/weapons/gunshot_silenced.ogg'
|
|
var/suppressed_volume = 10
|
|
var/can_unsuppress = TRUE
|
|
var/recoil = 0 //boom boom shake the room
|
|
var/clumsy_check = TRUE
|
|
var/obj/item/ammo_casing/chambered = null
|
|
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_off = FALSE
|
|
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 //Spread induced by the gun itself.
|
|
var/randomspread = 1 //Set to 0 for shotguns. This is used for weapons that don't fire all their bullets at once.
|
|
|
|
lefthand_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi'
|
|
righthand_file = 'icons/mob/inhands/weapons/guns_righthand.dmi'
|
|
|
|
var/obj/item/firing_pin/pin = /obj/item/firing_pin //standard firing pin for most guns
|
|
|
|
var/can_flashlight = FALSE //if a flashlight can be added or removed if it already has one.
|
|
var/obj/item/flashlight/seclite/gun_light
|
|
var/mutable_appearance/flashlight_overlay
|
|
var/datum/action/item_action/toggle_gunlight/alight
|
|
|
|
var/can_bayonet = FALSE //if a bayonet can be added or removed if it already has one.
|
|
var/obj/item/kitchen/knife/bayonet
|
|
var/mutable_appearance/knife_overlay
|
|
var/knife_x_offset = 0
|
|
var/knife_y_offset = 0
|
|
|
|
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
|
|
var/zoomed = FALSE //Zoom toggle
|
|
var/zoom_amt = 3 //Distance in TURFs to move the user's screen forward (the "zoom" effect)
|
|
var/zoom_out_amt = 0
|
|
var/datum/action/toggle_scope_zoom/azoom
|
|
|
|
/obj/item/gun/Initialize()
|
|
. = ..()
|
|
if(pin)
|
|
pin = new pin(src)
|
|
if(gun_light)
|
|
alight = new(src)
|
|
build_zooming()
|
|
|
|
/obj/item/gun/Destroy()
|
|
if(pin)
|
|
QDEL_NULL(pin)
|
|
if(gun_light)
|
|
QDEL_NULL(gun_light)
|
|
if(bayonet)
|
|
QDEL_NULL(bayonet)
|
|
if(chambered) //Not all guns are chambered (EMP'ed energy guns etc)
|
|
QDEL_NULL(chambered)
|
|
if(azoom)
|
|
QDEL_NULL(azoom)
|
|
return ..()
|
|
|
|
/obj/item/gun/handle_atom_del(atom/A)
|
|
if(A == pin)
|
|
pin = null
|
|
if(A == chambered)
|
|
chambered = null
|
|
update_icon()
|
|
if(A == bayonet)
|
|
clear_bayonet()
|
|
if(A == gun_light)
|
|
clear_gunlight()
|
|
return ..()
|
|
|
|
/obj/item/gun/CheckParts(list/parts_list)
|
|
..()
|
|
var/obj/item/gun/G = locate(/obj/item/gun) in contents
|
|
if(G)
|
|
G.forceMove(loc)
|
|
QDEL_NULL(G.pin)
|
|
visible_message("[G] can now fit a new pin, but the old one was destroyed in the process.", null, null, 3)
|
|
qdel(src)
|
|
|
|
/obj/item/gun/examine(mob/user)
|
|
..()
|
|
if(pin)
|
|
to_chat(user, "It has \a [pin] installed.")
|
|
else
|
|
to_chat(user, "It doesn't have a <b>firing pin</b> installed, and won't fire.")
|
|
|
|
if(gun_light)
|
|
to_chat(user, "It has \a [gun_light] [can_flashlight ? "" : "permanently "]mounted on it.")
|
|
if(can_flashlight) //if it has a light and this is false, the light is permanent.
|
|
to_chat(user, "<span class='info'>[gun_light] looks like it can be <b>unscrewed</b> from [src].</span>")
|
|
else if(can_flashlight)
|
|
to_chat(user, "It has a mounting point for a <b>seclite</b>.")
|
|
|
|
if(bayonet)
|
|
to_chat(user, "It has \a [bayonet] [can_bayonet ? "" : "permanently "]affixed to it.")
|
|
if(can_bayonet) //if it has a bayonet and this is false, the bayonet is permanent.
|
|
to_chat(user, "<span class='info'>[bayonet] looks like it can be <b>unscrewed</b> from [src].</span>")
|
|
else if(can_bayonet)
|
|
to_chat(user, "It has a <b>bayonet</b> lug on it.")
|
|
|
|
/obj/item/gun/equipped(mob/living/user, slot)
|
|
. = ..()
|
|
if(zoomed && user.get_active_held_item() != src)
|
|
zoom(user, FALSE) //we can only stay zoomed in if it's in our hands //yeah and we only unzoom if we're actually zoomed using the gun!!
|
|
|
|
//called after the gun has successfully fired its chambered ammo.
|
|
/obj/item/gun/proc/process_chamber()
|
|
return FALSE
|
|
|
|
//check if there's enough ammo/energy/whatever to shoot one time
|
|
//i.e if clicking would make it shoot
|
|
/obj/item/gun/proc/can_shoot()
|
|
return TRUE
|
|
|
|
/obj/item/gun/proc/shoot_with_empty_chamber(mob/living/user as mob|obj)
|
|
to_chat(user, "<span class='danger'>*click*</span>")
|
|
playsound(src, dry_fire_sound, 30, TRUE)
|
|
|
|
|
|
/obj/item/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, suppressed_sound, suppressed_volume, vary_fire_sound)
|
|
else
|
|
playsound(user, fire_sound, fire_sound_volume, vary_fire_sound)
|
|
if(message)
|
|
if(pointblank)
|
|
user.visible_message("<span class='danger'>[user] fires [src] point blank at [pbtarget]!</span>", null, null, COMBAT_MESSAGE_RANGE)
|
|
else
|
|
user.visible_message("<span class='danger'>[user] fires [src]!</span>", null, null, COMBAT_MESSAGE_RANGE)
|
|
|
|
/obj/item/gun/emp_act(severity)
|
|
. = ..()
|
|
if(!(. & EMP_PROTECT_CONTENTS))
|
|
for(var/obj/O in contents)
|
|
O.emp_act(severity)
|
|
|
|
/obj/item/gun/afterattack(atom/target, mob/living/user, flag, params)
|
|
. = ..()
|
|
if(!target)
|
|
return
|
|
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 == INTENT_HARM) //melee attack
|
|
return
|
|
if(target == user && user.zone_selected != BODY_ZONE_PRECISE_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(flag)
|
|
if(user.zone_selected == BODY_ZONE_PRECISE_MOUTH)
|
|
handle_suicide(user, target, params)
|
|
return
|
|
|
|
if(!can_shoot()) //Just because you can pull the trigger doesn't mean it can shoot.
|
|
shoot_with_empty_chamber(user)
|
|
return
|
|
|
|
//Exclude lasertag guns from the TRAIT_CLUMSY check.
|
|
if(clumsy_check)
|
|
if(istype(user))
|
|
if (HAS_TRAIT(user, TRAIT_CLUMSY) && prob(40))
|
|
to_chat(user, "<span class='userdanger'>You shoot yourself in the foot with [src]!</span>")
|
|
var/shot_leg = pick(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
|
|
process_fire(user, user, FALSE, params, shot_leg)
|
|
user.dropItemToGround(src, TRUE)
|
|
return
|
|
|
|
if(weapon_weight == WEAPON_HEAVY && user.get_inactive_held_item())
|
|
to_chat(user, "<span class='userdanger'>You need both hands free to fire \the [src]!</span>")
|
|
return
|
|
|
|
//DUAL (or more!) WIELDING
|
|
var/bonus_spread = 0
|
|
var/loop_counter = 0
|
|
if(ishuman(user) && user.a_intent == INTENT_HARM)
|
|
var/mob/living/carbon/human/H = user
|
|
for(var/obj/item/gun/G in H.held_items)
|
|
if(G == src || G.weapon_weight >= WEAPON_MEDIUM)
|
|
continue
|
|
else if(G.can_trigger_gun(user))
|
|
bonus_spread += 24 * G.weapon_weight
|
|
loop_counter++
|
|
addtimer(CALLBACK(G, /obj/item/gun.proc/process_fire, target, user, TRUE, params, null, bonus_spread), loop_counter)
|
|
|
|
process_fire(target, user, TRUE, params, null, bonus_spread)
|
|
|
|
|
|
|
|
/obj/item/gun/can_trigger_gun(mob/living/user)
|
|
. = ..()
|
|
if(!handle_pins(user))
|
|
return FALSE
|
|
|
|
/obj/item/gun/proc/handle_pins(mob/living/user)
|
|
if(pin)
|
|
if(pin.pin_auth(user) || (pin.obj_flags & EMAGGED))
|
|
return TRUE
|
|
else
|
|
pin.auth_fail(user)
|
|
return FALSE
|
|
else
|
|
to_chat(user, "<span class='warning'>[src]'s trigger is locked. This weapon doesn't have a firing pin installed!</span>")
|
|
return FALSE
|
|
|
|
/obj/item/gun/proc/recharge_newshot()
|
|
return
|
|
|
|
/obj/item/gun/proc/process_burst(mob/living/user, atom/target, message = TRUE, params=null, zone_override = "", sprd = 0, randomized_gun_spread = 0, randomized_bonus_spread = 0, rand_spr = 0, iteration = 0)
|
|
if(!user || !firing_burst)
|
|
firing_burst = FALSE
|
|
return FALSE
|
|
if(!issilicon(user))
|
|
if(iteration > 1 && !(user.is_holding(src))) //for burst firing
|
|
firing_burst = FALSE
|
|
return FALSE
|
|
if(chambered && chambered.BB)
|
|
if(HAS_TRAIT(user, TRAIT_PACIFISM)) // If the user has the pacifist trait, then they won't be able to fire [src] if the round chambered inside of [src] is lethal.
|
|
if(chambered.harmful) // Is the bullet chambered harmful?
|
|
to_chat(user, "<span class='notice'> [src] is lethally chambered! You don't want to risk harming anyone...</span>")
|
|
return
|
|
if(randomspread)
|
|
sprd = round((rand() - 0.5) * DUALWIELD_PENALTY_EXTRA_MULTIPLIER * (randomized_gun_spread + randomized_bonus_spread))
|
|
else //Smart spread
|
|
sprd = round((((rand_spr/burst_size) * iteration) - (0.5 + (rand_spr * 0.25))) * (randomized_gun_spread + randomized_bonus_spread))
|
|
before_firing(target,user)
|
|
if(!chambered.fire_casing(target, user, params, ,suppressed, zone_override, sprd, src))
|
|
shoot_with_empty_chamber(user)
|
|
firing_burst = FALSE
|
|
return FALSE
|
|
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)
|
|
if (iteration >= burst_size)
|
|
firing_burst = FALSE
|
|
else
|
|
shoot_with_empty_chamber(user)
|
|
firing_burst = FALSE
|
|
return FALSE
|
|
process_chamber()
|
|
update_icon()
|
|
return TRUE
|
|
|
|
/obj/item/gun/proc/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0)
|
|
add_fingerprint(user)
|
|
|
|
if(semicd)
|
|
return
|
|
|
|
var/sprd = 0
|
|
var/randomized_gun_spread = 0
|
|
var/rand_spr = rand()
|
|
if(spread)
|
|
randomized_gun_spread = rand(0,spread)
|
|
if(HAS_TRAIT(user, TRAIT_POOR_AIM)) //nice shootin' tex
|
|
bonus_spread += 25
|
|
var/randomized_bonus_spread = rand(0, bonus_spread)
|
|
|
|
if(burst_size > 1)
|
|
firing_burst = TRUE
|
|
for(var/i = 1 to burst_size)
|
|
addtimer(CALLBACK(src, .proc/process_burst, user, target, message, params, zone_override, sprd, randomized_gun_spread, randomized_bonus_spread, rand_spr, i), fire_delay * (i - 1))
|
|
else
|
|
if(chambered)
|
|
if(HAS_TRAIT(user, TRAIT_PACIFISM)) // If the user has the pacifist trait, then they won't be able to fire [src] if the round chambered inside of [src] is lethal.
|
|
if(chambered.harmful) // Is the bullet chambered harmful?
|
|
to_chat(user, "<span class='notice'> [src] is lethally chambered! You don't want to risk harming anyone...</span>")
|
|
return
|
|
sprd = round((rand() - 0.5) * DUALWIELD_PENALTY_EXTRA_MULTIPLIER * (randomized_gun_spread + randomized_bonus_spread))
|
|
before_firing(target,user)
|
|
if(!chambered.fire_casing(target, user, params, , suppressed, zone_override, sprd, src))
|
|
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 = TRUE
|
|
addtimer(CALLBACK(src, .proc/reset_semicd), fire_delay)
|
|
|
|
if(user)
|
|
user.update_inv_hands()
|
|
SSblackbox.record_feedback("tally", "gun_fired", 1, type)
|
|
return TRUE
|
|
|
|
/obj/item/gun/update_icon()
|
|
..()
|
|
|
|
|
|
/obj/item/gun/proc/reset_semicd()
|
|
semicd = FALSE
|
|
|
|
/obj/item/gun/attack(mob/M as mob, mob/user)
|
|
if(user.a_intent == INTENT_HARM) //Flogging
|
|
if(bayonet)
|
|
M.attackby(bayonet, user)
|
|
return
|
|
else
|
|
return ..()
|
|
return
|
|
|
|
/obj/item/gun/attack_obj(obj/O, mob/user)
|
|
if(user.a_intent == INTENT_HARM)
|
|
if(bayonet)
|
|
O.attackby(bayonet, user)
|
|
return
|
|
return ..()
|
|
|
|
/obj/item/gun/attackby(obj/item/I, mob/user, params)
|
|
if(user.a_intent == INTENT_HARM)
|
|
return ..()
|
|
else if(istype(I, /obj/item/flashlight/seclite))
|
|
if(!can_flashlight)
|
|
return ..()
|
|
var/obj/item/flashlight/seclite/S = I
|
|
if(!gun_light)
|
|
if(!user.transferItemToLoc(I, src))
|
|
return
|
|
to_chat(user, "<span class='notice'>You click [S] into place on [src].</span>")
|
|
if(S.on)
|
|
set_light(0)
|
|
gun_light = S
|
|
update_gunlight()
|
|
alight = new(src)
|
|
if(loc == user)
|
|
alight.Grant(user)
|
|
else if(istype(I, /obj/item/kitchen/knife))
|
|
var/obj/item/kitchen/knife/K = I
|
|
if(!can_bayonet || !K.bayonet || bayonet) //ensure the gun has an attachment point available, and that the knife is compatible with it.
|
|
return ..()
|
|
if(!user.transferItemToLoc(I, src))
|
|
return
|
|
to_chat(user, "<span class='notice'>You attach [K] to [src]'s bayonet lug.</span>")
|
|
bayonet = K
|
|
var/state = "bayonet" //Generic state.
|
|
if(bayonet.icon_state in icon_states('icons/obj/guns/bayonets.dmi')) //Snowflake state?
|
|
state = bayonet.icon_state
|
|
var/icon/bayonet_icons = 'icons/obj/guns/bayonets.dmi'
|
|
knife_overlay = mutable_appearance(bayonet_icons, state)
|
|
knife_overlay.pixel_x = knife_x_offset
|
|
knife_overlay.pixel_y = knife_y_offset
|
|
add_overlay(knife_overlay, TRUE)
|
|
else
|
|
return ..()
|
|
|
|
/obj/item/gun/screwdriver_act(mob/living/user, obj/item/I)
|
|
. = ..()
|
|
if(.)
|
|
return
|
|
if(!user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
|
|
return
|
|
if((can_flashlight && gun_light) && (can_bayonet && bayonet)) //give them a choice instead of removing both
|
|
var/list/possible_items = list(gun_light, bayonet)
|
|
var/obj/item/item_to_remove = input(user, "Select an attachment to remove", "Attachment Removal") as null|obj in possible_items
|
|
if(!item_to_remove || !user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
|
|
return
|
|
return remove_gun_attachment(user, I, item_to_remove)
|
|
|
|
else if(gun_light && can_flashlight) //if it has a gun_light and can_flashlight is false, the flashlight is permanently attached.
|
|
return remove_gun_attachment(user, I, gun_light, "unscrewed")
|
|
|
|
else if(bayonet && can_bayonet) //if it has a bayonet, and the bayonet can be removed
|
|
return remove_gun_attachment(user, I, bayonet, "unfix")
|
|
|
|
/obj/item/gun/proc/remove_gun_attachment(mob/living/user, obj/item/tool_item, obj/item/item_to_remove, removal_verb)
|
|
if(tool_item)
|
|
tool_item.play_tool_sound(src)
|
|
to_chat(user, "<span class='notice'>You [removal_verb ? removal_verb : "remove"] [item_to_remove] from [src].</span>")
|
|
item_to_remove.forceMove(drop_location())
|
|
|
|
if(Adjacent(user) && !issilicon(user))
|
|
user.put_in_hands(item_to_remove)
|
|
|
|
if(item_to_remove == bayonet)
|
|
return clear_bayonet()
|
|
else if(item_to_remove == gun_light)
|
|
return clear_gunlight()
|
|
|
|
/obj/item/gun/proc/clear_bayonet()
|
|
if(!bayonet)
|
|
return
|
|
bayonet = null
|
|
if(knife_overlay)
|
|
cut_overlay(knife_overlay, TRUE)
|
|
knife_overlay = null
|
|
return TRUE
|
|
|
|
/obj/item/gun/proc/clear_gunlight()
|
|
if(!gun_light)
|
|
return
|
|
var/obj/item/flashlight/seclite/removed_light = gun_light
|
|
gun_light = null
|
|
update_gunlight()
|
|
removed_light.update_brightness()
|
|
QDEL_NULL(alight)
|
|
return TRUE
|
|
|
|
/obj/item/gun/ui_action_click(mob/user, actiontype)
|
|
if(istype(actiontype, alight))
|
|
toggle_gunlight()
|
|
else
|
|
..()
|
|
|
|
/obj/item/gun/proc/toggle_gunlight()
|
|
if(!gun_light)
|
|
return
|
|
|
|
var/mob/living/carbon/human/user = usr
|
|
gun_light.on = !gun_light.on
|
|
to_chat(user, "<span class='notice'>You toggle the gunlight [gun_light.on ? "on":"off"].</span>")
|
|
|
|
playsound(user, 'sound/weapons/empty.ogg', 100, TRUE)
|
|
update_gunlight()
|
|
|
|
/obj/item/gun/proc/update_gunlight()
|
|
if(gun_light)
|
|
if(gun_light.on)
|
|
set_light(gun_light.brightness_on)
|
|
else
|
|
set_light(0)
|
|
cut_overlays(flashlight_overlay, TRUE)
|
|
var/state = "flight[gun_light.on? "_on":""]" //Generic state.
|
|
if(gun_light.icon_state in icon_states('icons/obj/guns/flashlights.dmi')) //Snowflake state?
|
|
state = gun_light.icon_state
|
|
flashlight_overlay = mutable_appearance('icons/obj/guns/flashlights.dmi', state)
|
|
flashlight_overlay.pixel_x = flight_x_offset
|
|
flashlight_overlay.pixel_y = flight_y_offset
|
|
add_overlay(flashlight_overlay, TRUE)
|
|
else
|
|
set_light(0)
|
|
cut_overlays(flashlight_overlay, TRUE)
|
|
flashlight_overlay = null
|
|
update_icon(TRUE)
|
|
for(var/X in actions)
|
|
var/datum/action/A = X
|
|
A.UpdateButtonIcon()
|
|
|
|
/obj/item/gun/pickup(mob/user)
|
|
..()
|
|
if(azoom)
|
|
azoom.Grant(user)
|
|
|
|
/obj/item/gun/dropped(mob/user)
|
|
. = ..()
|
|
if(azoom)
|
|
azoom.Remove(user)
|
|
if(zoomed)
|
|
zoom(user,FALSE)
|
|
|
|
/obj/item/gun/proc/handle_suicide(mob/living/carbon/human/user, mob/living/carbon/human/target, params, bypass_timer)
|
|
if(!ishuman(user) || !ishuman(target))
|
|
return
|
|
|
|
if(semicd)
|
|
return
|
|
|
|
if(user == target)
|
|
target.visible_message("<span class='warning'>[user] sticks [src] in [user.p_their()] mouth, ready to pull the trigger...</span>", \
|
|
"<span class='userdanger'>You stick [src] in your mouth, ready to pull the trigger...</span>")
|
|
else
|
|
target.visible_message("<span class='warning'>[user] points [src] at [target]'s head, ready to pull the trigger...</span>", \
|
|
"<span class='userdanger'>[user] points [src] at your head, ready to pull the trigger...</span>")
|
|
|
|
semicd = TRUE
|
|
|
|
if(!bypass_timer && (!do_mob(user, target, 120) || user.zone_selected != BODY_ZONE_PRECISE_MOUTH))
|
|
if(user)
|
|
if(user == target)
|
|
user.visible_message("<span class='notice'>[user] decided not to shoot.</span>")
|
|
else if(target && target.Adjacent(user))
|
|
target.visible_message("<span class='notice'>[user] has decided to spare [target]</span>", "<span class='notice'>[user] has decided to spare your life!</span>")
|
|
semicd = FALSE
|
|
return
|
|
|
|
semicd = FALSE
|
|
|
|
target.visible_message("<span class='warning'>[user] pulls the trigger!</span>", "<span class='userdanger'>[(user == target) ? "You pull" : "[user] pulls"] the trigger!</span>")
|
|
|
|
if(chambered && chambered.BB)
|
|
chambered.BB.damage *= 5
|
|
|
|
process_fire(target, user, TRUE, params)
|
|
|
|
/obj/item/gun/proc/unlock() //used in summon guns and as a convience for admins
|
|
if(pin)
|
|
qdel(pin)
|
|
pin = new /obj/item/firing_pin
|
|
|
|
//Happens before the actual projectile creation
|
|
/obj/item/gun/proc/before_firing(atom/target,mob/user)
|
|
return
|
|
|
|
/////////////
|
|
// ZOOMING //
|
|
/////////////
|
|
|
|
/datum/action/toggle_scope_zoom
|
|
name = "Toggle Scope"
|
|
check_flags = AB_CHECK_CONSCIOUS|AB_CHECK_RESTRAINED|AB_CHECK_STUN|AB_CHECK_LYING
|
|
icon_icon = 'icons/mob/actions/actions_items.dmi'
|
|
button_icon_state = "sniper_zoom"
|
|
var/obj/item/gun/gun = null
|
|
|
|
/datum/action/toggle_scope_zoom/Trigger()
|
|
gun.zoom(owner)
|
|
|
|
/datum/action/toggle_scope_zoom/IsAvailable()
|
|
. = ..()
|
|
if(!. && gun)
|
|
gun.zoom(owner, FALSE)
|
|
|
|
/datum/action/toggle_scope_zoom/Remove(mob/living/L)
|
|
gun.zoom(L, FALSE)
|
|
..()
|
|
|
|
|
|
/obj/item/gun/proc/zoom(mob/living/user, forced_zoom)
|
|
if(!user || !user.client)
|
|
return
|
|
|
|
switch(forced_zoom)
|
|
if(FALSE)
|
|
zoomed = FALSE
|
|
if(TRUE)
|
|
zoomed = TRUE
|
|
else
|
|
zoomed = !zoomed
|
|
|
|
if(zoomed)
|
|
var/_x = 0
|
|
var/_y = 0
|
|
switch(user.dir)
|
|
if(NORTH)
|
|
_y = zoom_amt
|
|
if(EAST)
|
|
_x = zoom_amt
|
|
if(SOUTH)
|
|
_y = -zoom_amt
|
|
if(WEST)
|
|
_x = -zoom_amt
|
|
|
|
user.client.change_view(zoom_out_amt)
|
|
user.client.pixel_x = world.icon_size*_x
|
|
user.client.pixel_y = world.icon_size*_y
|
|
else
|
|
user.client.change_view(CONFIG_GET(string/default_view))
|
|
user.client.pixel_x = 0
|
|
user.client.pixel_y = 0
|
|
return zoomed
|
|
|
|
//Proc, so that gun accessories/scopes/etc. can easily add zooming.
|
|
/obj/item/gun/proc/build_zooming()
|
|
if(azoom)
|
|
return
|
|
|
|
if(zoomable)
|
|
azoom = new()
|
|
azoom.gun = src
|