@@ -0,0 +1,63 @@
|
||||
/obj/item/ammo_casing/proc/fire_casing(atom/target, mob/living/user, params, distro, quiet, zone_override, spread, atom/fired_from)
|
||||
distro += variance
|
||||
for (var/i = max(1, pellets), i > 0, i--)
|
||||
var/targloc = get_turf(target)
|
||||
ready_proj(target, user, quiet, zone_override, fired_from)
|
||||
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 = "", fired_from)
|
||||
if (!BB)
|
||||
return
|
||||
BB.original = target
|
||||
BB.firer = user
|
||||
BB.fired_from = fired_from
|
||||
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
|
||||
|
||||
var/firing_dir
|
||||
if(BB.firer)
|
||||
firing_dir = BB.firer.dir
|
||||
if(!BB.suppressed && firing_effect_type)
|
||||
new firing_effect_type(get_turf(src), firing_dir)
|
||||
|
||||
var/direct_target
|
||||
if(targloc == curloc)
|
||||
if(target) //if the target is right on our location we'll skip the travelling code in the proj's fire()
|
||||
direct_target = target
|
||||
if(!direct_target)
|
||||
BB.preparePixelProjectile(target, user, params, spread)
|
||||
BB.fire(null, direct_target)
|
||||
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)
|
||||
@@ -0,0 +1,50 @@
|
||||
// .357 (Syndie Revolver)
|
||||
|
||||
/obj/item/ammo_casing/a357
|
||||
name = ".357 bullet casing"
|
||||
desc = "A .357 bullet casing."
|
||||
caliber = "357"
|
||||
projectile_type = /obj/item/projectile/bullet/a357
|
||||
|
||||
/obj/item/ammo_casing/a357/ap
|
||||
name = ".357 armor-piercing bullet casing"
|
||||
desc = "A .357 armor-piercing bullet casing."
|
||||
projectile_type = /obj/item/projectile/bullet/a357/ap
|
||||
|
||||
// 7.62x38mmR (Nagant Revolver)
|
||||
|
||||
/obj/item/ammo_casing/n762
|
||||
name = "7.62x38mmR bullet casing"
|
||||
desc = "A 7.62x38mmR bullet casing."
|
||||
caliber = "n762"
|
||||
projectile_type = /obj/item/projectile/bullet/n762
|
||||
|
||||
// .38 (Detective's Gun)
|
||||
|
||||
/obj/item/ammo_casing/c38
|
||||
name = ".38 rubber bullet casing"
|
||||
desc = "A .38 rubber bullet casing."
|
||||
caliber = "38"
|
||||
projectile_type = /obj/item/projectile/bullet/c38/rubber
|
||||
|
||||
/obj/item/ammo_casing/c38/lethal
|
||||
name = ".38 bullet casing"
|
||||
desc = "A .38 bullet casing"
|
||||
projectile_type = /obj/item/projectile/bullet/c38
|
||||
|
||||
/obj/item/ammo_casing/c38/trac
|
||||
name = ".38 TRAC bullet casing"
|
||||
desc = "A .38 \"TRAC\" bullet casing."
|
||||
projectile_type = /obj/item/projectile/bullet/c38/trac
|
||||
|
||||
/obj/item/ammo_casing/c38/hotshot
|
||||
name = ".38 Hot Shot bullet casing"
|
||||
desc = "A .38 Hot Shot bullet casing."
|
||||
caliber = "38"
|
||||
projectile_type = /obj/item/projectile/bullet/c38/hotshot
|
||||
|
||||
/obj/item/ammo_casing/c38/iceblox
|
||||
name = ".38 Iceblox bullet casing"
|
||||
desc = "A .38 Iceblox bullet casing."
|
||||
caliber = "38"
|
||||
projectile_type = /obj/item/projectile/bullet/c38/iceblox
|
||||
@@ -0,0 +1,16 @@
|
||||
/obj/item/ammo_casing/caseless
|
||||
desc = "A caseless bullet casing."
|
||||
firing_effect_type = null
|
||||
heavy_metal = FALSE
|
||||
|
||||
/obj/item/ammo_casing/caseless/fire_casing(atom/target, mob/living/user, params, distro, quiet, zone_override, spread, atom/fired_from)
|
||||
if (..()) //successfully firing
|
||||
moveToNullspace()
|
||||
QDEL_NULL(src)
|
||||
return TRUE
|
||||
else
|
||||
return FALSE
|
||||
|
||||
/obj/item/ammo_casing/caseless/update_icon()
|
||||
..()
|
||||
icon_state = "[initial(icon_state)]"
|
||||
@@ -0,0 +1,73 @@
|
||||
/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 = 83
|
||||
select_name = "kill"
|
||||
|
||||
/obj/item/ammo_casing/energy/lasergun/old
|
||||
projectile_type = /obj/item/projectile/beam/laser
|
||||
e_cost = 200
|
||||
select_name = "kill"
|
||||
|
||||
/obj/item/ammo_casing/energy/laser/hos
|
||||
e_cost = 100
|
||||
|
||||
/obj/item/ammo_casing/energy/laser/practice
|
||||
projectile_type = /obj/item/projectile/beam/practice
|
||||
select_name = "practice"
|
||||
harmful = FALSE
|
||||
|
||||
/obj/item/ammo_casing/energy/laser/scatter
|
||||
projectile_type = /obj/item/projectile/beam/scatter
|
||||
pellets = 5
|
||||
variance = 25
|
||||
select_name = "scatter"
|
||||
|
||||
/obj/item/ammo_casing/energy/laser/scatter/disabler
|
||||
projectile_type = /obj/item/projectile/beam/disabler
|
||||
pellets = 3
|
||||
variance = 15
|
||||
harmful = FALSE
|
||||
|
||||
/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/bluetag
|
||||
projectile_type = /obj/item/projectile/beam/lasertag/bluetag
|
||||
select_name = "bluetag"
|
||||
harmful = FALSE
|
||||
|
||||
/obj/item/ammo_casing/energy/laser/bluetag/hitscan
|
||||
projectile_type = /obj/item/projectile/beam/lasertag/bluetag/hitscan
|
||||
|
||||
/obj/item/ammo_casing/energy/laser/redtag
|
||||
projectile_type = /obj/item/projectile/beam/lasertag/redtag
|
||||
select_name = "redtag"
|
||||
harmful = FALSE
|
||||
|
||||
/obj/item/ammo_casing/energy/laser/redtag/hitscan
|
||||
projectile_type = /obj/item/projectile/beam/lasertag/redtag/hitscan
|
||||
|
||||
/obj/item/ammo_casing/energy/laser/redtag/hitscan/holy
|
||||
projectile_type = /obj/item/projectile/beam/lasertag/redtag/hitscan/holy
|
||||
|
||||
/obj/item/ammo_casing/energy/xray
|
||||
projectile_type = /obj/item/projectile/beam/xray
|
||||
e_cost = 50
|
||||
fire_sound = 'sound/weapons/laser3.ogg'
|
||||
|
||||
/obj/item/ammo_casing/energy/mindflayer
|
||||
projectile_type = /obj/item/projectile/beam/mindflayer
|
||||
select_name = "MINDFUCK"
|
||||
fire_sound = 'sound/weapons/laser.ogg'
|
||||
@@ -0,0 +1,93 @@
|
||||
/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
|
||||
caliber = "357"
|
||||
max_ammo = 7
|
||||
multiple_sprites = 1
|
||||
|
||||
/obj/item/ammo_box/a357/ap
|
||||
name = "speed loader (.357 AP)"
|
||||
ammo_type = /obj/item/ammo_casing/a357/ap
|
||||
|
||||
/obj/item/ammo_box/c38
|
||||
name = "speed loader (.38 rubber)"
|
||||
desc = "Designed to quickly reload revolvers."
|
||||
icon_state = "38"
|
||||
caliber = "38"
|
||||
ammo_type = /obj/item/ammo_casing/c38
|
||||
max_ammo = 6
|
||||
multiple_sprites = 1
|
||||
materials = list(MAT_METAL = 20000)
|
||||
|
||||
/obj/item/ammo_box/c38/lethal
|
||||
name = "speed loader (.38)"
|
||||
ammo_type = /obj/item/ammo_casing/c38/lethal
|
||||
|
||||
/obj/item/ammo_box/c38/trac
|
||||
name = "speed loader (.38 TRAC)"
|
||||
desc = "Designed to quickly reload revolvers. TRAC bullets embed a tracking implant within the target's body."
|
||||
ammo_type = /obj/item/ammo_casing/c38/trac
|
||||
|
||||
/obj/item/ammo_box/c38/hotshot
|
||||
name = "speed loader (.38 Hot Shot)"
|
||||
desc = "Designed to quickly reload revolvers. Hot Shot bullets contain an incendiary payload."
|
||||
ammo_type = /obj/item/ammo_casing/c38/hotshot
|
||||
|
||||
/obj/item/ammo_box/c38/iceblox
|
||||
name = "speed loader (.38 Iceblox)"
|
||||
desc = "Designed to quickly reload revolvers. Iceblox bullets contain a cryogenic payload."
|
||||
ammo_type = /obj/item/ammo_casing/c38/iceblox
|
||||
|
||||
/obj/item/ammo_box/c9mm
|
||||
name = "ammo box (9mm)"
|
||||
icon_state = "9mmbox"
|
||||
ammo_type = /obj/item/ammo_casing/c9mm
|
||||
max_ammo = 30
|
||||
|
||||
/obj/item/ammo_box/c10mm
|
||||
name = "ammo box (10mm)"
|
||||
icon_state = "10mmbox"
|
||||
ammo_type = /obj/item/ammo_casing/c10mm
|
||||
max_ammo = 20
|
||||
|
||||
/obj/item/ammo_box/c45
|
||||
name = "ammo box (.45)"
|
||||
icon_state = "45box"
|
||||
ammo_type = /obj/item/ammo_casing/c45
|
||||
max_ammo = 20
|
||||
|
||||
/obj/item/ammo_box/a40mm
|
||||
name = "ammo box (40mm grenades)"
|
||||
icon_state = "40mm"
|
||||
ammo_type = /obj/item/ammo_casing/a40mm
|
||||
max_ammo = 4
|
||||
multiple_sprites = 1
|
||||
|
||||
/obj/item/ammo_box/a762
|
||||
name = "stripper clip (7.62mm)"
|
||||
desc = "A stripper clip."
|
||||
icon_state = "762"
|
||||
ammo_type = /obj/item/ammo_casing/a762
|
||||
max_ammo = 5
|
||||
multiple_sprites = 1
|
||||
|
||||
/obj/item/ammo_box/n762
|
||||
name = "ammo box (7.62x38mmR)"
|
||||
icon_state = "10mmbox"
|
||||
ammo_type = /obj/item/ammo_casing/n762
|
||||
max_ammo = 14
|
||||
|
||||
/obj/item/ammo_box/foambox
|
||||
name = "ammo box (Foam Darts)"
|
||||
icon = 'icons/obj/guns/toy.dmi'
|
||||
icon_state = "foambox"
|
||||
ammo_type = /obj/item/ammo_casing/caseless/foam_dart
|
||||
max_ammo = 40
|
||||
materials = list(MAT_METAL = 500)
|
||||
|
||||
/obj/item/ammo_box/foambox/riot
|
||||
icon_state = "foambox_riot"
|
||||
ammo_type = /obj/item/ammo_casing/caseless/foam_dart/riot
|
||||
materials = list(MAT_METAL = 50000)
|
||||
@@ -0,0 +1,76 @@
|
||||
/obj/item/ammo_box/magazine/wt550m9
|
||||
name = "wt550 magazine (4.6x30mm)"
|
||||
icon_state = "46x30mmt-20"
|
||||
ammo_type = /obj/item/ammo_casing/c46x30mm
|
||||
caliber = "4.6x30mm"
|
||||
max_ammo = 32
|
||||
|
||||
/obj/item/ammo_box/magazine/wt550m9/update_icon()
|
||||
..()
|
||||
icon_state = "46x30mmt-[round(20*(ammo_count()/max_ammo),4)]"
|
||||
|
||||
/obj/item/ammo_box/magazine/wt550m9/wtap
|
||||
name = "wt550 magazine (Armour Piercing 4.6x30mm)"
|
||||
icon_state = "46x30mmtA-20"
|
||||
ammo_type = /obj/item/ammo_casing/c46x30mm/ap
|
||||
|
||||
/obj/item/ammo_box/magazine/wt550m9/wtap/update_icon()
|
||||
..()
|
||||
icon_state = "46x30mmtA-[round(20*(ammo_count()/max_ammo),4)]"
|
||||
|
||||
/obj/item/ammo_box/magazine/wt550m9/wtic
|
||||
name = "wt550 magazine (Incendiary 4.6x30mm)"
|
||||
icon_state = "46x30mmtI-20"
|
||||
ammo_type = /obj/item/ammo_casing/c46x30mm/inc
|
||||
|
||||
/obj/item/ammo_box/magazine/wt550m9/wtic/update_icon()
|
||||
..()
|
||||
icon_state = "46x30mmtI-[round(20*(ammo_count()/max_ammo),4)]"
|
||||
|
||||
/obj/item/ammo_box/magazine/uzim9mm
|
||||
name = "uzi magazine (9mm)"
|
||||
icon_state = "uzi9mm-32"
|
||||
ammo_type = /obj/item/ammo_casing/c9mm
|
||||
caliber = "9mm"
|
||||
max_ammo = 32
|
||||
|
||||
/obj/item/ammo_box/magazine/uzim9mm/update_icon()
|
||||
..()
|
||||
icon_state = "uzi9mm-[round(ammo_count(),4)]"
|
||||
|
||||
/obj/item/ammo_box/magazine/smgm9mm
|
||||
name = "SMG magazine (9mm)"
|
||||
icon_state = "smg9mm-42"
|
||||
ammo_type = /obj/item/ammo_casing/c9mm
|
||||
caliber = "9mm"
|
||||
max_ammo = 21
|
||||
|
||||
/obj/item/ammo_box/magazine/smgm9mm/update_icon()
|
||||
..()
|
||||
icon_state = "smg9mm-[ammo_count() ? "42" : "0"]"
|
||||
|
||||
/obj/item/ammo_box/magazine/smgm9mm/ap
|
||||
name = "SMG magazine (Armour Piercing 9mm)"
|
||||
ammo_type = /obj/item/ammo_casing/c9mm/ap
|
||||
|
||||
/obj/item/ammo_box/magazine/smgm9mm/fire
|
||||
name = "SMG Magazine (Incendiary 9mm)"
|
||||
ammo_type = /obj/item/ammo_casing/c9mm/inc
|
||||
|
||||
/obj/item/ammo_box/magazine/smgm45
|
||||
name = "SMG magazine (.45)"
|
||||
icon_state = "c20r45-24"
|
||||
ammo_type = /obj/item/ammo_casing/c45/nostamina
|
||||
caliber = ".45"
|
||||
max_ammo = 24
|
||||
|
||||
/obj/item/ammo_box/magazine/smgm45/update_icon()
|
||||
..()
|
||||
icon_state = "c20r45-[round(ammo_count(),2)]"
|
||||
|
||||
/obj/item/ammo_box/magazine/tommygunm45
|
||||
name = "drum magazine (.45)"
|
||||
icon_state = "drum45"
|
||||
ammo_type = /obj/item/ammo_casing/c45
|
||||
caliber = ".45"
|
||||
max_ammo = 50
|
||||
@@ -0,0 +1,533 @@
|
||||
|
||||
#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/suppressed = null //whether or not a message is displayed when fired
|
||||
var/can_suppress = FALSE
|
||||
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 //currently only used for inaccuracy
|
||||
var/spread = 0 //Spread induced by the gun itself.
|
||||
var/burst_spread = 0 //Spread induced by the gun itself during burst fire per iteration. Only checked if spread is 0.
|
||||
var/randomspread = 1 //Set to 0 for shotguns. This is used for weapons that don't fire all their bullets at once.
|
||||
var/inaccuracy_modifier = 1
|
||||
|
||||
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/obj/item/flashlight/gun_light
|
||||
var/can_flashlight = 0
|
||||
var/obj/item/kitchen/knife/bayonet
|
||||
var/mutable_appearance/knife_overlay
|
||||
var/can_bayonet = FALSE
|
||||
var/datum/action/item_action/toggle_gunlight/alight
|
||||
var/mutable_appearance/flashlight_overlay
|
||||
|
||||
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
|
||||
var/knife_x_offset = 0
|
||||
var/knife_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/item_action/toggle_scope_zoom/azoom
|
||||
|
||||
var/dualwield_spread_mult = 1 //dualwield spread multiplier
|
||||
|
||||
/obj/item/gun/Initialize()
|
||||
. = ..()
|
||||
if(pin)
|
||||
pin = new pin(src)
|
||||
if(gun_light)
|
||||
alight = new (src)
|
||||
if(zoomable)
|
||||
azoom = new (src)
|
||||
|
||||
/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 firing pin installed, and won't fire.")
|
||||
|
||||
/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, "gun_dry_fire", 30, 1)
|
||||
|
||||
|
||||
/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(isliving(user)) //CIT CHANGE - makes gun recoil cause staminaloss
|
||||
user.adjustStaminaLossBuffered(getstamcost(user)*(firing_burst && burst_size >= 2 ? 1/burst_size : 1)) //CIT CHANGE - ditto
|
||||
|
||||
if(suppressed)
|
||||
playsound(user, fire_sound, 10, 1)
|
||||
else
|
||||
playsound(user, fire_sound, 50, 1)
|
||||
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(!can_shoot()) //Just because you can pull the trigger doesn't mean it can shoot.
|
||||
shoot_with_empty_chamber(user)
|
||||
return
|
||||
|
||||
if(flag)
|
||||
if(user.zone_selected == BODY_ZONE_PRECISE_MOUTH)
|
||||
handle_suicide(user, target, params)
|
||||
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
|
||||
|
||||
bonus_spread += getinaccuracy(user) //CIT CHANGE - adds bonus spread while not aiming
|
||||
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 * G.dualwield_spread_mult
|
||||
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), 1)
|
||||
else //Smart spread
|
||||
sprd = round((((rand_spr/burst_size) * iteration) - (0.5 + (rand_spr * 0.25))) * (randomized_gun_spread + randomized_bonus_spread), 1)
|
||||
|
||||
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)
|
||||
else if(burst_size > 1 && burst_spread)
|
||||
randomized_gun_spread = rand(0, burst_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))
|
||||
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()
|
||||
SEND_SIGNAL(user, COMSIG_LIVING_GUN_PROCESS_FIRE, target, params, zone_override)
|
||||
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 \the [S] into place on \the [src].</span>")
|
||||
if(S.on)
|
||||
set_light(0)
|
||||
gun_light = S
|
||||
update_gunlight(user)
|
||||
alight = new /datum/action/item_action/toggle_gunlight(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 \the [K] to the front of \the [src].</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 if(istype(I, /obj/item/screwdriver))
|
||||
if(gun_light)
|
||||
var/obj/item/flashlight/seclite/S = gun_light
|
||||
to_chat(user, "<span class='notice'>You unscrew the seclite from \the [src].</span>")
|
||||
gun_light = null
|
||||
S.forceMove(get_turf(user))
|
||||
update_gunlight(user)
|
||||
S.update_brightness(user)
|
||||
QDEL_NULL(alight)
|
||||
if(bayonet)
|
||||
to_chat(user, "<span class='notice'>You unscrew the bayonet from \the [src].</span>")
|
||||
var/obj/item/kitchen/knife/K = bayonet
|
||||
K.forceMove(get_turf(user))
|
||||
bayonet = null
|
||||
cut_overlay(knife_overlay, TRUE)
|
||||
knife_overlay = null
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/item/gun/ui_action_click(mob/user, action)
|
||||
if(istype(action, /datum/action/item_action/toggle_scope_zoom))
|
||||
zoom(user)
|
||||
else if(istype(action, alight))
|
||||
toggle_gunlight()
|
||||
|
||||
/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, 1)
|
||||
update_gunlight(user)
|
||||
return
|
||||
|
||||
/obj/item/gun/proc/update_gunlight(mob/user = null)
|
||||
if(gun_light)
|
||||
if(gun_light.on)
|
||||
set_light(gun_light.brightness_on, gun_light.flashlight_power, gun_light.light_color)
|
||||
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/item_action_slot_check(slot, mob/user, datum/action/A)
|
||||
if(istype(A, /datum/action/item_action/toggle_scope_zoom) && slot != SLOT_HANDS)
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/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] pulls the trigger!</span>")
|
||||
|
||||
playsound('sound/weapons/dink.ogg', 30, 1)
|
||||
|
||||
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
|
||||
|
||||
/////////////
|
||||
// ZOOMING //
|
||||
/////////////
|
||||
|
||||
/datum/action/item_action/toggle_scope_zoom
|
||||
name = "Toggle Scope"
|
||||
icon_icon = 'icons/mob/actions/actions_items.dmi'
|
||||
button_icon_state = "sniper_zoom"
|
||||
|
||||
/datum/action/item_action/toggle_scope_zoom/IsAvailable()
|
||||
. = ..()
|
||||
if(!.)
|
||||
var/obj/item/gun/G = target
|
||||
G.zoom(owner, FALSE)
|
||||
|
||||
/datum/action/item_action/toggle_scope_zoom/Remove(mob/living/L)
|
||||
var/obj/item/gun/G = target
|
||||
G.zoom(L, FALSE)
|
||||
return ..()
|
||||
|
||||
/obj/item/gun/proc/zoom(mob/living/user, forced_zoom)
|
||||
if(!(user?.client))
|
||||
return
|
||||
|
||||
if(!isnull(forced_zoom))
|
||||
if(zoomed == forced_zoom)
|
||||
return
|
||||
zoomed = forced_zoom
|
||||
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
|
||||
|
||||
/obj/item/gun/handle_atom_del(atom/A)
|
||||
if(A == chambered)
|
||||
chambered = null
|
||||
update_icon()
|
||||
|
||||
/obj/item/gun/proc/getinaccuracy(mob/living/user)
|
||||
if(!iscarbon(user))
|
||||
return FALSE
|
||||
else
|
||||
var/mob/living/carbon/holdingdude = user
|
||||
if(istype(holdingdude) && holdingdude.combatmode)
|
||||
return (max((holdingdude.lastdirchange + weapon_weight * 25) - world.time,0) * inaccuracy_modifier)
|
||||
else
|
||||
return ((weapon_weight * 25) * inaccuracy_modifier)
|
||||
@@ -0,0 +1,232 @@
|
||||
/obj/item/gun/ballistic
|
||||
desc = "Now comes in flavors like GUN. Uses 10mm ammo, for some reason."
|
||||
name = "projectile gun"
|
||||
icon_state = "pistol"
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
var/spawnwithmagazine = TRUE
|
||||
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/casing_ejector = TRUE //whether the gun ejects the chambered casing
|
||||
var/magazine_wording = "magazine"
|
||||
|
||||
/obj/item/gun/ballistic/Initialize()
|
||||
. = ..()
|
||||
if(!spawnwithmagazine)
|
||||
update_icon()
|
||||
return
|
||||
if (!magazine)
|
||||
magazine = new mag_type(src)
|
||||
chamber_round()
|
||||
update_icon()
|
||||
|
||||
/obj/item/gun/ballistic/update_icon()
|
||||
..()
|
||||
if(current_skin)
|
||||
icon_state = "[unique_reskin[current_skin]][suppressed ? "-suppressed" : ""][sawn_off ? "-sawn" : ""]"
|
||||
else
|
||||
icon_state = "[initial(icon_state)][suppressed ? "-suppressed" : ""][sawn_off ? "-sawn" : ""]"
|
||||
|
||||
/obj/item/gun/ballistic/process_chamber(empty_chamber = 1)
|
||||
var/obj/item/ammo_casing/AC = chambered //Find chambered round
|
||||
if(istype(AC)) //there's a chambered round
|
||||
if(casing_ejector)
|
||||
AC.forceMove(drop_location()) //Eject casing onto ground.
|
||||
AC.bounce_away(TRUE)
|
||||
chambered = null
|
||||
else if(empty_chamber)
|
||||
chambered = null
|
||||
chamber_round()
|
||||
|
||||
|
||||
/obj/item/gun/ballistic/proc/chamber_round()
|
||||
if (chambered || !magazine)
|
||||
return
|
||||
else if (magazine.ammo_count())
|
||||
chambered = magazine.get_round()
|
||||
chambered.forceMove(src)
|
||||
|
||||
/obj/item/gun/ballistic/can_shoot()
|
||||
if(!magazine || !magazine.ammo_count(0))
|
||||
return 0
|
||||
return 1
|
||||
|
||||
/obj/item/gun/ballistic/attackby(obj/item/A, mob/user, params)
|
||||
..()
|
||||
if (istype(A, /obj/item/ammo_box/magazine))
|
||||
var/obj/item/ammo_box/magazine/AM = A
|
||||
if (!magazine && istype(AM, mag_type))
|
||||
if(user.transferItemToLoc(AM, src))
|
||||
magazine = AM
|
||||
to_chat(user, "<span class='notice'>You load a new [magazine_wording] into \the [src].</span>")
|
||||
if(magazine.ammo_count())
|
||||
playsound(src, "gun_insert_full_magazine", 70, 1)
|
||||
if(!chambered)
|
||||
chamber_round()
|
||||
addtimer(CALLBACK(GLOBAL_PROC, .proc/playsound, src, 'sound/weapons/gun_chamber_round.ogg', 100, 1), 3)
|
||||
else
|
||||
playsound(src, "gun_insert_empty_magazine", 70, 1)
|
||||
A.update_icon()
|
||||
update_icon()
|
||||
return 1
|
||||
else
|
||||
to_chat(user, "<span class='warning'>You cannot seem to get \the [src] out of your hands!</span>")
|
||||
return
|
||||
else if (magazine)
|
||||
to_chat(user, "<span class='notice'>There's already a [magazine_wording] in \the [src].</span>")
|
||||
if(istype(A, /obj/item/suppressor))
|
||||
var/obj/item/suppressor/S = A
|
||||
if(!can_suppress)
|
||||
to_chat(user, "<span class='warning'>You can't seem to figure out how to fit [S] on [src]!</span>")
|
||||
return
|
||||
if(!user.is_holding(src))
|
||||
to_chat(user, "<span class='notice'>You need be holding [src] to fit [S] to it!</span>")
|
||||
return
|
||||
if(suppressed)
|
||||
to_chat(user, "<span class='warning'>[src] already has a suppressor!</span>")
|
||||
return
|
||||
if(user.transferItemToLoc(A, src))
|
||||
to_chat(user, "<span class='notice'>You screw [S] onto [src].</span>")
|
||||
install_suppressor(A)
|
||||
return
|
||||
return 0
|
||||
|
||||
/obj/item/gun/ballistic/proc/install_suppressor(obj/item/suppressor/S)
|
||||
// this proc assumes that the suppressor is already inside src
|
||||
suppressed = S
|
||||
S.oldsound = fire_sound
|
||||
fire_sound = 'sound/weapons/gunshot_silenced.ogg'
|
||||
w_class += S.w_class //so pistols do not fit in pockets when suppressed
|
||||
update_icon()
|
||||
|
||||
//ATTACK HAND IGNORING PARENT RETURN VALUE
|
||||
/obj/item/gun/ballistic/attack_hand(mob/user)
|
||||
if(loc == user)
|
||||
if(suppressed && can_unsuppress)
|
||||
var/obj/item/suppressor/S = suppressed
|
||||
if(!user.is_holding(src))
|
||||
return ..()
|
||||
to_chat(user, "<span class='notice'>You unscrew [suppressed] from [src].</span>")
|
||||
user.put_in_hands(suppressed)
|
||||
fire_sound = S.oldsound
|
||||
w_class -= S.w_class
|
||||
suppressed = null
|
||||
update_icon()
|
||||
return
|
||||
return ..()
|
||||
|
||||
/obj/item/gun/ballistic/attack_self(mob/living/user)
|
||||
var/obj/item/ammo_casing/AC = chambered //Find chambered round
|
||||
if(magazine)
|
||||
magazine.forceMove(drop_location())
|
||||
user.put_in_hands(magazine)
|
||||
magazine.update_icon()
|
||||
if(magazine.ammo_count())
|
||||
playsound(src, 'sound/weapons/gun_magazine_remove_full.ogg', 70, 1)
|
||||
else
|
||||
playsound(src, "gun_remove_empty_magazine", 70, 1)
|
||||
magazine = null
|
||||
to_chat(user, "<span class='notice'>You pull the magazine out of \the [src].</span>")
|
||||
else if(chambered)
|
||||
AC.forceMove(drop_location())
|
||||
AC.bounce_away()
|
||||
chambered = null
|
||||
to_chat(user, "<span class='notice'>You unload the round from \the [src]'s chamber.</span>")
|
||||
playsound(src, "gun_slide_lock", 70, 1)
|
||||
else
|
||||
to_chat(user, "<span class='notice'>There's no magazine in \the [src].</span>")
|
||||
update_icon()
|
||||
return
|
||||
|
||||
|
||||
/obj/item/gun/ballistic/examine(mob/user)
|
||||
..()
|
||||
to_chat(user, "It has [get_ammo()] round\s remaining.")
|
||||
|
||||
/obj/item/gun/ballistic/proc/get_ammo(countchambered = 1)
|
||||
var/boolets = 0 //mature var names for mature people
|
||||
if (chambered && countchambered)
|
||||
boolets++
|
||||
if (magazine)
|
||||
boolets += magazine.ammo_count()
|
||||
return boolets
|
||||
|
||||
#define BRAINS_BLOWN_THROW_RANGE 3
|
||||
#define BRAINS_BLOWN_THROW_SPEED 1
|
||||
/obj/item/gun/ballistic/suicide_act(mob/living/user)
|
||||
var/obj/item/organ/brain/B = user.getorganslot(ORGAN_SLOT_BRAIN)
|
||||
if (B && chambered && chambered.BB && can_trigger_gun(user) && !chambered.BB.nodamage)
|
||||
user.visible_message("<span class='suicide'>[user] is putting the barrel of [src] in [user.p_their()] mouth. It looks like [user.p_theyre()] trying to commit suicide!</span>")
|
||||
sleep(25)
|
||||
if(user.is_holding(src))
|
||||
var/turf/T = get_turf(user)
|
||||
process_fire(user, user, FALSE, null, BODY_ZONE_HEAD)
|
||||
user.visible_message("<span class='suicide'>[user] blows [user.p_their()] brain[user.p_s()] out with [src]!</span>")
|
||||
playsound(src, 'sound/weapons/dink.ogg', 30, 1)
|
||||
var/turf/target = get_ranged_target_turf(user, turn(user.dir, 180), BRAINS_BLOWN_THROW_RANGE)
|
||||
B.Remove(user)
|
||||
B.forceMove(T)
|
||||
if(iscarbon(user))
|
||||
var/mob/living/carbon/C = user
|
||||
B.add_blood_DNA(C.dna, C.diseases)
|
||||
var/datum/callback/gibspawner = CALLBACK(user, /mob/living/proc/spawn_gibs, FALSE, B)
|
||||
B.throw_at(target, BRAINS_BLOWN_THROW_RANGE, BRAINS_BLOWN_THROW_SPEED, callback=gibspawner)
|
||||
return(BRUTELOSS)
|
||||
else
|
||||
user.visible_message("<span class='suicide'>[user] panics and starts choking to death!</span>")
|
||||
return(OXYLOSS)
|
||||
else
|
||||
user.visible_message("<span class='suicide'>[user] is pretending to blow [user.p_their()] brain[user.p_s()] out with [src]! It looks like [user.p_theyre()] trying to commit suicide!</b></span>")
|
||||
playsound(src, "gun_dry_fire", 30, 1)
|
||||
return (OXYLOSS)
|
||||
#undef BRAINS_BLOWN_THROW_SPEED
|
||||
#undef BRAINS_BLOWN_THROW_RANGE
|
||||
|
||||
|
||||
|
||||
/obj/item/gun/ballistic/proc/sawoff(mob/user)
|
||||
if(sawn_off)
|
||||
to_chat(user, "<span class='warning'>\The [src] is already shortened!</span>")
|
||||
return
|
||||
user.changeNext_move(CLICK_CD_MELEE)
|
||||
user.visible_message("[user] begins to shorten \the [src].", "<span class='notice'>You begin to shorten \the [src]...</span>")
|
||||
|
||||
//if there's any live ammo inside the gun, makes it go off
|
||||
if(blow_up(user))
|
||||
user.visible_message("<span class='danger'>\The [src] goes off!</span>", "<span class='danger'>\The [src] goes off in your face!</span>")
|
||||
return
|
||||
|
||||
if(do_after(user, 30, target = src))
|
||||
if(sawn_off)
|
||||
return
|
||||
user.visible_message("[user] shortens \the [src]!", "<span class='notice'>You shorten \the [src].</span>")
|
||||
name = "sawn-off [src.name]"
|
||||
desc = sawn_desc
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
item_state = "gun"
|
||||
slot_flags &= ~ITEM_SLOT_BACK //you can't sling it on your back
|
||||
slot_flags |= ITEM_SLOT_BELT //but you can wear it on your belt (poorly concealed under a trenchcoat, ideally)
|
||||
sawn_off = TRUE
|
||||
update_icon()
|
||||
return 1
|
||||
|
||||
// Sawing guns related proc
|
||||
/obj/item/gun/ballistic/proc/blow_up(mob/user)
|
||||
. = 0
|
||||
for(var/obj/item/ammo_casing/AC in magazine.stored_ammo)
|
||||
if(AC.BB)
|
||||
process_fire(user, user, FALSE)
|
||||
. = 1
|
||||
|
||||
|
||||
/obj/item/suppressor
|
||||
name = "suppressor"
|
||||
desc = "A syndicate small-arms suppressor for maximum espionage."
|
||||
icon = 'icons/obj/guns/projectile.dmi'
|
||||
icon_state = "suppressor"
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
var/oldsound = null
|
||||
|
||||
|
||||
/obj/item/suppressor/specialoffer
|
||||
name = "cheap suppressor"
|
||||
desc = "A foreign knock-off suppressor, it feels flimsy, cheap, and brittle. Still fits some weapons."
|
||||
@@ -0,0 +1,432 @@
|
||||
/obj/item/gun/ballistic/automatic
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
var/alarmed = 0
|
||||
var/select = 1
|
||||
var/automatic_burst_overlay = TRUE
|
||||
can_suppress = TRUE
|
||||
burst_size = 3
|
||||
fire_delay = 2
|
||||
actions_types = list(/datum/action/item_action/toggle_firemode)
|
||||
|
||||
/obj/item/gun/ballistic/automatic/proto
|
||||
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
|
||||
pin = null
|
||||
|
||||
/obj/item/gun/ballistic/automatic/proto/unrestricted
|
||||
pin = /obj/item/firing_pin
|
||||
|
||||
/obj/item/gun/ballistic/automatic/update_icon()
|
||||
..()
|
||||
if(automatic_burst_overlay)
|
||||
if(!select)
|
||||
add_overlay("[initial(icon_state)]semi")
|
||||
if(select == 1)
|
||||
add_overlay("[initial(icon_state)]burst")
|
||||
icon_state = "[initial(icon_state)][magazine ? "-[magazine.max_ammo]" : ""][chambered ? "" : "-e"][suppressed ? "-suppressed" : ""]"
|
||||
|
||||
/obj/item/gun/ballistic/automatic/attackby(obj/item/A, mob/user, params)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
if(istype(A, /obj/item/ammo_box/magazine))
|
||||
var/obj/item/ammo_box/magazine/AM = A
|
||||
if(istype(AM, mag_type))
|
||||
var/obj/item/ammo_box/magazine/oldmag = magazine
|
||||
if(user.transferItemToLoc(AM, src))
|
||||
magazine = AM
|
||||
if(oldmag)
|
||||
to_chat(user, "<span class='notice'>You perform a tactical reload on \the [src], replacing the magazine.</span>")
|
||||
oldmag.dropped()
|
||||
oldmag.forceMove(get_turf(src.loc))
|
||||
oldmag.update_icon()
|
||||
else
|
||||
to_chat(user, "<span class='notice'>You insert the magazine into \the [src].</span>")
|
||||
|
||||
playsound(user, 'sound/weapons/autoguninsert.ogg', 60, 1)
|
||||
chamber_round()
|
||||
A.update_icon()
|
||||
update_icon()
|
||||
return 1
|
||||
else
|
||||
to_chat(user, "<span class='warning'>You cannot seem to get \the [src] out of your hands!</span>")
|
||||
|
||||
/obj/item/gun/ballistic/automatic/ui_action_click(mob/user, action)
|
||||
if(istype(action, /datum/action/item_action/toggle_firemode))
|
||||
burst_select()
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/item/gun/ballistic/automatic/proc/burst_select()
|
||||
var/mob/living/carbon/human/user = usr
|
||||
select = !select
|
||||
if(!select)
|
||||
disable_burst()
|
||||
to_chat(user, "<span class='notice'>You switch to semi-automatic.</span>")
|
||||
else
|
||||
enable_burst()
|
||||
to_chat(user, "<span class='notice'>You switch to [burst_size]-rnd burst.</span>")
|
||||
|
||||
playsound(user, 'sound/weapons/empty.ogg', 100, 1)
|
||||
update_icon()
|
||||
for(var/X in actions)
|
||||
var/datum/action/A = X
|
||||
A.UpdateButtonIcon()
|
||||
|
||||
/obj/item/gun/ballistic/automatic/proc/enable_burst()
|
||||
burst_size = initial(burst_size)
|
||||
fire_delay = initial(fire_delay)
|
||||
|
||||
/obj/item/gun/ballistic/automatic/proc/disable_burst()
|
||||
burst_size = 1
|
||||
fire_delay = 0
|
||||
|
||||
/obj/item/gun/ballistic/automatic/can_shoot()
|
||||
return get_ammo()
|
||||
|
||||
/obj/item/gun/ballistic/automatic/proc/empty_alarm()
|
||||
if(!chambered && !get_ammo() && !alarmed)
|
||||
playsound(src.loc, 'sound/weapons/smg_empty_alarm.ogg', 40, 1)
|
||||
update_icon()
|
||||
alarmed = 1
|
||||
return
|
||||
|
||||
/obj/item/gun/ballistic/automatic/c20r
|
||||
name = "\improper C-20r SMG"
|
||||
desc = "A bullpup two-round burst .45 SMG, designated 'C-20r'. Has a 'Scarborough Arms - Per falcis, per pravitas' buttstamp."
|
||||
icon_state = "c20r"
|
||||
item_state = "c20r"
|
||||
mag_type = /obj/item/ammo_box/magazine/smgm45
|
||||
fire_sound = 'sound/weapons/gunshot_smg.ogg'
|
||||
fire_delay = 2
|
||||
burst_size = 2
|
||||
pin = /obj/item/firing_pin/implant/pindicate
|
||||
can_bayonet = TRUE
|
||||
knife_x_offset = 26
|
||||
knife_y_offset = 12
|
||||
|
||||
/obj/item/gun/ballistic/automatic/c20r/unrestricted
|
||||
pin = /obj/item/firing_pin
|
||||
|
||||
/obj/item/gun/ballistic/automatic/c20r/Initialize()
|
||||
. = ..()
|
||||
update_icon()
|
||||
|
||||
/obj/item/gun/ballistic/automatic/c20r/afterattack()
|
||||
. = ..()
|
||||
empty_alarm()
|
||||
|
||||
/obj/item/gun/ballistic/automatic/c20r/update_icon()
|
||||
..()
|
||||
icon_state = "c20r[magazine ? "-[CEILING(get_ammo(0)/4, 1)*4]" : ""][chambered ? "" : "-e"][suppressed ? "-suppressed" : ""]"
|
||||
|
||||
/obj/item/gun/ballistic/automatic/wt550
|
||||
name = "security semi-auto smg"
|
||||
desc = "An outdated personal defence weapon. Uses 4.6x30mm rounds and is designated the WT-550 Semi-Automatic SMG."
|
||||
icon_state = "wt550"
|
||||
item_state = "arg"
|
||||
mag_type = /obj/item/ammo_box/magazine/wt550m9
|
||||
can_suppress = FALSE
|
||||
burst_size = 2
|
||||
fire_delay = 1
|
||||
can_bayonet = TRUE
|
||||
knife_x_offset = 25
|
||||
knife_y_offset = 12
|
||||
automatic_burst_overlay = FALSE
|
||||
|
||||
/obj/item/gun/ballistic/automatic/wt550/enable_burst()
|
||||
. = ..()
|
||||
spread = 15
|
||||
|
||||
/obj/item/gun/ballistic/automatic/wt550/disable_burst()
|
||||
. = ..()
|
||||
spread = 0
|
||||
|
||||
/obj/item/gun/ballistic/automatic/wt550/update_icon()
|
||||
..()
|
||||
icon_state = "wt550[magazine ? "-[CEILING(( (get_ammo(FALSE) / magazine.max_ammo) * 20) /4, 1)*4]" : "-0"]" //Sprites only support up to 20.
|
||||
|
||||
/obj/item/gun/ballistic/automatic/mini_uzi
|
||||
name = "\improper Type U3 Uzi"
|
||||
desc = "A lightweight, burst-fire submachine gun, for when you really want someone dead. Uses 9mm rounds."
|
||||
icon_state = "mini-uzi"
|
||||
mag_type = /obj/item/ammo_box/magazine/uzim9mm
|
||||
burst_size = 2
|
||||
|
||||
/obj/item/gun/ballistic/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"
|
||||
mag_type = /obj/item/ammo_box/magazine/m556
|
||||
fire_sound = 'sound/weapons/gunshot_smg.ogg'
|
||||
can_suppress = FALSE
|
||||
var/obj/item/gun/ballistic/revolver/grenadelauncher/underbarrel
|
||||
burst_size = 3
|
||||
fire_delay = 2
|
||||
pin = /obj/item/firing_pin/implant/pindicate
|
||||
|
||||
/obj/item/gun/ballistic/automatic/m90/Initialize()
|
||||
. = ..()
|
||||
underbarrel = new /obj/item/gun/ballistic/revolver/grenadelauncher(src)
|
||||
update_icon()
|
||||
|
||||
/obj/item/gun/ballistic/automatic/m90/unrestricted
|
||||
pin = /obj/item/firing_pin
|
||||
|
||||
/obj/item/gun/ballistic/automatic/m90/unrestricted/Initialize()
|
||||
. = ..()
|
||||
underbarrel = new /obj/item/gun/ballistic/revolver/grenadelauncher/unrestricted(src)
|
||||
update_icon()
|
||||
|
||||
/obj/item/gun/ballistic/automatic/m90/afterattack(atom/target, mob/living/user, flag, params)
|
||||
if(select == 2)
|
||||
underbarrel.afterattack(target, user, flag, params)
|
||||
else
|
||||
. = ..()
|
||||
return
|
||||
/obj/item/gun/ballistic/automatic/m90/attackby(obj/item/A, mob/user, params)
|
||||
if(istype(A, /obj/item/ammo_casing))
|
||||
if(istype(A, underbarrel.magazine.ammo_type))
|
||||
underbarrel.attack_self()
|
||||
underbarrel.attackby(A, user, params)
|
||||
else
|
||||
..()
|
||||
/obj/item/gun/ballistic/automatic/m90/update_icon()
|
||||
..()
|
||||
cut_overlays()
|
||||
switch(select)
|
||||
if(0)
|
||||
add_overlay("[initial(icon_state)]semi")
|
||||
if(1)
|
||||
add_overlay("[initial(icon_state)]burst")
|
||||
if(2)
|
||||
add_overlay("[initial(icon_state)]gren")
|
||||
icon_state = "[initial(icon_state)][magazine ? "" : "-e"]"
|
||||
return
|
||||
/obj/item/gun/ballistic/automatic/m90/burst_select()
|
||||
var/mob/living/carbon/human/user = usr
|
||||
switch(select)
|
||||
if(0)
|
||||
select = 1
|
||||
burst_size = initial(burst_size)
|
||||
fire_delay = initial(fire_delay)
|
||||
to_chat(user, "<span class='notice'>You switch to [burst_size]-rnd burst.</span>")
|
||||
if(1)
|
||||
select = 2
|
||||
to_chat(user, "<span class='notice'>You switch to grenades.</span>")
|
||||
if(2)
|
||||
select = 0
|
||||
burst_size = 1
|
||||
fire_delay = 0
|
||||
to_chat(user, "<span class='notice'>You switch to semi-auto.</span>")
|
||||
playsound(user, 'sound/weapons/empty.ogg', 100, 1)
|
||||
update_icon()
|
||||
return
|
||||
|
||||
/obj/item/gun/ballistic/automatic/tommygun
|
||||
name = "\improper Thompson SMG"
|
||||
desc = "Based on the classic 'Chicago Typewriter'."
|
||||
icon_state = "tommygun"
|
||||
item_state = "shotgun"
|
||||
w_class = WEIGHT_CLASS_HUGE
|
||||
slot_flags = 0
|
||||
mag_type = /obj/item/ammo_box/magazine/tommygunm45
|
||||
fire_sound = 'sound/weapons/gunshot_smg.ogg'
|
||||
can_suppress = FALSE
|
||||
burst_size = 4
|
||||
fire_delay = 1
|
||||
|
||||
/obj/item/gun/ballistic/automatic/ar
|
||||
name = "\improper NT-ARG 'Boarder'"
|
||||
desc = "A robust assault rifle used by Nanotrasen fighting forces."
|
||||
icon_state = "arg"
|
||||
item_state = "arg"
|
||||
slot_flags = 0
|
||||
mag_type = /obj/item/ammo_box/magazine/m556
|
||||
fire_sound = 'sound/weapons/gunshot_smg.ogg'
|
||||
can_suppress = FALSE
|
||||
burst_size = 3
|
||||
fire_delay = 1
|
||||
|
||||
// Bulldog shotgun //
|
||||
|
||||
/obj/item/gun/ballistic/automatic/shotgun/bulldog
|
||||
name = "\improper Bulldog Shotgun"
|
||||
desc = "A semi-auto, mag-fed 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 = WEIGHT_CLASS_NORMAL
|
||||
weapon_weight = WEAPON_MEDIUM
|
||||
mag_type = /obj/item/ammo_box/magazine/m12g
|
||||
fire_sound = 'sound/weapons/gunshot.ogg'
|
||||
can_suppress = FALSE
|
||||
burst_size = 1
|
||||
fire_delay = 0
|
||||
pin = /obj/item/firing_pin/implant/pindicate
|
||||
actions_types = list()
|
||||
|
||||
/obj/item/gun/ballistic/automatic/shotgun/bulldog/unrestricted
|
||||
pin = /obj/item/firing_pin
|
||||
|
||||
/obj/item/gun/ballistic/automatic/shotgun/bulldog/Initialize()
|
||||
. = ..()
|
||||
update_icon()
|
||||
|
||||
/obj/item/gun/ballistic/automatic/shotgun/bulldog/update_icon()
|
||||
cut_overlays()
|
||||
if(magazine)
|
||||
add_overlay("[magazine.icon_state]")
|
||||
icon_state = "bulldog[chambered ? "" : "-e"]"
|
||||
|
||||
/obj/item/gun/ballistic/automatic/shotgun/bulldog/afterattack()
|
||||
. = ..()
|
||||
empty_alarm()
|
||||
return
|
||||
|
||||
// L6 SAW //
|
||||
|
||||
/obj/item/gun/ballistic/automatic/l6_saw
|
||||
name = "\improper L6 SAW"
|
||||
desc = "A heavily modified 1.95x129mm light machine gun, designated 'L6 SAW'. Has 'Aussec Armoury - 2531' engraved on the receiver below the designation."
|
||||
icon_state = "l6closed100"
|
||||
item_state = "l6closedmag"
|
||||
w_class = WEIGHT_CLASS_HUGE
|
||||
slot_flags = 0
|
||||
mag_type = /obj/item/ammo_box/magazine/mm195x129
|
||||
weapon_weight = WEAPON_HEAVY
|
||||
fire_sound = 'sound/weapons/gunshot_smg.ogg'
|
||||
var/cover_open = FALSE
|
||||
can_suppress = FALSE
|
||||
burst_size = 3
|
||||
fire_delay = 1
|
||||
spread = 7
|
||||
pin = /obj/item/firing_pin/implant/pindicate
|
||||
|
||||
/obj/item/gun/ballistic/automatic/l6_saw/unrestricted
|
||||
pin = /obj/item/firing_pin
|
||||
|
||||
/obj/item/gun/ballistic/automatic/l6_saw/examine(mob/user)
|
||||
..()
|
||||
if(cover_open && magazine)
|
||||
to_chat(user, "<span class='notice'>It seems like you could use an <b>empty hand</b> to remove the magazine.</span>")
|
||||
|
||||
/obj/item/gun/ballistic/automatic/l6_saw/attack_self(mob/user)
|
||||
cover_open = !cover_open
|
||||
to_chat(user, "<span class='notice'>You [cover_open ? "open" : "close"] [src]'s cover.</span>")
|
||||
if(cover_open)
|
||||
playsound(user, 'sound/weapons/sawopen.ogg', 60, 1)
|
||||
else
|
||||
playsound(user, 'sound/weapons/sawclose.ogg', 60, 1)
|
||||
update_icon()
|
||||
|
||||
/obj/item/gun/ballistic/automatic/l6_saw/update_icon()
|
||||
icon_state = "l6[cover_open ? "open" : "closed"][magazine ? CEILING(get_ammo(0)/12.5, 1)*25 : "-empty"][suppressed ? "-suppressed" : ""]"
|
||||
item_state = "l6[cover_open ? "openmag" : "closedmag"]"
|
||||
|
||||
/obj/item/gun/ballistic/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, "<span class='warning'>[src]'s cover is open! Close it before firing!</span>")
|
||||
else
|
||||
. = ..()
|
||||
update_icon()
|
||||
|
||||
//ATTACK HAND IGNORING PARENT RETURN VALUE
|
||||
/obj/item/gun/ballistic/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.forceMove(drop_location())
|
||||
user.put_in_hands(magazine)
|
||||
magazine = null
|
||||
update_icon()
|
||||
to_chat(user, "<span class='notice'>You remove the magazine from [src].</span>")
|
||||
playsound(user, 'sound/weapons/magout.ogg', 60, 1)
|
||||
|
||||
/obj/item/gun/ballistic/automatic/l6_saw/attackby(obj/item/A, mob/user, params)
|
||||
if(!cover_open && istype(A, mag_type))
|
||||
to_chat(user, "<span class='warning'>[src]'s cover is closed! You can't insert a new mag.</span>")
|
||||
return
|
||||
..()
|
||||
|
||||
// SNIPER //
|
||||
|
||||
/obj/item/gun/ballistic/automatic/sniper_rifle
|
||||
name = "sniper rifle"
|
||||
desc = "A long ranged weapon that does significant damage. No, you can't quickscope."
|
||||
icon_state = "sniper"
|
||||
item_state = "sniper"
|
||||
recoil = 2
|
||||
weapon_weight = WEAPON_HEAVY
|
||||
mag_type = /obj/item/ammo_box/magazine/sniper_rounds
|
||||
fire_delay = 40
|
||||
burst_size = 1
|
||||
can_unsuppress = TRUE
|
||||
can_suppress = TRUE
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
zoomable = TRUE
|
||||
zoom_amt = 10 //Long range, enough to see in front of you, but no tiles behind you.
|
||||
zoom_out_amt = 13
|
||||
slot_flags = ITEM_SLOT_BACK
|
||||
actions_types = list()
|
||||
|
||||
/obj/item/gun/ballistic/automatic/sniper_rifle/update_icon()
|
||||
if(magazine)
|
||||
icon_state = "sniper-mag"
|
||||
else
|
||||
icon_state = "sniper"
|
||||
|
||||
/obj/item/gun/ballistic/automatic/sniper_rifle/syndicate
|
||||
name = "syndicate sniper rifle"
|
||||
desc = "An illegally modified .50 cal sniper rifle with suppression compatibility. Quickscoping still doesn't work."
|
||||
pin = /obj/item/firing_pin/implant/pindicate
|
||||
|
||||
// Old Semi-Auto Rifle //
|
||||
|
||||
/obj/item/gun/ballistic/automatic/surplus
|
||||
name = "Surplus Rifle"
|
||||
desc = "One of countless obsolete ballistic rifles that still sees use as a cheap deterrent. Uses 10mm ammo and its bulky frame prevents one-hand firing."
|
||||
icon_state = "surplus"
|
||||
item_state = "moistnugget"
|
||||
weapon_weight = WEAPON_HEAVY
|
||||
mag_type = /obj/item/ammo_box/magazine/m10mm/rifle
|
||||
fire_delay = 30
|
||||
burst_size = 1
|
||||
can_unsuppress = TRUE
|
||||
can_suppress = TRUE
|
||||
w_class = WEIGHT_CLASS_HUGE
|
||||
slot_flags = ITEM_SLOT_BACK
|
||||
actions_types = list()
|
||||
|
||||
/obj/item/gun/ballistic/automatic/surplus/update_icon()
|
||||
if(magazine)
|
||||
icon_state = "surplus"
|
||||
else
|
||||
icon_state = "surplus-e"
|
||||
|
||||
// Laser rifle (rechargeable magazine) //
|
||||
|
||||
/obj/item/gun/ballistic/automatic/laser
|
||||
name = "laser rifle"
|
||||
desc = "Though sometimes mocked for the relatively weak firepower of their energy weapons, the logistic miracle of rechargeable ammunition has given Nanotrasen a decisive edge over many a foe."
|
||||
icon_state = "oldrifle"
|
||||
item_state = "arg"
|
||||
mag_type = /obj/item/ammo_box/magazine/recharge
|
||||
fire_delay = 2
|
||||
can_suppress = FALSE
|
||||
burst_size = 0
|
||||
actions_types = list()
|
||||
fire_sound = 'sound/weapons/laser.ogg'
|
||||
casing_ejector = FALSE
|
||||
|
||||
/obj/item/gun/ballistic/automatic/laser/update_icon()
|
||||
..()
|
||||
icon_state = "oldrifle[magazine ? "-[CEILING(get_ammo(0)/4, 1)*4]" : ""]"
|
||||
return
|
||||
@@ -0,0 +1,361 @@
|
||||
/obj/item/gun/ballistic/revolver
|
||||
name = "\improper .357 revolver"
|
||||
desc = "A suspicious revolver. Uses .357 ammo." //usually used by syndicates
|
||||
icon_state = "revolver"
|
||||
mag_type = /obj/item/ammo_box/magazine/internal/cylinder
|
||||
casing_ejector = FALSE
|
||||
|
||||
/obj/item/gun/ballistic/revolver/Initialize()
|
||||
. = ..()
|
||||
if(!istype(magazine, /obj/item/ammo_box/magazine/internal/cylinder))
|
||||
verbs -= /obj/item/gun/ballistic/revolver/verb/spin
|
||||
|
||||
/obj/item/gun/ballistic/revolver/chamber_round(spin = 1)
|
||||
if(spin)
|
||||
chambered = magazine.get_round(1)
|
||||
else
|
||||
chambered = magazine.stored_ammo[1]
|
||||
|
||||
/obj/item/gun/ballistic/revolver/shoot_with_empty_chamber(mob/living/user as mob|obj)
|
||||
..()
|
||||
chamber_round(1)
|
||||
|
||||
/obj/item/gun/ballistic/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, "<span class='notice'>You load [num_loaded] shell\s into \the [src].</span>")
|
||||
playsound(user, 'sound/weapons/bulletinsert.ogg', 60, 1)
|
||||
A.update_icon()
|
||||
update_icon()
|
||||
chamber_round(0)
|
||||
|
||||
/obj/item/gun/ballistic/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)
|
||||
if(CB)
|
||||
CB.forceMove(drop_location())
|
||||
CB.bounce_away(FALSE, NONE)
|
||||
num_unloaded++
|
||||
if (num_unloaded)
|
||||
to_chat(user, "<span class='notice'>You unload [num_unloaded] shell\s from [src].</span>")
|
||||
else
|
||||
to_chat(user, "<span class='warning'>[src] is empty!</span>")
|
||||
|
||||
/obj/item/gun/ballistic/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(do_spin())
|
||||
usr.visible_message("[usr] spins [src]'s chamber.", "<span class='notice'>You spin [src]'s chamber.</span>")
|
||||
else
|
||||
verbs -= /obj/item/gun/ballistic/revolver/verb/spin
|
||||
|
||||
/obj/item/gun/ballistic/revolver/proc/do_spin()
|
||||
var/obj/item/ammo_box/magazine/internal/cylinder/C = magazine
|
||||
. = istype(C)
|
||||
if(.)
|
||||
C.spin()
|
||||
chamber_round(0)
|
||||
|
||||
/obj/item/gun/ballistic/revolver/can_shoot()
|
||||
return get_ammo(0,0)
|
||||
|
||||
/obj/item/gun/ballistic/revolver/get_ammo(countchambered = 0, countempties = 1)
|
||||
var/boolets = 0 //mature var names for mature people
|
||||
if (chambered && countchambered)
|
||||
boolets++
|
||||
if (magazine)
|
||||
boolets += magazine.ammo_count(countempties)
|
||||
return boolets
|
||||
|
||||
/obj/item/gun/ballistic/revolver/examine(mob/user)
|
||||
..()
|
||||
to_chat(user, "[get_ammo(0,0)] of those are live rounds.")
|
||||
|
||||
/obj/item/gun/ballistic/revolver/detective
|
||||
name = "\improper .38 Mars Special"
|
||||
desc = "A cheap Martian knock-off of a classic law enforcement firearm. Uses .38-special rounds."
|
||||
icon_state = "detective"
|
||||
mag_type = /obj/item/ammo_box/magazine/internal/cylinder/rev38
|
||||
obj_flags = UNIQUE_RENAME
|
||||
unique_reskin = list("Default" = "detective",
|
||||
"Leopard Spots" = "detective_leopard",
|
||||
"Black Panther" = "detective_panther",
|
||||
"Gold Trim" = "detective_gold",
|
||||
"The Peacemaker" = "detective_peacemaker"
|
||||
)
|
||||
|
||||
/obj/item/gun/ballistic/revolver/detective/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0)
|
||||
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, "<span class='userdanger'>[src] blows up in your face!</span>")
|
||||
user.take_bodypart_damage(0,20)
|
||||
user.dropItemToGround(src)
|
||||
return 0
|
||||
..()
|
||||
|
||||
/obj/item/gun/ballistic/revolver/detective/screwdriver_act(mob/living/user, obj/item/I)
|
||||
if(..())
|
||||
return TRUE
|
||||
if(magazine.caliber == "38")
|
||||
to_chat(user, "<span class='notice'>You begin to reinforce the barrel of [src]...</span>")
|
||||
if(magazine.ammo_count())
|
||||
afterattack(user, user) //you know the drill
|
||||
user.visible_message("<span class='danger'>[src] goes off!</span>", "<span class='userdanger'>[src] goes off in your face!</span>")
|
||||
return TRUE
|
||||
if(I.use_tool(src, user, 30))
|
||||
if(magazine.ammo_count())
|
||||
to_chat(user, "<span class='warning'>You can't modify it!</span>")
|
||||
return TRUE
|
||||
magazine.caliber = "357"
|
||||
desc = "The barrel and chamber assembly seems to have been modified."
|
||||
to_chat(user, "<span class='notice'>You reinforce the barrel of [src]. Now it will fire .357 rounds.</span>")
|
||||
else
|
||||
to_chat(user, "<span class='notice'>You begin to revert the modifications to [src]...</span>")
|
||||
if(magazine.ammo_count())
|
||||
afterattack(user, user) //and again
|
||||
user.visible_message("<span class='danger'>[src] goes off!</span>", "<span class='userdanger'>[src] goes off in your face!</span>")
|
||||
return TRUE
|
||||
if(I.use_tool(src, user, 30))
|
||||
if(magazine.ammo_count())
|
||||
to_chat(user, "<span class='warning'>You can't modify it!</span>")
|
||||
return
|
||||
magazine.caliber = "38"
|
||||
desc = initial(desc)
|
||||
to_chat(user, "<span class='notice'>You remove the modifications on [src]. Now it will fire .38 rounds.</span>")
|
||||
return TRUE
|
||||
|
||||
|
||||
/obj/item/gun/ballistic/revolver/mateba
|
||||
name = "\improper Unica 6 auto-revolver"
|
||||
desc = "A retro high-powered autorevolver typically used by officers of the New Russia military. Uses .357 ammo."
|
||||
icon_state = "mateba"
|
||||
|
||||
/obj/item/gun/ballistic/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
|
||||
pin = /obj/item/firing_pin
|
||||
|
||||
/obj/item/gun/ballistic/revolver/nagant
|
||||
name = "\improper Nagant revolver"
|
||||
desc = "An old model of revolver that originated in Russia. Able to be suppressed. Uses 7.62x38mmR ammo."
|
||||
icon_state = "nagant"
|
||||
can_suppress = TRUE
|
||||
|
||||
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.
|
||||
|
||||
/obj/item/gun/ballistic/revolver/russian
|
||||
name = "\improper Russian revolver"
|
||||
desc = "A Russian-made revolver for drinking games. Uses .357 ammo, and has a mechanism requiring you to spin the chamber before each trigger pull."
|
||||
icon_state = "russianrevolver"
|
||||
mag_type = /obj/item/ammo_box/magazine/internal/cylinder/rus357
|
||||
var/spun = FALSE
|
||||
|
||||
/obj/item/gun/ballistic/revolver/russian/Initialize()
|
||||
. = ..()
|
||||
do_spin()
|
||||
spun = TRUE
|
||||
update_icon()
|
||||
|
||||
/obj/item/gun/ballistic/revolver/russian/attackby(obj/item/A, mob/user, params)
|
||||
..()
|
||||
if(get_ammo() > 0)
|
||||
spin()
|
||||
spun = TRUE
|
||||
update_icon()
|
||||
A.update_icon()
|
||||
return
|
||||
|
||||
/obj/item/gun/ballistic/revolver/russian/attack_self(mob/user)
|
||||
if(!spun && can_shoot())
|
||||
spin()
|
||||
spun = TRUE
|
||||
return
|
||||
..()
|
||||
|
||||
/obj/item/gun/ballistic/revolver/russian/afterattack(atom/target, mob/living/user, flag, params)
|
||||
. = ..(null, user, flag, params)
|
||||
|
||||
if(flag)
|
||||
if(!(target in user.contents) && ismob(target))
|
||||
if(user.a_intent == INTENT_HARM) // Flogging action
|
||||
return
|
||||
|
||||
if(isliving(user))
|
||||
if(!can_trigger_gun(user))
|
||||
return
|
||||
if(target != user)
|
||||
if(ismob(target))
|
||||
to_chat(user, "<span class='warning'>A mechanism prevents you from shooting anyone but yourself!</span>")
|
||||
return
|
||||
|
||||
if(ishuman(user))
|
||||
var/mob/living/carbon/human/H = user
|
||||
if(!spun)
|
||||
to_chat(user, "<span class='warning'>You need to spin the revolver's chamber first!</span>")
|
||||
return
|
||||
|
||||
spun = FALSE
|
||||
|
||||
if(chambered)
|
||||
var/obj/item/ammo_casing/AC = chambered
|
||||
if(AC.fire_casing(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 == BODY_ZONE_HEAD || zone == BODY_ZONE_PRECISE_EYES || zone == BODY_ZONE_PRECISE_MOUTH)
|
||||
shoot_self(user, affecting)
|
||||
else
|
||||
user.visible_message("<span class='danger'>[user.name] cowardly fires [src] at [user.p_their()] [affecting.name]!</span>", "<span class='userdanger'>You cowardly fire [src] at your [affecting.name]!</span>", "<span class='italics'>You hear a gunshot!</span>")
|
||||
chambered = null
|
||||
return
|
||||
|
||||
user.visible_message("<span class='danger'>*click*</span>")
|
||||
playsound(src, "gun_dry_fire", 30, 1)
|
||||
|
||||
/obj/item/gun/ballistic/revolver/russian/proc/shoot_self(mob/living/carbon/human/user, affecting = BODY_ZONE_HEAD)
|
||||
user.apply_damage(300, BRUTE, affecting)
|
||||
user.visible_message("<span class='danger'>[user.name] fires [src] at [user.p_their()] head!</span>", "<span class='userdanger'>You fire [src] at your head!</span>", "<span class='italics'>You hear a gunshot!</span>")
|
||||
|
||||
/obj/item/gun/ballistic/revolver/russian/soul
|
||||
name = "cursed Russian revolver"
|
||||
desc = "To play with this revolver requires wagering your very soul."
|
||||
|
||||
/obj/item/gun/ballistic/revolver/russian/soul/shoot_self(mob/living/user)
|
||||
..()
|
||||
var/obj/item/soulstone/anybody/SS = new /obj/item/soulstone/anybody(get_turf(src))
|
||||
if(!SS.transfer_soul("FORCE", user)) //Something went wrong
|
||||
qdel(SS)
|
||||
return
|
||||
user.visible_message("<span class='danger'>[user.name]'s soul is captured by \the [src]!</span>", "<span class='userdanger'>You've lost the gamble! Your soul is forfeit!</span>")
|
||||
|
||||
/////////////////////////////
|
||||
// DOUBLE BARRELED SHOTGUN //
|
||||
/////////////////////////////
|
||||
|
||||
/obj/item/gun/ballistic/revolver/doublebarrel
|
||||
name = "double-barreled shotgun"
|
||||
desc = "A true classic."
|
||||
icon_state = "dshotgun"
|
||||
item_state = "shotgun"
|
||||
w_class = WEIGHT_CLASS_BULKY
|
||||
weapon_weight = WEAPON_MEDIUM
|
||||
force = 10
|
||||
flags_1 = CONDUCT_1
|
||||
slot_flags = ITEM_SLOT_BACK
|
||||
mag_type = /obj/item/ammo_box/magazine/internal/shot/dual
|
||||
sawn_desc = "Omar's coming!"
|
||||
obj_flags = UNIQUE_RENAME
|
||||
unique_reskin = list("Default" = "dshotgun",
|
||||
"Dark Red Finish" = "dshotgun-d",
|
||||
"Ash" = "dshotgun-f",
|
||||
"Faded Grey" = "dshotgun-g",
|
||||
"Maple" = "dshotgun-l",
|
||||
"Rosewood" = "dshotgun-p"
|
||||
)
|
||||
|
||||
/obj/item/gun/ballistic/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(A.tool_behaviour == TOOL_SAW || istype(A, /obj/item/gun/energy/plasmacutter))
|
||||
sawoff(user)
|
||||
if(istype(A, /obj/item/melee/transforming/energy))
|
||||
var/obj/item/melee/transforming/energy/W = A
|
||||
if(W.active)
|
||||
sawoff(user)
|
||||
|
||||
/obj/item/gun/ballistic/revolver/doublebarrel/attack_self(mob/living/user)
|
||||
var/num_unloaded = 0
|
||||
while (get_ammo() > 0)
|
||||
var/obj/item/ammo_casing/CB
|
||||
CB = magazine.get_round(0)
|
||||
chambered = null
|
||||
CB.forceMove(drop_location())
|
||||
CB.update_icon()
|
||||
num_unloaded++
|
||||
if (num_unloaded)
|
||||
to_chat(user, "<span class='notice'>You break open \the [src] and unload [num_unloaded] shell\s.</span>")
|
||||
else
|
||||
to_chat(user, "<span class='warning'>[src] is empty!</span>")
|
||||
|
||||
// IMPROVISED SHOTGUN //
|
||||
|
||||
/obj/item/gun/ballistic/revolver/doublebarrel/improvised
|
||||
name = "improvised shotgun"
|
||||
desc = "Essentially a tube that aims shotgun shells."
|
||||
icon_state = "ishotgun"
|
||||
item_state = "shotgun"
|
||||
w_class = WEIGHT_CLASS_BULKY
|
||||
force = 10
|
||||
slot_flags = null
|
||||
mag_type = /obj/item/ammo_box/magazine/internal/shot/improvised
|
||||
sawn_desc = "I'm just here for the gasoline."
|
||||
unique_reskin = null
|
||||
var/slung = FALSE
|
||||
|
||||
/obj/item/gun/ballistic/revolver/doublebarrel/improvised/attackby(obj/item/A, mob/user, params)
|
||||
..()
|
||||
if(istype(A, /obj/item/stack/cable_coil) && !sawn_off)
|
||||
var/obj/item/stack/cable_coil/C = A
|
||||
if(C.use(10))
|
||||
slot_flags = ITEM_SLOT_BACK
|
||||
to_chat(user, "<span class='notice'>You tie the lengths of cable to the shotgun, making a sling.</span>")
|
||||
slung = TRUE
|
||||
update_icon()
|
||||
else
|
||||
to_chat(user, "<span class='warning'>You need at least ten lengths of cable if you want to make a sling!</span>")
|
||||
|
||||
/obj/item/gun/ballistic/revolver/doublebarrel/improvised/update_icon()
|
||||
..()
|
||||
if(slung)
|
||||
icon_state += "sling"
|
||||
|
||||
/obj/item/gun/ballistic/revolver/doublebarrel/improvised/sawoff(mob/user)
|
||||
. = ..()
|
||||
if(. && slung) //sawing off the gun removes the sling
|
||||
new /obj/item/stack/cable_coil(get_turf(src), 10)
|
||||
slung = 0
|
||||
update_icon()
|
||||
|
||||
/obj/item/gun/ballistic/revolver/doublebarrel/improvised/sawn
|
||||
name = "sawn-off improvised shotgun"
|
||||
desc = "A single-shot shotgun. Better not miss."
|
||||
icon_state = "ishotgun"
|
||||
item_state = "gun"
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
sawn_off = TRUE
|
||||
slot_flags = ITEM_SLOT_BELT
|
||||
|
||||
|
||||
/obj/item/gun/ballistic/revolver/reverse //Fires directly at its user... unless the user is a clown, of course.
|
||||
clumsy_check = 0
|
||||
|
||||
/obj/item/gun/ballistic/revolver/reverse/can_trigger_gun(mob/living/user)
|
||||
if((HAS_TRAIT(user, TRAIT_CLUMSY)) || (user.mind && user.mind.assigned_role == "Clown"))
|
||||
return ..()
|
||||
if(process_fire(user, user, FALSE, null, BODY_ZONE_HEAD))
|
||||
user.visible_message("<span class='warning'>[user] somehow manages to shoot [user.p_them()]self in the face!</span>", "<span class='userdanger'>You somehow shoot yourself in the face! How the hell?!</span>")
|
||||
user.emote("scream")
|
||||
user.drop_all_held_items()
|
||||
user.Knockdown(80)
|
||||
@@ -0,0 +1,234 @@
|
||||
/obj/item/gun/energy
|
||||
icon_state = "energy"
|
||||
name = "energy gun"
|
||||
desc = "A basic energy-based gun."
|
||||
icon = 'icons/obj/guns/energy.dmi'
|
||||
|
||||
var/obj/item/stock_parts/cell/cell //What type of power cell this uses
|
||||
var/cell_type = /obj/item/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 //Can it be charged in a recharger?
|
||||
var/automatic_charge_overlays = TRUE //Do we handle overlays with base update_icon()?
|
||||
var/charge_sections = 4
|
||||
ammo_x_offset = 2
|
||||
var/shaded_charge = FALSE //if this gun uses a stateful charge bar for more detail
|
||||
var/old_ratio = 0 // stores the gun's previous ammo "ratio" to see if it needs an updated icon
|
||||
var/selfcharge = EGUN_NO_SELFCHARGE // EGUN_SELFCHARGE if true, EGUN_SELFCHARGE_BORG drains the cyborg's cell to recharge its own
|
||||
var/charge_tick = 0
|
||||
var/charge_delay = 4
|
||||
var/use_cyborg_cell = FALSE //whether the gun drains the cyborg user's cell instead, not to be confused with EGUN_SELFCHARGE_BORG
|
||||
var/dead_cell = FALSE //set to true so the gun is given an empty cell
|
||||
|
||||
/obj/item/gun/energy/emp_act(severity)
|
||||
. = ..()
|
||||
if(!(. & EMP_PROTECT_CONTENTS))
|
||||
cell.use(round(cell.charge / severity))
|
||||
chambered = null //we empty the chamber
|
||||
recharge_newshot() //and try to charge a new shot
|
||||
update_icon()
|
||||
|
||||
/obj/item/gun/energy/get_cell()
|
||||
return cell
|
||||
|
||||
/obj/item/gun/energy/Initialize()
|
||||
. = ..()
|
||||
if(cell_type)
|
||||
cell = new cell_type(src)
|
||||
else
|
||||
cell = new(src)
|
||||
if(!dead_cell)
|
||||
cell.give(cell.maxcharge)
|
||||
update_ammo_types()
|
||||
recharge_newshot(TRUE)
|
||||
if(selfcharge)
|
||||
START_PROCESSING(SSobj, src)
|
||||
update_icon()
|
||||
|
||||
/obj/item/gun/energy/proc/update_ammo_types()
|
||||
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
|
||||
|
||||
/obj/item/gun/energy/Destroy()
|
||||
QDEL_NULL(cell)
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
return ..()
|
||||
|
||||
/obj/item/gun/energy/process()
|
||||
if(selfcharge && cell?.charge < cell.maxcharge)
|
||||
charge_tick++
|
||||
if(charge_tick < charge_delay)
|
||||
return
|
||||
charge_tick = 0
|
||||
if(selfcharge == EGUN_SELFCHARGE_BORG)
|
||||
var/atom/owner = loc
|
||||
if(istype(owner, /obj/item/robot_module))
|
||||
owner = owner.loc
|
||||
if(!iscyborg(owner))
|
||||
return
|
||||
var/mob/living/silicon/robot/R = owner
|
||||
if(!R.cell?.use(100))
|
||||
return
|
||||
cell.give(100)
|
||||
if(!chambered) //if empty chamber we try to charge a new shot
|
||||
recharge_newshot(TRUE)
|
||||
update_icon()
|
||||
|
||||
/obj/item/gun/energy/attack_self(mob/living/user as mob)
|
||||
if(ammo_type.len > 1)
|
||||
select_fire(user)
|
||||
update_icon()
|
||||
|
||||
/obj/item/gun/energy/can_shoot()
|
||||
var/obj/item/ammo_casing/energy/shot = ammo_type[select]
|
||||
return !QDELETED(cell) ? (cell.charge >= shot.e_cost) : FALSE
|
||||
|
||||
/obj/item/gun/energy/recharge_newshot(no_cyborg_drain)
|
||||
if (!ammo_type || !cell)
|
||||
return
|
||||
if(use_cyborg_cell && !no_cyborg_drain)
|
||||
if(iscyborg(loc))
|
||||
var/mob/living/silicon/robot/R = loc
|
||||
if(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)) //Take power from the borg...
|
||||
cell.give(shot.e_cost) //... to recharge the shot
|
||||
if(!chambered)
|
||||
var/obj/item/ammo_casing/energy/AC = ammo_type[select]
|
||||
if(cell.charge >= AC.e_cost) //if there's enough power in the cell cell...
|
||||
chambered = AC //...prepare a new shot based on the current ammo type selected
|
||||
if(!chambered.BB)
|
||||
chambered.newshot()
|
||||
|
||||
/obj/item/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
|
||||
cell.use(shot.e_cost)//... drain the cell cell
|
||||
chambered = null //either way, released the prepared shot
|
||||
recharge_newshot() //try to charge a new shot
|
||||
|
||||
/obj/item/gun/energy/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0)
|
||||
if(!chambered && can_shoot())
|
||||
process_chamber() // If the gun was drained and then recharged, load a new shot.
|
||||
return ..()
|
||||
|
||||
/obj/item/gun/energy/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(!chambered && can_shoot())
|
||||
process_chamber() // Ditto.
|
||||
return ..()
|
||||
|
||||
/obj/item/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, "<span class='notice'>[src] is now set to [shot.select_name].</span>")
|
||||
chambered = null
|
||||
recharge_newshot(TRUE)
|
||||
update_icon(TRUE)
|
||||
return
|
||||
|
||||
/obj/item/gun/energy/update_icon(force_update)
|
||||
if(QDELETED(src))
|
||||
return
|
||||
..()
|
||||
if(!automatic_charge_overlays)
|
||||
return
|
||||
var/ratio = CEILING(CLAMP(cell.charge / cell.maxcharge, 0, 1) * charge_sections, 1)
|
||||
if(ratio == old_ratio && !force_update)
|
||||
return
|
||||
old_ratio = ratio
|
||||
cut_overlays()
|
||||
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)
|
||||
add_overlay("[icon_state]_[shot.select_name]")
|
||||
iconState += "_[shot.select_name]"
|
||||
if(itemState)
|
||||
itemState += "[shot.select_name]"
|
||||
if(cell.charge < shot.e_cost)
|
||||
add_overlay("[icon_state]_empty")
|
||||
else
|
||||
if(!shaded_charge)
|
||||
var/mutable_appearance/charge_overlay = mutable_appearance(icon, iconState)
|
||||
for(var/i = ratio, i >= 1, i--)
|
||||
charge_overlay.pixel_x = ammo_x_offset * (i - 1)
|
||||
charge_overlay.pixel_y = ammo_y_offset * (i - 1)
|
||||
add_overlay(charge_overlay)
|
||||
else
|
||||
add_overlay("[icon_state]_charge[ratio]")
|
||||
if(itemState)
|
||||
itemState += "[ratio]"
|
||||
item_state = itemState
|
||||
|
||||
/obj/item/gun/energy/suicide_act(mob/living/user)
|
||||
if (istype(user) && can_shoot() && can_trigger_gun(user) && user.get_bodypart(BODY_ZONE_HEAD))
|
||||
user.visible_message("<span class='suicide'>[user] is putting the barrel of [src] in [user.p_their()] mouth. It looks like [user.p_theyre()] trying to commit suicide!</span>")
|
||||
sleep(25)
|
||||
if(user.is_holding(src))
|
||||
user.visible_message("<span class='suicide'>[user] melts [user.p_their()] face off with [src]!</span>")
|
||||
playsound(loc, fire_sound, 50, 1, -1)
|
||||
playsound(src, 'sound/weapons/dink.ogg', 30, 1)
|
||||
var/obj/item/ammo_casing/energy/shot = ammo_type[select]
|
||||
cell.use(shot.e_cost)
|
||||
update_icon()
|
||||
return(FIRELOSS)
|
||||
else
|
||||
user.visible_message("<span class='suicide'>[user] panics and starts choking to death!</span>")
|
||||
return(OXYLOSS)
|
||||
else
|
||||
user.visible_message("<span class='suicide'>[user] is pretending to melt [user.p_their()] face off with [src]! It looks like [user.p_theyre()] trying to commit suicide!</b></span>")
|
||||
playsound(src, "gun_dry_fire", 30, 1)
|
||||
return (OXYLOSS)
|
||||
|
||||
|
||||
/obj/item/gun/energy/vv_edit_var(var_name, var_value)
|
||||
switch(var_name)
|
||||
if("selfcharge")
|
||||
if(var_value)
|
||||
START_PROCESSING(SSobj, src)
|
||||
else
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
. = ..()
|
||||
|
||||
|
||||
/obj/item/gun/energy/ignition_effect(atom/A, mob/living/user)
|
||||
if(!can_shoot() || !ammo_type[select])
|
||||
shoot_with_empty_chamber()
|
||||
. = ""
|
||||
else
|
||||
var/obj/item/ammo_casing/energy/E = ammo_type[select]
|
||||
var/obj/item/projectile/energy/BB = E.BB
|
||||
if(!BB)
|
||||
. = ""
|
||||
else if(BB.nodamage || !BB.damage || BB.damage_type == STAMINA)
|
||||
user.visible_message("<span class='danger'>[user] tries to light [user.p_their()] [A.name] with [src], but it doesn't do anything. Dumbass.</span>")
|
||||
playsound(user, E.fire_sound, 50, 1)
|
||||
playsound(user, BB.hitsound, 50, 1)
|
||||
cell.use(E.e_cost)
|
||||
. = ""
|
||||
else if(BB.damage_type != BURN)
|
||||
user.visible_message("<span class='danger'>[user] tries to light [user.p_their()] [A.name] with [src], but only succeeds in utterly destroying it. Dumbass.</span>")
|
||||
playsound(user, E.fire_sound, 50, 1)
|
||||
playsound(user, BB.hitsound, 50, 1)
|
||||
cell.use(E.e_cost)
|
||||
qdel(A)
|
||||
. = ""
|
||||
else
|
||||
playsound(user, E.fire_sound, 50, 1)
|
||||
playsound(user, BB.hitsound, 50, 1)
|
||||
cell.use(E.e_cost)
|
||||
. = "<span class='danger'>[user] casually lights their [A.name] with [src]. Damn.</span>"
|
||||
@@ -0,0 +1,146 @@
|
||||
/obj/item/gun/energy/e_gun
|
||||
name = "energy gun"
|
||||
desc = "A basic hybrid energy gun with two settings: disable and kill."
|
||||
icon_state = "energy"
|
||||
item_state = null //so the human update icon uses the icon_state instead.
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/disabler, /obj/item/ammo_casing/energy/laser)
|
||||
modifystate = 1
|
||||
can_flashlight = 1
|
||||
ammo_x_offset = 3
|
||||
flight_x_offset = 15
|
||||
flight_y_offset = 10
|
||||
|
||||
/obj/item/gun/energy/e_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 = WEIGHT_CLASS_SMALL
|
||||
cell_type = /obj/item/stock_parts/cell{charge = 600; maxcharge = 600}
|
||||
ammo_x_offset = 2
|
||||
charge_sections = 3
|
||||
can_flashlight = 0 // Can't attach or detach the flashlight, and override it's icon update
|
||||
|
||||
/obj/item/gun/energy/e_gun/mini/Initialize()
|
||||
gun_light = new /obj/item/flashlight/seclite(src)
|
||||
return ..()
|
||||
|
||||
/obj/item/gun/energy/e_gun/mini/update_icon()
|
||||
..()
|
||||
if(gun_light && gun_light.on)
|
||||
add_overlay("mini-light")
|
||||
|
||||
/obj/item/gun/energy/e_gun/stun
|
||||
name = "tactical energy gun"
|
||||
desc = "Military issue energy gun, is able to fire stun rounds."
|
||||
icon_state = "energytac"
|
||||
ammo_x_offset = 2
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/electrode/spec, /obj/item/ammo_casing/energy/disabler, /obj/item/ammo_casing/energy/laser)
|
||||
|
||||
/obj/item/gun/energy/e_gun/old
|
||||
name = "prototype energy gun"
|
||||
desc = "NT-P:01 Prototype Energy Gun. Early stage development of a unique laser rifle that has multifaceted energy lens allowing the gun to alter the form of projectile it fires on command."
|
||||
icon_state = "protolaser"
|
||||
ammo_x_offset = 2
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/laser, /obj/item/ammo_casing/energy/electrode/old)
|
||||
|
||||
/obj/item/gun/energy/e_gun/mini/practice_phaser
|
||||
name = "practice phaser"
|
||||
desc = "A modified version of the basic phaser gun, this one fires less concentrated energy bolts designed for target practice."
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/disabler, /obj/item/ammo_casing/energy/laser/practice)
|
||||
icon_state = "decloner"
|
||||
|
||||
/obj/item/gun/energy/e_gun/hos
|
||||
name = "\improper X-01 MultiPhase Energy Gun"
|
||||
desc = "This is an expensive, modern recreation of an 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/disabler, /obj/item/ammo_casing/energy/laser/hos)
|
||||
ammo_x_offset = 4
|
||||
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
|
||||
|
||||
/obj/item/gun/energy/e_gun/dragnet
|
||||
name = "\improper DRAGnet"
|
||||
desc = "The \"Dynamic Rapid-Apprehension of the Guilty\" net is a revolution in law enforcement technology."
|
||||
icon_state = "dragnet"
|
||||
item_state = "dragnet"
|
||||
lefthand_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/weapons/guns_righthand.dmi'
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/net, /obj/item/ammo_casing/energy/trap)
|
||||
can_flashlight = 0
|
||||
ammo_x_offset = 1
|
||||
|
||||
/obj/item/gun/energy/e_gun/dragnet/snare
|
||||
name = "Energy Snare Launcher"
|
||||
desc = "Fires an energy snare that slows the target down."
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/trap)
|
||||
|
||||
/obj/item/gun/energy/e_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 = WEIGHT_CLASS_HUGE
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/laser)
|
||||
weapon_weight = WEAPON_HEAVY
|
||||
can_flashlight = 0
|
||||
trigger_guard = TRIGGER_GUARD_NONE
|
||||
ammo_x_offset = 2
|
||||
|
||||
/obj/item/gun/energy/e_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"
|
||||
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/disabler, /obj/item/ammo_casing/energy/laser)
|
||||
selfcharge = EGUN_SELFCHARGE
|
||||
var/fail_tick = 0
|
||||
var/fail_chance = 0
|
||||
|
||||
/obj/item/gun/energy/e_gun/nuclear/process()
|
||||
if(fail_tick > 0)
|
||||
fail_tick--
|
||||
..()
|
||||
|
||||
/obj/item/gun/energy/e_gun/nuclear/shoot_live_shot()
|
||||
failcheck()
|
||||
update_icon()
|
||||
..()
|
||||
|
||||
/obj/item/gun/energy/e_gun/nuclear/proc/failcheck()
|
||||
if(prob(fail_chance) && isliving(loc))
|
||||
var/mob/living/M = loc
|
||||
switch(fail_tick)
|
||||
if(0 to 200)
|
||||
fail_tick += (2*(fail_chance))
|
||||
M.rad_act(400)
|
||||
to_chat(M, "<span class='userdanger'>Your [name] feels warmer.</span>")
|
||||
if(201 to INFINITY)
|
||||
SSobj.processing.Remove(src)
|
||||
M.rad_act(800)
|
||||
crit_fail = 1
|
||||
to_chat(M, "<span class='userdanger'>Your [name]'s reactor overloads!</span>")
|
||||
|
||||
/obj/item/gun/energy/e_gun/nuclear/emp_act(severity)
|
||||
. = ..()
|
||||
if(. & EMP_PROTECT_SELF)
|
||||
return
|
||||
fail_chance = min(fail_chance + round(15/severity), 100)
|
||||
|
||||
/obj/item/gun/energy/e_gun/nuclear/update_icon()
|
||||
..()
|
||||
if(crit_fail)
|
||||
add_overlay("[icon_state]_fail_3")
|
||||
else
|
||||
switch(fail_tick)
|
||||
if(0)
|
||||
add_overlay("[icon_state]_fail_0")
|
||||
if(1 to 150)
|
||||
add_overlay("[icon_state]_fail_1")
|
||||
if(151 to INFINITY)
|
||||
add_overlay("[icon_state]_fail_2")
|
||||
@@ -0,0 +1,625 @@
|
||||
/obj/item/gun/energy/kinetic_accelerator
|
||||
name = "proto-kinetic accelerator"
|
||||
desc = "A self recharging, ranged mining tool that does increased damage in low pressure."
|
||||
icon_state = "kineticgun"
|
||||
item_state = "kineticgun"
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/kinetic)
|
||||
cell_type = /obj/item/stock_parts/cell/emproof
|
||||
item_flags = NONE
|
||||
obj_flags = UNIQUE_RENAME
|
||||
weapon_weight = WEAPON_LIGHT
|
||||
can_flashlight = 1
|
||||
flight_x_offset = 15
|
||||
flight_y_offset = 9
|
||||
automatic_charge_overlays = FALSE
|
||||
var/overheat_time = 16
|
||||
var/holds_charge = FALSE
|
||||
var/unique_frequency = FALSE // modified by KA modkits
|
||||
var/overheat = FALSE
|
||||
can_bayonet = TRUE
|
||||
knife_x_offset = 20
|
||||
knife_y_offset = 12
|
||||
|
||||
var/max_mod_capacity = 100
|
||||
var/list/modkits = list()
|
||||
|
||||
var/recharge_timerid
|
||||
|
||||
/obj/item/gun/energy/kinetic_accelerator/premiumka
|
||||
name = "premium accelerator"
|
||||
desc = "A premium kinetic accelerator fitted with an extended barrel and increased pressure tank."
|
||||
icon_state = "premiumgun"
|
||||
item_state = "premiumgun"
|
||||
lefthand_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/weapons/guns_righthand.dmi'
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/kinetic/premium)
|
||||
|
||||
/obj/item/gun/energy/kinetic_accelerator/premiumka/dropped()
|
||||
. = ..()
|
||||
if(!QDELING(src) && !holds_charge)
|
||||
// Put it on a delay because moving item from slot to hand
|
||||
// calls dropped().
|
||||
addtimer(CALLBACK(src, .proc/empty_if_not_held), 1.60)
|
||||
|
||||
/obj/item/ammo_casing/energy/kinetic/premium
|
||||
projectile_type = /obj/item/projectile/kinetic/premium
|
||||
|
||||
/obj/item/projectile/kinetic/premium
|
||||
name = "kinetic force"
|
||||
icon_state = null
|
||||
damage = 50
|
||||
damage_type = BRUTE
|
||||
flag = "bomb"
|
||||
range = 4
|
||||
log_override = TRUE
|
||||
|
||||
/obj/item/gun/energy/kinetic_accelerator/premiumka/update_icon()
|
||||
..()
|
||||
if(!can_shoot())
|
||||
add_overlay("[icon_state]_empty")
|
||||
else
|
||||
cut_overlays()
|
||||
|
||||
/obj/item/gun/energy/kinetic_accelerator/examine(mob/user)
|
||||
..()
|
||||
if(max_mod_capacity)
|
||||
to_chat(user, "<b>[get_remaining_mod_capacity()]%</b> mod capacity remaining.")
|
||||
for(var/A in get_modkits())
|
||||
var/obj/item/borg/upgrade/modkit/M = A
|
||||
to_chat(user, "<span class='notice'>There is \a [M] installed, using <b>[M.cost]%</b> capacity.</span>")
|
||||
|
||||
/obj/item/gun/energy/kinetic_accelerator/crowbar_act(mob/living/user, obj/item/I)
|
||||
. = TRUE
|
||||
if(modkits.len)
|
||||
to_chat(user, "<span class='notice'>You pry the modifications out.</span>")
|
||||
I.play_tool_sound(src, 100)
|
||||
for(var/obj/item/borg/upgrade/modkit/M in modkits)
|
||||
M.uninstall(src)
|
||||
else
|
||||
to_chat(user, "<span class='notice'>There are no modifications currently installed.</span>")
|
||||
|
||||
/obj/item/gun/energy/kinetic_accelerator/Exited(atom/movable/AM)
|
||||
. = ..()
|
||||
if((AM in modkits) && istype(AM, /obj/item/borg/upgrade/modkit))
|
||||
var/obj/item/borg/upgrade/modkit/M = AM
|
||||
M.uninstall(src, FALSE)
|
||||
|
||||
/obj/item/gun/energy/kinetic_accelerator/attackby(obj/item/I, mob/user)
|
||||
if(istype(I, /obj/item/borg/upgrade/modkit))
|
||||
var/obj/item/borg/upgrade/modkit/MK = I
|
||||
MK.install(src, user)
|
||||
else
|
||||
..()
|
||||
|
||||
/obj/item/gun/energy/kinetic_accelerator/proc/get_remaining_mod_capacity()
|
||||
var/current_capacity_used = 0
|
||||
for(var/A in get_modkits())
|
||||
var/obj/item/borg/upgrade/modkit/M = A
|
||||
current_capacity_used += M.cost
|
||||
return max_mod_capacity - current_capacity_used
|
||||
|
||||
/obj/item/gun/energy/kinetic_accelerator/proc/get_modkits()
|
||||
. = list()
|
||||
for(var/A in modkits)
|
||||
. += A
|
||||
|
||||
/obj/item/gun/energy/kinetic_accelerator/proc/modify_projectile(obj/item/projectile/kinetic/K)
|
||||
K.kinetic_gun = src //do something special on-hit, easy!
|
||||
for(var/A in get_modkits())
|
||||
var/obj/item/borg/upgrade/modkit/M = A
|
||||
M.modify_projectile(K)
|
||||
|
||||
/obj/item/gun/energy/kinetic_accelerator/cyborg
|
||||
holds_charge = TRUE
|
||||
unique_frequency = TRUE
|
||||
max_mod_capacity = 80
|
||||
|
||||
/obj/item/gun/energy/kinetic_accelerator/minebot
|
||||
trigger_guard = TRIGGER_GUARD_ALLOW_ALL
|
||||
overheat_time = 20
|
||||
holds_charge = TRUE
|
||||
unique_frequency = TRUE
|
||||
|
||||
/obj/item/gun/energy/kinetic_accelerator/Initialize()
|
||||
. = ..()
|
||||
if(!holds_charge)
|
||||
empty()
|
||||
|
||||
/obj/item/gun/energy/kinetic_accelerator/shoot_live_shot()
|
||||
. = ..()
|
||||
attempt_reload()
|
||||
|
||||
/obj/item/gun/energy/kinetic_accelerator/equipped(mob/user)
|
||||
. = ..()
|
||||
if(!can_shoot())
|
||||
attempt_reload()
|
||||
|
||||
/obj/item/gun/energy/kinetic_accelerator/dropped()
|
||||
. = ..()
|
||||
if(!QDELING(src) && !holds_charge)
|
||||
// Put it on a delay because moving item from slot to hand
|
||||
// calls dropped().
|
||||
addtimer(CALLBACK(src, .proc/empty_if_not_held), 2)
|
||||
|
||||
/obj/item/gun/energy/kinetic_accelerator/proc/empty_if_not_held()
|
||||
if(!ismob(loc))
|
||||
empty()
|
||||
|
||||
/obj/item/gun/energy/kinetic_accelerator/proc/empty()
|
||||
if(cell)
|
||||
cell.use(cell.charge)
|
||||
update_icon()
|
||||
|
||||
/obj/item/gun/energy/kinetic_accelerator/proc/attempt_reload(recharge_time)
|
||||
if(!cell)
|
||||
return
|
||||
if(overheat)
|
||||
return
|
||||
if(!recharge_time)
|
||||
recharge_time = overheat_time
|
||||
overheat = TRUE
|
||||
|
||||
var/carried = 0
|
||||
if(!unique_frequency)
|
||||
for(var/obj/item/gun/energy/kinetic_accelerator/K in loc.GetAllContents())
|
||||
|
||||
carried++
|
||||
|
||||
carried = max(carried, 1)
|
||||
else
|
||||
carried = 1
|
||||
|
||||
deltimer(recharge_timerid)
|
||||
recharge_timerid = addtimer(CALLBACK(src, .proc/reload), recharge_time * carried, TIMER_STOPPABLE)
|
||||
|
||||
/obj/item/gun/energy/kinetic_accelerator/emp_act(severity)
|
||||
return
|
||||
|
||||
/obj/item/gun/energy/kinetic_accelerator/proc/reload()
|
||||
cell.give(cell.maxcharge)
|
||||
if(!suppressed)
|
||||
playsound(src.loc, 'sound/weapons/kenetic_reload.ogg', 60, 1)
|
||||
else
|
||||
to_chat(loc, "<span class='warning'>[src] silently charges up.</span>")
|
||||
update_icon()
|
||||
overheat = FALSE
|
||||
|
||||
/obj/item/gun/energy/kinetic_accelerator/update_icon()
|
||||
..()
|
||||
if(!can_shoot())
|
||||
add_overlay("[icon_state]_empty")
|
||||
else
|
||||
cut_overlays()
|
||||
|
||||
//Casing
|
||||
/obj/item/ammo_casing/energy/kinetic
|
||||
projectile_type = /obj/item/projectile/kinetic
|
||||
select_name = "kinetic"
|
||||
e_cost = 500
|
||||
fire_sound = 'sound/weapons/kenetic_accel.ogg' // fine spelling there chap
|
||||
|
||||
/obj/item/ammo_casing/energy/kinetic/ready_proj(atom/target, mob/living/user, quiet, zone_override = "")
|
||||
..()
|
||||
if(loc && istype(loc, /obj/item/gun/energy/kinetic_accelerator))
|
||||
var/obj/item/gun/energy/kinetic_accelerator/KA = loc
|
||||
KA.modify_projectile(BB)
|
||||
|
||||
//Projectiles
|
||||
/obj/item/projectile/kinetic
|
||||
name = "kinetic force"
|
||||
icon_state = null
|
||||
damage = 40
|
||||
damage_type = BRUTE
|
||||
flag = "bomb"
|
||||
range = 3
|
||||
log_override = TRUE
|
||||
|
||||
var/pressure_decrease_active = FALSE
|
||||
var/pressure_decrease = 0.25
|
||||
var/obj/item/gun/energy/kinetic_accelerator/kinetic_gun
|
||||
|
||||
/obj/item/projectile/kinetic/Destroy()
|
||||
kinetic_gun = null
|
||||
return ..()
|
||||
|
||||
/obj/item/projectile/kinetic/prehit(atom/target)
|
||||
. = ..()
|
||||
if(.)
|
||||
if(kinetic_gun)
|
||||
var/list/mods = kinetic_gun.get_modkits()
|
||||
for(var/obj/item/borg/upgrade/modkit/M in mods)
|
||||
M.projectile_prehit(src, target, kinetic_gun)
|
||||
if(!lavaland_equipment_pressure_check(get_turf(target)))
|
||||
name = "weakened [name]"
|
||||
damage = damage * pressure_decrease
|
||||
pressure_decrease_active = TRUE
|
||||
|
||||
/obj/item/projectile/kinetic/on_range()
|
||||
strike_thing()
|
||||
..()
|
||||
|
||||
/obj/item/projectile/kinetic/on_hit(atom/target)
|
||||
strike_thing(target)
|
||||
. = ..()
|
||||
|
||||
/obj/item/projectile/kinetic/proc/strike_thing(atom/target)
|
||||
var/turf/target_turf = get_turf(target)
|
||||
if(!target_turf)
|
||||
target_turf = get_turf(src)
|
||||
if(kinetic_gun) //hopefully whoever shot this was not very, very unfortunate.
|
||||
var/list/mods = kinetic_gun.get_modkits()
|
||||
for(var/obj/item/borg/upgrade/modkit/M in mods)
|
||||
M.projectile_strike_predamage(src, target_turf, target, kinetic_gun)
|
||||
for(var/obj/item/borg/upgrade/modkit/M in mods)
|
||||
M.projectile_strike(src, target_turf, target, kinetic_gun)
|
||||
if(ismineralturf(target_turf))
|
||||
var/turf/closed/mineral/M = target_turf
|
||||
M.gets_drilled(firer)
|
||||
var/obj/effect/temp_visual/kinetic_blast/K = new /obj/effect/temp_visual/kinetic_blast(target_turf)
|
||||
K.color = color
|
||||
|
||||
|
||||
//Modkits
|
||||
/obj/item/borg/upgrade/modkit
|
||||
name = "kinetic accelerator modification kit"
|
||||
desc = "An upgrade for kinetic accelerators."
|
||||
icon = 'icons/obj/objects.dmi'
|
||||
icon_state = "modkit"
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
require_module = 1
|
||||
module_type = list(/obj/item/robot_module/miner)
|
||||
var/denied_type = null
|
||||
var/maximum_of_type = 1
|
||||
var/cost = 30
|
||||
var/modifier = 1 //For use in any mod kit that has numerical modifiers
|
||||
var/minebot_upgrade = TRUE
|
||||
var/minebot_exclusive = FALSE
|
||||
|
||||
/obj/item/borg/upgrade/modkit/examine(mob/user)
|
||||
..()
|
||||
to_chat(user, "<span class='notice'>Occupies <b>[cost]%</b> of mod capacity.</span>")
|
||||
|
||||
/obj/item/borg/upgrade/modkit/attackby(obj/item/A, mob/user)
|
||||
if(istype(A, /obj/item/gun/energy/kinetic_accelerator) && !issilicon(user))
|
||||
install(A, user)
|
||||
else
|
||||
..()
|
||||
|
||||
/obj/item/borg/upgrade/modkit/action(mob/living/silicon/robot/R)
|
||||
. = ..()
|
||||
if (.)
|
||||
for(var/obj/item/gun/energy/kinetic_accelerator/cyborg/H in R.module.modules)
|
||||
return install(H, usr)
|
||||
|
||||
/obj/item/borg/upgrade/modkit/proc/install(obj/item/gun/energy/kinetic_accelerator/KA, mob/user)
|
||||
. = TRUE
|
||||
if(src in KA.modkits) // Sanity check to prevent installing the same modkit twice thanks to occasional click/lag delays.
|
||||
return
|
||||
if(minebot_upgrade)
|
||||
if(minebot_exclusive && !istype(KA.loc, /mob/living/simple_animal/hostile/mining_drone))
|
||||
to_chat(user, "<span class='notice'>The modkit you're trying to install is only rated for minebot use.</span>")
|
||||
return FALSE
|
||||
else if(istype(KA.loc, /mob/living/simple_animal/hostile/mining_drone))
|
||||
to_chat(user, "<span class='notice'>The modkit you're trying to install is not rated for minebot use.</span>")
|
||||
return FALSE
|
||||
if(denied_type)
|
||||
var/number_of_denied = 0
|
||||
for(var/A in KA.get_modkits())
|
||||
var/obj/item/borg/upgrade/modkit/M = A
|
||||
if(istype(M, denied_type))
|
||||
number_of_denied++
|
||||
if(number_of_denied >= maximum_of_type)
|
||||
. = FALSE
|
||||
break
|
||||
if(KA.get_remaining_mod_capacity() >= cost)
|
||||
if(.)
|
||||
if(!user.transferItemToLoc(src, KA))
|
||||
return
|
||||
to_chat(user, "<span class='notice'>You install the modkit.</span>")
|
||||
playsound(loc, 'sound/items/screwdriver.ogg', 100, 1)
|
||||
KA.modkits += src
|
||||
else
|
||||
to_chat(user, "<span class='notice'>The modkit you're trying to install would conflict with an already installed modkit. Use a crowbar to remove existing modkits.</span>")
|
||||
else
|
||||
to_chat(user, "<span class='notice'>You don't have room(<b>[KA.get_remaining_mod_capacity()]%</b> remaining, [cost]% needed) to install this modkit. Use a crowbar to remove existing modkits.</span>")
|
||||
. = FALSE
|
||||
|
||||
/obj/item/borg/upgrade/modkit/deactivate(mob/living/silicon/robot/R, user = usr)
|
||||
. = ..()
|
||||
if (.)
|
||||
for(var/obj/item/gun/energy/kinetic_accelerator/cyborg/KA in R.module.modules)
|
||||
uninstall(KA)
|
||||
|
||||
/obj/item/borg/upgrade/modkit/proc/uninstall(obj/item/gun/energy/kinetic_accelerator/KA, forcemove = TRUE)
|
||||
KA.modkits -= src
|
||||
if(forcemove)
|
||||
forceMove(get_turf(KA))
|
||||
|
||||
/obj/item/borg/upgrade/modkit/proc/modify_projectile(obj/item/projectile/kinetic/K)
|
||||
|
||||
//use this one for effects you want to trigger before any damage is done at all and before damage is decreased by pressure
|
||||
/obj/item/borg/upgrade/modkit/proc/projectile_prehit(obj/item/projectile/kinetic/K, atom/target, obj/item/gun/energy/kinetic_accelerator/KA)
|
||||
//use this one for effects you want to trigger before mods that do damage
|
||||
/obj/item/borg/upgrade/modkit/proc/projectile_strike_predamage(obj/item/projectile/kinetic/K, turf/target_turf, atom/target, obj/item/gun/energy/kinetic_accelerator/KA)
|
||||
//and this one for things that don't need to trigger before other damage-dealing mods
|
||||
/obj/item/borg/upgrade/modkit/proc/projectile_strike(obj/item/projectile/kinetic/K, turf/target_turf, atom/target, obj/item/gun/energy/kinetic_accelerator/KA)
|
||||
|
||||
//Range
|
||||
/obj/item/borg/upgrade/modkit/range
|
||||
name = "range increase"
|
||||
desc = "Increases the range of a kinetic accelerator when installed."
|
||||
modifier = 1
|
||||
cost = 25
|
||||
|
||||
/obj/item/borg/upgrade/modkit/range/modify_projectile(obj/item/projectile/kinetic/K)
|
||||
K.range += modifier
|
||||
|
||||
|
||||
//Damage
|
||||
/obj/item/borg/upgrade/modkit/damage
|
||||
name = "damage increase"
|
||||
desc = "Increases the damage of kinetic accelerator when installed."
|
||||
modifier = 10
|
||||
|
||||
/obj/item/borg/upgrade/modkit/damage/modify_projectile(obj/item/projectile/kinetic/K)
|
||||
K.damage += modifier
|
||||
|
||||
|
||||
//Cooldown
|
||||
/obj/item/borg/upgrade/modkit/cooldown
|
||||
name = "cooldown decrease"
|
||||
desc = "Decreases the cooldown of a kinetic accelerator. Not rated for minebot use."
|
||||
modifier = 2.5
|
||||
minebot_upgrade = FALSE
|
||||
|
||||
/obj/item/borg/upgrade/modkit/cooldown/install(obj/item/gun/energy/kinetic_accelerator/KA, mob/user)
|
||||
. = ..()
|
||||
if(.)
|
||||
KA.overheat_time -= modifier
|
||||
|
||||
/obj/item/borg/upgrade/modkit/cooldown/uninstall(obj/item/gun/energy/kinetic_accelerator/KA)
|
||||
KA.overheat_time += modifier
|
||||
..()
|
||||
|
||||
/obj/item/borg/upgrade/modkit/cooldown/minebot
|
||||
name = "minebot cooldown decrease"
|
||||
desc = "Decreases the cooldown of a kinetic accelerator. Only rated for minebot use."
|
||||
icon_state = "door_electronics"
|
||||
icon = 'icons/obj/module.dmi'
|
||||
denied_type = /obj/item/borg/upgrade/modkit/cooldown/minebot
|
||||
modifier = 10
|
||||
cost = 0
|
||||
minebot_upgrade = TRUE
|
||||
minebot_exclusive = TRUE
|
||||
|
||||
|
||||
//AoE blasts
|
||||
/obj/item/borg/upgrade/modkit/aoe
|
||||
modifier = 0
|
||||
var/turf_aoe = FALSE
|
||||
var/stats_stolen = FALSE
|
||||
|
||||
/obj/item/borg/upgrade/modkit/aoe/install(obj/item/gun/energy/kinetic_accelerator/KA, mob/user)
|
||||
. = ..()
|
||||
if(.)
|
||||
for(var/obj/item/borg/upgrade/modkit/aoe/AOE in KA.modkits) //make sure only one of the aoe modules has values if somebody has multiple
|
||||
if(AOE.stats_stolen || AOE == src)
|
||||
continue
|
||||
modifier += AOE.modifier //take its modifiers
|
||||
AOE.modifier = 0
|
||||
turf_aoe += AOE.turf_aoe
|
||||
AOE.turf_aoe = FALSE
|
||||
AOE.stats_stolen = TRUE
|
||||
|
||||
/obj/item/borg/upgrade/modkit/aoe/uninstall(obj/item/gun/energy/kinetic_accelerator/KA)
|
||||
..()
|
||||
modifier = initial(modifier) //get our modifiers back
|
||||
turf_aoe = initial(turf_aoe)
|
||||
stats_stolen = FALSE
|
||||
|
||||
/obj/item/borg/upgrade/modkit/aoe/modify_projectile(obj/item/projectile/kinetic/K)
|
||||
K.name = "kinetic explosion"
|
||||
|
||||
/obj/item/borg/upgrade/modkit/aoe/projectile_strike(obj/item/projectile/kinetic/K, turf/target_turf, atom/target, obj/item/gun/energy/kinetic_accelerator/KA)
|
||||
if(stats_stolen)
|
||||
return
|
||||
new /obj/effect/temp_visual/explosion/fast(target_turf)
|
||||
if(turf_aoe)
|
||||
for(var/T in RANGE_TURFS(1, target_turf) - target_turf)
|
||||
if(ismineralturf(T))
|
||||
var/turf/closed/mineral/M = T
|
||||
M.gets_drilled(K.firer)
|
||||
if(modifier)
|
||||
for(var/mob/living/L in range(1, target_turf) - K.firer - target)
|
||||
var/armor = L.run_armor_check(K.def_zone, K.flag, null, null, K.armour_penetration)
|
||||
L.apply_damage(K.damage*modifier, K.damage_type, K.def_zone, armor)
|
||||
to_chat(L, "<span class='userdanger'>You're struck by a [K.name]!</span>")
|
||||
|
||||
/obj/item/borg/upgrade/modkit/aoe/turfs
|
||||
name = "mining explosion"
|
||||
desc = "Causes the kinetic accelerator to destroy rock in an AoE."
|
||||
denied_type = /obj/item/borg/upgrade/modkit/aoe/turfs
|
||||
turf_aoe = TRUE
|
||||
|
||||
/obj/item/borg/upgrade/modkit/aoe/turfs/andmobs
|
||||
name = "offensive mining explosion"
|
||||
desc = "Causes the kinetic accelerator to destroy rock and damage mobs in an AoE."
|
||||
maximum_of_type = 3
|
||||
modifier = 0.25
|
||||
|
||||
/obj/item/borg/upgrade/modkit/aoe/mobs
|
||||
name = "offensive explosion"
|
||||
desc = "Causes the kinetic accelerator to damage mobs in an AoE."
|
||||
modifier = 0.2
|
||||
|
||||
//Minebot passthrough
|
||||
/obj/item/borg/upgrade/modkit/minebot_passthrough
|
||||
name = "minebot passthrough"
|
||||
desc = "Causes kinetic accelerator shots to pass through minebots."
|
||||
cost = 0
|
||||
|
||||
//Tendril-unique modules
|
||||
/obj/item/borg/upgrade/modkit/cooldown/repeater
|
||||
name = "rapid repeater"
|
||||
desc = "Quarters the kinetic accelerator's cooldown on striking a living target, but greatly increases the base cooldown."
|
||||
denied_type = /obj/item/borg/upgrade/modkit/cooldown/repeater
|
||||
modifier = -14 //Makes the cooldown 3 seconds(with no cooldown mods) if you miss. Don't miss.
|
||||
cost = 50
|
||||
|
||||
/obj/item/borg/upgrade/modkit/cooldown/repeater/projectile_strike_predamage(obj/item/projectile/kinetic/K, turf/target_turf, atom/target, obj/item/gun/energy/kinetic_accelerator/KA)
|
||||
var/valid_repeat = FALSE
|
||||
if(isliving(target))
|
||||
var/mob/living/L = target
|
||||
if(L.stat != DEAD)
|
||||
valid_repeat = TRUE
|
||||
if(ismineralturf(target_turf))
|
||||
valid_repeat = TRUE
|
||||
if(valid_repeat)
|
||||
KA.overheat = FALSE
|
||||
KA.attempt_reload(KA.overheat_time * 0.25) //If you hit, the cooldown drops to 0.75 seconds.
|
||||
|
||||
/obj/item/borg/upgrade/modkit/lifesteal
|
||||
name = "lifesteal crystal"
|
||||
desc = "Causes kinetic accelerator shots to slightly heal the firer on striking a living target."
|
||||
icon_state = "modkit_crystal"
|
||||
modifier = 2.5 //Not a very effective method of healing.
|
||||
cost = 20
|
||||
var/static/list/damage_heal_order = list(BRUTE, BURN, OXY)
|
||||
|
||||
/obj/item/borg/upgrade/modkit/lifesteal/projectile_prehit(obj/item/projectile/kinetic/K, atom/target, obj/item/gun/energy/kinetic_accelerator/KA)
|
||||
if(isliving(target) && isliving(K.firer))
|
||||
var/mob/living/L = target
|
||||
if(L.stat == DEAD)
|
||||
return
|
||||
L = K.firer
|
||||
L.heal_ordered_damage(modifier, damage_heal_order)
|
||||
|
||||
/obj/item/borg/upgrade/modkit/resonator_blasts
|
||||
name = "resonator blast"
|
||||
desc = "Causes kinetic accelerator shots to leave and detonate resonator blasts."
|
||||
denied_type = /obj/item/borg/upgrade/modkit/resonator_blasts
|
||||
cost = 30
|
||||
modifier = 0.25 //A bonus 15 damage if you burst the field on a target, 60 if you lure them into it.
|
||||
|
||||
/obj/item/borg/upgrade/modkit/resonator_blasts/projectile_strike(obj/item/projectile/kinetic/K, turf/target_turf, atom/target, obj/item/gun/energy/kinetic_accelerator/KA)
|
||||
if(target_turf && !ismineralturf(target_turf)) //Don't make fields on mineral turfs.
|
||||
var/obj/effect/temp_visual/resonance/R = locate(/obj/effect/temp_visual/resonance) in target_turf
|
||||
if(R)
|
||||
R.damage_multiplier = modifier
|
||||
R.burst()
|
||||
return
|
||||
new /obj/effect/temp_visual/resonance(target_turf, K.firer, null, 30)
|
||||
|
||||
/obj/item/borg/upgrade/modkit/bounty
|
||||
name = "death syphon"
|
||||
desc = "Killing or assisting in killing a creature permanently increases your damage against that type of creature."
|
||||
denied_type = /obj/item/borg/upgrade/modkit/bounty
|
||||
modifier = 1.25
|
||||
cost = 30
|
||||
var/maximum_bounty = 25
|
||||
var/list/bounties_reaped = list()
|
||||
|
||||
/obj/item/borg/upgrade/modkit/bounty/projectile_prehit(obj/item/projectile/kinetic/K, atom/target, obj/item/gun/energy/kinetic_accelerator/KA)
|
||||
if(isliving(target))
|
||||
var/mob/living/L = target
|
||||
var/list/existing_marks = L.has_status_effect_list(STATUS_EFFECT_SYPHONMARK)
|
||||
for(var/i in existing_marks)
|
||||
var/datum/status_effect/syphon_mark/SM = i
|
||||
if(SM.reward_target == src) //we want to allow multiple people with bounty modkits to use them, but we need to replace our own marks so we don't multi-reward
|
||||
SM.reward_target = null
|
||||
qdel(SM)
|
||||
L.apply_status_effect(STATUS_EFFECT_SYPHONMARK, src)
|
||||
|
||||
/obj/item/borg/upgrade/modkit/bounty/projectile_strike(obj/item/projectile/kinetic/K, turf/target_turf, atom/target, obj/item/gun/energy/kinetic_accelerator/KA)
|
||||
if(isliving(target))
|
||||
var/mob/living/L = target
|
||||
if(bounties_reaped[L.type])
|
||||
var/kill_modifier = 1
|
||||
if(K.pressure_decrease_active)
|
||||
kill_modifier *= K.pressure_decrease
|
||||
var/armor = L.run_armor_check(K.def_zone, K.flag, null, null, K.armour_penetration)
|
||||
L.apply_damage(bounties_reaped[L.type]*kill_modifier, K.damage_type, K.def_zone, armor)
|
||||
|
||||
/obj/item/borg/upgrade/modkit/bounty/proc/get_kill(mob/living/L)
|
||||
var/bonus_mod = 1
|
||||
if(ismegafauna(L)) //megafauna reward
|
||||
bonus_mod = 4
|
||||
if(!bounties_reaped[L.type])
|
||||
bounties_reaped[L.type] = min(modifier * bonus_mod, maximum_bounty)
|
||||
else
|
||||
bounties_reaped[L.type] = min(bounties_reaped[L.type] + (modifier * bonus_mod), maximum_bounty)
|
||||
|
||||
//Indoors
|
||||
/obj/item/borg/upgrade/modkit/indoors
|
||||
name = "decrease pressure penalty"
|
||||
desc = "A syndicate modification kit that increases the damage a kinetic accelerator does in high pressure environments."
|
||||
modifier = 2
|
||||
denied_type = /obj/item/borg/upgrade/modkit/indoors
|
||||
maximum_of_type = 2
|
||||
cost = 35
|
||||
|
||||
/obj/item/borg/upgrade/modkit/indoors/modify_projectile(obj/item/projectile/kinetic/K)
|
||||
K.pressure_decrease *= modifier
|
||||
|
||||
|
||||
//Trigger Guard
|
||||
/obj/item/borg/upgrade/modkit/trigger_guard
|
||||
name = "modified trigger guard"
|
||||
desc = "Allows creatures normally incapable of firing guns to operate the weapon when installed."
|
||||
cost = 20
|
||||
denied_type = /obj/item/borg/upgrade/modkit/trigger_guard
|
||||
|
||||
/obj/item/borg/upgrade/modkit/trigger_guard/install(obj/item/gun/energy/kinetic_accelerator/KA, mob/user)
|
||||
. = ..()
|
||||
if(.)
|
||||
KA.trigger_guard = TRIGGER_GUARD_ALLOW_ALL
|
||||
|
||||
/obj/item/borg/upgrade/modkit/trigger_guard/uninstall(obj/item/gun/energy/kinetic_accelerator/KA)
|
||||
KA.trigger_guard = TRIGGER_GUARD_NORMAL
|
||||
..()
|
||||
|
||||
|
||||
//Cosmetic
|
||||
|
||||
/obj/item/borg/upgrade/modkit/chassis_mod
|
||||
name = "super chassis"
|
||||
desc = "Makes your KA yellow. All the fun of having a more powerful KA without actually having a more powerful KA."
|
||||
cost = 0
|
||||
denied_type = /obj/item/borg/upgrade/modkit/chassis_mod
|
||||
var/chassis_icon = "kineticgun_u"
|
||||
var/chassis_name = "super-kinetic accelerator"
|
||||
|
||||
/obj/item/borg/upgrade/modkit/chassis_mod/install(obj/item/gun/energy/kinetic_accelerator/KA, mob/user)
|
||||
. = ..()
|
||||
if(.)
|
||||
KA.icon_state = chassis_icon
|
||||
KA.name = chassis_name
|
||||
|
||||
/obj/item/borg/upgrade/modkit/chassis_mod/uninstall(obj/item/gun/energy/kinetic_accelerator/KA)
|
||||
KA.icon_state = initial(KA.icon_state)
|
||||
KA.name = initial(KA.name)
|
||||
..()
|
||||
|
||||
/obj/item/borg/upgrade/modkit/chassis_mod/orange
|
||||
name = "hyper chassis"
|
||||
desc = "Makes your KA orange. All the fun of having explosive blasts without actually having explosive blasts."
|
||||
chassis_icon = "kineticgun_h"
|
||||
chassis_name = "hyper-kinetic accelerator"
|
||||
|
||||
/obj/item/borg/upgrade/modkit/tracer
|
||||
name = "white tracer bolts"
|
||||
desc = "Causes kinetic accelerator bolts to have a white tracer trail and explosion."
|
||||
cost = 0
|
||||
denied_type = /obj/item/borg/upgrade/modkit/tracer
|
||||
var/bolt_color = "#FFFFFF"
|
||||
|
||||
/obj/item/borg/upgrade/modkit/tracer/modify_projectile(obj/item/projectile/kinetic/K)
|
||||
K.icon_state = "ka_tracer"
|
||||
K.color = bolt_color
|
||||
|
||||
/obj/item/borg/upgrade/modkit/tracer/adjustable
|
||||
name = "adjustable tracer bolts"
|
||||
desc = "Causes kinetic accelerator bolts to have an adjustable-colored tracer trail and explosion. Use in-hand to change color."
|
||||
|
||||
/obj/item/borg/upgrade/modkit/tracer/adjustable/attack_self(mob/user)
|
||||
bolt_color = input(user,"","Choose Color",bolt_color) as color|null
|
||||
@@ -0,0 +1,225 @@
|
||||
/obj/item/gun/energy/laser
|
||||
name = "laser gun"
|
||||
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"
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
materials = list(MAT_METAL=2000)
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/lasergun)
|
||||
ammo_x_offset = 1
|
||||
shaded_charge = 1
|
||||
|
||||
/obj/item/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."
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/laser/practice)
|
||||
clumsy_check = 0
|
||||
item_flags = NONE
|
||||
|
||||
/obj/item/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 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/gun/energy/laser/retro/old
|
||||
name ="laser gun"
|
||||
icon_state = "retro"
|
||||
desc = "First generation lasergun, developed by Nanotrasen. Suffers from ammo issues but its unique ability to recharge its ammo without the need of a magazine helps compensate. You really hope someone has developed a better lasergun while you were in cryo."
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/lasergun/old)
|
||||
ammo_x_offset = 3
|
||||
|
||||
/obj/item/gun/energy/laser/captain
|
||||
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
|
||||
ammo_x_offset = 3
|
||||
selfcharge = EGUN_SELFCHARGE
|
||||
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
|
||||
|
||||
/obj/item/gun/energy/laser/carbine
|
||||
name = "laser carbine"
|
||||
desc = "A ruggedized laser carbine featuring much higher capacity and improved handling when compared to a normal laser gun."
|
||||
icon = 'icons/obj/guns/energy.dmi'
|
||||
icon_state = "lasernew"
|
||||
item_state = "laser"
|
||||
force = 10
|
||||
throwforce = 10
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/lasergun)
|
||||
cell_type = /obj/item/stock_parts/cell/lascarbine
|
||||
resistance_flags = FIRE_PROOF | ACID_PROOF
|
||||
|
||||
/obj/item/gun/energy/laser/carbine/nopin
|
||||
pin = null
|
||||
|
||||
/obj/item/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 lens to scatter its shot into multiple smaller lasers. The inner-core can self-charge for theoretically infinite use."
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/laser/scatter, /obj/item/ammo_casing/energy/laser)
|
||||
|
||||
/obj/item/gun/energy/laser/cyborg
|
||||
can_charge = FALSE
|
||||
desc = "An energy-based laser gun that draws power from the cyborg's internal energy cell directly. So this is what freedom looks like?"
|
||||
icon = 'icons/obj/items_cyborg.dmi'
|
||||
icon_state = "laser_cyborg"
|
||||
selfcharge = EGUN_SELFCHARGE_BORG
|
||||
cell_type = /obj/item/stock_parts/cell/secborg
|
||||
charge_delay = 3
|
||||
|
||||
/obj/item/gun/energy/laser/cyborg/emp_act()
|
||||
return
|
||||
|
||||
/obj/item/gun/energy/laser/cyborg/mean
|
||||
use_cyborg_cell = TRUE
|
||||
selfcharge = EGUN_NO_SELFCHARGE
|
||||
|
||||
/obj/item/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)
|
||||
|
||||
/obj/item/gun/energy/laser/scatter/shotty
|
||||
name = "energy shotgun"
|
||||
icon = 'icons/obj/guns/projectile.dmi'
|
||||
icon_state = "cshotgun"
|
||||
item_state = "shotgun"
|
||||
desc = "A combat shotgun gutted and refitted with an internal laser system. Can switch between taser and scattered disabler shots."
|
||||
shaded_charge = 0
|
||||
pin = /obj/item/firing_pin/implant/mindshield
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/laser/scatter/disabler, /obj/item/ammo_casing/energy/electrode)
|
||||
|
||||
///Laser Cannon
|
||||
|
||||
/obj/item/gun/energy/lasercannon
|
||||
name = "accelerator laser cannon"
|
||||
desc = "An advanced laser cannon that does more damage the farther away the target is."
|
||||
icon_state = "lasercannon"
|
||||
item_state = "laser"
|
||||
w_class = WEIGHT_CLASS_BULKY
|
||||
force = 10
|
||||
flags_1 = CONDUCT_1
|
||||
slot_flags = ITEM_SLOT_BACK
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/laser/accelerator)
|
||||
pin = null
|
||||
ammo_x_offset = 3
|
||||
|
||||
/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 = "scatterlaser"
|
||||
range = 255
|
||||
damage = 6
|
||||
|
||||
/obj/item/projectile/beam/laser/accelerator/Range()
|
||||
..()
|
||||
damage += 7
|
||||
transform *= 1 + ((damage/7) * 0.2)//20% larger per tile
|
||||
|
||||
/obj/item/gun/energy/xray
|
||||
name = "\improper X-ray laser gun"
|
||||
desc = "A high-power laser gun capable of expelling concentrated X-ray blasts that pass through multiple soft targets and heavier materials."
|
||||
icon_state = "xray"
|
||||
item_state = null
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/xray)
|
||||
pin = null
|
||||
ammo_x_offset = 3
|
||||
|
||||
////////Laser Tag////////////////////
|
||||
|
||||
/obj/item/gun/energy/laser/bluetag
|
||||
name = "laser tag gun"
|
||||
icon_state = "bluetag"
|
||||
desc = "A retro laser gun modified to fire harmless blue beams of light. Sound effects included!"
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/laser/bluetag)
|
||||
item_flags = NONE
|
||||
clumsy_check = FALSE
|
||||
pin = /obj/item/firing_pin/tag/blue
|
||||
ammo_x_offset = 2
|
||||
selfcharge = EGUN_SELFCHARGE
|
||||
|
||||
/obj/item/gun/energy/laser/bluetag/hitscan
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/laser/bluetag/hitscan)
|
||||
|
||||
/obj/item/gun/energy/laser/redtag
|
||||
name = "laser tag gun"
|
||||
icon_state = "redtag"
|
||||
desc = "A retro laser gun modified to fire harmless beams red of light. Sound effects included!"
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/laser/redtag)
|
||||
item_flags = NONE
|
||||
clumsy_check = FALSE
|
||||
pin = /obj/item/firing_pin/tag/red
|
||||
ammo_x_offset = 2
|
||||
selfcharge = EGUN_SELFCHARGE
|
||||
|
||||
/obj/item/gun/energy/laser/redtag/hitscan
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/laser/redtag/hitscan)
|
||||
|
||||
/obj/item/gun/energy/laser/redtag/hitscan/chaplain
|
||||
name = "\improper holy lasrifle"
|
||||
desc = "A lasrifle from the old Imperium. This one seems to be blessed by techpriests."
|
||||
icon_state = "LaserAK"
|
||||
item_state = null
|
||||
force = 14
|
||||
pin = /obj/item/firing_pin/holy
|
||||
icon = 'modular_citadel/icons/obj/guns/VGguns.dmi'
|
||||
ammo_x_offset = 4
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/laser/redtag/hitscan/holy)
|
||||
lefthand_file = 'modular_citadel/icons/mob/citadel/guns_lefthand.dmi'
|
||||
righthand_file = 'modular_citadel/icons/mob/citadel/guns_righthand.dmi'
|
||||
var/chaplain_spawnable = TRUE
|
||||
total_mass = TOTAL_MASS_MEDIEVAL_WEAPON
|
||||
throw_speed = 3
|
||||
throw_range = 4
|
||||
throwforce = 10
|
||||
obj_flags = UNIQUE_RENAME
|
||||
|
||||
/obj/item/gun/energy/laser/redtag/hitscan/chaplain/Initialize()
|
||||
. = ..()
|
||||
AddComponent(/datum/component/anti_magic, TRUE, TRUE, FALSE, null, null, FALSE)
|
||||
|
||||
/obj/item/gun/energy/laser/redtag/hitscan/chaplain/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] pulls the trigger!</span>")
|
||||
|
||||
playsound('sound/weapons/dink.ogg', 30, 1)
|
||||
|
||||
if((iscultist(target)) || (is_servant_of_ratvar(target)))
|
||||
chambered.BB.damage *= 1500
|
||||
|
||||
else if(chambered && chambered.BB)
|
||||
chambered.BB.damage *= 5
|
||||
|
||||
process_fire(target, user, TRUE, params)
|
||||
@@ -0,0 +1,64 @@
|
||||
/obj/item/gun/energy/taser
|
||||
name = "taser gun"
|
||||
desc = "A low-capacity, energy-based stun gun used by security teams to subdue targets at range."
|
||||
icon_state = "taser"
|
||||
item_state = null //so the human update icon uses the icon_state instead.
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/electrode)
|
||||
ammo_x_offset = 3
|
||||
|
||||
/obj/item/gun/energy/tesla_revolver
|
||||
name = "tesla gun"
|
||||
desc = "An experimental gun based on an experimental engine, it's about as likely to kill its operator as it is the target."
|
||||
icon_state = "tesla"
|
||||
item_state = "tesla"
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/tesla_revolver)
|
||||
can_flashlight = 0
|
||||
pin = null
|
||||
shaded_charge = 1
|
||||
|
||||
/obj/item/gun/energy/e_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/disabler, /obj/item/ammo_casing/energy/electrode)
|
||||
ammo_x_offset = 2
|
||||
|
||||
/obj/item/gun/energy/e_gun/advtaser/cyborg
|
||||
name = "cyborg taser"
|
||||
desc = "An integrated hybrid taser that draws directly from a cyborg's power cell. The one contains a limiter to prevent the cyborg's power cell from overheating."
|
||||
icon = 'icons/obj/items_cyborg.dmi'
|
||||
icon_state = "taser"
|
||||
can_flashlight = FALSE
|
||||
can_charge = FALSE
|
||||
selfcharge = EGUN_SELFCHARGE_BORG
|
||||
cell_type = /obj/item/stock_parts/cell/secborg
|
||||
charge_delay = 5
|
||||
|
||||
/obj/item/gun/energy/e_gun/advtaser/cyborg/mean
|
||||
desc = "An integrated hybrid taser that draws directly from a cyborg's power cell."
|
||||
use_cyborg_cell = TRUE
|
||||
selfcharge = EGUN_NO_SELFCHARGE
|
||||
|
||||
/obj/item/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/gun/energy/disabler/cyborg
|
||||
name = "cyborg disabler"
|
||||
desc = "An integrated disabler that draws from a cyborg's power cell. This one contains a limiter to prevent the cyborg's power cell from overheating."
|
||||
icon = 'icons/obj/items_cyborg.dmi'
|
||||
icon_state = "taser"
|
||||
can_charge = FALSE
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/disabler/secborg)
|
||||
selfcharge = EGUN_SELFCHARGE_BORG
|
||||
cell_type = /obj/item/stock_parts/cell/secborg
|
||||
charge_delay = 5
|
||||
|
||||
/obj/item/gun/energy/disabler/cyborg/mean
|
||||
desc = "An integrated disabler that draws from a cyborg's power cell."
|
||||
use_cyborg_cell = TRUE
|
||||
selfcharge = EGUN_NO_SELFCHARGE
|
||||
@@ -0,0 +1,91 @@
|
||||
/obj/item/gun/magic
|
||||
name = "staff of nothing"
|
||||
desc = "This staff is boring to watch because even though it came first you've seen everything it can do in other staves for years."
|
||||
icon = 'icons/obj/guns/magic.dmi'
|
||||
icon_state = "staffofnothing"
|
||||
item_state = "staff"
|
||||
lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi'
|
||||
fire_sound = 'sound/weapons/emitter.ogg'
|
||||
flags_1 = CONDUCT_1
|
||||
w_class = WEIGHT_CLASS_HUGE
|
||||
var/checks_antimagic = FALSE
|
||||
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
|
||||
clumsy_check = 0
|
||||
trigger_guard = TRIGGER_GUARD_ALLOW_ALL // Has no trigger at all, uses magic instead
|
||||
pin = /obj/item/firing_pin/magic
|
||||
|
||||
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/gun/magic/afterattack(atom/target, mob/living/user, flag)
|
||||
if(no_den_usage)
|
||||
var/area/A = get_area(user)
|
||||
if(istype(A, /area/wizard_station))
|
||||
to_chat(user, "<span class='warning'>You know better than to violate the security of The Den, best wait until you leave to use [src].</span>")
|
||||
return
|
||||
else
|
||||
no_den_usage = 0
|
||||
if(checks_antimagic && user.anti_magic_check(TRUE, FALSE, FALSE, 0, TRUE))
|
||||
to_chat(user, "<span class='warning'>Something is interfering with [src].</span>")
|
||||
return
|
||||
. = ..()
|
||||
|
||||
/obj/item/gun/magic/can_shoot()
|
||||
return charges
|
||||
|
||||
/obj/item/gun/magic/recharge_newshot()
|
||||
if (charges && chambered && !chambered.BB)
|
||||
chambered.newshot()
|
||||
|
||||
/obj/item/gun/magic/process_chamber()
|
||||
if(chambered && !chambered.BB) //if BB is null, i.e the shot has been fired...
|
||||
charges--//... drain a charge
|
||||
recharge_newshot()
|
||||
|
||||
/obj/item/gun/magic/Initialize()
|
||||
. = ..()
|
||||
charges = max_charges
|
||||
chambered = new ammo_type(src)
|
||||
if(can_charge)
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
|
||||
/obj/item/gun/magic/Destroy()
|
||||
if(can_charge)
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
return ..()
|
||||
|
||||
|
||||
/obj/item/gun/magic/process()
|
||||
charge_tick++
|
||||
if(charge_tick < recharge_rate || charges >= max_charges)
|
||||
return 0
|
||||
charge_tick = 0
|
||||
charges++
|
||||
if(charges == 1)
|
||||
recharge_newshot()
|
||||
return 1
|
||||
|
||||
/obj/item/gun/magic/update_icon()
|
||||
return
|
||||
|
||||
/obj/item/gun/magic/shoot_with_empty_chamber(mob/living/user as mob|obj)
|
||||
to_chat(user, "<span class='warning'>The [name] whizzles quietly.</span>")
|
||||
|
||||
/obj/item/gun/magic/suicide_act(mob/user)
|
||||
user.visible_message("<span class='suicide'>[user] is twisting [src] above [user.p_their()] head, releasing a magical blast! It looks like [user.p_theyre()] trying to commit suicide!</span>")
|
||||
playsound(loc, fire_sound, 50, 1, -1)
|
||||
return (FIRELOSS)
|
||||
|
||||
/obj/item/gun/magic/vv_edit_var(var_name, var_value)
|
||||
. = ..()
|
||||
switch (var_name)
|
||||
if ("charges")
|
||||
recharge_newshot()
|
||||
@@ -0,0 +1,177 @@
|
||||
/obj/item/gun/magic/wand
|
||||
name = "wand of nothing"
|
||||
desc = "It's not just a stick, it's a MAGIC stick!"
|
||||
ammo_type = /obj/item/ammo_casing/magic
|
||||
icon_state = "nothingwand"
|
||||
item_state = "wand"
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
can_charge = 0
|
||||
max_charges = 100 //100, 50, 50, 34 (max charge distribution by 25%ths)
|
||||
var/variable_charges = 1
|
||||
|
||||
/obj/item/gun/magic/wand/Initialize()
|
||||
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
|
||||
if(prob(33))
|
||||
max_charges = CEILING(max_charges / 3, 1)
|
||||
else
|
||||
max_charges = CEILING(max_charges / 2, 1)
|
||||
return ..()
|
||||
|
||||
/obj/item/gun/magic/wand/examine(mob/user)
|
||||
..()
|
||||
to_chat(user, "Has [charges] charge\s remaining.")
|
||||
|
||||
/obj/item/gun/magic/wand/update_icon()
|
||||
icon_state = "[initial(icon_state)][charges ? "" : "-drained"]"
|
||||
|
||||
/obj/item/gun/magic/wand/attack(atom/target, mob/living/user)
|
||||
if(target == user)
|
||||
return
|
||||
..()
|
||||
|
||||
/obj/item/gun/magic/wand/afterattack(atom/target, mob/living/user)
|
||||
if(!charges)
|
||||
shoot_with_empty_chamber(user)
|
||||
return
|
||||
if(target == user)
|
||||
if(no_den_usage)
|
||||
var/area/A = get_area(user)
|
||||
if(istype(A, /area/wizard_station))
|
||||
to_chat(user, "<span class='warning'>You know better than to violate the security of The Den, best wait until you leave to use [src].</span>")
|
||||
return
|
||||
else
|
||||
no_den_usage = 0
|
||||
zap_self(user)
|
||||
else
|
||||
. = ..()
|
||||
update_icon()
|
||||
|
||||
|
||||
/obj/item/gun/magic/wand/proc/zap_self(mob/living/user)
|
||||
user.visible_message("<span class='danger'>[user] zaps [user.p_them()]self with [src].</span>")
|
||||
playsound(user, fire_sound, 50, 1)
|
||||
user.log_message("zapped [user.p_them()]self with a <b>[src]</b>", LOG_ATTACK)
|
||||
|
||||
|
||||
/////////////////////////////////////
|
||||
//WAND OF DEATH
|
||||
/////////////////////////////////////
|
||||
|
||||
/obj/item/gun/magic/wand/death
|
||||
name = "wand of death"
|
||||
desc = "This deadly wand overwhelms the victim's body with pure energy, slaying them without fail."
|
||||
fire_sound = 'sound/magic/wandodeath.ogg'
|
||||
ammo_type = /obj/item/ammo_casing/magic/death
|
||||
icon_state = "deathwand"
|
||||
max_charges = 3 //3, 2, 2, 1
|
||||
|
||||
/obj/item/gun/magic/wand/death/zap_self(mob/living/user)
|
||||
..()
|
||||
to_chat(user, "<span class='warning'>You irradiate yourself with pure energy! \
|
||||
[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?")]\
|
||||
</span>")
|
||||
user.adjustOxyLoss(500)
|
||||
charges--
|
||||
|
||||
|
||||
/////////////////////////////////////
|
||||
//WAND OF HEALING
|
||||
/////////////////////////////////////
|
||||
|
||||
/obj/item/gun/magic/wand/resurrection
|
||||
name = "wand of healing"
|
||||
desc = "This wand uses healing magics to heal and revive. They are rarely utilized within the Wizard Federation for some reason."
|
||||
ammo_type = /obj/item/ammo_casing/magic/heal
|
||||
fire_sound = 'sound/magic/staff_healing.ogg'
|
||||
icon_state = "revivewand"
|
||||
max_charges = 10 //10, 5, 5, 4
|
||||
|
||||
/obj/item/gun/magic/wand/resurrection/zap_self(mob/living/user)
|
||||
..()
|
||||
charges--
|
||||
if(user.anti_magic_check())
|
||||
user.visible_message("<span class='warning'>[src] has no effect on [user]!</span>")
|
||||
return
|
||||
user.revive(full_heal = 1)
|
||||
if(iscarbon(user))
|
||||
var/mob/living/carbon/C = user
|
||||
C.regenerate_limbs()
|
||||
C.regenerate_organs()
|
||||
to_chat(user, "<span class='notice'>You feel great!</span>")
|
||||
|
||||
/obj/item/gun/magic/wand/resurrection/debug //for testing
|
||||
name = "debug wand of healing"
|
||||
max_charges = 500
|
||||
|
||||
/////////////////////////////////////
|
||||
//WAND OF POLYMORPH
|
||||
/////////////////////////////////////
|
||||
|
||||
/obj/item/gun/magic/wand/polymorph
|
||||
name = "wand of polymorph"
|
||||
desc = "This wand is attuned to chaos and will radically alter the victim's form."
|
||||
ammo_type = /obj/item/ammo_casing/magic/change
|
||||
icon_state = "polywand"
|
||||
fire_sound = 'sound/magic/staff_change.ogg'
|
||||
max_charges = 10 //10, 5, 5, 4
|
||||
|
||||
/obj/item/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/gun/magic/wand/teleport
|
||||
name = "wand of teleportation"
|
||||
desc = "This wand will wrench targets through space and time to move them somewhere else."
|
||||
ammo_type = /obj/item/ammo_casing/magic/teleport
|
||||
fire_sound = 'sound/magic/wand_teleport.ogg'
|
||||
icon_state = "telewand"
|
||||
max_charges = 10 //10, 5, 5, 4
|
||||
no_den_usage = 1
|
||||
|
||||
/obj/item/gun/magic/wand/teleport/zap_self(mob/living/user)
|
||||
if(do_teleport(user, user, 10, channel = TELEPORT_CHANNEL_MAGIC))
|
||||
var/datum/effect_system/smoke_spread/smoke = new
|
||||
smoke.set_up(3, user.loc)
|
||||
smoke.start()
|
||||
charges--
|
||||
..()
|
||||
|
||||
/////////////////////////////////////
|
||||
//WAND OF DOOR CREATION
|
||||
/////////////////////////////////////
|
||||
|
||||
/obj/item/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."
|
||||
ammo_type = /obj/item/ammo_casing/magic/door
|
||||
icon_state = "doorwand"
|
||||
fire_sound = 'sound/magic/staff_door.ogg'
|
||||
max_charges = 20 //20, 10, 10, 7
|
||||
no_den_usage = 1
|
||||
|
||||
/obj/item/gun/magic/wand/door/zap_self(mob/living/user)
|
||||
to_chat(user, "<span class='notice'>You feel vaguely more open with your feelings.</span>")
|
||||
charges--
|
||||
..()
|
||||
|
||||
/////////////////////////////////////
|
||||
//WAND OF FIREBALL
|
||||
/////////////////////////////////////
|
||||
|
||||
/obj/item/gun/magic/wand/fireball
|
||||
name = "wand of fireball"
|
||||
desc = "This wand shoots scorching balls of fire that explode into destructive flames."
|
||||
fire_sound = 'sound/magic/fireball.ogg'
|
||||
ammo_type = /obj/item/ammo_casing/magic/fireball
|
||||
icon_state = "firewand"
|
||||
max_charges = 8 //8, 4, 4, 3
|
||||
|
||||
/obj/item/gun/magic/wand/fireball/zap_self(mob/living/user)
|
||||
..()
|
||||
explosion(user.loc, -1, 0, 2, 3, 0, flame_range = 2)
|
||||
charges--
|
||||
@@ -0,0 +1,572 @@
|
||||
|
||||
#define ZOOM_LOCK_AUTOZOOM_FREEMOVE 0
|
||||
#define ZOOM_LOCK_AUTOZOOM_ANGLELOCK 1
|
||||
#define ZOOM_LOCK_CENTER_VIEW 2
|
||||
#define ZOOM_LOCK_OFF 3
|
||||
|
||||
#define AUTOZOOM_PIXEL_STEP_FACTOR 48
|
||||
|
||||
#define AIMING_BEAM_ANGLE_CHANGE_THRESHOLD 0.1
|
||||
|
||||
/obj/item/gun/energy/beam_rifle
|
||||
name = "particle acceleration rifle"
|
||||
desc = "An energy-based anti material marksman rifle that uses highly charged particle beams moving at extreme velocities to decimate whatever is unfortunate enough to be targeted by one. \
|
||||
<span class='boldnotice'>Hold down left click while scoped to aim, when weapon is fully aimed (Tracer goes from red to green as it charges), release to fire. Moving while aiming or \
|
||||
changing where you're pointing at while aiming will delay the aiming process depending on how much you changed.</span>"
|
||||
icon = 'icons/obj/guns/energy.dmi'
|
||||
icon_state = "esniper"
|
||||
item_state = "esniper"
|
||||
fire_sound = 'sound/weapons/beam_sniper.ogg'
|
||||
slot_flags = ITEM_SLOT_BACK
|
||||
force = 15
|
||||
materials = list()
|
||||
recoil = 4
|
||||
ammo_x_offset = 3
|
||||
ammo_y_offset = 3
|
||||
modifystate = FALSE
|
||||
weapon_weight = WEAPON_HEAVY
|
||||
w_class = WEIGHT_CLASS_BULKY
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/beam_rifle/hitscan)
|
||||
cell_type = /obj/item/stock_parts/cell/beam_rifle
|
||||
canMouseDown = TRUE
|
||||
//Cit changes: beam rifle stats.
|
||||
slowdown = 1
|
||||
item_flags = NO_MAT_REDEMPTION | SLOWS_WHILE_IN_HAND | NEEDS_PERMIT
|
||||
pin = null
|
||||
var/aiming = FALSE
|
||||
var/aiming_time = 14
|
||||
var/aiming_time_fire_threshold = 5
|
||||
var/aiming_time_left = 14
|
||||
var/aiming_time_increase_user_movement = 7
|
||||
var/aiming_time_increase_angle_multiplier = 0.30
|
||||
var/last_process = 0
|
||||
|
||||
var/lastangle = 0
|
||||
var/aiming_lastangle = 0
|
||||
var/mob/current_user = null
|
||||
var/list/obj/effect/projectile/tracer/current_tracers
|
||||
|
||||
var/structure_piercing = 1
|
||||
var/structure_bleed_coeff = 0.7
|
||||
var/wall_pierce_amount = 0
|
||||
var/wall_devastate = 0
|
||||
var/aoe_structure_range = 1
|
||||
var/aoe_structure_damage = 35
|
||||
var/aoe_fire_range = 1
|
||||
var/aoe_fire_chance = 100
|
||||
var/aoe_mob_range = 1
|
||||
var/aoe_mob_damage = 20
|
||||
var/impact_structure_damage = 75
|
||||
var/projectile_damage = 40
|
||||
var/projectile_stun = 0
|
||||
var/projectile_setting_pierce = TRUE
|
||||
var/delay = 30
|
||||
var/lastfire = 0
|
||||
|
||||
//ZOOMING
|
||||
var/zoom_current_view_increase = 0
|
||||
var/zoom_target_view_increase = 10
|
||||
var/zooming = FALSE
|
||||
var/zoom_lock = ZOOM_LOCK_OFF
|
||||
var/zooming_angle
|
||||
var/current_zoom_x = 0
|
||||
var/current_zoom_y = 0
|
||||
|
||||
var/static/image/charged_overlay = image(icon = 'icons/obj/guns/energy.dmi', icon_state = "esniper_charged")
|
||||
var/static/image/drained_overlay = image(icon = 'icons/obj/guns/energy.dmi', icon_state = "esniper_empty")
|
||||
|
||||
var/datum/action/item_action/zoom_lock_action/zoom_lock_action
|
||||
var/mob/listeningTo
|
||||
|
||||
/obj/item/gun/energy/beam_rifle/debug
|
||||
delay = 0
|
||||
cell_type = /obj/item/stock_parts/cell/infinite
|
||||
aiming_time = 0
|
||||
recoil = 0
|
||||
pin = /obj/item/firing_pin
|
||||
|
||||
/obj/item/gun/energy/beam_rifle/equipped(mob/user)
|
||||
set_user(user)
|
||||
. = ..()
|
||||
|
||||
/obj/item/gun/energy/beam_rifle/pickup(mob/user)
|
||||
set_user(user)
|
||||
. = ..()
|
||||
|
||||
/obj/item/gun/energy/beam_rifle/dropped(mob/user)
|
||||
set_user()
|
||||
. = ..()
|
||||
|
||||
/obj/item/gun/energy/beam_rifle/ui_action_click(owner, action)
|
||||
if(istype(action, /datum/action/item_action/zoom_lock_action))
|
||||
zoom_lock++
|
||||
if(zoom_lock > 3)
|
||||
zoom_lock = 0
|
||||
switch(zoom_lock)
|
||||
if(ZOOM_LOCK_AUTOZOOM_FREEMOVE)
|
||||
to_chat(owner, "<span class='boldnotice'>You switch [src]'s zooming processor to free directional.</span>")
|
||||
if(ZOOM_LOCK_AUTOZOOM_ANGLELOCK)
|
||||
to_chat(owner, "<span class='boldnotice'>You switch [src]'s zooming processor to locked directional.</span>")
|
||||
if(ZOOM_LOCK_CENTER_VIEW)
|
||||
to_chat(owner, "<span class='boldnotice'>You switch [src]'s zooming processor to center mode.</span>")
|
||||
if(ZOOM_LOCK_OFF)
|
||||
to_chat(owner, "<span class='boldnotice'>You disable [src]'s zooming system.</span>")
|
||||
reset_zooming()
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/item/gun/energy/beam_rifle/proc/set_autozoom_pixel_offsets_immediate(current_angle)
|
||||
if(zoom_lock == ZOOM_LOCK_CENTER_VIEW || zoom_lock == ZOOM_LOCK_OFF)
|
||||
return
|
||||
current_zoom_x = sin(current_angle) + sin(current_angle) * AUTOZOOM_PIXEL_STEP_FACTOR * zoom_current_view_increase
|
||||
current_zoom_y = cos(current_angle) + cos(current_angle) * AUTOZOOM_PIXEL_STEP_FACTOR * zoom_current_view_increase
|
||||
|
||||
/obj/item/gun/energy/beam_rifle/proc/handle_zooming()
|
||||
if(!zooming || !check_user())
|
||||
return
|
||||
set_autozoom_pixel_offsets_immediate(zooming_angle)
|
||||
|
||||
/obj/item/gun/energy/beam_rifle/proc/start_zooming()
|
||||
if(zoom_lock == ZOOM_LOCK_OFF)
|
||||
return
|
||||
zooming = TRUE
|
||||
current_user.client.change_view(world.view + zoom_target_view_increase)
|
||||
zoom_current_view_increase = zoom_target_view_increase
|
||||
|
||||
/obj/item/gun/energy/beam_rifle/proc/stop_zooming(mob/user)
|
||||
if(zooming)
|
||||
zooming = FALSE
|
||||
reset_zooming(user)
|
||||
|
||||
/obj/item/gun/energy/beam_rifle/proc/reset_zooming(mob/user)
|
||||
if(!user)
|
||||
user = current_user
|
||||
if(!user || !user.client)
|
||||
return FALSE
|
||||
animate(user.client, pixel_x = 0, pixel_y = 0, 0, FALSE, LINEAR_EASING, ANIMATION_END_NOW)
|
||||
zoom_current_view_increase = 0
|
||||
user.client.change_view(CONFIG_GET(string/default_view))
|
||||
zooming_angle = 0
|
||||
current_zoom_x = 0
|
||||
current_zoom_y = 0
|
||||
|
||||
/obj/item/gun/energy/beam_rifle/update_icon()
|
||||
cut_overlays()
|
||||
var/obj/item/ammo_casing/energy/primary_ammo = ammo_type[1]
|
||||
if(!QDELETED(cell) && (cell.charge > primary_ammo.e_cost))
|
||||
add_overlay(charged_overlay)
|
||||
else
|
||||
add_overlay(drained_overlay)
|
||||
|
||||
/obj/item/gun/energy/beam_rifle/attack_self(mob/user)
|
||||
projectile_setting_pierce = !projectile_setting_pierce
|
||||
to_chat(user, "<span class='boldnotice'>You set \the [src] to [projectile_setting_pierce? "pierce":"impact"] mode.</span>")
|
||||
aiming_beam()
|
||||
|
||||
/obj/item/gun/energy/beam_rifle/Initialize()
|
||||
. = ..()
|
||||
fire_delay = delay
|
||||
current_tracers = list()
|
||||
START_PROCESSING(SSfastprocess, src)
|
||||
zoom_lock_action = new(src)
|
||||
|
||||
/obj/item/gun/energy/beam_rifle/Destroy()
|
||||
STOP_PROCESSING(SSfastprocess, src)
|
||||
set_user(null)
|
||||
QDEL_LIST(current_tracers)
|
||||
listeningTo = null
|
||||
return ..()
|
||||
|
||||
/obj/item/gun/energy/beam_rifle/emp_act(severity)
|
||||
. = ..()
|
||||
if(. & EMP_PROTECT_SELF)
|
||||
return
|
||||
chambered = null
|
||||
recharge_newshot()
|
||||
|
||||
/obj/item/gun/energy/beam_rifle/proc/aiming_beam(force_update = FALSE)
|
||||
var/diff = abs(aiming_lastangle - lastangle)
|
||||
check_user()
|
||||
if(diff < AIMING_BEAM_ANGLE_CHANGE_THRESHOLD && !force_update)
|
||||
return
|
||||
aiming_lastangle = lastangle
|
||||
var/obj/item/projectile/beam/beam_rifle/hitscan/aiming_beam/P = new
|
||||
P.gun = src
|
||||
P.wall_pierce_amount = wall_pierce_amount
|
||||
P.structure_pierce_amount = structure_piercing
|
||||
P.do_pierce = projectile_setting_pierce
|
||||
if(aiming_time)
|
||||
var/percent = ((100/aiming_time)*aiming_time_left)
|
||||
P.color = rgb(255 * percent,255 * ((100 - percent) / 100),0)
|
||||
else
|
||||
P.color = rgb(0, 255, 0)
|
||||
var/turf/curloc = get_turf(src)
|
||||
var/turf/targloc = get_turf(current_user.client.mouseObject)
|
||||
if(!istype(targloc))
|
||||
if(!istype(curloc))
|
||||
return
|
||||
targloc = get_turf_in_angle(lastangle, curloc, 10)
|
||||
P.preparePixelProjectile(targloc, current_user, current_user.client.mouseParams, 0)
|
||||
P.fire(lastangle)
|
||||
|
||||
/obj/item/gun/energy/beam_rifle/process()
|
||||
if(!aiming)
|
||||
last_process = world.time
|
||||
return
|
||||
check_user()
|
||||
handle_zooming()
|
||||
aiming_time_left = max(0, aiming_time_left - (world.time - last_process))
|
||||
aiming_beam(TRUE)
|
||||
last_process = world.time
|
||||
|
||||
/obj/item/gun/energy/beam_rifle/proc/check_user(automatic_cleanup = TRUE)
|
||||
if(!istype(current_user) || !isturf(current_user.loc) || !(src in current_user.held_items) || current_user.incapacitated()) //Doesn't work if you're not holding it!
|
||||
if(automatic_cleanup)
|
||||
stop_aiming()
|
||||
set_user(null)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/item/gun/energy/beam_rifle/proc/process_aim()
|
||||
if(istype(current_user) && current_user.client && current_user.client.mouseParams)
|
||||
var/angle = mouse_angle_from_client(current_user.client)
|
||||
current_user.setDir(angle2dir_cardinal(angle))
|
||||
var/difference = abs(closer_angle_difference(lastangle, angle))
|
||||
delay_penalty(difference * aiming_time_increase_angle_multiplier)
|
||||
lastangle = angle
|
||||
|
||||
/obj/item/gun/energy/beam_rifle/proc/on_mob_move()
|
||||
check_user()
|
||||
if(aiming)
|
||||
delay_penalty(aiming_time_increase_user_movement)
|
||||
process_aim()
|
||||
aiming_beam(TRUE)
|
||||
|
||||
/obj/item/gun/energy/beam_rifle/proc/start_aiming()
|
||||
aiming_time_left = aiming_time
|
||||
aiming = TRUE
|
||||
process_aim()
|
||||
aiming_beam(TRUE)
|
||||
zooming_angle = lastangle
|
||||
start_zooming()
|
||||
|
||||
/obj/item/gun/energy/beam_rifle/proc/stop_aiming(mob/user)
|
||||
set waitfor = FALSE
|
||||
aiming_time_left = aiming_time
|
||||
aiming = FALSE
|
||||
QDEL_LIST(current_tracers)
|
||||
stop_zooming(user)
|
||||
|
||||
/obj/item/gun/energy/beam_rifle/proc/set_user(mob/user)
|
||||
if(user == current_user)
|
||||
return
|
||||
stop_aiming(current_user)
|
||||
if(listeningTo)
|
||||
UnregisterSignal(listeningTo, COMSIG_MOVABLE_MOVED)
|
||||
listeningTo = null
|
||||
if(istype(current_user))
|
||||
LAZYREMOVE(current_user.mousemove_intercept_objects, src)
|
||||
current_user = null
|
||||
if(istype(user))
|
||||
current_user = user
|
||||
LAZYOR(current_user.mousemove_intercept_objects, src)
|
||||
RegisterSignal(user, COMSIG_MOVABLE_MOVED, .proc/on_mob_move)
|
||||
listeningTo = user
|
||||
|
||||
/obj/item/gun/energy/beam_rifle/onMouseDrag(src_object, over_object, src_location, over_location, params, mob)
|
||||
if(aiming)
|
||||
process_aim()
|
||||
aiming_beam()
|
||||
if(zoom_lock == ZOOM_LOCK_AUTOZOOM_FREEMOVE)
|
||||
zooming_angle = lastangle
|
||||
set_autozoom_pixel_offsets_immediate(zooming_angle)
|
||||
return ..()
|
||||
|
||||
/obj/item/gun/energy/beam_rifle/onMouseDown(object, location, params, mob/mob)
|
||||
if(istype(mob))
|
||||
set_user(mob)
|
||||
if(istype(object, /obj/screen) && !istype(object, /obj/screen/click_catcher))
|
||||
return
|
||||
if((object in mob.contents) || (object == mob))
|
||||
return
|
||||
start_aiming()
|
||||
return ..()
|
||||
|
||||
/obj/item/gun/energy/beam_rifle/onMouseUp(object, location, params, mob/M)
|
||||
if(istype(object, /obj/screen) && !istype(object, /obj/screen/click_catcher))
|
||||
return
|
||||
process_aim()
|
||||
if(aiming_time_left <= aiming_time_fire_threshold && check_user())
|
||||
sync_ammo()
|
||||
afterattack(M.client.mouseObject, M, FALSE, M.client.mouseParams, passthrough = TRUE)
|
||||
stop_aiming()
|
||||
QDEL_LIST(current_tracers)
|
||||
return ..()
|
||||
|
||||
/obj/item/gun/energy/beam_rifle/afterattack(atom/target, mob/living/user, flag, params, passthrough = FALSE)
|
||||
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(!passthrough && (aiming_time > aiming_time_fire_threshold))
|
||||
return
|
||||
if(lastfire > world.time + delay)
|
||||
return
|
||||
lastfire = world.time
|
||||
. = ..()
|
||||
stop_aiming()
|
||||
|
||||
/obj/item/gun/energy/beam_rifle/proc/sync_ammo()
|
||||
for(var/obj/item/ammo_casing/energy/beam_rifle/AC in contents)
|
||||
AC.sync_stats()
|
||||
|
||||
/obj/item/gun/energy/beam_rifle/proc/delay_penalty(amount)
|
||||
aiming_time_left = CLAMP(aiming_time_left + amount, 0, aiming_time)
|
||||
|
||||
/obj/item/ammo_casing/energy/beam_rifle
|
||||
name = "particle acceleration lens"
|
||||
desc = "Don't look into barrel!"
|
||||
var/wall_pierce_amount = 0
|
||||
var/wall_devastate = 0
|
||||
var/aoe_structure_range = 1
|
||||
var/aoe_structure_damage = 30
|
||||
var/aoe_fire_range = 2
|
||||
var/aoe_fire_chance = 66
|
||||
var/aoe_mob_range = 1
|
||||
var/aoe_mob_damage = 20
|
||||
var/impact_structure_damage = 50
|
||||
var/projectile_damage = 40
|
||||
var/projectile_stun = 0
|
||||
var/structure_piercing = 2
|
||||
var/structure_bleed_coeff = 0.7
|
||||
var/do_pierce = TRUE
|
||||
var/obj/item/gun/energy/beam_rifle/host
|
||||
|
||||
/obj/item/ammo_casing/energy/beam_rifle/proc/sync_stats()
|
||||
var/obj/item/gun/energy/beam_rifle/BR = loc
|
||||
if(!istype(BR))
|
||||
stack_trace("Beam rifle syncing error")
|
||||
host = BR
|
||||
do_pierce = BR.projectile_setting_pierce
|
||||
wall_pierce_amount = BR.wall_pierce_amount
|
||||
wall_devastate = BR.wall_devastate
|
||||
aoe_structure_range = BR.aoe_structure_range
|
||||
aoe_structure_damage = BR.aoe_structure_damage
|
||||
aoe_fire_range = BR.aoe_fire_range
|
||||
aoe_fire_chance = BR.aoe_fire_chance
|
||||
aoe_mob_range = BR.aoe_mob_range
|
||||
aoe_mob_damage = BR.aoe_mob_damage
|
||||
impact_structure_damage = BR.impact_structure_damage
|
||||
projectile_damage = BR.projectile_damage
|
||||
projectile_stun = BR.projectile_stun
|
||||
delay = BR.delay
|
||||
structure_piercing = BR.structure_piercing
|
||||
structure_bleed_coeff = BR.structure_bleed_coeff
|
||||
|
||||
/obj/item/ammo_casing/energy/beam_rifle/ready_proj(atom/target, mob/living/user, quiet, zone_override = "")
|
||||
. = ..()
|
||||
var/obj/item/projectile/beam/beam_rifle/hitscan/HS_BB = BB
|
||||
if(!istype(HS_BB))
|
||||
return
|
||||
HS_BB.impact_direct_damage = projectile_damage
|
||||
HS_BB.stun = projectile_stun
|
||||
HS_BB.impact_structure_damage = impact_structure_damage
|
||||
HS_BB.aoe_mob_damage = aoe_mob_damage
|
||||
HS_BB.aoe_mob_range = CLAMP(aoe_mob_range, 0, 15) //Badmin safety lock
|
||||
HS_BB.aoe_fire_chance = aoe_fire_chance
|
||||
HS_BB.aoe_fire_range = aoe_fire_range
|
||||
HS_BB.aoe_structure_damage = aoe_structure_damage
|
||||
HS_BB.aoe_structure_range = CLAMP(aoe_structure_range, 0, 15) //Badmin safety lock
|
||||
HS_BB.wall_devastate = wall_devastate
|
||||
HS_BB.wall_pierce_amount = wall_pierce_amount
|
||||
HS_BB.structure_pierce_amount = structure_piercing
|
||||
HS_BB.structure_bleed_coeff = structure_bleed_coeff
|
||||
HS_BB.do_pierce = do_pierce
|
||||
HS_BB.gun = host
|
||||
|
||||
/obj/item/ammo_casing/energy/beam_rifle/throw_proj(atom/target, turf/targloc, mob/living/user, params, spread)
|
||||
var/turf/curloc = get_turf(user)
|
||||
if(!istype(curloc) || !BB)
|
||||
return FALSE
|
||||
var/obj/item/gun/energy/beam_rifle/gun = loc
|
||||
if(!targloc && gun)
|
||||
targloc = get_turf_in_angle(gun.lastangle, curloc, 10)
|
||||
else if(!targloc)
|
||||
return FALSE
|
||||
var/firing_dir
|
||||
if(BB.firer)
|
||||
firing_dir = BB.firer.dir
|
||||
if(!BB.suppressed && firing_effect_type)
|
||||
new firing_effect_type(get_turf(src), firing_dir)
|
||||
BB.preparePixelProjectile(target, user, params, spread)
|
||||
BB.fire(gun? gun.lastangle : null, null)
|
||||
BB = null
|
||||
return TRUE
|
||||
|
||||
/obj/item/ammo_casing/energy/beam_rifle/hitscan
|
||||
projectile_type = /obj/item/projectile/beam/beam_rifle/hitscan
|
||||
select_name = "beam"
|
||||
e_cost = 5000
|
||||
fire_sound = 'sound/weapons/beam_sniper.ogg'
|
||||
|
||||
/obj/item/projectile/beam/beam_rifle
|
||||
name = "particle beam"
|
||||
icon = null
|
||||
hitsound = 'sound/effects/explosion3.ogg'
|
||||
damage = 0 //Handled manually.
|
||||
damage_type = BURN
|
||||
flag = "energy"
|
||||
range = 150
|
||||
jitter = 10
|
||||
var/obj/item/gun/energy/beam_rifle/gun
|
||||
var/structure_pierce_amount = 0 //All set to 0 so the gun can manually set them during firing.
|
||||
var/structure_bleed_coeff = 0
|
||||
var/structure_pierce = 0
|
||||
var/do_pierce = TRUE
|
||||
var/wall_pierce_amount = 0
|
||||
var/wall_pierce = 0
|
||||
var/wall_devastate = 0
|
||||
var/aoe_structure_range = 0
|
||||
var/aoe_structure_damage = 0
|
||||
var/aoe_fire_range = 0
|
||||
var/aoe_fire_chance = 0
|
||||
var/aoe_mob_range = 0
|
||||
var/aoe_mob_damage = 0
|
||||
var/impact_structure_damage = 0
|
||||
var/impact_direct_damage = 0
|
||||
var/turf/cached
|
||||
var/list/pierced = list()
|
||||
|
||||
/obj/item/projectile/beam/beam_rifle/proc/AOE(turf/epicenter)
|
||||
set waitfor = FALSE
|
||||
if(!epicenter)
|
||||
return
|
||||
new /obj/effect/temp_visual/explosion/fast(epicenter)
|
||||
for(var/mob/living/L in range(aoe_mob_range, epicenter)) //handle aoe mob damage
|
||||
L.adjustFireLoss(aoe_mob_damage)
|
||||
to_chat(L, "<span class='userdanger'>\The [src] sears you!</span>")
|
||||
for(var/turf/T in range(aoe_fire_range, epicenter)) //handle aoe fire
|
||||
if(prob(aoe_fire_chance))
|
||||
new /obj/effect/hotspot(T)
|
||||
for(var/obj/O in range(aoe_structure_range, epicenter))
|
||||
if(!isitem(O))
|
||||
if(O.level == 1) //Please don't break underfloor items!
|
||||
continue
|
||||
O.take_damage(aoe_structure_damage * get_damage_coeff(O), BURN, "laser", FALSE)
|
||||
|
||||
/obj/item/projectile/beam/beam_rifle/proc/check_pierce(atom/target)
|
||||
if(!do_pierce)
|
||||
return FALSE
|
||||
if(pierced[target]) //we already pierced them go away
|
||||
return TRUE
|
||||
if(isclosedturf(target))
|
||||
if(wall_pierce++ < wall_pierce_amount)
|
||||
if(prob(wall_devastate))
|
||||
if(iswallturf(target))
|
||||
var/turf/closed/wall/W = target
|
||||
W.dismantle_wall(TRUE, TRUE)
|
||||
else
|
||||
target.ex_act(EXPLODE_HEAVY)
|
||||
return TRUE
|
||||
if(ismovableatom(target))
|
||||
var/atom/movable/AM = target
|
||||
if(AM.density && !AM.CanPass(src, get_turf(target)) && !ismob(AM))
|
||||
if(structure_pierce < structure_pierce_amount)
|
||||
if(isobj(AM))
|
||||
var/obj/O = AM
|
||||
O.take_damage((impact_structure_damage + aoe_structure_damage) * structure_bleed_coeff * get_damage_coeff(AM), BURN, "energy", FALSE)
|
||||
pierced[AM] = TRUE
|
||||
structure_pierce++
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/obj/item/projectile/beam/beam_rifle/proc/get_damage_coeff(atom/target)
|
||||
if(istype(target, /obj/machinery/door))
|
||||
return 0.4
|
||||
if(istype(target, /obj/structure/window))
|
||||
return 0.5
|
||||
if(istype(target, /obj/structure/blob))
|
||||
return 0.65 //CIT CHANGE.
|
||||
return 1
|
||||
|
||||
/obj/item/projectile/beam/beam_rifle/proc/handle_impact(atom/target)
|
||||
if(isobj(target))
|
||||
var/obj/O = target
|
||||
O.take_damage(impact_structure_damage * get_damage_coeff(target), BURN, "laser", FALSE)
|
||||
if(isliving(target))
|
||||
var/mob/living/L = target
|
||||
L.adjustFireLoss(impact_direct_damage)
|
||||
L.emote("scream")
|
||||
|
||||
/obj/item/projectile/beam/beam_rifle/proc/handle_hit(atom/target)
|
||||
set waitfor = FALSE
|
||||
if(!cached && !QDELETED(target))
|
||||
cached = get_turf(target)
|
||||
if(nodamage)
|
||||
return FALSE
|
||||
playsound(cached, 'sound/effects/explosion3.ogg', 100, 1)
|
||||
AOE(cached)
|
||||
if(!QDELETED(target))
|
||||
handle_impact(target)
|
||||
|
||||
/obj/item/projectile/beam/beam_rifle/Bump(atom/target)
|
||||
if(check_pierce(target))
|
||||
permutated += target
|
||||
trajectory_ignore_forcemove = TRUE
|
||||
forceMove(target.loc)
|
||||
trajectory_ignore_forcemove = FALSE
|
||||
return FALSE
|
||||
if(!QDELETED(target))
|
||||
cached = get_turf(target)
|
||||
. = ..()
|
||||
|
||||
/obj/item/projectile/beam/beam_rifle/on_hit(atom/target, blocked = FALSE)
|
||||
if(!QDELETED(target))
|
||||
cached = get_turf(target)
|
||||
handle_hit(target)
|
||||
. = ..()
|
||||
|
||||
/obj/item/projectile/beam/beam_rifle/hitscan
|
||||
icon_state = ""
|
||||
hitscan = TRUE
|
||||
tracer_type = /obj/effect/projectile/tracer/tracer/beam_rifle
|
||||
var/constant_tracer = FALSE
|
||||
|
||||
/obj/item/projectile/beam/beam_rifle/hitscan/generate_hitscan_tracers(cleanup = TRUE, duration = 5, impacting = TRUE, highlander)
|
||||
set waitfor = FALSE
|
||||
if(isnull(highlander))
|
||||
highlander = constant_tracer
|
||||
if(highlander && istype(gun))
|
||||
QDEL_LIST(gun.current_tracers)
|
||||
for(var/datum/point/p in beam_segments)
|
||||
gun.current_tracers += generate_tracer_between_points(p, beam_segments[p], tracer_type, color, 0, hitscan_light_range, hitscan_light_color_override, hitscan_light_intensity)
|
||||
else
|
||||
for(var/datum/point/p in beam_segments)
|
||||
generate_tracer_between_points(p, beam_segments[p], tracer_type, color, duration, hitscan_light_range, hitscan_light_color_override, hitscan_light_intensity)
|
||||
if(cleanup)
|
||||
QDEL_LIST(beam_segments)
|
||||
beam_segments = null
|
||||
QDEL_NULL(beam_index)
|
||||
|
||||
/obj/item/projectile/beam/beam_rifle/hitscan/aiming_beam
|
||||
tracer_type = /obj/effect/projectile/tracer/tracer/aiming
|
||||
name = "aiming beam"
|
||||
hitsound = null
|
||||
hitsound_wall = null
|
||||
nodamage = TRUE
|
||||
damage = 0
|
||||
constant_tracer = TRUE
|
||||
hitscan_light_range = 0
|
||||
hitscan_light_intensity = 0
|
||||
hitscan_light_color_override = "#99ff99"
|
||||
|
||||
/obj/item/projectile/beam/beam_rifle/hitscan/aiming_beam/prehit(atom/target)
|
||||
qdel(src)
|
||||
return FALSE
|
||||
|
||||
/obj/item/projectile/beam/beam_rifle/hitscan/aiming_beam/on_hit()
|
||||
qdel(src)
|
||||
return FALSE
|
||||
@@ -0,0 +1,153 @@
|
||||
/obj/item/gun/syringe
|
||||
name = "syringe gun"
|
||||
desc = "A spring loaded rifle designed to fit syringes, used to incapacitate unruly patients from a distance."
|
||||
icon_state = "syringegun"
|
||||
item_state = "syringegun"
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
throw_speed = 3
|
||||
throw_range = 7
|
||||
force = 4
|
||||
materials = list(MAT_METAL=2000)
|
||||
clumsy_check = 0
|
||||
fire_sound = 'sound/items/syringeproj.ogg'
|
||||
var/list/syringes = list()
|
||||
var/max_syringes = 1
|
||||
|
||||
/obj/item/gun/syringe/Initialize()
|
||||
. = ..()
|
||||
chambered = new /obj/item/ammo_casing/syringegun(src)
|
||||
|
||||
/obj/item/gun/syringe/recharge_newshot()
|
||||
if(!syringes.len)
|
||||
return
|
||||
chambered.newshot()
|
||||
|
||||
/obj/item/gun/syringe/can_shoot()
|
||||
return syringes.len
|
||||
|
||||
/obj/item/gun/syringe/process_chamber()
|
||||
if(chambered && !chambered.BB) //we just fired
|
||||
recharge_newshot()
|
||||
|
||||
/obj/item/gun/syringe/examine(mob/user)
|
||||
..()
|
||||
to_chat(user, "Can hold [max_syringes] syringe\s. Has [syringes.len] syringe\s remaining.")
|
||||
|
||||
/obj/item/gun/syringe/attack_self(mob/living/user)
|
||||
if(!syringes.len)
|
||||
to_chat(user, "<span class='warning'>[src] is empty!</span>")
|
||||
return 0
|
||||
|
||||
var/obj/item/reagent_containers/syringe/S = syringes[syringes.len]
|
||||
|
||||
if(!S)
|
||||
return 0
|
||||
S.forceMove(user.loc)
|
||||
|
||||
syringes.Remove(S)
|
||||
to_chat(user, "<span class='notice'>You unload [S] from \the [src].</span>")
|
||||
|
||||
return 1
|
||||
|
||||
/obj/item/gun/syringe/attackby(obj/item/A, mob/user, params, show_msg = TRUE)
|
||||
if(istype(A, /obj/item/reagent_containers/syringe))
|
||||
if(syringes.len < max_syringes)
|
||||
if(!user.transferItemToLoc(A, src))
|
||||
return FALSE
|
||||
to_chat(user, "<span class='notice'>You load [A] into \the [src].</span>")
|
||||
syringes += A
|
||||
recharge_newshot()
|
||||
return TRUE
|
||||
else
|
||||
to_chat(user, "<span class='warning'>[src] cannot hold more syringes!</span>")
|
||||
return FALSE
|
||||
|
||||
/obj/item/gun/syringe/rapidsyringe
|
||||
name = "rapid syringe gun"
|
||||
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/gun/syringe/syndicate
|
||||
name = "dart pistol"
|
||||
desc = "A small spring-loaded sidearm that functions identically to a syringe gun."
|
||||
icon_state = "syringe_pistol"
|
||||
item_state = "gun" //Smaller inhand
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
force = 2 //Also very weak because it's smaller
|
||||
suppressed = TRUE //Softer fire sound
|
||||
can_unsuppress = FALSE //Permanently silenced
|
||||
|
||||
/obj/item/gun/syringe/dna
|
||||
name = "modified syringe gun"
|
||||
desc = "A syringe gun that has been modified to fit DNA injectors instead of normal syringes."
|
||||
|
||||
/obj/item/gun/syringe/dna/Initialize()
|
||||
. = ..()
|
||||
chambered = new /obj/item/ammo_casing/dnainjector(src)
|
||||
|
||||
/obj/item/gun/syringe/dna/attackby(obj/item/A, mob/user, params, show_msg = TRUE)
|
||||
if(istype(A, /obj/item/dnainjector))
|
||||
var/obj/item/dnainjector/D = A
|
||||
if(D.used)
|
||||
to_chat(user, "<span class='warning'>This injector is used up!</span>")
|
||||
return
|
||||
if(syringes.len < max_syringes)
|
||||
if(!user.transferItemToLoc(D, src))
|
||||
return FALSE
|
||||
to_chat(user, "<span class='notice'>You load \the [D] into \the [src].</span>")
|
||||
syringes += D
|
||||
recharge_newshot()
|
||||
return TRUE
|
||||
else
|
||||
to_chat(user, "<span class='warning'>[src] cannot hold more syringes!</span>")
|
||||
return FALSE
|
||||
|
||||
/obj/item/gun/syringe/dart
|
||||
name = "dart gun"
|
||||
desc = "A compressed air gun, designed to fit medicinal darts for application of medicine for those patients just out of reach."
|
||||
icon_state = "dartgun"
|
||||
item_state = "dartgun"
|
||||
materials = list(MAT_METAL=2000, MAT_GLASS=500)
|
||||
suppressed = TRUE //Softer fire sound
|
||||
can_unsuppress = FALSE
|
||||
|
||||
/obj/item/gun/syringe/dart/Initialize()
|
||||
..()
|
||||
chambered = new /obj/item/ammo_casing/syringegun/dart(src)
|
||||
|
||||
/obj/item/gun/syringe/dart/attackby(obj/item/A, mob/user, params, show_msg = TRUE)
|
||||
if(istype(A, /obj/item/reagent_containers/syringe/dart))
|
||||
..()
|
||||
else
|
||||
to_chat(user, "<span class='notice'>You can't put the [A] into \the [src]!</span>")
|
||||
return FALSE
|
||||
|
||||
/obj/item/gun/syringe/dart/rapiddart
|
||||
name = "Repeating dart gun"
|
||||
icon_state = "rapiddartgun"
|
||||
item_state = "rapiddartgun"
|
||||
|
||||
/obj/item/gun/syringe/dart/rapiddart/CheckParts(list/parts_list)
|
||||
var/obj/item/reagent_containers/glass/beaker/B = locate(/obj/item/reagent_containers/glass/beaker) in parts_list
|
||||
|
||||
if(istype(B, /obj/item/reagent_containers/glass/beaker/large))
|
||||
desc = "[initial(desc)] A modification of the dart gun's pressure chamber has been perfomed using a [B], extending it's holding size to [max_syringes]."
|
||||
max_syringes = 2
|
||||
return
|
||||
else if(istype(B, /obj/item/reagent_containers/glass/beaker/plastic))
|
||||
desc = "[initial(desc)] A modification of the dart gun's pressure chamber has been perfomed using a [B], extending it's holding size to [max_syringes]."
|
||||
max_syringes = 3
|
||||
return
|
||||
else if(istype(B, /obj/item/reagent_containers/glass/beaker/meta))
|
||||
desc = "[initial(desc)] A modification of the dart gun's pressure chamber has been perfomed using a [B], extending it's holding size to [max_syringes]."
|
||||
max_syringes = 4
|
||||
return
|
||||
else if(istype(B, /obj/item/reagent_containers/glass/beaker/bluespace))
|
||||
desc = "[initial(desc)] A modification of the dart gun's pressure chamber has been perfomed using a [B], extending it's holding size to [max_syringes]."
|
||||
max_syringes = 6
|
||||
return
|
||||
else
|
||||
max_syringes = 1
|
||||
desc = "[initial(desc)] It has a [B] strapped to it, but it doesn't seem to be doing anything."
|
||||
..()
|
||||
@@ -0,0 +1,648 @@
|
||||
|
||||
#define MOVES_HITSCAN -1 //Not actually hitscan but close as we get without actual hitscan.
|
||||
#define MUZZLE_EFFECT_PIXEL_INCREMENT 17 //How many pixels to move the muzzle flash up so your character doesn't look like they're shitting out lasers.
|
||||
|
||||
/obj/item/projectile
|
||||
name = "projectile"
|
||||
icon = 'icons/obj/projectiles.dmi'
|
||||
icon_state = "bullet"
|
||||
density = FALSE
|
||||
anchored = TRUE
|
||||
item_flags = ABSTRACT
|
||||
pass_flags = PASSTABLE
|
||||
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
hitsound = 'sound/weapons/pierce.ogg'
|
||||
var/hitsound_wall = ""
|
||||
|
||||
resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
|
||||
var/def_zone = "" //Aiming at
|
||||
var/atom/movable/firer = null//Who shot it
|
||||
var/atom/fired_from = null // the atom that the projectile was fired from (gun, turret)
|
||||
var/suppressed = FALSE //Attack message
|
||||
var/candink = FALSE //Can this projectile play the dink sound when hitting the head?
|
||||
var/yo = null
|
||||
var/xo = null
|
||||
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/p_x = 16
|
||||
var/p_y = 16 // the pixel location of the tile that the player clicked. Default is the center
|
||||
|
||||
//Fired processing vars
|
||||
var/fired = FALSE //Have we been fired yet
|
||||
var/paused = FALSE //for suspending the projectile midair
|
||||
var/last_projectile_move = 0
|
||||
var/last_process = 0
|
||||
var/time_offset = 0
|
||||
var/datum/point/vector/trajectory
|
||||
var/trajectory_ignore_forcemove = FALSE //instructs forceMove to NOT reset our trajectory to the new location!
|
||||
|
||||
var/speed = 0.8 //Amount of deciseconds it takes for projectile to travel
|
||||
var/Angle = 0
|
||||
var/original_angle = 0 //Angle at firing
|
||||
var/nondirectional_sprite = FALSE //Set TRUE to prevent projectiles from having their sprites rotated based on firing angle
|
||||
var/spread = 0 //amount (in degrees) of projectile spread
|
||||
animate_movement = 0 //Use SLIDE_STEPS in conjunction with legacy
|
||||
var/ricochets = 0
|
||||
var/ricochets_max = 2
|
||||
var/ricochet_chance = 30
|
||||
|
||||
//Hitscan
|
||||
var/hitscan = FALSE //Whether this is hitscan. If it is, speed is basically ignored.
|
||||
var/list/beam_segments //assoc list of datum/point or datum/point/vector, start = end. Used for hitscan effect generation.
|
||||
var/datum/point/beam_index
|
||||
var/turf/hitscan_last //last turf touched during hitscanning.
|
||||
var/tracer_type
|
||||
var/muzzle_type
|
||||
var/impact_type
|
||||
|
||||
//Fancy hitscan lighting effects!
|
||||
var/hitscan_light_intensity = 1.5
|
||||
var/hitscan_light_range = 0.75
|
||||
var/hitscan_light_color_override
|
||||
var/muzzle_flash_intensity = 3
|
||||
var/muzzle_flash_range = 1.5
|
||||
var/muzzle_flash_color_override
|
||||
var/impact_light_intensity = 3
|
||||
var/impact_light_range = 2
|
||||
var/impact_light_color_override
|
||||
|
||||
//Homing
|
||||
var/homing = FALSE
|
||||
var/atom/homing_target
|
||||
var/homing_turn_speed = 10 //Angle per tick.
|
||||
var/homing_inaccuracy_min = 0 //in pixels for these. offsets are set once when setting target.
|
||||
var/homing_inaccuracy_max = 0
|
||||
var/homing_offset_x = 0
|
||||
var/homing_offset_y = 0
|
||||
|
||||
var/ignore_source_check = FALSE
|
||||
|
||||
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
|
||||
var/projectile_type = /obj/item/projectile
|
||||
var/range = 50 //This will de-increment every step. When 0, it will deletze the projectile.
|
||||
var/decayedRange
|
||||
var/is_reflectable = FALSE // Can it be reflected or not?
|
||||
//Effects
|
||||
var/stun = 0
|
||||
var/knockdown = 0
|
||||
var/knockdown_stamoverride
|
||||
var/knockdown_stam_max
|
||||
var/unconscious = 0
|
||||
var/irradiate = 0
|
||||
var/stutter = 0
|
||||
var/slur = 0
|
||||
var/eyeblur = 0
|
||||
var/drowsy = 0
|
||||
var/stamina = 0
|
||||
var/jitter = 0
|
||||
var/forcedodge = 0 //to pass through everything
|
||||
var/dismemberment = 0 //The higher the number, the greater the bonus to dismembering. 0 will not dismember at all.
|
||||
var/impact_effect_type //what type of impact effect to show when hitting something
|
||||
var/log_override = FALSE //is this type spammed enough to not log? (KAs)
|
||||
|
||||
/obj/item/projectile/Initialize()
|
||||
. = ..()
|
||||
permutated = list()
|
||||
decayedRange = range
|
||||
|
||||
/obj/item/projectile/proc/Range()
|
||||
range--
|
||||
if(range <= 0 && loc)
|
||||
on_range()
|
||||
|
||||
/obj/item/projectile/proc/on_range() //if we want there to be effects when they reach the end of their range
|
||||
qdel(src)
|
||||
|
||||
//to get the correct limb (if any) for the projectile hit message
|
||||
/mob/living/proc/check_limb_hit(hit_zone)
|
||||
if(has_limbs)
|
||||
return hit_zone
|
||||
|
||||
/mob/living/carbon/check_limb_hit(hit_zone)
|
||||
if(get_bodypart(hit_zone))
|
||||
return hit_zone
|
||||
else //when a limb is missing the damage is actually passed to the chest
|
||||
return BODY_ZONE_CHEST
|
||||
|
||||
/obj/item/projectile/proc/prehit(atom/target)
|
||||
return TRUE
|
||||
|
||||
/obj/item/projectile/proc/on_hit(atom/target, blocked = FALSE)
|
||||
if(fired_from)
|
||||
SEND_SIGNAL(fired_from, COMSIG_PROJECTILE_ON_HIT, firer, target, Angle)
|
||||
var/turf/target_loca = get_turf(target)
|
||||
|
||||
var/hitx
|
||||
var/hity
|
||||
if(target == original)
|
||||
hitx = target.pixel_x + p_x - 16
|
||||
hity = target.pixel_y + p_y - 16
|
||||
else
|
||||
hitx = target.pixel_x + rand(-8, 8)
|
||||
hity = target.pixel_y + rand(-8, 8)
|
||||
|
||||
if(!nodamage && (damage_type == BRUTE || damage_type == BURN) && iswallturf(target_loca) && prob(75))
|
||||
var/turf/closed/wall/W = target_loca
|
||||
if(impact_effect_type && !hitscan)
|
||||
new impact_effect_type(target_loca, hitx, hity)
|
||||
|
||||
W.add_dent(WALL_DENT_SHOT, hitx, hity)
|
||||
|
||||
return 0
|
||||
|
||||
if(!isliving(target))
|
||||
if(impact_effect_type && !hitscan)
|
||||
new impact_effect_type(target_loca, hitx, hity)
|
||||
return 0
|
||||
|
||||
var/mob/living/L = target
|
||||
|
||||
if(blocked != 100) // not completely blocked
|
||||
if(damage && L.blood_volume && damage_type == BRUTE)
|
||||
var/splatter_dir = dir
|
||||
if(starting)
|
||||
splatter_dir = get_dir(starting, target_loca)
|
||||
var/obj/item/bodypart/B = L.get_bodypart(def_zone)
|
||||
if(B && B.status == BODYPART_ROBOTIC) // So if you hit a robotic, it sparks instead of bloodspatters
|
||||
do_sparks(2, FALSE, target.loc)
|
||||
if(prob(25))
|
||||
new /obj/effect/decal/cleanable/oil(target_loca)
|
||||
else if(isalien(L))
|
||||
new /obj/effect/temp_visual/dir_setting/bloodsplatter/xenosplatter(target_loca, splatter_dir)
|
||||
else
|
||||
if(ishuman(target))
|
||||
var/mob/living/carbon/human/H = target
|
||||
new /obj/effect/temp_visual/dir_setting/bloodsplatter(target_loca, splatter_dir, bloodtype_to_color(H.dna.blood_type))
|
||||
else
|
||||
new /obj/effect/temp_visual/dir_setting/bloodsplatter(target_loca, splatter_dir, bloodtype_to_color())
|
||||
|
||||
if(iscarbon(L))
|
||||
var/mob/living/carbon/C = L
|
||||
C.bleed(damage)
|
||||
else
|
||||
L.add_splatter_floor(target_loca)
|
||||
|
||||
else if(impact_effect_type && !hitscan)
|
||||
new impact_effect_type(target_loca, hitx, hity)
|
||||
|
||||
var/organ_hit_text = ""
|
||||
var/limb_hit = L.check_limb_hit(def_zone)//to get the correct message info.
|
||||
if(limb_hit)
|
||||
organ_hit_text = " in \the [parse_zone(limb_hit)]"
|
||||
if(suppressed)
|
||||
playsound(loc, hitsound, 5, 1, -1)
|
||||
to_chat(L, "<span class='userdanger'>You're shot by \a [src][organ_hit_text]!</span>")
|
||||
else
|
||||
if(hitsound)
|
||||
var/volume = vol_by_damage()
|
||||
playsound(loc, hitsound, volume, 1, -1)
|
||||
L.visible_message("<span class='danger'>[L] is hit by \a [src][organ_hit_text]!</span>", \
|
||||
"<span class='userdanger'>[L] is hit by \a [src][organ_hit_text]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
if(candink && def_zone == BODY_ZONE_HEAD)
|
||||
playsound(src, 'sound/weapons/dink.ogg', 30, 1)
|
||||
L.on_hit(src)
|
||||
|
||||
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) + ") "
|
||||
|
||||
if(ismob(firer))
|
||||
log_combat(firer, L, "shot", src, reagent_note)
|
||||
else
|
||||
L.log_message("has been shot by [firer] with [src]", LOG_ATTACK, color="orange")
|
||||
|
||||
return L.apply_effects(stun, knockdown, unconscious, irradiate, slur, stutter, eyeblur, drowsy, blocked, stamina, jitter, knockdown_stamoverride, knockdown_stam_max)
|
||||
|
||||
/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/proc/on_ricochet(atom/A)
|
||||
return
|
||||
|
||||
/obj/item/projectile/proc/store_hitscan_collision(datum/point/pcache)
|
||||
beam_segments[beam_index] = pcache
|
||||
beam_index = pcache
|
||||
beam_segments[beam_index] = null
|
||||
|
||||
/obj/item/projectile/Bump(atom/A)
|
||||
if(trajectory && check_ricochet(A) && check_ricochet_flag(A) && ricochets < ricochets_max)
|
||||
var/datum/point/pcache = trajectory.copy_to()
|
||||
ricochets++
|
||||
if(A.handle_ricochet(src))
|
||||
on_ricochet(A)
|
||||
ignore_source_check = TRUE
|
||||
range = initial(range)
|
||||
if(hitscan)
|
||||
store_hitscan_collision(pcache)
|
||||
return TRUE
|
||||
if(firer && !ignore_source_check)
|
||||
if(A == firer || (A == firer.loc && ismecha(A))) //cannot shoot yourself or your mech
|
||||
trajectory_ignore_forcemove = TRUE
|
||||
forceMove(get_turf(A))
|
||||
trajectory_ignore_forcemove = FALSE
|
||||
return FALSE
|
||||
|
||||
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(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)
|
||||
|
||||
if(!prehit(A))
|
||||
if(forcedodge)
|
||||
trajectory_ignore_forcemove = TRUE
|
||||
forceMove(target_turf)
|
||||
trajectory_ignore_forcemove = FALSE
|
||||
return FALSE
|
||||
|
||||
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!
|
||||
trajectory_ignore_forcemove = TRUE
|
||||
forceMove(target_turf)
|
||||
trajectory_ignore_forcemove = FALSE
|
||||
if(A)
|
||||
permutated.Add(A)
|
||||
return FALSE
|
||||
else
|
||||
var/atom/alt = select_target(A)
|
||||
if(alt)
|
||||
if(!prehit(alt))
|
||||
return FALSE
|
||||
alt.bullet_act(src, def_zone)
|
||||
qdel(src)
|
||||
return TRUE
|
||||
|
||||
/obj/item/projectile/proc/select_target(atom/A) //Selects another target from a wall if we hit a wall.
|
||||
if(!A || !A.density || (A.flags_1 & ON_BORDER_1) || ismob(A) || A == original) //if we hit a dense non-border obj or dense turf then we also hit one of the mobs or machines/structures on that tile.
|
||||
return
|
||||
var/turf/T = get_turf(A)
|
||||
if(original in T)
|
||||
return original
|
||||
var/list/mob/possible_mobs = typecache_filter_list(T, GLOB.typecache_mob) - A
|
||||
var/list/mob/mobs = list()
|
||||
for(var/i in possible_mobs)
|
||||
var/mob/M = i
|
||||
if(M.lying)
|
||||
continue
|
||||
mobs += M
|
||||
var/mob/M = safepick(mobs)
|
||||
if(M)
|
||||
return M.lowest_buckled_mob()
|
||||
var/obj/O = safepick(typecache_filter_list(T, GLOB.typecache_machine_or_structure) - A)
|
||||
if(O)
|
||||
return O
|
||||
|
||||
/obj/item/projectile/proc/check_ricochet()
|
||||
if(prob(ricochet_chance))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/obj/item/projectile/proc/check_ricochet_flag(atom/A)
|
||||
if(A.flags_1 & CHECK_RICOCHET_1)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/obj/item/projectile/proc/return_predicted_turf_after_moves(moves, forced_angle) //I say predicted because there's no telling that the projectile won't change direction/location in flight.
|
||||
if(!trajectory && isnull(forced_angle) && isnull(Angle))
|
||||
return FALSE
|
||||
var/datum/point/vector/current = trajectory
|
||||
if(!current)
|
||||
var/turf/T = get_turf(src)
|
||||
current = new(T.x, T.y, T.z, pixel_x, pixel_y, isnull(forced_angle)? Angle : forced_angle, SSprojectiles.global_pixel_speed)
|
||||
var/datum/point/vector/v = current.return_vector_after_increments(moves * SSprojectiles.global_iterations_per_move)
|
||||
return v.return_turf()
|
||||
|
||||
/obj/item/projectile/proc/return_pathing_turfs_in_moves(moves, forced_angle)
|
||||
var/turf/current = get_turf(src)
|
||||
var/turf/ending = return_predicted_turf_after_moves(moves, forced_angle)
|
||||
return getline(current, ending)
|
||||
|
||||
/obj/item/projectile/Process_Spacemove(var/movement_dir = 0)
|
||||
return TRUE //Bullets don't drift in space
|
||||
|
||||
/obj/item/projectile/process()
|
||||
last_process = world.time
|
||||
if(!loc || !fired || !trajectory)
|
||||
fired = FALSE
|
||||
return PROCESS_KILL
|
||||
if(paused || !isturf(loc))
|
||||
last_projectile_move += world.time - last_process //Compensates for pausing, so it doesn't become a hitscan projectile when unpaused from charged up ticks.
|
||||
return
|
||||
var/elapsed_time_deciseconds = (world.time - last_projectile_move) + time_offset
|
||||
time_offset = 0
|
||||
var/required_moves = speed > 0? FLOOR(elapsed_time_deciseconds / speed, 1) : MOVES_HITSCAN //Would be better if a 0 speed made hitscan but everyone hates those so I can't make it a universal system :<
|
||||
if(required_moves == MOVES_HITSCAN)
|
||||
required_moves = SSprojectiles.global_max_tick_moves
|
||||
else
|
||||
if(required_moves > SSprojectiles.global_max_tick_moves)
|
||||
var/overrun = required_moves - SSprojectiles.global_max_tick_moves
|
||||
required_moves = SSprojectiles.global_max_tick_moves
|
||||
time_offset += overrun * speed
|
||||
time_offset += MODULUS(elapsed_time_deciseconds, speed)
|
||||
|
||||
for(var/i in 1 to required_moves)
|
||||
pixel_move(1, FALSE)
|
||||
|
||||
/obj/item/projectile/proc/fire(angle, atom/direct_target)
|
||||
if(fired_from)
|
||||
SEND_SIGNAL(fired_from, COMSIG_PROJECTILE_BEFORE_FIRE, src, original)
|
||||
//If no angle needs to resolve it from xo/yo!
|
||||
if(!log_override && firer && original)
|
||||
log_combat(firer, original, "fired at", src, "from [get_area_name(src, TRUE)]")
|
||||
if(direct_target)
|
||||
if(prehit(direct_target))
|
||||
direct_target.bullet_act(src, def_zone)
|
||||
qdel(src)
|
||||
return
|
||||
if(isnum(angle))
|
||||
setAngle(angle)
|
||||
if(spread)
|
||||
setAngle(Angle + ((rand() - 0.5) * spread))
|
||||
var/turf/starting = get_turf(src)
|
||||
if(isnull(Angle)) //Try to resolve through offsets if there's no angle set.
|
||||
if(isnull(xo) || isnull(yo))
|
||||
stack_trace("WARNING: Projectile [type] deleted due to being unable to resolve a target after angle was null!")
|
||||
qdel(src)
|
||||
return
|
||||
var/turf/target = locate(CLAMP(starting + xo, 1, world.maxx), CLAMP(starting + yo, 1, world.maxy), starting.z)
|
||||
setAngle(Get_Angle(src, target))
|
||||
original_angle = Angle
|
||||
if(!nondirectional_sprite)
|
||||
var/matrix/M = new
|
||||
M.Turn(Angle)
|
||||
transform = M
|
||||
trajectory_ignore_forcemove = TRUE
|
||||
forceMove(starting)
|
||||
trajectory_ignore_forcemove = FALSE
|
||||
trajectory = new(starting.x, starting.y, starting.z, pixel_x, pixel_y, Angle, SSprojectiles.global_pixel_speed)
|
||||
last_projectile_move = world.time
|
||||
fired = TRUE
|
||||
if(hitscan)
|
||||
process_hitscan()
|
||||
if(!(datum_flags & DF_ISPROCESSING))
|
||||
START_PROCESSING(SSprojectiles, src)
|
||||
pixel_move(1, FALSE) //move it now!
|
||||
|
||||
/obj/item/projectile/proc/setAngle(new_angle) //wrapper for overrides.
|
||||
Angle = new_angle
|
||||
if(!nondirectional_sprite)
|
||||
var/matrix/M = new
|
||||
M.Turn(Angle)
|
||||
transform = M
|
||||
if(trajectory)
|
||||
trajectory.set_angle(new_angle)
|
||||
return TRUE
|
||||
|
||||
/obj/item/projectile/forceMove(atom/target)
|
||||
if(!isloc(target) || !isloc(loc) || !z)
|
||||
return ..()
|
||||
var/zc = target.z != z
|
||||
var/old = loc
|
||||
if(zc)
|
||||
before_z_change(old, target)
|
||||
. = ..()
|
||||
if(trajectory && !trajectory_ignore_forcemove && isturf(target))
|
||||
if(hitscan)
|
||||
finalize_hitscan_and_generate_tracers(FALSE)
|
||||
trajectory.initialize_location(target.x, target.y, target.z, 0, 0)
|
||||
if(hitscan)
|
||||
record_hitscan_start(RETURN_PRECISE_POINT(src))
|
||||
if(zc)
|
||||
after_z_change(old, target)
|
||||
|
||||
/obj/item/projectile/proc/after_z_change(atom/olcloc, atom/newloc)
|
||||
|
||||
/obj/item/projectile/proc/before_z_change(atom/oldloc, atom/newloc)
|
||||
|
||||
/obj/item/projectile/vv_edit_var(var_name, var_value)
|
||||
switch(var_name)
|
||||
if(NAMEOF(src, Angle))
|
||||
setAngle(var_value)
|
||||
return TRUE
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/item/projectile/proc/set_pixel_speed(new_speed)
|
||||
if(trajectory)
|
||||
trajectory.set_speed(new_speed)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/obj/item/projectile/proc/record_hitscan_start(datum/point/pcache)
|
||||
if(pcache)
|
||||
beam_segments = list()
|
||||
beam_index = pcache
|
||||
beam_segments[beam_index] = null //record start.
|
||||
|
||||
/obj/item/projectile/proc/process_hitscan()
|
||||
var/safety = range * 3
|
||||
record_hitscan_start(RETURN_POINT_VECTOR_INCREMENT(src, Angle, MUZZLE_EFFECT_PIXEL_INCREMENT, 1))
|
||||
while(loc && !QDELETED(src))
|
||||
if(paused)
|
||||
stoplag(1)
|
||||
continue
|
||||
if(safety-- <= 0)
|
||||
if(loc)
|
||||
Bump(loc)
|
||||
if(!QDELETED(src))
|
||||
qdel(src)
|
||||
return //Kill!
|
||||
pixel_move(1, TRUE)
|
||||
|
||||
/obj/item/projectile/proc/pixel_move(trajectory_multiplier, hitscanning = FALSE)
|
||||
if(!loc || !trajectory)
|
||||
return
|
||||
last_projectile_move = world.time
|
||||
if(!nondirectional_sprite && !hitscanning)
|
||||
var/matrix/M = new
|
||||
M.Turn(Angle)
|
||||
transform = M
|
||||
if(homing)
|
||||
process_homing()
|
||||
var/forcemoved = FALSE
|
||||
for(var/i in 1 to SSprojectiles.global_iterations_per_move)
|
||||
if(QDELETED(src))
|
||||
return
|
||||
trajectory.increment(trajectory_multiplier)
|
||||
var/turf/T = trajectory.return_turf()
|
||||
if(!istype(T))
|
||||
qdel(src)
|
||||
return
|
||||
if(T.z != loc.z)
|
||||
var/old = loc
|
||||
before_z_change(loc, T)
|
||||
trajectory_ignore_forcemove = TRUE
|
||||
forceMove(T)
|
||||
trajectory_ignore_forcemove = FALSE
|
||||
after_z_change(old, loc)
|
||||
if(!hitscanning)
|
||||
pixel_x = trajectory.return_px()
|
||||
pixel_y = trajectory.return_py()
|
||||
forcemoved = TRUE
|
||||
hitscan_last = loc
|
||||
else if(T != loc)
|
||||
step_towards(src, T)
|
||||
hitscan_last = loc
|
||||
if(can_hit_target(original, permutated))
|
||||
Bump(original)
|
||||
if(!hitscanning && !forcemoved)
|
||||
pixel_x = trajectory.return_px() - trajectory.mpx * trajectory_multiplier * SSprojectiles.global_iterations_per_move
|
||||
pixel_y = trajectory.return_py() - trajectory.mpy * trajectory_multiplier * SSprojectiles.global_iterations_per_move
|
||||
animate(src, pixel_x = trajectory.return_px(), pixel_y = trajectory.return_py(), time = 1, flags = ANIMATION_END_NOW)
|
||||
Range()
|
||||
|
||||
/obj/item/projectile/proc/process_homing() //may need speeding up in the future performance wise.
|
||||
if(!homing_target)
|
||||
return FALSE
|
||||
var/datum/point/PT = RETURN_PRECISE_POINT(homing_target)
|
||||
PT.x += CLAMP(homing_offset_x, 1, world.maxx)
|
||||
PT.y += CLAMP(homing_offset_y, 1, world.maxy)
|
||||
var/angle = closer_angle_difference(Angle, angle_between_points(RETURN_PRECISE_POINT(src), PT))
|
||||
setAngle(Angle + CLAMP(angle, -homing_turn_speed, homing_turn_speed))
|
||||
|
||||
/obj/item/projectile/proc/set_homing_target(atom/A)
|
||||
if(!A || (!isturf(A) && !isturf(A.loc)))
|
||||
return FALSE
|
||||
homing = TRUE
|
||||
homing_target = A
|
||||
homing_offset_x = rand(homing_inaccuracy_min, homing_inaccuracy_max)
|
||||
homing_offset_y = rand(homing_inaccuracy_min, homing_inaccuracy_max)
|
||||
if(prob(50))
|
||||
homing_offset_x = -homing_offset_x
|
||||
if(prob(50))
|
||||
homing_offset_y = -homing_offset_y
|
||||
|
||||
//Returns true if the target atom is on our current turf and above the right layer
|
||||
/obj/item/projectile/proc/can_hit_target(atom/target, var/list/passthrough)
|
||||
return (target && ((target.layer >= PROJECTILE_HIT_THRESHHOLD_LAYER) || ismob(target)) && (loc == get_turf(target)) && (!(target in passthrough)))
|
||||
|
||||
//Spread is FORCED!
|
||||
/obj/item/projectile/proc/preparePixelProjectile(atom/target, atom/source, params, spread = 0)
|
||||
var/turf/curloc = get_turf(source)
|
||||
var/turf/targloc = get_turf(target)
|
||||
trajectory_ignore_forcemove = TRUE
|
||||
forceMove(get_turf(source))
|
||||
trajectory_ignore_forcemove = FALSE
|
||||
starting = get_turf(source)
|
||||
original = target
|
||||
if(targloc || !params)
|
||||
yo = targloc.y - curloc.y
|
||||
xo = targloc.x - curloc.x
|
||||
setAngle(Get_Angle(src, targloc) + spread)
|
||||
|
||||
if(isliving(source) && params)
|
||||
var/list/calculated = calculate_projectile_angle_and_pixel_offsets(source, params)
|
||||
p_x = calculated[2]
|
||||
p_y = calculated[3]
|
||||
|
||||
setAngle(calculated[1] + spread)
|
||||
else if(targloc)
|
||||
yo = targloc.y - curloc.y
|
||||
xo = targloc.x - curloc.x
|
||||
setAngle(Get_Angle(src, targloc) + spread)
|
||||
else
|
||||
stack_trace("WARNING: Projectile [type] fired without either mouse parameters, or a target atom to aim at!")
|
||||
qdel(src)
|
||||
|
||||
/proc/calculate_projectile_angle_and_pixel_offsets(mob/user, params)
|
||||
var/list/mouse_control = params2list(params)
|
||||
var/p_x = 0
|
||||
var/p_y = 0
|
||||
var/angle = 0
|
||||
if(mouse_control["icon-x"])
|
||||
p_x = text2num(mouse_control["icon-x"])
|
||||
if(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"], ",")
|
||||
|
||||
//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/list/screenview = getviewsize(user.client.view)
|
||||
var/screenviewX = screenview[1] * world.icon_size
|
||||
var/screenviewY = screenview[2] * world.icon_size
|
||||
|
||||
var/ox = round(screenviewX/2) - user.client.pixel_x //"origin" x
|
||||
var/oy = round(screenviewY/2) - user.client.pixel_y //"origin" y
|
||||
angle = ATAN2(y - oy, x - ox)
|
||||
return list(angle, p_x, p_y)
|
||||
|
||||
/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 || AM == original) && !(src.pass_flags & PASSMOB))
|
||||
Bump(AM)
|
||||
|
||||
/obj/item/projectile/Destroy()
|
||||
if(hitscan)
|
||||
finalize_hitscan_and_generate_tracers()
|
||||
STOP_PROCESSING(SSprojectiles, src)
|
||||
cleanup_beam_segments()
|
||||
qdel(trajectory)
|
||||
return ..()
|
||||
|
||||
/obj/item/projectile/proc/cleanup_beam_segments()
|
||||
QDEL_LIST_ASSOC(beam_segments)
|
||||
beam_segments = list()
|
||||
qdel(beam_index)
|
||||
|
||||
/obj/item/projectile/proc/finalize_hitscan_and_generate_tracers(impacting = TRUE)
|
||||
if(trajectory && beam_index)
|
||||
var/datum/point/pcache = trajectory.copy_to()
|
||||
beam_segments[beam_index] = pcache
|
||||
generate_hitscan_tracers(null, null, impacting)
|
||||
|
||||
/obj/item/projectile/proc/generate_hitscan_tracers(cleanup = TRUE, duration = 3, impacting = TRUE)
|
||||
if(!length(beam_segments))
|
||||
return
|
||||
if(tracer_type)
|
||||
var/tempref = REF(src)
|
||||
for(var/datum/point/p in beam_segments)
|
||||
generate_tracer_between_points(p, beam_segments[p], tracer_type, color, duration, hitscan_light_range, hitscan_light_color_override, hitscan_light_intensity, tempref)
|
||||
if(muzzle_type && duration > 0)
|
||||
var/datum/point/p = beam_segments[1]
|
||||
var/atom/movable/thing = new muzzle_type
|
||||
p.move_atom_to_src(thing)
|
||||
var/matrix/M = new
|
||||
M.Turn(original_angle)
|
||||
thing.transform = M
|
||||
thing.color = color
|
||||
thing.set_light(muzzle_flash_range, muzzle_flash_intensity, muzzle_flash_color_override? muzzle_flash_color_override : color)
|
||||
QDEL_IN(thing, duration)
|
||||
if(impacting && impact_type && duration > 0)
|
||||
var/datum/point/p = beam_segments[beam_segments[beam_segments.len]]
|
||||
var/atom/movable/thing = new impact_type
|
||||
p.move_atom_to_src(thing)
|
||||
var/matrix/M = new
|
||||
M.Turn(Angle)
|
||||
thing.transform = M
|
||||
thing.color = color
|
||||
thing.set_light(impact_light_range, impact_light_intensity, impact_light_color_override? impact_light_color_override : color)
|
||||
QDEL_IN(thing, duration)
|
||||
if(cleanup)
|
||||
cleanup_beam_segments()
|
||||
|
||||
/obj/item/projectile/experience_pressure_difference()
|
||||
return
|
||||
@@ -0,0 +1,192 @@
|
||||
/obj/item/projectile/beam
|
||||
name = "laser"
|
||||
icon_state = "laser"
|
||||
pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE
|
||||
damage = 20
|
||||
light_range = 2
|
||||
damage_type = BURN
|
||||
hitsound = 'sound/weapons/sear.ogg'
|
||||
hitsound_wall = 'sound/weapons/effects/searwall.ogg'
|
||||
flag = "laser"
|
||||
eyeblur = 2
|
||||
impact_effect_type = /obj/effect/temp_visual/impact_effect/red_laser
|
||||
light_color = LIGHT_COLOR_RED
|
||||
ricochets_max = 50 //Honk!
|
||||
ricochet_chance = 80
|
||||
is_reflectable = TRUE
|
||||
|
||||
/obj/item/projectile/beam/laser
|
||||
tracer_type = /obj/effect/projectile/tracer/laser
|
||||
muzzle_type = /obj/effect/projectile/muzzle/laser
|
||||
impact_type = /obj/effect/projectile/impact/laser
|
||||
|
||||
/obj/item/projectile/beam/laser/heavylaser
|
||||
name = "heavy laser"
|
||||
icon_state = "heavylaser"
|
||||
damage = 40
|
||||
tracer_type = /obj/effect/projectile/tracer/heavy_laser
|
||||
muzzle_type = /obj/effect/projectile/muzzle/heavy_laser
|
||||
impact_type = /obj/effect/projectile/impact/heavy_laser
|
||||
|
||||
/obj/item/projectile/beam/laser/on_hit(atom/target, blocked = FALSE)
|
||||
. = ..()
|
||||
if(iscarbon(target))
|
||||
var/mob/living/carbon/M = target
|
||||
M.IgniteMob()
|
||||
else if(isturf(target))
|
||||
impact_effect_type = /obj/effect/temp_visual/impact_effect/red_laser/wall
|
||||
|
||||
/obj/item/projectile/beam/weak
|
||||
damage = 15
|
||||
|
||||
/obj/item/projectile/beam/weak/penetrator
|
||||
armour_penetration = 50
|
||||
|
||||
/obj/item/projectile/beam/practice
|
||||
name = "practice laser"
|
||||
damage = 0
|
||||
nodamage = 1
|
||||
|
||||
/obj/item/projectile/beam/scatter
|
||||
name = "laser pellet"
|
||||
icon_state = "scatterlaser"
|
||||
damage = 5
|
||||
|
||||
/obj/item/projectile/beam/xray
|
||||
name = "\improper X-ray beam"
|
||||
icon_state = "xray"
|
||||
damage = 15
|
||||
irradiate = 300
|
||||
range = 15
|
||||
pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE | PASSCLOSEDTURF
|
||||
|
||||
impact_effect_type = /obj/effect/temp_visual/impact_effect/green_laser
|
||||
light_color = LIGHT_COLOR_GREEN
|
||||
tracer_type = /obj/effect/projectile/tracer/xray
|
||||
muzzle_type = /obj/effect/projectile/muzzle/xray
|
||||
impact_type = /obj/effect/projectile/impact/xray
|
||||
|
||||
/obj/item/projectile/beam/disabler
|
||||
name = "disabler beam"
|
||||
icon_state = "omnilaser"
|
||||
damage = 24 // Citadel change for balance from 36
|
||||
damage_type = STAMINA
|
||||
flag = "energy"
|
||||
hitsound = 'sound/weapons/tap.ogg'
|
||||
eyeblur = 0
|
||||
speed = 0.7
|
||||
impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser
|
||||
light_color = LIGHT_COLOR_BLUE
|
||||
tracer_type = /obj/effect/projectile/tracer/disabler
|
||||
muzzle_type = /obj/effect/projectile/muzzle/disabler
|
||||
impact_type = /obj/effect/projectile/impact/disabler
|
||||
|
||||
/obj/item/projectile/beam/pulse
|
||||
name = "pulse"
|
||||
icon_state = "u_laser"
|
||||
damage = 50
|
||||
impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser
|
||||
light_color = LIGHT_COLOR_BLUE
|
||||
tracer_type = /obj/effect/projectile/tracer/pulse
|
||||
muzzle_type = /obj/effect/projectile/muzzle/pulse
|
||||
impact_type = /obj/effect/projectile/impact/pulse
|
||||
|
||||
/obj/item/projectile/beam/pulse/on_hit(atom/target, blocked = FALSE)
|
||||
. = ..()
|
||||
if (!QDELETED(target) && (isturf(target) || istype(target, /obj/structure/)))
|
||||
target.ex_act(EXPLODE_HEAVY)
|
||||
|
||||
/obj/item/projectile/beam/pulse/shotgun
|
||||
damage = 40
|
||||
|
||||
/obj/item/projectile/beam/pulse/heavy
|
||||
name = "heavy pulse laser"
|
||||
icon_state = "pulse1_bl"
|
||||
var/life = 20
|
||||
|
||||
/obj/item/projectile/beam/pulse/heavy/on_hit(atom/target, blocked = FALSE)
|
||||
life -= 10
|
||||
if(life > 0)
|
||||
. = -1
|
||||
..()
|
||||
|
||||
/obj/item/projectile/beam/emitter
|
||||
name = "emitter beam"
|
||||
icon_state = "emitter"
|
||||
damage = 30
|
||||
impact_effect_type = /obj/effect/temp_visual/impact_effect/green_laser
|
||||
light_color = LIGHT_COLOR_GREEN
|
||||
|
||||
/obj/item/projectile/beam/emitter/singularity_pull()
|
||||
return //don't want the emitters to miss
|
||||
|
||||
/obj/item/projectile/beam/lasertag
|
||||
name = "laser tag beam"
|
||||
icon_state = "omnilaser"
|
||||
hitsound = null
|
||||
damage = 0
|
||||
damage_type = STAMINA
|
||||
flag = "laser"
|
||||
var/suit_types = list(/obj/item/clothing/suit/redtag, /obj/item/clothing/suit/bluetag)
|
||||
impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser
|
||||
light_color = LIGHT_COLOR_BLUE
|
||||
|
||||
/obj/item/projectile/beam/lasertag/on_hit(atom/target, blocked = FALSE)
|
||||
. = ..()
|
||||
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)
|
||||
|
||||
/obj/item/projectile/beam/lasertag/redtag
|
||||
icon_state = "laser"
|
||||
suit_types = list(/obj/item/clothing/suit/bluetag)
|
||||
impact_effect_type = /obj/effect/temp_visual/impact_effect/red_laser
|
||||
light_color = LIGHT_COLOR_RED
|
||||
tracer_type = /obj/effect/projectile/tracer/laser
|
||||
muzzle_type = /obj/effect/projectile/muzzle/laser
|
||||
impact_type = /obj/effect/projectile/impact/laser
|
||||
|
||||
/obj/item/projectile/beam/lasertag/redtag/hitscan
|
||||
hitscan = TRUE
|
||||
|
||||
/obj/item/projectile/beam/lasertag/redtag/hitscan/holy
|
||||
name = "lasrifle beam"
|
||||
damage = 0.1
|
||||
damage_type = BURN
|
||||
|
||||
/obj/item/projectile/beam/lasertag/bluetag
|
||||
icon_state = "bluelaser"
|
||||
suit_types = list(/obj/item/clothing/suit/redtag)
|
||||
tracer_type = /obj/effect/projectile/tracer/laser/blue
|
||||
muzzle_type = /obj/effect/projectile/muzzle/laser/blue
|
||||
impact_type = /obj/effect/projectile/impact/laser/blue
|
||||
|
||||
/obj/item/projectile/beam/lasertag/bluetag/hitscan
|
||||
hitscan = TRUE
|
||||
|
||||
/obj/item/projectile/beam/instakill
|
||||
name = "instagib laser"
|
||||
icon_state = "purple_laser"
|
||||
damage = 200
|
||||
damage_type = BURN
|
||||
impact_effect_type = /obj/effect/temp_visual/impact_effect/purple_laser
|
||||
light_color = LIGHT_COLOR_PURPLE
|
||||
|
||||
/obj/item/projectile/beam/instakill/blue
|
||||
icon_state = "blue_laser"
|
||||
impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser
|
||||
light_color = LIGHT_COLOR_BLUE
|
||||
|
||||
/obj/item/projectile/beam/instakill/red
|
||||
icon_state = "red_laser"
|
||||
impact_effect_type = /obj/effect/temp_visual/impact_effect/red_laser
|
||||
light_color = LIGHT_COLOR_RED
|
||||
|
||||
/obj/item/projectile/beam/instakill/on_hit(atom/target)
|
||||
. = ..()
|
||||
if(iscarbon(target))
|
||||
var/mob/living/carbon/M = target
|
||||
M.visible_message("<span class='danger'>[M] explodes into a shower of gibs!</span>")
|
||||
M.gib()
|
||||
@@ -0,0 +1,71 @@
|
||||
// 7.62x38mmR (Nagant Revolver)
|
||||
|
||||
/obj/item/projectile/bullet/n762
|
||||
name = "7.62x38mmR bullet"
|
||||
damage = 60
|
||||
|
||||
// .50AE (Desert Eagle)
|
||||
|
||||
/obj/item/projectile/bullet/a50AE
|
||||
name = ".50AE bullet"
|
||||
damage = 60
|
||||
|
||||
// .38 (Detective's Gun)
|
||||
|
||||
/obj/item/projectile/bullet/c38
|
||||
name = ".38 bullet"
|
||||
damage = 25
|
||||
|
||||
/obj/item/projectile/bullet/c38/rubber
|
||||
name = ".38 rubber bullet"
|
||||
damage = 15
|
||||
stamina = 48
|
||||
|
||||
/obj/item/projectile/bullet/c38/trac
|
||||
name = ".38 TRAC bullet"
|
||||
damage = 10
|
||||
|
||||
/obj/item/projectile/bullet/c38/trac/on_hit(atom/target, blocked = FALSE)
|
||||
. = ..()
|
||||
var/mob/living/carbon/M = target
|
||||
var/obj/item/implant/tracking/c38/imp
|
||||
for(var/obj/item/implant/tracking/c38/TI in M.implants) //checks if the target already contains a tracking implant
|
||||
imp = TI
|
||||
return
|
||||
if(!imp)
|
||||
imp = new /obj/item/implant/tracking/c38(M)
|
||||
imp.implant(M)
|
||||
|
||||
/obj/item/projectile/bullet/c38/hotshot //similar to incendiary bullets, but do not leave a flaming trail
|
||||
name = ".38 Hot Shot bullet"
|
||||
damage = 20
|
||||
|
||||
/obj/item/projectile/bullet/c38/hotshot/on_hit(atom/target, blocked = FALSE)
|
||||
. = ..()
|
||||
if(iscarbon(target))
|
||||
var/mob/living/carbon/M = target
|
||||
M.adjust_fire_stacks(6)
|
||||
M.IgniteMob()
|
||||
|
||||
/obj/item/projectile/bullet/c38/iceblox //see /obj/item/projectile/temp for the original code
|
||||
name = ".38 Iceblox bullet"
|
||||
damage = 20
|
||||
var/temperature = 100
|
||||
|
||||
/obj/item/projectile/bullet/c38/iceblox/on_hit(atom/target, blocked = FALSE)
|
||||
. = ..()
|
||||
if(isliving(target))
|
||||
var/mob/living/M = target
|
||||
M.adjust_bodytemperature(((100-blocked)/100)*(temperature - M.bodytemperature))
|
||||
|
||||
|
||||
// .357 (Syndie Revolver)
|
||||
|
||||
/obj/item/projectile/bullet/a357
|
||||
name = ".357 bullet"
|
||||
damage = 60
|
||||
|
||||
/obj/item/projectile/bullet/a357/ap
|
||||
name = ".357 armor-piercing bullet"
|
||||
damage = 45
|
||||
armour_penetration = 45
|
||||
@@ -0,0 +1,69 @@
|
||||
// .45 (M1911 & C20r)
|
||||
|
||||
/obj/item/projectile/bullet/c45
|
||||
name = ".45 bullet"
|
||||
damage = 20
|
||||
stamina = 65
|
||||
|
||||
/obj/item/projectile/bullet/c45_nostamina
|
||||
name = ".45 bullet"
|
||||
damage = 30
|
||||
|
||||
/obj/item/projectile/bullet/c45_cleaning
|
||||
name = ".45 bullet"
|
||||
damage = 24
|
||||
stamina = 10
|
||||
|
||||
/obj/item/projectile/bullet/c45_cleaning/on_hit(atom/target, blocked = FALSE)
|
||||
. = ..()
|
||||
var/turf/T = get_turf(target)
|
||||
SEND_SIGNAL(T, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
|
||||
for(var/A in T)
|
||||
if(is_cleanable(A))
|
||||
qdel(A)
|
||||
else if(isitem(A))
|
||||
var/obj/item/cleaned_item = A
|
||||
SEND_SIGNAL(cleaned_item, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
|
||||
cleaned_item.clean_blood()
|
||||
if(ismob(cleaned_item.loc))
|
||||
var/mob/M = cleaned_item.loc
|
||||
M.regenerate_icons()
|
||||
else if(ishuman(A))
|
||||
var/mob/living/carbon/human/cleaned_human = A
|
||||
if(cleaned_human.lying)
|
||||
if(cleaned_human.head)
|
||||
SEND_SIGNAL(cleaned_human.head, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
|
||||
cleaned_human.head.clean_blood()
|
||||
cleaned_human.update_inv_head()
|
||||
if(cleaned_human.wear_suit)
|
||||
SEND_SIGNAL(cleaned_human.wear_suit, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
|
||||
cleaned_human.wear_suit.clean_blood()
|
||||
cleaned_human.update_inv_wear_suit()
|
||||
else if(cleaned_human.w_uniform)
|
||||
SEND_SIGNAL(cleaned_human.w_uniform, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
|
||||
cleaned_human.w_uniform.clean_blood()
|
||||
cleaned_human.update_inv_w_uniform()
|
||||
if(cleaned_human.shoes)
|
||||
SEND_SIGNAL(cleaned_human.shoes, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
|
||||
cleaned_human.shoes.clean_blood()
|
||||
cleaned_human.update_inv_shoes()
|
||||
SEND_SIGNAL(cleaned_human, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
|
||||
cleaned_human.clean_blood()
|
||||
cleaned_human.wash_cream()
|
||||
cleaned_human.regenerate_icons()
|
||||
|
||||
// 4.6x30mm (Autorifles)
|
||||
|
||||
/obj/item/projectile/bullet/c46x30mm
|
||||
name = "4.6x30mm bullet"
|
||||
damage = 15
|
||||
|
||||
/obj/item/projectile/bullet/c46x30mm_ap
|
||||
name = "4.6x30mm armor-piercing bullet"
|
||||
damage = 12.5
|
||||
armour_penetration = 40
|
||||
|
||||
/obj/item/projectile/bullet/incendiary/c46x30mm
|
||||
name = "4.6x30mm incendiary bullet"
|
||||
damage = 7.5
|
||||
fire_stacks = 1
|
||||
@@ -0,0 +1,46 @@
|
||||
// .50 (Sniper)
|
||||
|
||||
/obj/item/projectile/bullet/p50
|
||||
name =".50 bullet"
|
||||
speed = 0.4
|
||||
damage = 70
|
||||
knockdown = 100
|
||||
dismemberment = 50
|
||||
armour_penetration = 50
|
||||
var/breakthings = TRUE
|
||||
|
||||
/obj/item/projectile/bullet/p50/on_hit(atom/target, blocked = 0)
|
||||
if(isobj(target) && (blocked != 100) && breakthings)
|
||||
var/obj/O = target
|
||||
O.take_damage(80, BRUTE, "bullet", FALSE)
|
||||
return ..()
|
||||
|
||||
/obj/item/projectile/bullet/p50/soporific
|
||||
name =".50 soporific bullet"
|
||||
armour_penetration = 0
|
||||
damage = 0
|
||||
dismemberment = 0
|
||||
knockdown = 0
|
||||
breakthings = FALSE
|
||||
|
||||
/obj/item/projectile/bullet/p50/soporific/on_hit(atom/target, blocked = FALSE)
|
||||
if((blocked != 100) && isliving(target))
|
||||
var/mob/living/L = target
|
||||
L.Sleeping(400)
|
||||
return ..()
|
||||
|
||||
/obj/item/projectile/bullet/p50/penetrator
|
||||
name =".50 penetrator bullet"
|
||||
icon_state = "gauss"
|
||||
name = "penetrator round"
|
||||
damage = 60
|
||||
forcedodge = TRUE
|
||||
dismemberment = 0 //It goes through you cleanly.
|
||||
knockdown = 0
|
||||
breakthings = FALSE
|
||||
|
||||
/obj/item/projectile/bullet/p50/penetrator/shuttle //Nukeop Shuttle Variety
|
||||
icon_state = "gaussstrong"
|
||||
damage = 25
|
||||
speed = 0.3
|
||||
range = 16
|
||||
@@ -0,0 +1,29 @@
|
||||
//Nuclear particle projectile - a deadly side effect of fusion
|
||||
/obj/item/projectile/energy/nuclear_particle
|
||||
name = "nuclear particle"
|
||||
icon_state = "nuclear_particle"
|
||||
pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE
|
||||
flag = "rad"
|
||||
irradiate = 5000
|
||||
speed = 0.4
|
||||
hitsound = 'sound/weapons/emitter2.ogg'
|
||||
impact_type = /obj/effect/projectile/impact/xray
|
||||
var/static/list/particle_colors = list(
|
||||
"red" = "#FF0000",
|
||||
"blue" = "#00FF00",
|
||||
"green" = "#0000FF",
|
||||
"yellow" = "#FFFF00",
|
||||
"cyan" = "#00FFFF",
|
||||
"purple" = "#FF00FF"
|
||||
)
|
||||
|
||||
/obj/item/projectile/energy/nuclear_particle/Initialize()
|
||||
. = ..()
|
||||
//Random color time!
|
||||
var/our_color = pick(particle_colors)
|
||||
add_atom_colour(particle_colors[our_color], FIXED_COLOUR_PRIORITY)
|
||||
set_light(4, 3, particle_colors[our_color]) //Range of 4, brightness of 3 - Same range as a flashlight
|
||||
|
||||
/atom/proc/fire_nuclear_particle(angle = rand(0,360)) //used by fusion to fire random nuclear particles. Fires one particle in a random direction.
|
||||
var/obj/item/projectile/energy/nuclear_particle/P = new /obj/item/projectile/energy/nuclear_particle(src)
|
||||
P.fire(angle)
|
||||
@@ -0,0 +1,34 @@
|
||||
/obj/item/projectile/energy/electrode
|
||||
name = "electrode"
|
||||
icon_state = "spark"
|
||||
color = "#FFFF00"
|
||||
nodamage = 1
|
||||
knockdown = 60
|
||||
knockdown_stamoverride = 36
|
||||
knockdown_stam_max = 50
|
||||
stutter = 5
|
||||
jitter = 20
|
||||
hitsound = 'sound/weapons/taserhit.ogg'
|
||||
range = 7
|
||||
tracer_type = /obj/effect/projectile/tracer/stun
|
||||
muzzle_type = /obj/effect/projectile/muzzle/stun
|
||||
impact_type = /obj/effect/projectile/impact/stun
|
||||
var/tase_duration = 50
|
||||
|
||||
/obj/item/projectile/energy/electrode/on_hit(atom/target, blocked = FALSE)
|
||||
. = ..()
|
||||
if(!ismob(target) || blocked >= 100) //Fully blocked by mob or collided with dense object - burst into sparks!
|
||||
do_sparks(1, TRUE, src)
|
||||
else if(iscarbon(target))
|
||||
var/mob/living/carbon/C = target
|
||||
SEND_SIGNAL(C, COMSIG_ADD_MOOD_EVENT, "tased", /datum/mood_event/tased)
|
||||
SEND_SIGNAL(C, COMSIG_LIVING_MINOR_SHOCK)
|
||||
if(C.dna && C.dna.check_mutation(HULK))
|
||||
C.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" ), forced = "hulk")
|
||||
else if((C.status_flags & CANKNOCKDOWN) && !HAS_TRAIT(C, TRAIT_STUNIMMUNE))
|
||||
C.apply_status_effect(STATUS_EFFECT_TASED, tase_duration)
|
||||
addtimer(CALLBACK(C, /mob/living/carbon.proc/do_jitter_animation, jitter), 5)
|
||||
|
||||
/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
|
||||
do_sparks(1, TRUE, src)
|
||||
..()
|
||||
@@ -0,0 +1,510 @@
|
||||
/obj/item/projectile/magic
|
||||
name = "bolt of nothing"
|
||||
icon_state = "energy"
|
||||
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"
|
||||
|
||||
/obj/item/projectile/magic/death/on_hit(target)
|
||||
. = ..()
|
||||
if(ismob(target))
|
||||
var/mob/M = target
|
||||
if(M.anti_magic_check())
|
||||
M.visible_message("<span class='warning'>[src] vanishes on contact with [target]!</span>")
|
||||
return
|
||||
M.death(0)
|
||||
|
||||
/obj/item/projectile/magic/resurrection
|
||||
name = "bolt of resurrection"
|
||||
icon_state = "ion"
|
||||
damage = 0
|
||||
damage_type = OXY
|
||||
nodamage = 1
|
||||
|
||||
/obj/item/projectile/magic/resurrection/on_hit(mob/living/carbon/target)
|
||||
. = ..()
|
||||
if(isliving(target))
|
||||
if(target.hellbound)
|
||||
return
|
||||
if(target.anti_magic_check())
|
||||
target.visible_message("<span class='warning'>[src] vanishes on contact with [target]!</span>")
|
||||
return
|
||||
if(iscarbon(target))
|
||||
var/mob/living/carbon/C = target
|
||||
C.regenerate_limbs()
|
||||
C.regenerate_organs()
|
||||
if(target.revive(full_heal = 1))
|
||||
target.grab_ghost(force = TRUE) // even suicides
|
||||
to_chat(target, "<span class='notice'>You rise with a start, you're alive!!!</span>")
|
||||
else if(target.stat != DEAD)
|
||||
to_chat(target, "<span class='notice'>You feel great!</span>")
|
||||
|
||||
/obj/item/projectile/magic/teleport
|
||||
name = "bolt of teleportation"
|
||||
icon_state = "bluespace"
|
||||
damage = 0
|
||||
damage_type = OXY
|
||||
nodamage = 1
|
||||
var/inner_tele_radius = 0
|
||||
var/outer_tele_radius = 6
|
||||
|
||||
/obj/item/projectile/magic/teleport/on_hit(mob/target)
|
||||
. = ..()
|
||||
if(ismob(target))
|
||||
var/mob/M = target
|
||||
if(M.anti_magic_check())
|
||||
M.visible_message("<span class='warning'>[src] fizzles on contact with [target]!</span>")
|
||||
return
|
||||
var/teleammount = 0
|
||||
var/teleloc = target
|
||||
if(!isturf(target))
|
||||
teleloc = target.loc
|
||||
for(var/atom/movable/stuff in teleloc)
|
||||
if(!stuff.anchored && stuff.loc)
|
||||
if(do_teleport(stuff, stuff, 10, channel = TELEPORT_CHANNEL_MAGIC))
|
||||
teleammount++
|
||||
var/datum/effect_system/smoke_spread/smoke = new
|
||||
smoke.set_up(max(round(4 - teleammount),0), stuff.loc) //Smoke drops off if a lot of stuff is moved for the sake of sanity
|
||||
smoke.start()
|
||||
|
||||
/obj/item/projectile/magic/door
|
||||
name = "bolt of door creation"
|
||||
icon_state = "energy"
|
||||
damage = 0
|
||||
damage_type = OXY
|
||||
nodamage = 1
|
||||
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(atom/target)
|
||||
. = ..()
|
||||
if(istype(target, /obj/machinery/door))
|
||||
OpenDoor(target)
|
||||
else
|
||||
var/turf/T = get_turf(target)
|
||||
if(isclosedturf(T) && !isindestructiblewall(T))
|
||||
CreateDoor(T)
|
||||
|
||||
/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/door/proc/OpenDoor(var/obj/machinery/door/D)
|
||||
if(istype(D, /obj/machinery/door/airlock))
|
||||
var/obj/machinery/door/airlock/A = D
|
||||
A.locked = FALSE
|
||||
D.open()
|
||||
|
||||
/obj/item/projectile/magic/change
|
||||
name = "bolt of change"
|
||||
icon_state = "ice_1"
|
||||
damage = 0
|
||||
damage_type = BURN
|
||||
nodamage = 1
|
||||
|
||||
/obj/item/projectile/magic/change/on_hit(atom/change)
|
||||
. = ..()
|
||||
if(ismob(change))
|
||||
var/mob/M = change
|
||||
if(M.anti_magic_check())
|
||||
M.visible_message("<span class='warning'>[src] fizzles on contact with [M]!</span>")
|
||||
qdel(src)
|
||||
return
|
||||
wabbajack(change)
|
||||
qdel(src)
|
||||
|
||||
/proc/wabbajack(mob/living/M)
|
||||
if(!istype(M) || M.stat == DEAD || M.notransform || (GODMODE & M.status_flags))
|
||||
return
|
||||
|
||||
M.notransform = 1
|
||||
M.canmove = 0
|
||||
M.icon = null
|
||||
M.cut_overlays()
|
||||
M.invisibility = INVISIBILITY_ABSTRACT
|
||||
|
||||
var/list/contents = M.contents.Copy()
|
||||
|
||||
if(iscyborg(M))
|
||||
var/mob/living/silicon/robot/Robot = M
|
||||
if(Robot.mmi)
|
||||
qdel(Robot.mmi)
|
||||
Robot.notify_ai(NEW_BORG)
|
||||
else
|
||||
for(var/obj/item/W in contents)
|
||||
if(!M.dropItemToGround(W))
|
||||
qdel(W)
|
||||
|
||||
var/mob/living/new_mob
|
||||
|
||||
var/randomize = pick("monkey","robot","slime","xeno","humanoid","animal")
|
||||
switch(randomize)
|
||||
if("monkey")
|
||||
new_mob = new /mob/living/carbon/monkey(M.loc)
|
||||
|
||||
if("robot")
|
||||
var/robot = pick(200;/mob/living/silicon/robot,
|
||||
/mob/living/silicon/robot/modules/syndicate,
|
||||
/mob/living/silicon/robot/modules/syndicate/medical,
|
||||
/mob/living/silicon/robot/modules/syndicate/saboteur,
|
||||
200;/mob/living/simple_animal/drone/polymorphed)
|
||||
new_mob = new robot(M.loc)
|
||||
if(issilicon(new_mob))
|
||||
new_mob.gender = M.gender
|
||||
new_mob.invisibility = 0
|
||||
new_mob.job = "Cyborg"
|
||||
var/mob/living/silicon/robot/Robot = new_mob
|
||||
Robot.lawupdate = FALSE
|
||||
Robot.connected_ai = null
|
||||
Robot.mmi.transfer_identity(M) //Does not transfer key/client.
|
||||
Robot.clear_inherent_laws(0)
|
||||
Robot.clear_zeroth_law(0)
|
||||
|
||||
if("slime")
|
||||
new_mob = new /mob/living/simple_animal/slime/random(M.loc)
|
||||
|
||||
if("xeno")
|
||||
var/Xe
|
||||
if(M.ckey)
|
||||
Xe = pick(/mob/living/carbon/alien/humanoid/hunter,/mob/living/carbon/alien/humanoid/sentinel)
|
||||
else
|
||||
Xe = pick(/mob/living/carbon/alien/humanoid/hunter,/mob/living/simple_animal/hostile/alien/sentinel)
|
||||
new_mob = new Xe(M.loc)
|
||||
|
||||
if("animal")
|
||||
var/path = pick(/mob/living/simple_animal/hostile/carp,
|
||||
/mob/living/simple_animal/hostile/bear,
|
||||
/mob/living/simple_animal/hostile/mushroom,
|
||||
/mob/living/simple_animal/hostile/statue,
|
||||
/mob/living/simple_animal/hostile/retaliate/bat,
|
||||
/mob/living/simple_animal/hostile/retaliate/goat,
|
||||
/mob/living/simple_animal/hostile/killertomato,
|
||||
/mob/living/simple_animal/hostile/poison/giant_spider,
|
||||
/mob/living/simple_animal/hostile/poison/giant_spider/hunter,
|
||||
/mob/living/simple_animal/hostile/blob/blobbernaut/independent,
|
||||
/mob/living/simple_animal/hostile/carp/ranged,
|
||||
/mob/living/simple_animal/hostile/carp/ranged/chaos,
|
||||
/mob/living/simple_animal/hostile/asteroid/basilisk/watcher,
|
||||
/mob/living/simple_animal/hostile/asteroid/goliath/beast,
|
||||
/mob/living/simple_animal/hostile/headcrab,
|
||||
/mob/living/simple_animal/hostile/morph,
|
||||
/mob/living/simple_animal/hostile/stickman,
|
||||
/mob/living/simple_animal/hostile/stickman/dog,
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/lesser,
|
||||
/mob/living/simple_animal/hostile/gorilla,
|
||||
/mob/living/simple_animal/parrot,
|
||||
/mob/living/simple_animal/pet/dog/corgi,
|
||||
/mob/living/simple_animal/crab,
|
||||
/mob/living/simple_animal/pet/dog/pug,
|
||||
/mob/living/simple_animal/pet/cat,
|
||||
/mob/living/simple_animal/mouse,
|
||||
/mob/living/simple_animal/chicken,
|
||||
/mob/living/simple_animal/cow,
|
||||
/mob/living/simple_animal/hostile/lizard,
|
||||
/mob/living/simple_animal/pet/fox,
|
||||
/mob/living/simple_animal/butterfly,
|
||||
/mob/living/simple_animal/pet/cat/cak,
|
||||
/mob/living/simple_animal/chick)
|
||||
new_mob = new path(M.loc)
|
||||
|
||||
if("humanoid")
|
||||
if(prob(50))
|
||||
new_mob = new /mob/living/carbon/human(M.loc)
|
||||
else
|
||||
var/hooman = pick(subtypesof(/mob/living/carbon/human/species))
|
||||
new_mob =new hooman(M.loc)
|
||||
|
||||
var/datum/preferences/A = new() //Randomize appearance for the human
|
||||
A.copy_to(new_mob, FALSE)
|
||||
|
||||
var/mob/living/carbon/human/H = new_mob
|
||||
H.update_body()
|
||||
H.update_hair()
|
||||
H.update_body_parts()
|
||||
H.dna.update_dna_identity()
|
||||
|
||||
if(!new_mob)
|
||||
return
|
||||
new_mob.grant_language(/datum/language/common)
|
||||
|
||||
// Some forms can still wear some items
|
||||
for(var/obj/item/W in contents)
|
||||
new_mob.equip_to_appropriate_slot(W)
|
||||
|
||||
M.log_message("became [new_mob.real_name]", LOG_ATTACK, color="orange")
|
||||
|
||||
new_mob.a_intent = INTENT_HARM
|
||||
|
||||
M.wabbajack_act(new_mob)
|
||||
|
||||
to_chat(new_mob, "<span class='warning'>Your form morphs into that of a [randomize].</span>")
|
||||
|
||||
var/poly_msg = CONFIG_GET(keyed_list/policy)["polymorph"]
|
||||
if(poly_msg)
|
||||
to_chat(new_mob, poly_msg)
|
||||
|
||||
M.transfer_observers_to(new_mob)
|
||||
|
||||
qdel(M)
|
||||
return new_mob
|
||||
|
||||
/obj/item/projectile/magic/animate
|
||||
name = "bolt of animation"
|
||||
icon_state = "red_1"
|
||||
damage = 0
|
||||
damage_type = BURN
|
||||
nodamage = 1
|
||||
|
||||
/obj/item/projectile/magic/animate/on_hit(atom/target, blocked = FALSE)
|
||||
target.animate_atom_living(firer)
|
||||
..()
|
||||
|
||||
/atom/proc/animate_atom_living(var/mob/living/owner = null)
|
||||
if((isitem(src) || isstructure(src)) && !is_type_in_list(src, GLOB.protected_objects))
|
||||
if(istype(src, /obj/structure/statue/petrified))
|
||||
var/obj/structure/statue/petrified/P = src
|
||||
if(P.petrified_mob)
|
||||
var/mob/living/L = P.petrified_mob
|
||||
var/mob/living/simple_animal/hostile/statue/S = new(P.loc, owner)
|
||||
S.name = "statue of [L.name]"
|
||||
if(owner)
|
||||
S.faction = list("[REF(owner)]")
|
||||
S.icon = P.icon
|
||||
S.icon_state = P.icon_state
|
||||
S.copy_overlays(P, TRUE)
|
||||
S.color = P.color
|
||||
S.atom_colours = P.atom_colours.Copy()
|
||||
if(L.mind)
|
||||
L.mind.transfer_to(S)
|
||||
if(owner)
|
||||
to_chat(S, "<span class='userdanger'>You are an animate statue. You cannot move when monitored, but are nearly invincible and deadly when unobserved! Do not harm [owner], your creator.</span>")
|
||||
P.forceMove(S)
|
||||
return
|
||||
else
|
||||
var/obj/O = src
|
||||
if(istype(O, /obj/item/gun))
|
||||
new /mob/living/simple_animal/hostile/mimic/copy/ranged(loc, src, owner)
|
||||
else
|
||||
new /mob/living/simple_animal/hostile/mimic/copy(loc, src, owner)
|
||||
|
||||
else if(istype(src, /mob/living/simple_animal/hostile/mimic/copy))
|
||||
// Change our allegiance!
|
||||
var/mob/living/simple_animal/hostile/mimic/copy/C = src
|
||||
if(owner)
|
||||
C.ChangeOwner(owner)
|
||||
|
||||
/obj/item/projectile/magic/spellblade
|
||||
name = "blade energy"
|
||||
icon_state = "lavastaff"
|
||||
damage = 15
|
||||
damage_type = BURN
|
||||
flag = "magic"
|
||||
dismemberment = 50
|
||||
nodamage = 0
|
||||
|
||||
/obj/item/projectile/magic/spellblade/on_hit(target)
|
||||
if(ismob(target))
|
||||
var/mob/M = target
|
||||
if(M.anti_magic_check())
|
||||
M.visible_message("<span class='warning'>[src] vanishes on contact with [target]!</span>")
|
||||
qdel(src)
|
||||
return
|
||||
. = ..()
|
||||
|
||||
/obj/item/projectile/magic/arcane_barrage
|
||||
name = "arcane bolt"
|
||||
icon_state = "arcane_barrage"
|
||||
damage = 20
|
||||
damage_type = BURN
|
||||
nodamage = 0
|
||||
armour_penetration = 0
|
||||
flag = "magic"
|
||||
hitsound = 'sound/weapons/barragespellhit.ogg'
|
||||
|
||||
/obj/item/projectile/magic/arcane_barrage/on_hit(target)
|
||||
if(ismob(target))
|
||||
var/mob/M = target
|
||||
if(M.anti_magic_check())
|
||||
M.visible_message("<span class='warning'>[src] vanishes on contact with [target]!</span>")
|
||||
qdel(src)
|
||||
return
|
||||
. = ..()
|
||||
|
||||
|
||||
/obj/item/projectile/magic/locker
|
||||
name = "locker bolt"
|
||||
icon_state = "locker"
|
||||
nodamage = TRUE
|
||||
flag = "magic"
|
||||
var/weld = TRUE
|
||||
var/created = FALSE //prevents creation of more then one locker if it has multiple hits
|
||||
var/locker_suck = TRUE
|
||||
|
||||
/obj/item/projectile/magic/locker/prehit(atom/A)
|
||||
if(ismob(A) && locker_suck)
|
||||
var/mob/M = A
|
||||
if(M.anti_magic_check())
|
||||
M.visible_message("<span class='warning'>[src] vanishes on contact with [A]!</span>")
|
||||
qdel(src)
|
||||
return
|
||||
if(M.anchored)
|
||||
return ..()
|
||||
M.forceMove(src)
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/obj/item/projectile/magic/locker/on_hit(target)
|
||||
if(created)
|
||||
return ..()
|
||||
var/obj/structure/closet/decay/C = new(get_turf(src))
|
||||
if(LAZYLEN(contents))
|
||||
for(var/atom/movable/AM in contents)
|
||||
C.insert(AM)
|
||||
C.welded = weld
|
||||
C.update_icon()
|
||||
created = TRUE
|
||||
return ..()
|
||||
|
||||
/obj/item/projectile/magic/locker/Destroy()
|
||||
locker_suck = FALSE
|
||||
for(var/atom/movable/AM in contents)
|
||||
AM.forceMove(get_turf(src))
|
||||
. = ..()
|
||||
|
||||
/obj/structure/closet/decay
|
||||
breakout_time = 600
|
||||
icon_welded = null
|
||||
var/magic_icon = "cursed"
|
||||
var/weakened_icon = "decursed"
|
||||
var/auto_destroy = TRUE
|
||||
|
||||
/obj/structure/closet/decay/Initialize()
|
||||
. = ..()
|
||||
if(auto_destroy)
|
||||
addtimer(CALLBACK(src, .proc/bust_open), 5 MINUTES)
|
||||
addtimer(CALLBACK(src, .proc/magicly_lock), 5)
|
||||
|
||||
/obj/structure/closet/decay/proc/magicly_lock()
|
||||
if(!welded)
|
||||
return
|
||||
icon_state = magic_icon
|
||||
update_icon()
|
||||
|
||||
/obj/structure/closet/decay/after_weld(weld_state)
|
||||
if(weld_state)
|
||||
unmagify()
|
||||
|
||||
/obj/structure/closet/decay/proc/decay()
|
||||
animate(src, alpha = 0, time = 30)
|
||||
addtimer(CALLBACK(GLOBAL_PROC, .proc/qdel, src), 30)
|
||||
|
||||
/obj/structure/closet/decay/open(mob/living/user)
|
||||
. = ..()
|
||||
if(.)
|
||||
if(icon_state == magic_icon) //check if we used the magic icon at all before giving it the lesser magic icon
|
||||
unmagify()
|
||||
else
|
||||
addtimer(CALLBACK(src, .proc/decay), 15 SECONDS)
|
||||
|
||||
/obj/structure/closet/decay/proc/unmagify()
|
||||
icon_state = weakened_icon
|
||||
update_icon()
|
||||
addtimer(CALLBACK(src, .proc/decay), 15 SECONDS)
|
||||
icon_welded = "welded"
|
||||
|
||||
/obj/item/projectile/magic/aoe
|
||||
name = "Area Bolt"
|
||||
desc = "What the fuck does this do?!"
|
||||
damage = 0
|
||||
var/proxdet = TRUE
|
||||
|
||||
/obj/item/projectile/magic/aoe/Range()
|
||||
if(proxdet)
|
||||
for(var/mob/living/L in range(1, get_turf(src)))
|
||||
if(L.stat != DEAD && L != firer && !L.anti_magic_check())
|
||||
return Bump(L)
|
||||
..()
|
||||
|
||||
|
||||
/obj/item/projectile/magic/aoe/lightning
|
||||
name = "lightning bolt"
|
||||
icon_state = "tesla_projectile" //Better sprites are REALLY needed and appreciated!~
|
||||
damage = 15
|
||||
damage_type = BURN
|
||||
nodamage = 0
|
||||
speed = 0.3
|
||||
flag = "magic"
|
||||
|
||||
var/tesla_power = 20000
|
||||
var/tesla_range = 15
|
||||
var/tesla_flags = TESLA_MOB_DAMAGE | TESLA_MOB_STUN | TESLA_OBJ_DAMAGE
|
||||
var/chain
|
||||
var/mob/living/caster
|
||||
|
||||
/obj/item/projectile/magic/aoe/lightning/fire(setAngle)
|
||||
if(caster)
|
||||
chain = caster.Beam(src, icon_state = "lightning[rand(1, 12)]", time = INFINITY, maxdistance = INFINITY)
|
||||
..()
|
||||
|
||||
/obj/item/projectile/magic/aoe/lightning/on_hit(target)
|
||||
. = ..()
|
||||
if(ismob(target))
|
||||
var/mob/M = target
|
||||
if(M.anti_magic_check())
|
||||
visible_message("<span class='warning'>[src] fizzles on contact with [target]!</span>")
|
||||
qdel(src)
|
||||
return
|
||||
tesla_zap(src, tesla_range, tesla_power, tesla_flags)
|
||||
qdel(src)
|
||||
|
||||
/obj/item/projectile/magic/aoe/lightning/Destroy()
|
||||
qdel(chain)
|
||||
. = ..()
|
||||
|
||||
/obj/item/projectile/magic/aoe/fireball
|
||||
name = "bolt of fireball"
|
||||
icon_state = "fireball"
|
||||
damage = 10
|
||||
damage_type = BRUTE
|
||||
nodamage = 0
|
||||
|
||||
//explosion values
|
||||
var/exp_heavy = 0
|
||||
var/exp_light = 2
|
||||
var/exp_flash = 3
|
||||
var/exp_fire = 2
|
||||
|
||||
/obj/item/projectile/magic/aoe/fireball/on_hit(target)
|
||||
. = ..()
|
||||
if(ismob(target))
|
||||
var/mob/living/M = target
|
||||
if(M.anti_magic_check())
|
||||
visible_message("<span class='warning'>[src] vanishes into smoke on contact with [target]!</span>")
|
||||
return
|
||||
M.take_overall_damage(0,10) //between this 10 burn, the 10 brute, the explosion brute, and the onfire burn, your at about 65 damage if you stop drop and roll immediately
|
||||
var/turf/T = get_turf(target)
|
||||
explosion(T, -1, exp_heavy, exp_light, exp_flash, 0, flame_range = exp_fire)
|
||||
|
||||
/obj/item/projectile/magic/aoe/fireball/infernal
|
||||
name = "infernal fireball"
|
||||
exp_heavy = -1
|
||||
exp_light = -1
|
||||
exp_flash = 4
|
||||
exp_fire= 5
|
||||
|
||||
/obj/item/projectile/magic/aoe/fireball/infernal/on_hit(target)
|
||||
. = ..()
|
||||
if(ismob(target))
|
||||
var/mob/living/M = target
|
||||
if(M.anti_magic_check())
|
||||
return
|
||||
var/turf/T = get_turf(target)
|
||||
for(var/i=0, i<50, i+=10)
|
||||
addtimer(CALLBACK(GLOBAL_PROC, .proc/explosion, T, -1, exp_heavy, exp_light, exp_flash, FALSE, FALSE, exp_fire), i)
|
||||
@@ -0,0 +1,14 @@
|
||||
/obj/item/projectile/bullet/neurotoxin
|
||||
name = "neurotoxin spit"
|
||||
icon_state = "neurotoxin"
|
||||
damage = 5
|
||||
damage_type = TOX
|
||||
|
||||
/obj/item/projectile/bullet/neurotoxin/on_hit(atom/target, blocked = FALSE)
|
||||
if(isalien(target))
|
||||
knockdown = 0
|
||||
nodamage = TRUE
|
||||
else if(iscarbon(target))
|
||||
var/mob/living/L = target
|
||||
L.Knockdown(100, TRUE, FALSE, 30, 25)
|
||||
return ..()
|
||||
Reference in New Issue
Block a user