mirror of
https://github.com/VOREStation/VOREStation.git
synced 2026-08-22 03:27:46 +01:00
Merge branch 'master' into upstream-merge-5944
This commit is contained in:
@@ -13,7 +13,7 @@
|
||||
var/caliber = "" //Which kind of guns it can be loaded into
|
||||
var/projectile_type //The bullet type to create when New() is called
|
||||
var/obj/item/projectile/BB = null //The loaded bullet - make it so that the projectiles are created only when needed?
|
||||
// var/spent_icon = null
|
||||
var/caseless = null //Caseless ammo deletes its self once the projectile is fired.
|
||||
|
||||
/obj/item/ammo_casing/New()
|
||||
..()
|
||||
@@ -47,9 +47,7 @@
|
||||
BB.name = "[initial(BB.name)] (\"[label_text]\")"
|
||||
|
||||
/obj/item/ammo_casing/update_icon()
|
||||
/* if(spent_icon && !BB)
|
||||
icon_state = spent_icon*/
|
||||
if(!BB) // This is really just a much better way of doing this.
|
||||
if(!BB)
|
||||
icon_state = "[initial(icon_state)]-spent"
|
||||
|
||||
/obj/item/ammo_casing/examine(mob/user)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# An explaination of the naming format for guns and ammo:
|
||||
#
|
||||
# a = Ammo, as in individual rounds of ammunition.
|
||||
# b = Box, intended to have ammo taken out one at a time by hand.
|
||||
# b = Box, intended to have ammo taken out one at a time by hand. Really obsolete. Don't use this.
|
||||
# c = Clips, intended to reload magazines or guns quickly.
|
||||
# m = Magazine, intended to hold rounds of ammo.
|
||||
# s = Speedloaders, intended to reload guns quickly.
|
||||
@@ -106,7 +106,7 @@
|
||||
initial_ammo = 0
|
||||
|
||||
/obj/item/ammo_magazine/m45tommy
|
||||
name = "tommygun magazine (.45)"
|
||||
name = "Tommy Gun magazine (.45)"
|
||||
icon_state = "tommy-mag"
|
||||
mag_type = MAGAZINE
|
||||
ammo_type = /obj/item/ammo_casing/a45
|
||||
@@ -115,14 +115,14 @@
|
||||
max_ammo = 20
|
||||
|
||||
/obj/item/ammo_magazine/m45tommy/ap
|
||||
name = "tommygun magazine (.45 AP)"
|
||||
name = "Tommy Gun magazine (.45 AP)"
|
||||
ammo_type = /obj/item/ammo_casing/a45/ap
|
||||
|
||||
/obj/item/ammo_magazine/m45tommy/empty
|
||||
initial_ammo = 0
|
||||
|
||||
/obj/item/ammo_magazine/m45tommydrum
|
||||
name = "tommygun drum magazine (.45)"
|
||||
name = "Tommy Gun drum magazine (.45)"
|
||||
icon_state = "tommy-drum"
|
||||
w_class = ITEMSIZE_NORMAL // Bulky ammo doesn't fit in your pockets!
|
||||
mag_type = MAGAZINE
|
||||
@@ -132,7 +132,7 @@
|
||||
max_ammo = 50
|
||||
|
||||
/obj/item/ammo_magazine/m45tommydrum/ap
|
||||
name = "tommygun drum magazine (.45 AP)"
|
||||
name = "Tommy Gun drum magazine (.45 AP)"
|
||||
ammo_type = /obj/item/ammo_casing/a45/ap
|
||||
|
||||
/obj/item/ammo_magazine/m45tommydrum/empty
|
||||
@@ -188,6 +188,21 @@
|
||||
name = "speedloader (.45 AP)"
|
||||
ammo_type = /obj/item/ammo_casing/a45/ap
|
||||
|
||||
///////// 5mm Caseless /////////
|
||||
|
||||
/obj/item/ammo_magazine/m5mmcaseless
|
||||
name = "prototype rifle magazine (5mm caseless)"
|
||||
ammo_type = /obj/item/ammo_casing/a5mmcaseless
|
||||
icon_state = "caseless-mag"
|
||||
caliber = "5mm caseless"
|
||||
mag_type = MAGAZINE
|
||||
max_ammo = 30
|
||||
multiple_sprites = 1
|
||||
|
||||
/obj/item/ammo_magazine/m5mmcaseless/stun
|
||||
icon_state = "caseless-mag-alt"
|
||||
ammo_type = /obj/item/ammo_casing/a5mmcaseless/stun
|
||||
|
||||
///////// 9mm /////////
|
||||
|
||||
/obj/item/ammo_magazine/m9mm
|
||||
@@ -313,31 +328,19 @@
|
||||
name = "ammo clip (.45 flash)"
|
||||
ammo_type = /obj/item/ammo_casing/a9mm/flash
|
||||
|
||||
/obj/item/ammo_magazine/box/c9mm // Made by RnD for Prototype SMG and should probably be removed because why does it require DIAMONDS to make bullets?
|
||||
name = "ammunition Box (9mm)"
|
||||
icon_state = "9mm"
|
||||
origin_tech = list(TECH_COMBAT = 2)
|
||||
matter = list(DEFAULT_WALL_MATERIAL = 1800)
|
||||
caliber = "9mm"
|
||||
ammo_type = /obj/item/ammo_casing/a9mm
|
||||
max_ammo = 30
|
||||
|
||||
/obj/item/ammo_magazine/box/c9mm/empty
|
||||
initial_ammo = 0
|
||||
|
||||
/obj/item/ammo_magazine/m9mmR/saber
|
||||
desc = "A very high capacity double stack magazine made specially for the SABER SMG. Filled with 22 9mm bullets."
|
||||
/obj/item/ammo_magazine/m9mmAdvanced
|
||||
desc = "A very high capacity double stack magazine made specially for the Advanced SMG. Filled with 21 9mm bullets."
|
||||
icon_state = "S9mm"
|
||||
mag_type = MAGAZINE
|
||||
ammo_type = /obj/item/ammo_casing/a9mm
|
||||
matter = list(DEFAULT_WALL_MATERIAL = 1200)
|
||||
caliber = "9mm"
|
||||
max_ammo = 22
|
||||
max_ammo = 21
|
||||
origin_tech = list(TECH_COMBAT = 2, TECH_ILLEGAL = 1)
|
||||
multiple_sprites = 1
|
||||
|
||||
/obj/item/ammo_magazine/m9mmR/saber/ap
|
||||
desc = "A high capacity double stack magazine made specially for the SABER SMG. Filled with 22 9mm armor piercing bullets."
|
||||
/obj/item/ammo_magazine/m9mmAdvanced/ap
|
||||
desc = "A high capacity double stack magazine made specially for the Advanced SMG. Filled with 21 9mm armor piercing bullets."
|
||||
icon_state = "S9mm"
|
||||
ammo_type = /obj/item/ammo_casing/a9mm/ap
|
||||
matter = list(DEFAULT_WALL_MATERIAL = 2000)
|
||||
@@ -626,10 +629,10 @@
|
||||
|
||||
/obj/item/ammo_magazine/m12gdrum
|
||||
name = "magazine (12 gauge)"
|
||||
icon_state = "12g"
|
||||
icon_state = "ashot-mag"
|
||||
mag_type = MAGAZINE
|
||||
caliber = "12g"
|
||||
matter = list(DEFAULT_WALL_MATERIAL = 13000) //did the math. now fixed the exploityness of this thing. Have fun!
|
||||
matter = list(DEFAULT_WALL_MATERIAL = 13000)
|
||||
ammo_type = /obj/item/ammo_casing/a12g
|
||||
max_ammo = 24
|
||||
multiple_sprites = 1
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
matter = list(DEFAULT_WALL_MATERIAL = 130, "uranium" = 100)
|
||||
|
||||
/*
|
||||
* .50 Action Express
|
||||
* .44
|
||||
*/
|
||||
|
||||
/obj/item/ammo_casing/a44
|
||||
@@ -110,7 +110,7 @@
|
||||
/obj/item/ammo_casing/a9mm/practice
|
||||
desc = "A 9mm practice bullet casing."
|
||||
icon_state = "r-casing"
|
||||
projectile_type = /obj/item/projectile/bullet/pistol/practice
|
||||
projectile_type = /obj/item/projectile/bullet/practice
|
||||
|
||||
/*
|
||||
* .45
|
||||
@@ -130,7 +130,7 @@
|
||||
/obj/item/ammo_casing/a45/practice
|
||||
desc = "A .45 practice bullet casing."
|
||||
icon_state = "r-casing"
|
||||
projectile_type = /obj/item/projectile/bullet/pistol/practice
|
||||
projectile_type = /obj/item/projectile/bullet/practice
|
||||
matter = list(DEFAULT_WALL_MATERIAL = 60)
|
||||
|
||||
/obj/item/ammo_casing/a45/rubber
|
||||
@@ -202,7 +202,7 @@
|
||||
name = "shotgun shell"
|
||||
desc = "A practice shell."
|
||||
icon_state = "pshell"
|
||||
projectile_type = /obj/item/projectile/bullet/shotgun/practice
|
||||
projectile_type = /obj/item/projectile/bullet/practice
|
||||
matter = list(DEFAULT_WALL_MATERIAL = 90)
|
||||
|
||||
/obj/item/ammo_casing/a12g/beanbag
|
||||
@@ -260,7 +260,7 @@
|
||||
/obj/item/ammo_casing/a762/practice
|
||||
desc = "A 7.62mm practice bullet casing."
|
||||
icon_state = "rifle-casing" // Need to make an icon for these
|
||||
projectile_type = /obj/item/projectile/bullet/rifle/practice
|
||||
projectile_type = /obj/item/projectile/bullet/practice
|
||||
matter = list(DEFAULT_WALL_MATERIAL = 90)
|
||||
|
||||
/obj/item/ammo_casing/a762/blank
|
||||
@@ -306,7 +306,7 @@
|
||||
/obj/item/ammo_casing/a545/practice
|
||||
desc = "A 5.45mm practice bullet casing."
|
||||
icon_state = "rifle-casing" // Need to make an icon for these
|
||||
projectile_type = /obj/item/projectile/bullet/rifle/practice
|
||||
projectile_type = /obj/item/projectile/bullet/practice
|
||||
matter = list(DEFAULT_WALL_MATERIAL = 90)
|
||||
|
||||
/obj/item/ammo_casing/a545/blank
|
||||
@@ -322,6 +322,22 @@
|
||||
desc = "A 5.45mm hunting bullet casing."
|
||||
projectile_type = /obj/item/projectile/bullet/rifle/a545/hunter
|
||||
|
||||
/*
|
||||
* 5mm Caseless
|
||||
*/
|
||||
|
||||
/obj/item/ammo_casing/a5mmcaseless
|
||||
desc = "A 5mm solid phoron caseless round."
|
||||
caliber = "5mm caseless"
|
||||
icon_state = "casing" // Placeholder. Should probably be purple.
|
||||
projectile_type = /obj/item/projectile/bullet/pistol // Close enough to be comparable.
|
||||
matter = list(DEFAULT_WALL_MATERIAL = 180)
|
||||
caseless = 1
|
||||
|
||||
/obj/item/ammo_casing/a5mmcaseless/stun
|
||||
desc = "A 5mm solid phoron caseless stun round."
|
||||
projectile_type = /obj/item/projectile/energy/electrode // Maybe nerf this considering there's 30 rounds in a mag.
|
||||
|
||||
/*
|
||||
* Misc
|
||||
*/
|
||||
|
||||
@@ -27,11 +27,11 @@
|
||||
var/emagged = 0 // If you emag the smart mag, you can get the bullets out by clicking it
|
||||
|
||||
/obj/item/ammo_magazine/smart/New()
|
||||
processing_objects |= src
|
||||
START_PROCESSING(SSobj, src)
|
||||
..()
|
||||
|
||||
/obj/item/ammo_magazine/smart/Destroy()
|
||||
processing_objects -= src
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
..()
|
||||
|
||||
/obj/item/ammo_magazine/smart/process()
|
||||
|
||||
@@ -1,291 +0,0 @@
|
||||
/obj/effect/projectile
|
||||
icon = 'icons/effects/projectiles.dmi'
|
||||
icon_state = "bolt"
|
||||
plane = ABOVE_PLANE
|
||||
|
||||
/obj/effect/projectile/New(var/turf/location)
|
||||
if(istype(location))
|
||||
loc = location
|
||||
|
||||
/obj/effect/projectile/proc/set_transform(var/matrix/M)
|
||||
if(istype(M))
|
||||
transform = M
|
||||
|
||||
/obj/effect/projectile/proc/activate(var/kill_delay = 5)
|
||||
update_light()
|
||||
spawn(kill_delay)
|
||||
qdel(src) //see effect_system.dm - sets loc to null and lets GC handle removing these effects
|
||||
|
||||
return
|
||||
|
||||
//----------------------------
|
||||
// Laser beam
|
||||
//----------------------------
|
||||
/obj/effect/projectile/laser/tracer
|
||||
icon_state = "beam"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#FF0D00"
|
||||
|
||||
/obj/effect/projectile/laser/muzzle
|
||||
icon_state = "muzzle_laser"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#FF0D00"
|
||||
|
||||
/obj/effect/projectile/laser/impact
|
||||
icon_state = "impact_laser"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#FF0D00"
|
||||
|
||||
//----------------------------
|
||||
// Blue laser beam
|
||||
//----------------------------
|
||||
/obj/effect/projectile/laser_blue/tracer
|
||||
icon_state = "beam_blue"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#0066FF"
|
||||
|
||||
/obj/effect/projectile/laser_blue/muzzle
|
||||
icon_state = "muzzle_blue"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#0066FF"
|
||||
|
||||
/obj/effect/projectile/laser_blue/impact
|
||||
icon_state = "impact_blue"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#0066FF"
|
||||
|
||||
//----------------------------
|
||||
// Omni laser beam
|
||||
//----------------------------
|
||||
/obj/effect/projectile/laser_omni/tracer
|
||||
icon_state = "beam_omni"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#00C6FF"
|
||||
|
||||
/obj/effect/projectile/laser_omni/muzzle
|
||||
icon_state = "muzzle_omni"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#00C6FF"
|
||||
|
||||
/obj/effect/projectile/laser_omni/impact
|
||||
icon_state = "impact_omni"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#00C6FF"
|
||||
|
||||
//----------------------------
|
||||
// Xray laser beam
|
||||
//----------------------------
|
||||
/obj/effect/projectile/xray/tracer
|
||||
icon_state = "xray"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#00CC33"
|
||||
|
||||
/obj/effect/projectile/xray/muzzle
|
||||
icon_state = "muzzle_xray"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#00CC33"
|
||||
|
||||
/obj/effect/projectile/xray/impact
|
||||
icon_state = "impact_xray"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#00CC33"
|
||||
|
||||
//----------------------------
|
||||
// Heavy laser beam
|
||||
//----------------------------
|
||||
/obj/effect/projectile/laser_heavy/tracer
|
||||
icon_state = "beam_heavy"
|
||||
light_range = 3
|
||||
light_power = 1
|
||||
light_color = "#FF0D00"
|
||||
|
||||
/obj/effect/projectile/laser_heavy/muzzle
|
||||
icon_state = "muzzle_beam_heavy"
|
||||
light_range = 3
|
||||
light_power = 1
|
||||
light_color = "#FF0D00"
|
||||
|
||||
/obj/effect/projectile/laser_heavy/impact
|
||||
icon_state = "impact_beam_heavy"
|
||||
light_range = 3
|
||||
light_power = 1
|
||||
light_color = "#FF0D00"
|
||||
|
||||
//----------------------------
|
||||
// Pulse laser beam
|
||||
//----------------------------
|
||||
/obj/effect/projectile/laser_pulse/tracer
|
||||
icon_state = "u_laser"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#0066FF"
|
||||
|
||||
/obj/effect/projectile/laser_pulse/muzzle
|
||||
icon_state = "muzzle_u_laser"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#0066FF"
|
||||
|
||||
/obj/effect/projectile/laser_pulse/impact
|
||||
icon_state = "impact_u_laser"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#0066FF"
|
||||
|
||||
//----------------------------
|
||||
// Pulse muzzle effect only
|
||||
//----------------------------
|
||||
/obj/effect/projectile/pulse/muzzle
|
||||
icon_state = "muzzle_pulse"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#0066FF"
|
||||
|
||||
//----------------------------
|
||||
// Emitter beam
|
||||
//----------------------------
|
||||
/obj/effect/projectile/emitter/tracer
|
||||
icon_state = "emitter"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#00CC33"
|
||||
|
||||
/obj/effect/projectile/emitter/muzzle
|
||||
icon_state = "muzzle_emitter"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#00CC33"
|
||||
|
||||
/obj/effect/projectile/emitter/impact
|
||||
icon_state = "impact_emitter"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#00CC33"
|
||||
|
||||
//----------------------------
|
||||
// Stun beam
|
||||
//----------------------------
|
||||
/obj/effect/projectile/stun/tracer
|
||||
icon_state = "stun"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#FFFFFF"
|
||||
|
||||
/obj/effect/projectile/stun/muzzle
|
||||
icon_state = "muzzle_stun"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#FFFFFF"
|
||||
|
||||
/obj/effect/projectile/stun/impact
|
||||
icon_state = "impact_stun"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#FFFFFF"
|
||||
|
||||
//----------------------------
|
||||
// Bullet
|
||||
//----------------------------
|
||||
/obj/effect/projectile/bullet/muzzle
|
||||
icon_state = "muzzle_bullet"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#FFFFFF"
|
||||
|
||||
//----------------------------
|
||||
// Lightning beam
|
||||
//----------------------------
|
||||
/obj/effect/projectile/lightning/tracer
|
||||
icon_state = "lightning"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#00C6FF"
|
||||
|
||||
/obj/effect/projectile/lightning/muzzle
|
||||
icon_state = "muzzle_lightning"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#00C6FF"
|
||||
|
||||
/obj/effect/projectile/lightning/impact
|
||||
icon_state = "impact_lightning"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#00C6FF"
|
||||
|
||||
//----------------------------
|
||||
// Dark matter stun
|
||||
//----------------------------
|
||||
|
||||
/obj/effect/projectile/darkmatterstun/tracer
|
||||
icon_state = "darkt"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#8837A3"
|
||||
|
||||
/obj/effect/projectile/darkmatterstun/muzzle
|
||||
icon_state = "muzzle_darkt"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#8837A3"
|
||||
|
||||
/obj/effect/projectile/darkmatterstun/impact
|
||||
icon_state = "impact_darkt"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#8837A3"
|
||||
|
||||
//----------------------------
|
||||
// Dark matter
|
||||
//----------------------------
|
||||
|
||||
/obj/effect/projectile/darkmatter/tracer
|
||||
icon_state = "darkb"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#8837A3"
|
||||
|
||||
/obj/effect/projectile/darkmatter/muzzle
|
||||
icon_state = "muzzle_darkb"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#8837A3"
|
||||
|
||||
/obj/effect/projectile/darkmatter/impact
|
||||
icon_state = "impact_darkb"
|
||||
light_range = 2
|
||||
light_power = 0.5
|
||||
light_color = "#8837A3"
|
||||
|
||||
//----------------------------
|
||||
// Inversion / Cult
|
||||
//----------------------------
|
||||
/obj/effect/projectile/inversion/tracer
|
||||
icon_state = "invert"
|
||||
light_range = 2
|
||||
light_power = -2
|
||||
light_color = "#FFFFFF"
|
||||
|
||||
/obj/effect/projectile/inversion/muzzle
|
||||
icon_state = "muzzle_invert"
|
||||
light_range = 2
|
||||
light_power = -2
|
||||
light_color = "#FFFFFF"
|
||||
|
||||
/obj/effect/projectile/inversion/impact
|
||||
icon_state = "impact_invert"
|
||||
light_range = 2
|
||||
light_power = -2
|
||||
light_color = "#FFFFFF"
|
||||
@@ -56,7 +56,7 @@
|
||||
var/fire_delay = 6 //delay after shooting before the gun can be used again
|
||||
var/burst_delay = 2 //delay between shots, if firing in bursts
|
||||
var/move_delay = 1
|
||||
var/fire_sound = 'sound/weapons/Gunshot.ogg'
|
||||
var/fire_sound = null // This is handled by projectile.dm's fire_sound var now, but you can override the projectile's fire_sound with this one if you want to.
|
||||
var/fire_sound_text = "gunshot"
|
||||
var/fire_anim = null
|
||||
var/recoil = 0 //screen shake
|
||||
@@ -173,7 +173,7 @@
|
||||
if(!user.IsAdvancedToolUser())
|
||||
return 0
|
||||
if(isanimal(user))
|
||||
var/mob/living/simple_animal/S = user
|
||||
var/mob/living/simple_mob/S = user
|
||||
if(!S.IsHumanoidToolUser(src))
|
||||
return 0
|
||||
|
||||
@@ -470,14 +470,12 @@
|
||||
P.shot_from = src.name
|
||||
P.silenced = silenced
|
||||
|
||||
P.launch(target)
|
||||
P.old_style_target(target)
|
||||
P.fire()
|
||||
|
||||
last_shot = world.time
|
||||
|
||||
if(silenced)
|
||||
playsound(src, fire_sound, 10, 1)
|
||||
else
|
||||
playsound(src, fire_sound, 50, 1)
|
||||
play_fire_sound()
|
||||
|
||||
if(muzzle_flash)
|
||||
set_light(muzzle_flash)
|
||||
@@ -526,7 +524,8 @@
|
||||
return 2
|
||||
//just assume we can shoot through glass and stuff. No big deal, the player can just choose to not target someone
|
||||
//on the other side of a window if it makes a difference. Or if they run behind a window, too bad.
|
||||
return check_trajectory(target, user)
|
||||
if(check_trajectory(target, user))
|
||||
return 1 // Magic numbers are fun.
|
||||
|
||||
//called if there was no projectile to shoot
|
||||
/obj/item/weapon/gun/proc/handle_click_empty(mob/user)
|
||||
@@ -542,18 +541,20 @@
|
||||
flick(fire_anim, src)
|
||||
|
||||
if(silenced)
|
||||
if(reflex)
|
||||
user.visible_message(
|
||||
"<span class='reflex_shoot'><b>\The [user] fires \the [src][pointblank ? " point blank at \the [target]":""] by reflex!</b></span>",
|
||||
"<span class='reflex_shoot'>You fire \the [src] by reflex!</span>",
|
||||
"You hear a [fire_sound_text]!"
|
||||
to_chat(user, "<span class='warning'>You fire \the [src][pointblank ? " point blank at \the [target]":""][reflex ? " by reflex":""]</span>")
|
||||
for(var/mob/living/L in oview(2,user))
|
||||
if(L.stat)
|
||||
continue
|
||||
if(L.blinded)
|
||||
to_chat(L, "You hear a [fire_sound_text]!")
|
||||
continue
|
||||
to_chat(L, "<span class='danger'>\The [user] fires \the [src][pointblank ? " point blank at \the [target]":""][reflex ? " by reflex":""]!</span>")
|
||||
else
|
||||
user.visible_message(
|
||||
"<span class='danger'>\The [user] fires \the [src][pointblank ? " point blank at \the [target]":""][reflex ? " by reflex":""]!</span>",
|
||||
"<span class='warning'>You fire \the [src][pointblank ? " point blank at \the [target]":""][reflex ? " by reflex":""]!</span>",
|
||||
"You hear a [fire_sound_text]!"
|
||||
)
|
||||
else
|
||||
user.visible_message(
|
||||
"<span class='danger'>\The [user] fires \the [src][pointblank ? " point blank at \the [target]":""]!</span>",
|
||||
"<span class='warning'>You fire \the [src]!</span>",
|
||||
"You hear a [fire_sound_text]!"
|
||||
)
|
||||
|
||||
if(muzzle_flash)
|
||||
set_light(muzzle_flash)
|
||||
@@ -618,7 +619,7 @@
|
||||
|
||||
if(one_handed_penalty)
|
||||
if(!held_twohanded)
|
||||
acc_mod += -ceil(one_handed_penalty/2)
|
||||
acc_mod += -CEILING(one_handed_penalty/2, 1)
|
||||
disp_mod += one_handed_penalty*0.5 //dispersion per point of two-handedness
|
||||
|
||||
//Accuracy modifiers
|
||||
@@ -645,24 +646,17 @@
|
||||
/obj/item/weapon/gun/proc/process_projectile(obj/projectile, mob/user, atom/target, var/target_zone, var/params=null)
|
||||
var/obj/item/projectile/P = projectile
|
||||
if(!istype(P))
|
||||
return 0 //default behaviour only applies to true projectiles
|
||||
|
||||
if(params)
|
||||
P.set_clickpoint(params)
|
||||
return FALSE //default behaviour only applies to true projectiles
|
||||
|
||||
//shooting while in shock
|
||||
var/x_offset = 0
|
||||
var/y_offset = 0
|
||||
var/forcespread
|
||||
if(istype(user, /mob/living/carbon))
|
||||
var/mob/living/carbon/mob = user
|
||||
if(mob.shock_stage > 120)
|
||||
y_offset = rand(-2,2)
|
||||
x_offset = rand(-2,2)
|
||||
forcespread = rand(50, 50)
|
||||
else if(mob.shock_stage > 70)
|
||||
y_offset = rand(-1,1)
|
||||
x_offset = rand(-1,1)
|
||||
|
||||
var/launched = !P.launch_from_gun(target, user, src, target_zone, x_offset, y_offset)
|
||||
forcespread = rand(-25, 25)
|
||||
var/launched = !P.launch_from_gun(target, target_zone, user, params, null, forcespread, src)
|
||||
|
||||
if(launched)
|
||||
play_fire_sound(user, P)
|
||||
@@ -670,7 +664,13 @@
|
||||
return launched
|
||||
|
||||
/obj/item/weapon/gun/proc/play_fire_sound(var/mob/user, var/obj/item/projectile/P)
|
||||
var/shot_sound = (istype(P) && P.fire_sound)? P.fire_sound : fire_sound
|
||||
var/shot_sound = fire_sound
|
||||
|
||||
if(!shot_sound && istype(P) && P.fire_sound) // If the gun didn't have a fire_sound, but the projectile exists, and has a sound...
|
||||
shot_sound = P.fire_sound
|
||||
if(!shot_sound) // If there's still no sound...
|
||||
return
|
||||
|
||||
if(silenced)
|
||||
playsound(user, shot_sound, 10, 1)
|
||||
else
|
||||
@@ -693,11 +693,7 @@
|
||||
var/obj/item/projectile/in_chamber = consume_next_projectile()
|
||||
if (istype(in_chamber))
|
||||
user.visible_message("<span class = 'warning'>[user] pulls the trigger.</span>")
|
||||
var/shot_sound = in_chamber.fire_sound? in_chamber.fire_sound : fire_sound
|
||||
if(silenced)
|
||||
playsound(user, shot_sound, 10, 1)
|
||||
else
|
||||
playsound(user, shot_sound, 50, 1)
|
||||
play_fire_sound()
|
||||
if(istype(in_chamber, /obj/item/projectile/beam/lastertag))
|
||||
user.show_message("<span class = 'warning'>You feel rather silly, trying to commit suicide with a toy.</span>")
|
||||
mouthshoot = 0
|
||||
|
||||
@@ -1,206 +1,208 @@
|
||||
/obj/item/weapon/gun/energy
|
||||
name = "energy gun"
|
||||
desc = "A basic energy-based gun."
|
||||
icon_state = "energy"
|
||||
fire_sound = 'sound/weapons/Taser.ogg'
|
||||
fire_sound_text = "laser blast"
|
||||
|
||||
var/obj/item/weapon/cell/power_supply //What type of power cell this uses
|
||||
var/charge_cost = 240 //How much energy is needed to fire.
|
||||
|
||||
var/cell_type = /obj/item/weapon/cell/device/weapon
|
||||
projectile_type = /obj/item/projectile/beam/practice
|
||||
|
||||
var/modifystate
|
||||
var/charge_meter = 1 //if set, the icon state will be chosen based on the current charge
|
||||
|
||||
//self-recharging
|
||||
var/self_recharge = 0 //if set, the weapon will recharge itself
|
||||
var/use_external_power = 0 //if set, the weapon will look for an external power source to draw from, otherwise it recharges magically
|
||||
var/recharge_time = 4
|
||||
var/charge_tick = 0
|
||||
var/charge_delay = 75 //delay between firing and charging
|
||||
|
||||
var/battery_lock = 0 //If set, weapon cannot switch batteries
|
||||
|
||||
/obj/item/weapon/gun/energy/New()
|
||||
..()
|
||||
if(self_recharge)
|
||||
power_supply = new /obj/item/weapon/cell/device/weapon(src)
|
||||
processing_objects.Add(src)
|
||||
else
|
||||
if(cell_type)
|
||||
power_supply = new cell_type(src)
|
||||
else
|
||||
power_supply = null
|
||||
|
||||
update_icon()
|
||||
|
||||
/obj/item/weapon/gun/energy/Destroy()
|
||||
if(self_recharge)
|
||||
processing_objects.Remove(src)
|
||||
return ..()
|
||||
|
||||
/obj/item/weapon/gun/energy/get_cell()
|
||||
return power_supply
|
||||
|
||||
/obj/item/weapon/gun/energy/process()
|
||||
if(self_recharge) //Every [recharge_time] ticks, recharge a shot for the battery
|
||||
if(world.time > last_shot + charge_delay) //Doesn't work if you've fired recently
|
||||
if(!power_supply || power_supply.charge >= power_supply.maxcharge)
|
||||
return 0 // check if we actually need to recharge
|
||||
|
||||
charge_tick++
|
||||
if(charge_tick < recharge_time) return 0
|
||||
charge_tick = 0
|
||||
|
||||
var/rechargeamt = power_supply.maxcharge*0.2
|
||||
|
||||
if(use_external_power)
|
||||
var/obj/item/weapon/cell/external = get_external_power_supply()
|
||||
if(!external || !external.use(rechargeamt)) //Take power from the borg...
|
||||
return 0
|
||||
|
||||
power_supply.give(rechargeamt) //... to recharge 1/5th the battery
|
||||
update_icon()
|
||||
else
|
||||
charge_tick = 0
|
||||
return 1
|
||||
|
||||
/obj/item/weapon/gun/energy/attackby(var/obj/item/A as obj, mob/user as mob)
|
||||
..()
|
||||
|
||||
/obj/item/weapon/gun/energy/switch_firemodes(mob/user)
|
||||
if(..())
|
||||
update_icon()
|
||||
|
||||
/obj/item/weapon/gun/energy/emp_act(severity)
|
||||
..()
|
||||
update_icon()
|
||||
|
||||
/obj/item/weapon/gun/energy/consume_next_projectile()
|
||||
if(!power_supply) return null
|
||||
if(!ispath(projectile_type)) return null
|
||||
if(!power_supply.checked_use(charge_cost)) return null
|
||||
return new projectile_type(src)
|
||||
|
||||
/obj/item/weapon/gun/energy/proc/load_ammo(var/obj/item/C, mob/user)
|
||||
if(istype(C, /obj/item/weapon/cell))
|
||||
if(self_recharge || battery_lock)
|
||||
user << "<span class='notice'>[src] does not have a battery port.</span>"
|
||||
return
|
||||
if(istype(C, /obj/item/weapon/cell/device))
|
||||
var/obj/item/weapon/cell/device/P = C
|
||||
if(power_supply)
|
||||
user << "<span class='notice'>[src] already has a power cell.</span>"
|
||||
else
|
||||
user.visible_message("[user] is reloading [src].", "<span class='notice'>You start to insert [P] into [src].</span>")
|
||||
if(do_after(user, 10))
|
||||
user.remove_from_mob(P)
|
||||
power_supply = P
|
||||
P.loc = src
|
||||
user.visible_message("[user] inserts [P] into [src].", "<span class='notice'>You insert [P] into [src].</span>")
|
||||
playsound(src.loc, 'sound/weapons/flipblade.ogg', 50, 1)
|
||||
update_icon()
|
||||
update_held_icon()
|
||||
else
|
||||
user << "<span class='notice'>This cell is not fitted for [src].</span>"
|
||||
return
|
||||
|
||||
/obj/item/weapon/gun/energy/proc/unload_ammo(mob/user)
|
||||
if(self_recharge || battery_lock)
|
||||
user << "<span class='notice'>[src] does not have a battery port.</span>"
|
||||
return
|
||||
if(power_supply)
|
||||
user.put_in_hands(power_supply)
|
||||
power_supply.update_icon()
|
||||
user.visible_message("[user] removes [power_supply] from [src].", "<span class='notice'>You remove [power_supply] from [src].</span>")
|
||||
power_supply = null
|
||||
playsound(src.loc, 'sound/weapons/empty.ogg', 50, 1)
|
||||
update_icon()
|
||||
update_held_icon()
|
||||
else
|
||||
user << "<span class='notice'>[src] does not have a power cell.</span>"
|
||||
|
||||
/obj/item/weapon/gun/energy/attackby(var/obj/item/A as obj, mob/user as mob)
|
||||
..()
|
||||
load_ammo(A, user)
|
||||
|
||||
/obj/item/weapon/gun/energy/attack_hand(mob/user as mob)
|
||||
if(user.get_inactive_hand() == src)
|
||||
unload_ammo(user)
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/item/weapon/gun/energy/proc/get_external_power_supply()
|
||||
if(isrobot(src.loc))
|
||||
var/mob/living/silicon/robot/R = src.loc
|
||||
return R.cell
|
||||
if(istype(src.loc, /obj/item/rig_module))
|
||||
var/obj/item/rig_module/module = src.loc
|
||||
if(module.holder && module.holder.wearer)
|
||||
var/mob/living/carbon/human/H = module.holder.wearer
|
||||
if(istype(H) && H.back)
|
||||
var/obj/item/weapon/rig/suit = H.back
|
||||
if(istype(suit))
|
||||
return suit.cell
|
||||
return null
|
||||
|
||||
/obj/item/weapon/gun/energy/examine(mob/user)
|
||||
. = ..()
|
||||
if(power_supply)
|
||||
<<<<<<< HEAD
|
||||
var/shots_remaining = round(power_supply.charge / charge_cost)
|
||||
user << "Has [shots_remaining] shot\s remaining."
|
||||
=======
|
||||
if(charge_cost)
|
||||
var/shots_remaining = round(power_supply.charge / max(1, charge_cost)) // Paranoia
|
||||
to_chat(user, "Has [shots_remaining] shot\s remaining.")
|
||||
else
|
||||
to_chat(user, "Has infinite shots remaining.")
|
||||
>>>>>>> 2ce4013... Merge pull request #5944 from Mechoid/Fix_DivZero
|
||||
else
|
||||
to_chat(user, "Does not have a power cell.")
|
||||
return
|
||||
|
||||
/obj/item/weapon/gun/energy/update_icon(var/ignore_inhands)
|
||||
if(power_supply == null)
|
||||
if(modifystate)
|
||||
icon_state = "[modifystate]_open"
|
||||
else
|
||||
icon_state = "[initial(icon_state)]_open"
|
||||
return
|
||||
else if(charge_meter)
|
||||
var/ratio = power_supply.charge / power_supply.maxcharge
|
||||
|
||||
//make sure that rounding down will not give us the empty state even if we have charge for a shot left.
|
||||
if(power_supply.charge < charge_cost)
|
||||
ratio = 0
|
||||
else
|
||||
ratio = max(round(ratio, 0.25) * 100, 25)
|
||||
|
||||
if(modifystate)
|
||||
icon_state = "[modifystate][ratio]"
|
||||
else
|
||||
icon_state = "[initial(icon_state)][ratio]"
|
||||
if(!ignore_inhands) update_held_icon()
|
||||
|
||||
/obj/item/weapon/gun/energy/proc/start_recharge()
|
||||
if(power_supply == null)
|
||||
power_supply = new /obj/item/weapon/cell/device/weapon(src)
|
||||
self_recharge = 1
|
||||
processing_objects.Add(src)
|
||||
update_icon()
|
||||
|
||||
/obj/item/weapon/gun/energy/get_description_interaction()
|
||||
var/list/results = list()
|
||||
|
||||
if(!battery_lock && !self_recharge)
|
||||
if(power_supply)
|
||||
results += "[desc_panel_image("offhand")]to remove the weapon cell."
|
||||
else
|
||||
results += "[desc_panel_image("weapon cell")]to add a new weapon cell."
|
||||
|
||||
results += ..()
|
||||
|
||||
/obj/item/weapon/gun/energy
|
||||
name = "energy gun"
|
||||
desc = "A basic energy-based gun."
|
||||
icon_state = "energy"
|
||||
fire_sound_text = "laser blast"
|
||||
|
||||
var/obj/item/weapon/cell/power_supply //What type of power cell this uses
|
||||
var/charge_cost = 240 //How much energy is needed to fire.
|
||||
|
||||
var/accept_cell_type = /obj/item/weapon/cell/device
|
||||
var/cell_type = /obj/item/weapon/cell/device/weapon
|
||||
projectile_type = /obj/item/projectile/beam/practice
|
||||
|
||||
var/modifystate
|
||||
var/charge_meter = 1 //if set, the icon state will be chosen based on the current charge
|
||||
|
||||
//self-recharging
|
||||
var/self_recharge = 0 //if set, the weapon will recharge itself
|
||||
var/use_external_power = 0 //if set, the weapon will look for an external power source to draw from, otherwise it recharges magically
|
||||
var/recharge_time = 4
|
||||
var/charge_tick = 0
|
||||
var/charge_delay = 75 //delay between firing and charging
|
||||
|
||||
var/battery_lock = 0 //If set, weapon cannot switch batteries
|
||||
|
||||
/obj/item/weapon/gun/energy/New()
|
||||
..()
|
||||
if(self_recharge)
|
||||
power_supply = new /obj/item/weapon/cell/device/weapon(src)
|
||||
START_PROCESSING(SSobj, src)
|
||||
else
|
||||
if(cell_type)
|
||||
power_supply = new cell_type(src)
|
||||
else
|
||||
power_supply = null
|
||||
|
||||
update_icon()
|
||||
|
||||
/obj/item/weapon/gun/energy/Destroy()
|
||||
if(self_recharge)
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
return ..()
|
||||
|
||||
/obj/item/weapon/gun/energy/get_cell()
|
||||
return power_supply
|
||||
|
||||
/obj/item/weapon/gun/energy/process()
|
||||
if(self_recharge) //Every [recharge_time] ticks, recharge a shot for the battery
|
||||
if(world.time > last_shot + charge_delay) //Doesn't work if you've fired recently
|
||||
if(!power_supply || power_supply.charge >= power_supply.maxcharge)
|
||||
return 0 // check if we actually need to recharge
|
||||
|
||||
charge_tick++
|
||||
if(charge_tick < recharge_time) return 0
|
||||
charge_tick = 0
|
||||
|
||||
var/rechargeamt = power_supply.maxcharge*0.2
|
||||
|
||||
if(use_external_power)
|
||||
var/obj/item/weapon/cell/external = get_external_power_supply()
|
||||
if(!external || !external.use(rechargeamt)) //Take power from the borg...
|
||||
return 0
|
||||
|
||||
power_supply.give(rechargeamt) //... to recharge 1/5th the battery
|
||||
update_icon()
|
||||
else
|
||||
charge_tick = 0
|
||||
return 1
|
||||
|
||||
/obj/item/weapon/gun/energy/attackby(var/obj/item/A as obj, mob/user as mob)
|
||||
..()
|
||||
|
||||
/obj/item/weapon/gun/energy/switch_firemodes(mob/user)
|
||||
if(..())
|
||||
update_icon()
|
||||
|
||||
/obj/item/weapon/gun/energy/emp_act(severity)
|
||||
..()
|
||||
update_icon()
|
||||
|
||||
/obj/item/weapon/gun/energy/consume_next_projectile()
|
||||
if(!power_supply) return null
|
||||
if(!ispath(projectile_type)) return null
|
||||
if(!power_supply.checked_use(charge_cost)) return null
|
||||
return new projectile_type(src)
|
||||
|
||||
/obj/item/weapon/gun/energy/proc/load_ammo(var/obj/item/C, mob/user)
|
||||
if(istype(C, /obj/item/weapon/cell))
|
||||
if(self_recharge || battery_lock)
|
||||
user << "<span class='notice'>[src] does not have a battery port.</span>"
|
||||
return
|
||||
if(istype(C, accept_cell_type))
|
||||
var/obj/item/weapon/cell/P = C
|
||||
if(power_supply)
|
||||
user << "<span class='notice'>[src] already has a power cell.</span>"
|
||||
else
|
||||
user.visible_message("[user] is reloading [src].", "<span class='notice'>You start to insert [P] into [src].</span>")
|
||||
if(do_after(user, 5 * P.w_class))
|
||||
user.remove_from_mob(P)
|
||||
power_supply = P
|
||||
P.loc = src
|
||||
user.visible_message("[user] inserts [P] into [src].", "<span class='notice'>You insert [P] into [src].</span>")
|
||||
playsound(src.loc, 'sound/weapons/flipblade.ogg', 50, 1)
|
||||
update_icon()
|
||||
update_held_icon()
|
||||
else
|
||||
user << "<span class='notice'>This cell is not fitted for [src].</span>"
|
||||
return
|
||||
|
||||
/obj/item/weapon/gun/energy/proc/unload_ammo(mob/user)
|
||||
if(self_recharge || battery_lock)
|
||||
user << "<span class='notice'>[src] does not have a battery port.</span>"
|
||||
return
|
||||
if(power_supply)
|
||||
user.put_in_hands(power_supply)
|
||||
power_supply.update_icon()
|
||||
user.visible_message("[user] removes [power_supply] from [src].", "<span class='notice'>You remove [power_supply] from [src].</span>")
|
||||
power_supply = null
|
||||
playsound(src.loc, 'sound/weapons/empty.ogg', 50, 1)
|
||||
update_icon()
|
||||
update_held_icon()
|
||||
else
|
||||
user << "<span class='notice'>[src] does not have a power cell.</span>"
|
||||
|
||||
/obj/item/weapon/gun/energy/attackby(var/obj/item/A as obj, mob/user as mob)
|
||||
..()
|
||||
load_ammo(A, user)
|
||||
|
||||
/obj/item/weapon/gun/energy/attack_hand(mob/user as mob)
|
||||
if(user.get_inactive_hand() == src)
|
||||
unload_ammo(user)
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/item/weapon/gun/energy/proc/get_external_power_supply()
|
||||
if(isrobot(src.loc))
|
||||
var/mob/living/silicon/robot/R = src.loc
|
||||
return R.cell
|
||||
if(istype(src.loc, /obj/item/rig_module))
|
||||
var/obj/item/rig_module/module = src.loc
|
||||
if(module.holder && module.holder.wearer)
|
||||
var/mob/living/carbon/human/H = module.holder.wearer
|
||||
if(istype(H) && H.back)
|
||||
var/obj/item/weapon/rig/suit = H.back
|
||||
if(istype(suit))
|
||||
return suit.cell
|
||||
return null
|
||||
|
||||
/obj/item/weapon/gun/energy/examine(mob/user)
|
||||
. = ..()
|
||||
if(power_supply)
|
||||
if(charge_cost)
|
||||
var/shots_remaining = round(power_supply.charge / max(1, charge_cost)) // Paranoia
|
||||
to_chat(user, "Has [shots_remaining] shot\s remaining.")
|
||||
else
|
||||
to_chat(user, "Has infinite shots remaining.")
|
||||
else
|
||||
to_chat(user, "Does not have a power cell.")
|
||||
return
|
||||
|
||||
/obj/item/weapon/gun/energy/update_icon(var/ignore_inhands)
|
||||
if(power_supply == null)
|
||||
if(modifystate)
|
||||
icon_state = "[modifystate]_open"
|
||||
else
|
||||
icon_state = "[initial(icon_state)]_open"
|
||||
return
|
||||
else if(charge_meter)
|
||||
var/ratio = power_supply.charge / power_supply.maxcharge
|
||||
|
||||
//make sure that rounding down will not give us the empty state even if we have charge for a shot left.
|
||||
if(power_supply.charge < charge_cost)
|
||||
ratio = 0
|
||||
else
|
||||
ratio = max(round(ratio, 0.25) * 100, 25)
|
||||
|
||||
if(modifystate)
|
||||
icon_state = "[modifystate][ratio]"
|
||||
else
|
||||
icon_state = "[initial(icon_state)][ratio]"
|
||||
|
||||
else if(power_supply)
|
||||
if(modifystate)
|
||||
icon_state = "[modifystate]"
|
||||
else
|
||||
icon_state = "[initial(icon_state)]"
|
||||
|
||||
if(!ignore_inhands) update_held_icon()
|
||||
|
||||
/obj/item/weapon/gun/energy/proc/start_recharge()
|
||||
if(power_supply == null)
|
||||
power_supply = new /obj/item/weapon/cell/device/weapon(src)
|
||||
self_recharge = 1
|
||||
START_PROCESSING(SSobj, src)
|
||||
update_icon()
|
||||
|
||||
/obj/item/weapon/gun/energy/get_description_interaction()
|
||||
var/list/results = list()
|
||||
|
||||
if(!battery_lock && !self_recharge)
|
||||
if(power_supply)
|
||||
results += "[desc_panel_image("offhand")]to remove the weapon cell."
|
||||
else
|
||||
results += "[desc_panel_image("weapon cell")]to add a new weapon cell."
|
||||
|
||||
results += ..()
|
||||
|
||||
return results
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Contains weapons primarily using the 'grappling hook' projectiles.
|
||||
*/
|
||||
|
||||
/obj/item/weapon/gun/energy/hooklauncher
|
||||
name = "gravity whip"
|
||||
desc = "A large, strange gauntlet."
|
||||
icon_state = "gravwhip"
|
||||
item_state = "gravwhip"
|
||||
fire_sound_text = "laser blast"
|
||||
|
||||
fire_delay = 15
|
||||
charge_cost = 300
|
||||
|
||||
cell_type = /obj/item/weapon/cell/device/weapon
|
||||
projectile_type = /obj/item/projectile/energy/hook
|
||||
|
||||
// An easily concealable not-ripoff version. It would be silenced, if it didn't make it blatant you're the one using it.
|
||||
|
||||
/obj/item/weapon/gun/energy/hooklauncher/ring
|
||||
name = "ominous ring"
|
||||
desc = "A small ring with strange symbols engraved upon it."
|
||||
icon = 'icons/obj/clothing/rings.dmi'
|
||||
icon_state = "seal-signet"
|
||||
item_state = "concealed"
|
||||
|
||||
w_class = ITEMSIZE_TINY
|
||||
|
||||
cell_type = /obj/item/weapon/cell/device/weapon/recharge/alien
|
||||
battery_lock = TRUE
|
||||
charge_cost = 400
|
||||
charge_meter = FALSE
|
||||
|
||||
projectile_type = /obj/item/projectile/energy/hook/ring
|
||||
|
||||
firemodes = list(
|
||||
list(mode_name="manipulate", fire_delay=15, projectile_type=/obj/item/projectile/energy/hook/ring, charge_cost = 400),
|
||||
list(mode_name="battle", fire_delay=8, projectile_type=/obj/item/projectile/beam/xray, charge_cost = 260),
|
||||
)
|
||||
@@ -100,7 +100,7 @@
|
||||
damage = 32
|
||||
damage_type = BRUTE
|
||||
check_armour = "bomb"
|
||||
kill_count = 3 // Our "range" var is named "kill_count". Yes it is.
|
||||
range = 3 // Our "range" var is named "kill_count". Yes it is.
|
||||
|
||||
var/pressure_decrease = 0.25
|
||||
var/turf_aoe = FALSE
|
||||
@@ -219,7 +219,7 @@
|
||||
cost = 24 //so you can fit four plus a tracer cosmetic
|
||||
|
||||
/obj/item/borg/upgrade/modkit/range/modify_projectile(obj/item/projectile/kinetic/K)
|
||||
K.kill_count += modifier
|
||||
K.range += modifier
|
||||
|
||||
|
||||
//Damage
|
||||
|
||||
@@ -130,7 +130,6 @@
|
||||
icon_state = "sniper"
|
||||
item_state = "sniper"
|
||||
item_state_slots = list(slot_r_hand_str = "z8carbine", slot_l_hand_str = "z8carbine") //placeholder
|
||||
fire_sound = 'sound/weapons/gauss_shoot.ogg'
|
||||
origin_tech = list(TECH_COMBAT = 6, TECH_MATERIAL = 5, TECH_POWER = 4)
|
||||
projectile_type = /obj/item/projectile/beam/sniper
|
||||
slot_flags = SLOT_BACK
|
||||
@@ -151,6 +150,41 @@
|
||||
|
||||
toggle_scope(2.0)
|
||||
|
||||
/obj/item/weapon/gun/energy/monorifle
|
||||
name = "antique mono-rifle"
|
||||
desc = "An old laser rifle. This one can only fire once before requiring recharging."
|
||||
description_fluff = "Modeled after ancient hunting rifles, this rifle was dubbed the 'Rainy Day Special' by some, due to its use as some barmens' fight-stopper of choice. One shot is all it takes, or so they say."
|
||||
icon_state = "eshotgun"
|
||||
item_state = "shotgun"
|
||||
origin_tech = list(TECH_COMBAT = 6, TECH_MATERIAL = 4, TECH_POWER = 3)
|
||||
projectile_type = /obj/item/projectile/beam/sniper
|
||||
slot_flags = SLOT_BACK
|
||||
charge_cost = 1300
|
||||
fire_delay = 20
|
||||
force = 8
|
||||
w_class = ITEMSIZE_LARGE
|
||||
accuracy = 10
|
||||
scoped_accuracy = 15
|
||||
var/scope_multiplier = 1.5
|
||||
|
||||
/obj/item/weapon/gun/energy/monorifle/verb/sights()
|
||||
set category = "Object"
|
||||
set name = "Aim Down Sights"
|
||||
set popup_menu = 1
|
||||
|
||||
toggle_scope(scope_multiplier)
|
||||
|
||||
/obj/item/weapon/gun/energy/monorifle/combat
|
||||
name = "combat mono-rifle"
|
||||
desc = "A modernized version of the mono-rifle. This one can fire twice before requiring recharging."
|
||||
description_fluff = "A modern design produced by a company once working from Saint Columbia, based on the antique mono-rifle 'Rainy Day Special' design."
|
||||
icon_state = "ecshotgun"
|
||||
item_state = "cshotgun"
|
||||
charge_cost = 1000
|
||||
force = 12
|
||||
accuracy = 0
|
||||
scoped_accuracy = 20
|
||||
|
||||
////////Laser Tag////////////////////
|
||||
|
||||
/obj/item/weapon/gun/energy/lasertag
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
/obj/item/weapon/gun/energy/pulse_rifle/destroyer
|
||||
name = "pulse destroyer"
|
||||
desc = "A heavy-duty, pulse-based energy weapon. Because of its complexity and cost, it is rarely seen in use except by specialists."
|
||||
fire_sound='sound/weapons/gauss_shoot.ogg'
|
||||
projectile_type=/obj/item/projectile/beam/pulse
|
||||
charge_cost = 120
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
desc = "A gun that discharges high amounts of controlled radiation to slowly break a target into component elements."
|
||||
icon_state = "decloner"
|
||||
item_state = "decloner"
|
||||
fire_sound = 'sound/weapons/pulse3.ogg'
|
||||
origin_tech = list(TECH_COMBAT = 5, TECH_MATERIAL = 4, TECH_POWER = 3)
|
||||
projectile_type = /obj/item/projectile/energy/declone
|
||||
|
||||
@@ -39,7 +38,6 @@
|
||||
desc = "A tool that discharges controlled radiation which induces mutation in plant cells."
|
||||
icon_state = "floramut100"
|
||||
item_state = "floramut"
|
||||
fire_sound = 'sound/effects/stealthoff.ogg'
|
||||
projectile_type = /obj/item/projectile/energy/floramut
|
||||
origin_tech = list(TECH_MATERIAL = 2, TECH_BIO = 3, TECH_POWER = 3)
|
||||
modifystate = "floramut"
|
||||
@@ -112,13 +110,11 @@
|
||||
desc = "A custom-built weapon of some kind."
|
||||
icon_state = "xray"
|
||||
projectile_type = /obj/item/projectile/beam/mindflayer
|
||||
fire_sound = 'sound/weapons/Laser.ogg'
|
||||
|
||||
/obj/item/weapon/gun/energy/toxgun
|
||||
name = "phoron pistol"
|
||||
desc = "A specialized firearm designed to fire lethal bolts of phoron."
|
||||
icon_state = "toxgun"
|
||||
fire_sound = 'sound/effects/stealthoff.ogg'
|
||||
w_class = ITEMSIZE_NORMAL
|
||||
origin_tech = list(TECH_COMBAT = 5, TECH_PHORON = 4)
|
||||
projectile_type = /obj/item/projectile/energy/phoron
|
||||
@@ -131,7 +127,6 @@
|
||||
icon = 'icons/obj/gun.dmi'
|
||||
item_icons = null
|
||||
icon_state = "staffofchange"
|
||||
fire_sound = 'sound/weapons/emitter.ogg'
|
||||
flags = CONDUCT
|
||||
slot_flags = SLOT_BACK
|
||||
w_class = ITEMSIZE_LARGE
|
||||
@@ -155,13 +150,13 @@
|
||||
else
|
||||
src.visible_message("*fizzle*")
|
||||
playsound(src.loc, 'sound/effects/sparks1.ogg', 100, 1)
|
||||
|
||||
/*
|
||||
/obj/item/weapon/gun/energy/staff/animate
|
||||
name = "staff of animation"
|
||||
desc = "An artifact that spits bolts of life force, which causes objects which are hit by it to animate and come to life! This magic doesn't affect machines."
|
||||
projectile_type = /obj/item/projectile/animate
|
||||
charge_cost = 240
|
||||
|
||||
*/
|
||||
obj/item/weapon/gun/energy/staff/focus
|
||||
name = "mental focus"
|
||||
desc = "An artifact that channels the will of the user into destructive bolts of force. If you aren't careful with it, you might poke someone's brain out."
|
||||
@@ -186,7 +181,7 @@ obj/item/weapon/gun/energy/staff/focus
|
||||
desc = "A massive weapon designed to pressure the opposition by raining down a torrent of energy pellets."
|
||||
icon_state = "dakkalaser"
|
||||
item_state = "dakkalaser"
|
||||
fire_sound = 'sound/weapons/Laser.ogg'
|
||||
wielded_item_state = "dakkalaser-wielded"
|
||||
w_class = ITEMSIZE_HUGE
|
||||
charge_cost = 24 // 100 shots, it's a spray and pray (to RNGesus) weapon.
|
||||
projectile_type = /obj/item/projectile/energy/blue_pellet
|
||||
@@ -200,4 +195,93 @@ obj/item/weapon/gun/energy/staff/focus
|
||||
list(mode_name="single shot", burst = 1, burst_accuracy = list(75), dispersion = list(0), charge_cost = 24),
|
||||
list(mode_name="five shot burst", burst = 5, burst_accuracy = list(75,75,75,75,75), dispersion = list(1,1,1,1,1)),
|
||||
list(mode_name="ten shot burst", burst = 10, burst_accuracy = list(75,75,75,75,75,75,75,75,75,75), dispersion = list(2,2,2,2,2,2,2,2,2,2)),
|
||||
)
|
||||
)
|
||||
|
||||
/obj/item/weapon/gun/energy/maghowitzer
|
||||
name = "portable MHD howitzer"
|
||||
desc = "A massive weapon designed to destroy fortifications with a stream of molten tungsten."
|
||||
description_fluff = "A weapon designed by joint cooperation of NanoTrasen, Hephaestus, and SCG scientists. Everything else is red tape and black highlighters."
|
||||
description_info = "This weapon requires a wind-up period before being able to fire. Clicking on a target will create a beam between you and its turf, starting the timer. Upon completion, it will fire at the designated location."
|
||||
icon_state = "mhdhowitzer"
|
||||
item_state = "mhdhowitzer"
|
||||
wielded_item_state = "mhdhowitzer-wielded"
|
||||
w_class = ITEMSIZE_HUGE
|
||||
|
||||
charge_cost = 10000 // Uses large cells, can at max have 3 shots.
|
||||
projectile_type = /obj/item/projectile/beam/tungsten
|
||||
cell_type = /obj/item/weapon/cell/high
|
||||
accept_cell_type = /obj/item/weapon/cell
|
||||
|
||||
accuracy = 75
|
||||
charge_meter = 0
|
||||
one_handed_penalty = 30
|
||||
|
||||
var/power_cycle = FALSE
|
||||
|
||||
/obj/item/weapon/gun/energy/maghowitzer/proc/pick_random_target(var/turf/T)
|
||||
var/foundmob = FALSE
|
||||
var/foundmobs = list()
|
||||
for(var/mob/living/L in T.contents)
|
||||
foundmob = TRUE
|
||||
foundmobs += L
|
||||
if(foundmob)
|
||||
var/return_target = pick(foundmobs)
|
||||
return return_target
|
||||
return FALSE
|
||||
|
||||
/obj/item/weapon/gun/energy/maghowitzer/attack(atom/A, mob/living/user, def_zone)
|
||||
if(power_cycle)
|
||||
to_chat(user, "<span class='notice'>\The [src] is already powering up!</span>")
|
||||
return 0
|
||||
var/turf/target_turf = get_turf(A)
|
||||
var/beameffect = user.Beam(target_turf,icon_state="sat_beam",icon='icons/effects/beam.dmi',time=31, maxdistance=10,beam_type=/obj/effect/ebeam,beam_sleep_time=3)
|
||||
if(beameffect)
|
||||
user.visible_message("<span class='cult'>[user] aims \the [src] at \the [A].</span>")
|
||||
if(power_supply && power_supply.charge >= charge_cost) //Do a delay for pointblanking too.
|
||||
power_cycle = TRUE
|
||||
if(do_after(user, 30))
|
||||
if(A.loc == target_turf)
|
||||
..(A, user, def_zone)
|
||||
else
|
||||
var/rand_target = pick_random_target(target_turf)
|
||||
if(rand_target)
|
||||
..(rand_target, user, def_zone)
|
||||
else
|
||||
..(target_turf, user, def_zone)
|
||||
else
|
||||
if(beameffect)
|
||||
qdel(beameffect)
|
||||
power_cycle = FALSE
|
||||
else
|
||||
..(A, user, def_zone) //If it can't fire, just bash with no delay.
|
||||
|
||||
/obj/item/weapon/gun/energy/maghowitzer/afterattack(atom/A, mob/living/user, adjacent, params)
|
||||
if(power_cycle)
|
||||
to_chat(user, "<span class='notice'>\The [src] is already powering up!</span>")
|
||||
return 0
|
||||
|
||||
var/turf/target_turf = get_turf(A)
|
||||
|
||||
var/beameffect = user.Beam(target_turf,icon_state="sat_beam",icon='icons/effects/beam.dmi',time=31, maxdistance=10,beam_type=/obj/effect/ebeam,beam_sleep_time=3)
|
||||
|
||||
if(beameffect)
|
||||
user.visible_message("<span class='cult'>[user] aims \the [src] at \the [A].</span>")
|
||||
|
||||
if(!power_cycle)
|
||||
power_cycle = TRUE
|
||||
if(do_after(user, 30))
|
||||
if(A.loc == target_turf)
|
||||
..(A, user, adjacent, params)
|
||||
else
|
||||
var/rand_target = pick_random_target(target_turf)
|
||||
if(rand_target)
|
||||
..(rand_target, user, adjacent, params)
|
||||
else
|
||||
..(target_turf, user, adjacent, params)
|
||||
else
|
||||
if(beameffect)
|
||||
qdel(beameffect)
|
||||
handle_click_empty(user)
|
||||
power_cycle = FALSE
|
||||
else
|
||||
to_chat(user, "<span class='notice'>\The [src] is already powering up!</span>")
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
icon_state = "taser"
|
||||
item_state = null //so the human update icon uses the icon_state instead.
|
||||
projectile_type = /obj/item/projectile/beam/stun
|
||||
charge_cost = 480
|
||||
|
||||
/obj/item/weapon/gun/energy/taser/mounted
|
||||
name = "mounted taser gun"
|
||||
@@ -36,7 +37,6 @@
|
||||
matter = list(DEFAULT_WALL_MATERIAL = 2000)
|
||||
slot_flags = SLOT_BELT | SLOT_HOLSTER
|
||||
silenced = 1
|
||||
fire_sound = 'sound/weapons/Genhit.ogg'
|
||||
projectile_type = /obj/item/projectile/energy/bolt
|
||||
charge_cost = 480
|
||||
cell_type = /obj/item/weapon/cell/device/weapon/recharge
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/obj/item/weapon/gun/energy/temperature
|
||||
name = "temperature gun"
|
||||
icon_state = "freezegun"
|
||||
fire_sound = 'sound/weapons/pulse3.ogg'
|
||||
desc = "A gun that can add or remove heat from entities it hits. In other words, it can fire 'cold', and 'hot' beams."
|
||||
charge_cost = 240
|
||||
origin_tech = list(TECH_COMBAT = 3, TECH_MATERIAL = 4, TECH_POWER = 3, TECH_MAGNET = 2)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
w_class = ITEMSIZE_LARGE
|
||||
force = 10
|
||||
|
||||
fire_sound = 'sound/weapons/empty.ogg'
|
||||
fire_sound = 'sound/weapons/grenade_launcher.ogg'
|
||||
fire_sound_text = "a metallic thunk"
|
||||
recoil = 0
|
||||
throw_distance = 7
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
flags = CONDUCT
|
||||
fire_sound_text = "a loud whoosh of moving air"
|
||||
fire_delay = 50
|
||||
fire_sound = 'sound/weapons/tablehit1.ogg'
|
||||
fire_sound = 'sound/weapons/grenade_launcher.ogg' // Formerly tablehit1.ogg but I like this better -Ace
|
||||
|
||||
var/fire_pressure // Used in fire checks/pressure checks.
|
||||
var/max_w_class = ITEMSIZE_NORMAL // Hopper intake size.
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
/obj/item/weapon/gun/magnetic/matfed
|
||||
name = "portable phoron bore"
|
||||
desc = "A large man-portable tunnel bore, using phorogenic plasma blasts. Point away from user."
|
||||
description_fluff = "An aging Grayson Manufactories mining tool used for rapidly digging through rock. Mass production was discontinued when many of the devices were stolen and used to break into a high security facility by Boiling Point drones."
|
||||
description_antag = "This device is exceptional at breaking down walls, though it is incredibly loud when doing so."
|
||||
description_info = "The projectile of this tool will travel six tiles before dissipating, excavating mineral walls as it does so. It can be reloaded with phoron sheets."
|
||||
|
||||
icon_state = "bore"
|
||||
item_state = "bore"
|
||||
wielded_item_state = "bore-wielded"
|
||||
one_handed_penalty = 5
|
||||
|
||||
projectile_type = /obj/item/projectile/bullet/magnetic/bore
|
||||
|
||||
gun_unreliable = 0
|
||||
|
||||
power_cost = 750
|
||||
load_type = /obj/item/stack/material
|
||||
var/mat_storage = 0 // How much material is stored inside? Input in multiples of 2000 as per auto/protolathe.
|
||||
var/max_mat_storage = 8000 // How much material can be stored inside?
|
||||
var/mat_cost = 500 // How much material is used per-shot?
|
||||
var/ammo_material = MAT_PHORON
|
||||
var/loading = FALSE
|
||||
|
||||
/obj/item/weapon/gun/magnetic/matfed/examine(mob/user)
|
||||
. = ..()
|
||||
show_ammo(user)
|
||||
|
||||
/obj/item/weapon/gun/magnetic/matfed/update_icon()
|
||||
var/list/overlays_to_add = list()
|
||||
if(removable_components)
|
||||
if(cell)
|
||||
overlays_to_add += image(icon, "[icon_state]_cell")
|
||||
if(capacitor)
|
||||
overlays_to_add += image(icon, "[icon_state]_capacitor")
|
||||
if(!cell || !capacitor)
|
||||
overlays_to_add += image(icon, "[icon_state]_red")
|
||||
else if(capacitor.charge < power_cost)
|
||||
overlays_to_add += image(icon, "[icon_state]_amber")
|
||||
else
|
||||
overlays_to_add += image(icon, "[icon_state]_green")
|
||||
if(mat_storage)
|
||||
overlays_to_add += image(icon, "[icon_state]_loaded")
|
||||
|
||||
overlays = overlays_to_add
|
||||
..()
|
||||
/obj/item/weapon/gun/magnetic/matfed/attack_hand(var/mob/user) // It doesn't keep a loaded item inside.
|
||||
if(user.get_inactive_hand() == src)
|
||||
var/obj/item/removing
|
||||
|
||||
if(cell && removable_components)
|
||||
removing = cell
|
||||
cell = null
|
||||
|
||||
if(removing)
|
||||
removing.forceMove(get_turf(src))
|
||||
user.put_in_hands(removing)
|
||||
user.visible_message("<span class='notice'>\The [user] removes \the [removing] from \the [src].</span>")
|
||||
playsound(loc, 'sound/machines/click.ogg', 10, 1)
|
||||
update_icon()
|
||||
return
|
||||
. = ..()
|
||||
|
||||
/obj/item/weapon/gun/magnetic/matfed/check_ammo()
|
||||
if(mat_storage - mat_cost >= 0)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/obj/item/weapon/gun/magnetic/matfed/use_ammo()
|
||||
mat_storage -= mat_cost
|
||||
|
||||
/obj/item/weapon/gun/magnetic/matfed/show_ammo(var/mob/user)
|
||||
if(mat_storage)
|
||||
to_chat(user, "<span class='notice'>It has [mat_storage] out of [max_mat_storage] units of [ammo_material] loaded.</span>")
|
||||
|
||||
/obj/item/weapon/gun/magnetic/matfed/attackby(var/obj/item/thing, var/mob/user)
|
||||
if(removable_components)
|
||||
if(istype(thing, /obj/item/weapon/cell))
|
||||
if(cell)
|
||||
to_chat(user, "<span class='warning'>\The [src] already has \a [cell] installed.</span>")
|
||||
return
|
||||
cell = thing
|
||||
user.drop_from_inventory(cell)
|
||||
cell.forceMove(src)
|
||||
playsound(loc, 'sound/machines/click.ogg', 10, 1)
|
||||
user.visible_message("<span class='notice'>\The [user] slots \the [cell] into \the [src].</span>")
|
||||
update_icon()
|
||||
return
|
||||
|
||||
if(thing.is_screwdriver())
|
||||
if(!capacitor)
|
||||
to_chat(user, "<span class='warning'>\The [src] has no capacitor installed.</span>")
|
||||
return
|
||||
capacitor.forceMove(get_turf(src))
|
||||
user.put_in_hands(capacitor)
|
||||
user.visible_message("<span class='notice'>\The [user] unscrews \the [capacitor] from \the [src].</span>")
|
||||
playsound(loc, 'sound/items/Screwdriver.ogg', 50, 1)
|
||||
capacitor = null
|
||||
update_icon()
|
||||
return
|
||||
|
||||
if(istype(thing, /obj/item/weapon/stock_parts/capacitor))
|
||||
if(capacitor)
|
||||
to_chat(user, "<span class='warning'>\The [src] already has \a [capacitor] installed.</span>")
|
||||
return
|
||||
capacitor = thing
|
||||
user.drop_from_inventory(capacitor)
|
||||
capacitor.forceMove(src)
|
||||
playsound(loc, 'sound/machines/click.ogg', 10, 1)
|
||||
power_per_tick = (power_cost*0.15) * capacitor.rating
|
||||
user.visible_message("<span class='notice'>\The [user] slots \the [capacitor] into \the [src].</span>")
|
||||
update_icon()
|
||||
return
|
||||
|
||||
if(istype(thing, load_type))
|
||||
loading = TRUE
|
||||
var/obj/item/stack/material/M = thing
|
||||
|
||||
if(!M.material || M.material.name != ammo_material)
|
||||
return
|
||||
|
||||
if(mat_storage + 2000 > max_mat_storage)
|
||||
to_chat(user, "<span class='warning'>\The [src] cannot hold more [ammo_material].</span>")
|
||||
return
|
||||
|
||||
var/can_hold_val = 0
|
||||
while(can_hold_val < round(max_mat_storage / 2000))
|
||||
if(mat_storage + 2000 <= max_mat_storage && do_after(user,1.5 SECONDS))
|
||||
can_hold_val ++
|
||||
mat_storage += 2000
|
||||
playsound(loc, 'sound/effects/phasein.ogg', 15, 1)
|
||||
else
|
||||
loading = FALSE
|
||||
break
|
||||
M.use(can_hold_val)
|
||||
|
||||
user.visible_message("<span class='notice'>\The [user] loads \the [src] with \the [M].</span>")
|
||||
playsound(loc, 'sound/weapons/flipblade.ogg', 50, 1)
|
||||
update_icon()
|
||||
return
|
||||
. = ..()
|
||||
@@ -20,17 +20,15 @@
|
||||
var/power_cost = 950 // Cost per fire, should consume almost an entire basic cell.
|
||||
var/power_per_tick // Capacitor charge per process(). Updated based on capacitor rating.
|
||||
|
||||
fire_sound = 'sound/weapons/railgun.ogg'
|
||||
|
||||
/obj/item/weapon/gun/magnetic/New()
|
||||
processing_objects.Add(src)
|
||||
START_PROCESSING(SSobj, src)
|
||||
if(capacitor)
|
||||
power_per_tick = (power_cost*0.15) * capacitor.rating
|
||||
update_icon()
|
||||
. = ..()
|
||||
|
||||
/obj/item/weapon/gun/magnetic/Destroy()
|
||||
processing_objects.Remove(src)
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
QDEL_NULL(cell)
|
||||
QDEL_NULL(loaded)
|
||||
QDEL_NULL(capacitor)
|
||||
|
||||
@@ -102,7 +102,6 @@
|
||||
load_type = /obj/item/weapon/magnetic_ammo
|
||||
projectile_type = /obj/item/projectile/bullet/magnetic/flechette
|
||||
loaded = /obj/item/weapon/magnetic_ammo
|
||||
fire_sound = 'sound/weapons/rapidslice.ogg'
|
||||
empty_sound = 'sound/weapons/smg_empty_alarm.ogg'
|
||||
|
||||
firemodes = list(
|
||||
|
||||
@@ -121,10 +121,10 @@
|
||||
chargecost_lethal = 200
|
||||
|
||||
firemodes = list(
|
||||
new /datum/firemode(src, list(mode_name="stun", projectile_type=beammode, fire_sound='sound/weapons/Taser.ogg', charge_cost = chargecost)),
|
||||
new /datum/firemode(src, list(mode_name="lethal", projectile_type=beammode_lethal, fire_sound='sound/weapons/Laser.ogg', charge_cost = chargecost_lethal)),
|
||||
new /datum/firemode(src, list(mode_name="[burstmode] shot stun", projectile_type=beammode, fire_sound='sound/weapons/Taser.ogg', charge_cost = chargecost, burst = burstmode)),
|
||||
new /datum/firemode(src, list(mode_name="[burstmode] shot lethal", projectile_type=beammode_lethal, fire_sound='sound/weapons/Laser.ogg', charge_cost = chargecost_lethal, burst = burstmode)),
|
||||
new /datum/firemode(src, list(mode_name="stun", projectile_type=beammode, charge_cost = chargecost)),
|
||||
new /datum/firemode(src, list(mode_name="lethal", projectile_type=beammode_lethal, charge_cost = chargecost_lethal)),
|
||||
new /datum/firemode(src, list(mode_name="[burstmode] shot stun", projectile_type=beammode, charge_cost = chargecost, burst = burstmode)),
|
||||
new /datum/firemode(src, list(mode_name="[burstmode] shot lethal", projectile_type=beammode_lethal, charge_cost = chargecost_lethal, burst = burstmode)),
|
||||
)
|
||||
|
||||
/obj/item/weapon/gun/energy/modular/load_ammo(var/obj/item/C, mob/user)
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
w_class = ITEMSIZE_NORMAL
|
||||
matter = list(DEFAULT_WALL_MATERIAL = 1000)
|
||||
recoil = 1
|
||||
projectile_type = /obj/item/projectile/bullet/pistol/strong //Only used for Cham Guns
|
||||
projectile_type = /obj/item/projectile/bullet/pistol/strong //Only used for chameleon guns
|
||||
|
||||
var/caliber = ".357" //determines which casings will fit
|
||||
var/handle_casings = EJECT_CASINGS //determines how spent casings should be handled
|
||||
@@ -84,8 +84,12 @@
|
||||
|
||||
switch(handle_casings)
|
||||
if(EJECT_CASINGS) //eject casing onto ground.
|
||||
chambered.loc = get_turf(src)
|
||||
playsound(src.loc, "casing", 50, 1)
|
||||
if(chambered.caseless)
|
||||
qdel(chambered)
|
||||
return
|
||||
else
|
||||
chambered.loc = get_turf(src)
|
||||
playsound(src.loc, "casing", 50, 1)
|
||||
if(CYCLE_CASINGS) //cycle the casing back to the end.
|
||||
if(ammo_magazine)
|
||||
ammo_magazine.stored_ammo += chambered
|
||||
|
||||
@@ -13,29 +13,32 @@
|
||||
list(mode_name="semiauto", burst=1, fire_delay=0, move_delay=null, burst_accuracy=null, dispersion=null),
|
||||
list(mode_name="3-round bursts", burst=3, fire_delay=null, move_delay=4, burst_accuracy=list(0,-15,-15), dispersion=list(0.0, 0.6, 1.0)))
|
||||
|
||||
/obj/item/weapon/gun/projectile/automatic/saber //Fixed it
|
||||
name = "prototype SMG"
|
||||
desc = "A protoype lightweight, fast firing gun. Uses 9mm rounds."
|
||||
/obj/item/weapon/gun/projectile/automatic/advanced_smg
|
||||
name = "advanced SMG"
|
||||
desc = "An advanced submachine gun with a reflective laser optic that makes burst fire less inaccurate than other SMGs. Uses 9mm rounds."
|
||||
icon = 'icons/obj/gun.dmi'
|
||||
icon_state = "saber"//Still ugly
|
||||
icon_state = "advanced_smg-empty"
|
||||
w_class = ITEMSIZE_NORMAL
|
||||
load_method = MAGAZINE //This should fix it
|
||||
max_shells = 22
|
||||
load_method = MAGAZINE
|
||||
caliber = "9mm"
|
||||
origin_tech = list(TECH_COMBAT = 4, TECH_MATERIAL = 2)
|
||||
slot_flags = SLOT_BELT
|
||||
magazine_type = /obj/item/ammo_magazine/m9mmR/saber
|
||||
allowed_magazines = list(/obj/item/ammo_magazine/m9mmR/saber, /obj/item/ammo_magazine/m9mmR/saber/ap)
|
||||
projectile_type = /obj/item/projectile/bullet/pistol
|
||||
multi_aim = 1
|
||||
burst_delay = 2
|
||||
magazine_type = null // R&D builds this. Starts unloaded.
|
||||
allowed_magazines = list(/obj/item/ammo_magazine/m9mmAdvanced, /obj/item/ammo_magazine/m9mm)
|
||||
|
||||
firemodes = list(
|
||||
list(mode_name="semiauto", burst=1, fire_delay=0, move_delay=null, burst_accuracy=null, dispersion=null),
|
||||
list(mode_name="3-round bursts", burst=3, fire_delay=null, move_delay=4, burst_accuracy=list(0,-15,-15), dispersion=list(0.0, 0.6, 1.0))
|
||||
// list(mode_name="short bursts", burst=5, fire_delay=null, move_delay=4, burst_accuracy=list(0,-15,-15,-30,-30), dispersion=list(0.6, 1.0, 1.0, 1.0, 1.2)),
|
||||
list(mode_name="3-round bursts", burst=3, fire_delay=null, move_delay=4, burst_accuracy=list(0,-10,-10), dispersion=list(0.0, 0.3, 0.6))
|
||||
)
|
||||
|
||||
/obj/item/weapon/gun/projectile/automatic/advanced_smg/update_icon()
|
||||
..()
|
||||
icon_state = (ammo_magazine)? "advanced_smg" : "advanced_smg-empty"
|
||||
return
|
||||
|
||||
/obj/item/weapon/gun/projectile/automatic/advanced_smg/loaded
|
||||
magazine_type = /obj/item/ammo_magazine/m9mmAdvanced
|
||||
|
||||
/obj/item/weapon/gun/projectile/automatic/c20r
|
||||
name = "submachine gun"
|
||||
desc = "The C-20r is a lightweight and rapid firing SMG, for when you REALLY need someone dead. It has 'Scarborough Arms - Per falcis, per pravitas' inscribed on the stock. Uses 10mm rounds."
|
||||
@@ -46,7 +49,6 @@
|
||||
caliber = "10mm"
|
||||
origin_tech = list(TECH_COMBAT = 5, TECH_MATERIAL = 2, TECH_ILLEGAL = 8)
|
||||
slot_flags = SLOT_BELT|SLOT_BACK
|
||||
fire_sound = 'sound/weapons/Gunshot_light.ogg'
|
||||
load_method = MAGAZINE
|
||||
magazine_type = /obj/item/ammo_magazine/m10mm
|
||||
allowed_magazines = list(/obj/item/ammo_magazine/m10mm)
|
||||
@@ -107,7 +109,6 @@
|
||||
origin_tech = list(TECH_COMBAT = 5, TECH_MATERIAL = 2)
|
||||
slot_flags = SLOT_BELT
|
||||
ammo_type = "/obj/item/ammo_casing/a9mmr"
|
||||
fire_sound = 'sound/weapons/Gunshot_light.ogg'
|
||||
load_method = MAGAZINE
|
||||
magazine_type = /obj/item/ammo_magazine/m9mmt/rubber
|
||||
allowed_magazines = list(/obj/item/ammo_magazine/m9mmt)
|
||||
@@ -131,7 +132,6 @@
|
||||
force = 10
|
||||
caliber = "7.62mm"
|
||||
origin_tech = list(TECH_COMBAT = 8, TECH_MATERIAL = 3)
|
||||
fire_sound = 'sound/weapons/Gunshot.ogg'
|
||||
slot_flags = SLOT_BACK
|
||||
load_method = MAGAZINE
|
||||
magazine_type = /obj/item/ammo_magazine/m762
|
||||
@@ -204,7 +204,6 @@
|
||||
caliber = "5.45mm"
|
||||
origin_tech = list(TECH_COMBAT = 6, TECH_MATERIAL = 1, TECH_ILLEGAL = 2)
|
||||
slot_flags = SLOT_BACK
|
||||
fire_sound = 'sound/weapons/machinegun.ogg'
|
||||
load_method = MAGAZINE
|
||||
magazine_type = /obj/item/ammo_magazine/m545saw
|
||||
allowed_magazines = list(/obj/item/ammo_magazine/m545saw, /obj/item/ammo_magazine/m545)
|
||||
@@ -300,9 +299,9 @@
|
||||
/obj/item/weapon/gun/projectile/automatic/as24/update_icon()
|
||||
..()
|
||||
if(ammo_magazine)
|
||||
icon_state = "ashot-[round(ammo_magazine.stored_ammo.len,12)]"
|
||||
else
|
||||
icon_state = "ashot"
|
||||
else
|
||||
icon_state = "ashot-empty"
|
||||
return
|
||||
|
||||
/obj/item/weapon/gun/projectile/automatic/mini_uzi
|
||||
@@ -337,7 +336,6 @@
|
||||
caliber = "9mm"
|
||||
origin_tech = list(TECH_COMBAT = 5, TECH_MATERIAL = 2)
|
||||
slot_flags = SLOT_BELT // ToDo: Belt sprite.
|
||||
fire_sound = 'sound/weapons/Gunshot_light.ogg'
|
||||
load_method = MAGAZINE
|
||||
magazine_type = /obj/item/ammo_magazine/m9mmp90
|
||||
allowed_magazines = list(/obj/item/ammo_magazine/m9mmp90, /obj/item/ammo_magazine/m9mmt) // ToDo: New sprite for the different mag.
|
||||
@@ -351,7 +349,7 @@
|
||||
icon_state = "p90smg-[ammo_magazine ? round(ammo_magazine.stored_ammo.len, 6) : "empty"]"
|
||||
|
||||
/obj/item/weapon/gun/projectile/automatic/tommygun
|
||||
name = "\improper Tommygun"
|
||||
name = "\improper Tommy Gun"
|
||||
desc = "This weapon was made famous by gangsters in the 20th century. Cybersun Industries is currently reproducing these for a target market of historic gun collectors and classy criminals. Uses .45 rounds."
|
||||
icon_state = "tommygun"
|
||||
w_class = ITEMSIZE_NORMAL
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
desc = "A reproduction of an almost ancient weapon design from the early 20th century. It's still popular among hunters and collectors due to its reliability. Uses 7.62mm rounds."
|
||||
item_state = "boltaction"
|
||||
icon_state = "boltaction"
|
||||
fire_sound = 'sound/weapons/rifleshot.ogg'
|
||||
fire_sound = 'sound/weapons/Gunshot_generic_rifle.ogg'
|
||||
max_shells = 5
|
||||
caliber = "7.62mm"
|
||||
origin_tech = list(TECH_COMBAT = 1)// Old as shit rifle doesn't have very good tech.
|
||||
@@ -29,7 +29,7 @@
|
||||
if(istype(A, /obj/item/weapon/surgical/circular_saw) || istype(A, /obj/item/weapon/melee/energy) || istype(A, /obj/item/weapon/pickaxe/plasmacutter) && w_class != ITEMSIZE_NORMAL)
|
||||
user << "<span class='notice'>You begin to shorten the barrel and stock of \the [src].</span>"
|
||||
if(loaded.len)
|
||||
afterattack(user, user) //will this work? //it will. we call it twice, for twice the FUN
|
||||
afterattack(user, user)
|
||||
playsound(user, fire_sound, 50, 1)
|
||||
user.visible_message("<span class='danger'>[src] goes off!</span>", "<span class='danger'>The rifle goes off in your face!</span>")
|
||||
return
|
||||
@@ -53,17 +53,13 @@
|
||||
desc = "A reproduction of an almost ancient weapon design from the 19th century. This one uses a lever-action to move new rounds into the chamber. Uses 7.62mm rounds."
|
||||
item_state = "leveraction"
|
||||
icon_state = "leveraction"
|
||||
fire_sound = 'sound/weapons/rifleshot.ogg'
|
||||
max_shells = 5
|
||||
caliber = "7.62mm"
|
||||
origin_tech = list(TECH_COMBAT = 1)// Old as shit rifle doesn't have very good tech.
|
||||
ammo_type = /obj/item/ammo_casing/a762
|
||||
load_method = SINGLE_CASING|SPEEDLOADER
|
||||
action_sound = 'sound/weapons/riflebolt.ogg'
|
||||
load_method = SINGLE_CASING
|
||||
|
||||
/obj/item/weapon/gun/projectile/shotgun/pump/rifle/lever/vintage
|
||||
name = "vintage repeater"
|
||||
desc = "An iconic manually operated lever action rifle, offering adequate stopping power due to it's still powerful cartridge while at the same time having a rather respectable firing rate due to it's mechanism. It is very probable this is a replica instead of a museum piece, but rifles of this pattern still see usage as colonist guns in some far off regions. Uses 7,62mm ammo."
|
||||
item_state = "levercarabine"
|
||||
desc = "An iconic manually operated lever action rifle, offering adequate stopping power due to it's still powerful cartridge while at the same time having a rather respectable firing rate due to it's mechanism. It is very probable this is a replica instead of a museum piece, but rifles of this pattern still see usage as colonist guns in some far off regions. Uses 7.62mm rounds."
|
||||
item_state = "levercarabine" // That isn't how carbine is spelled ya knob! :U
|
||||
icon_state = "levercarabine"
|
||||
animated_pump = 1
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/obj/item/weapon/gun/projectile/caseless/prototype
|
||||
name = "prototype caseless rifle"
|
||||
desc = "A rifle cooked up in NanoTrasen's R&D labs that operates with Kraut Space Magic™ clockwork internals. Uses solid phoron 5mm caseless rounds."
|
||||
icon_state = "caseless"
|
||||
item_state = "caseless"
|
||||
w_class = ITEMSIZE_LARGE
|
||||
caliber = "5mm caseless"
|
||||
origin_tech = list(TECH_COMBAT = 2, TECH_MATERIAL = 3)
|
||||
slot_flags = SLOT_BACK
|
||||
load_method = MAGAZINE
|
||||
magazine_type = null // R&D builds this. Starts unloaded.
|
||||
allowed_magazines = list(/obj/item/ammo_magazine/m5mmcaseless)
|
||||
|
||||
/obj/item/weapon/gun/projectile/caseless/prototype/update_icon()
|
||||
if(ammo_magazine)
|
||||
icon_state = initial(icon_state)
|
||||
else
|
||||
icon_state = "[initial(icon_state)]-empty"
|
||||
|
||||
/obj/item/weapon/gun/projectile/caseless/prototype/loaded
|
||||
magazine_type = /obj/item/ammo_magazine/m5mmcaseless
|
||||
@@ -3,7 +3,7 @@
|
||||
icon_state = "dart"
|
||||
damage = 5
|
||||
var/reagent_amount = 15
|
||||
kill_count = 15 //shorter range
|
||||
range = 15 //shorter range
|
||||
|
||||
muzzle_type = null
|
||||
|
||||
|
||||
@@ -132,8 +132,8 @@
|
||||
item_state = "deagle"
|
||||
force = 14.0
|
||||
caliber = ".44"
|
||||
fire_sound = 'sound/weapons/Gunshot_deagle.ogg'
|
||||
load_method = MAGAZINE
|
||||
fire_sound = 'sound/weapons/deagle.ogg'
|
||||
magazine_type = /obj/item/ammo_magazine/m44
|
||||
allowed_magazines = list(/obj/item/ammo_magazine/m44)
|
||||
|
||||
@@ -154,33 +154,13 @@
|
||||
icon_state = "deaglecamo"
|
||||
item_state = "deagleg"
|
||||
|
||||
/*
|
||||
/obj/item/weapon/gun/projectile/fiveseven
|
||||
name = "\improper WT-AP57"
|
||||
desc = "This tacticool pistol made by Ward-Takahashi trades stopping power for armor piercing and a large capacity. Uses 5mm rounds."
|
||||
icon_state = "fnseven"
|
||||
origin_tech = list(TECH_COMBAT = 3, TECH_MATERIAL = 2)
|
||||
caliber = "5mm"
|
||||
load_method = MAGAZINE
|
||||
fire_sound = 'sound/weapons/semiauto.ogg'
|
||||
magazine_type = /obj/item/ammo_magazine/c5mm
|
||||
allowed_magazines = list(/obj/item/ammo_magazine/c5mm)
|
||||
|
||||
/obj/item/weapon/gun/projectile/fiveseven/update_icon()
|
||||
..()
|
||||
if(ammo_magazine)
|
||||
icon_state = "fnseven"
|
||||
else
|
||||
icon_state = "fnseven-empty"
|
||||
*/
|
||||
|
||||
/obj/item/weapon/gun/projectile/gyropistol // Does this even appear anywhere outside of admin abuse?
|
||||
name = "gyrojet pistol"
|
||||
desc = "Speak softly, and carry a big gun. Fires rare .75 caliber self-propelled exploding bolts--because fuck you and everything around you."
|
||||
icon_state = "gyropistol"
|
||||
max_shells = 8
|
||||
caliber = ".75"
|
||||
fire_sound = 'sound/weapons/rpg.ogg'
|
||||
fire_sound = 'sound/weapons/railgun.ogg'
|
||||
origin_tech = list(TECH_COMBAT = 3)
|
||||
ammo_type = "/obj/item/ammo_casing/a75"
|
||||
load_method = MAGAZINE
|
||||
@@ -331,7 +311,6 @@
|
||||
origin_tech = list(TECH_COMBAT = 3, TECH_MATERIAL = 2)
|
||||
caliber = "9mm"
|
||||
load_method = MAGAZINE
|
||||
fire_sound = 'sound/weapons/gunshot3.ogg'
|
||||
magazine_type = /obj/item/ammo_magazine/m9mm
|
||||
allowed_magazines = list(/obj/item/ammo_magazine/m9mm) // Can accept illegal large capacity magazines, or compact magazines.
|
||||
|
||||
|
||||
@@ -46,7 +46,6 @@
|
||||
icon_state = "detective"
|
||||
caliber = ".38"
|
||||
origin_tech = list(TECH_COMBAT = 2, TECH_MATERIAL = 2)
|
||||
fire_sound = 'sound/weapons/gunshot3.ogg'
|
||||
ammo_type = /obj/item/ammo_casing/a38
|
||||
|
||||
/obj/item/weapon/gun/projectile/revolver/detective/verb/rename_gun()
|
||||
@@ -73,7 +72,6 @@
|
||||
icon_state = "detective"
|
||||
caliber = ".45"
|
||||
origin_tech = list(TECH_COMBAT = 2, TECH_MATERIAL = 2)
|
||||
fire_sound = 'sound/weapons/gunshot_heavy.ogg'
|
||||
ammo_type = /obj/item/ammo_casing/a45/rubber
|
||||
max_shells = 7
|
||||
|
||||
@@ -125,7 +123,6 @@ obj/item/weapon/gun/projectile/revolver/detective45/verb/rename_gun()
|
||||
icon_state = "deckard-empty"
|
||||
caliber = ".38"
|
||||
origin_tech = list(TECH_COMBAT = 2, TECH_MATERIAL = 2)
|
||||
fire_sound = 'sound/weapons/gunshot3.ogg'
|
||||
ammo_type = /obj/item/ammo_casing/a38
|
||||
|
||||
/obj/item/weapon/gun/projectile/revolver/deckard/emp
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
caliber = "7.62mm"
|
||||
origin_tech = list(TECH_COMBAT = 2, TECH_MATERIAL = 2)
|
||||
slot_flags = SLOT_BACK
|
||||
fire_sound = 'sound/weapons/rifleshot.ogg'
|
||||
//fire_sound = 'sound/weapons/rifleshot.ogg'
|
||||
load_method = MAGAZINE // ToDo: Make it so MAGAZINE, SPEEDLOADER and SINGLE_CASING can all be used on the same gun.
|
||||
magazine_type = /obj/item/ammo_magazine/m762garand
|
||||
allowed_magazines = list(/obj/item/ammo_magazine/m762garand)
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
scoped_accuracy = 0
|
||||
// requires_two_hands = 1
|
||||
// one_handed_penalty = 60 // The weapon itself is heavy, and the long barrel makes it hard to hold steady with just one hand.
|
||||
fire_sound = 'sound/weapons/SVD_shot.ogg'
|
||||
fire_sound = 'sound/weapons/Gunshot_SVD.ogg' // Has a very unique sound.
|
||||
magazine_type = /obj/item/ammo_magazine/m762svd
|
||||
allowed_magazines = list(/obj/item/ammo_magazine/m762svd)
|
||||
|
||||
|
||||
@@ -15,16 +15,16 @@
|
||||
icon = 'icons/obj/gun.dmi'
|
||||
icon_state = "spikethrower3"
|
||||
item_state = "spikethrower"
|
||||
fire_sound_text = "a strange noise"
|
||||
fire_sound = 'sound/weapons/bladeslice.ogg'
|
||||
fire_sound_text = "a strange noise"
|
||||
|
||||
/obj/item/weapon/gun/launcher/spikethrower/New()
|
||||
..()
|
||||
processing_objects.Add(src)
|
||||
START_PROCESSING(SSobj, src)
|
||||
last_regen = world.time
|
||||
|
||||
/obj/item/weapon/gun/launcher/spikethrower/Destroy()
|
||||
processing_objects.Remove(src)
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
..()
|
||||
|
||||
/obj/item/weapon/gun/launcher/spikethrower/process()
|
||||
@@ -79,9 +79,9 @@
|
||||
damage_type = HALLOSS
|
||||
light_color = "#8837A3"
|
||||
|
||||
muzzle_type = /obj/effect/projectile/darkmatterstun/muzzle
|
||||
tracer_type = /obj/effect/projectile/darkmatterstun/tracer
|
||||
impact_type = /obj/effect/projectile/darkmatterstun/impact
|
||||
muzzle_type = /obj/effect/projectile/muzzle/darkmatterstun
|
||||
tracer_type = /obj/effect/projectile/tracer/darkmatterstun
|
||||
impact_type = /obj/effect/projectile/impact/darkmatterstun
|
||||
|
||||
/obj/item/projectile/beam/darkmatter
|
||||
name = "dark matter bolt"
|
||||
@@ -95,9 +95,9 @@
|
||||
|
||||
embed_chance = 0
|
||||
|
||||
muzzle_type = /obj/effect/projectile/darkmatter/muzzle
|
||||
tracer_type = /obj/effect/projectile/darkmatter/tracer
|
||||
impact_type = /obj/effect/projectile/darkmatter/impact
|
||||
muzzle_type = /obj/effect/projectile/muzzle/darkmatter
|
||||
tracer_type = /obj/effect/projectile/tracer/darkmatter
|
||||
impact_type = /obj/effect/projectile/impact/darkmatter
|
||||
|
||||
/obj/item/projectile/energy/darkmatter
|
||||
name = "dark matter pellet"
|
||||
|
||||
@@ -1,37 +1,85 @@
|
||||
/*
|
||||
#define BRUTE "brute"
|
||||
#define BURN "burn"
|
||||
#define TOX "tox"
|
||||
#define OXY "oxy"
|
||||
#define CLONE "clone"
|
||||
|
||||
#define ADD "add"
|
||||
#define SET "set"
|
||||
*/
|
||||
#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 = 1
|
||||
unacidable = 1
|
||||
anchored = 1 //There's a reason this is here, Mport. God fucking damn it -Agouri. Find&Fix by Pete. The reason this is here is to stop the curving of emitter shots.
|
||||
density = FALSE
|
||||
anchored = TRUE
|
||||
unacidable = TRUE
|
||||
pass_flags = PASSTABLE
|
||||
mouse_opacity = 0
|
||||
var/bumped = 0 //Prevents it from hitting more than one guy at once
|
||||
|
||||
////TG PROJECTILE SYTSEM
|
||||
//Projectile stuff
|
||||
var/range = 50
|
||||
var/originalRange
|
||||
|
||||
//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/ignore_source_check = FALSE
|
||||
|
||||
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
|
||||
|
||||
//Targetting
|
||||
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
|
||||
|
||||
//Misc/Polaris variables
|
||||
|
||||
var/def_zone = "" //Aiming at
|
||||
var/mob/firer = null//Who shot it
|
||||
var/silenced = 0 //Attack message
|
||||
var/yo = null
|
||||
var/xo = null
|
||||
var/current = null
|
||||
var/shot_from = "" // name of the object which shot us
|
||||
var/atom/original = null // the target clicked (not necessarily where the projectile is headed). Should probably be renamed to 'target' or something.
|
||||
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
|
||||
|
||||
var/accuracy = 0
|
||||
var/dispersion = 0.0
|
||||
@@ -45,7 +93,6 @@
|
||||
var/check_armour = "bullet" //Defines what armor to use when it hits things. Must be set to bullet, laser, energy,or bomb //Cael - bio and rad are also valid
|
||||
var/projectile_type = /obj/item/projectile
|
||||
var/penetrating = 0 //If greater than zero, the projectile will pass through dense objects as specified by on_penetrate()
|
||||
var/kill_count = 50 //This will de-increment every process(). When 0, it will delete the projectile.
|
||||
//Effects
|
||||
var/incendiary = 0 //1 for ignite on hit, 2 for trail of fire. 3 maybe later for burst of fire around the impact point. - Mech
|
||||
var/flammability = 0 //Amount of fire stacks to add for the above.
|
||||
@@ -59,179 +106,407 @@
|
||||
var/drowsy = 0
|
||||
var/agony = 0
|
||||
var/reflected = 0 // This should be set to 1 if reflected by any means, to prevent infinite reflections.
|
||||
var/modifier_type_to_apply = null // If set, will apply a modifier to mobs that are hit by this projectile.
|
||||
var/modifier_duration = null // How long the above modifier should last for. Leave null to be permanent.
|
||||
|
||||
embed_chance = 0 //Base chance for a projectile to embed
|
||||
|
||||
var/hitscan = 0 // whether the projectile should be hitscan
|
||||
var/step_delay = 1 // the delay between iterations if not a hitscan projectile
|
||||
var/fire_sound = 'sound/weapons/Gunshot_old.ogg' // Can be overriden in gun.dm's fire_sound var. It can also be null but I don't know why you'd ever want to do that. -Ace
|
||||
|
||||
// effect types to be used
|
||||
var/muzzle_type
|
||||
var/tracer_type
|
||||
var/impact_type
|
||||
var/vacuum_traversal = TRUE //Determines if the projectile can exist in vacuum, if false, the projectile will be deleted if it enters vacuum.
|
||||
|
||||
var/fire_sound
|
||||
var/temporary_unstoppable_movement = FALSE
|
||||
|
||||
var/vacuum_traversal = 1 //Determines if the projectile can exist in vacuum, if false, the projectile will be deleted if it enters vacuum.
|
||||
/obj/item/projectile/proc/Range()
|
||||
range--
|
||||
if(range <= 0 && loc)
|
||||
on_range()
|
||||
|
||||
var/datum/plot_vector/trajectory // used to plot the path of the projectile
|
||||
var/datum/vector_loc/location // current location of the projectile in pixel space
|
||||
var/matrix/effect_transform // matrix to rotate and scale projectile effects - putting it here so it doesn't
|
||||
// have to be recreated multiple times
|
||||
/obj/item/projectile/proc/on_range() //if we want there to be effects when they reach the end of their range
|
||||
qdel(src)
|
||||
|
||||
//TODO: make it so this is called more reliably, instead of sometimes by bullet_act() and sometimes not
|
||||
/obj/item/projectile/proc/on_hit(var/atom/target, var/blocked = 0, var/def_zone = null)
|
||||
if(blocked >= 100) return 0//Full block
|
||||
if(!isliving(target)) return 0
|
||||
// if(isanimal(target)) return 0
|
||||
var/mob/living/L = target
|
||||
L.apply_effects(stun, weaken, paralyze, irradiate, stutter, eyeblur, drowsy, agony, blocked, incendiary, flammability) // add in AGONY!
|
||||
return 1
|
||||
/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()
|
||||
|
||||
//called when the projectile stops flying because it collided with something
|
||||
/obj/item/projectile/proc/on_impact(var/atom/A)
|
||||
impact_effect(effect_transform) // generate impact effect
|
||||
if(damage && damage_type == BURN)
|
||||
var/turf/T = get_turf(A)
|
||||
if(T)
|
||||
T.hotspot_expose(700, 5)
|
||||
/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/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(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)
|
||||
before_move()
|
||||
step_towards(src, T)
|
||||
hitscan_last = loc
|
||||
after_move()
|
||||
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/Crossed(atom/movable/AM) //A mob moving on a tile with a projectile is hit by it.
|
||||
..()
|
||||
if(isliving(AM) && !(pass_flags & PASSMOB))
|
||||
var/mob/living/L = AM
|
||||
if(can_hit_target(L, permutated, (AM == original)))
|
||||
Bump(AM)
|
||||
|
||||
/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
|
||||
|
||||
/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/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/fire(angle, atom/direct_target)
|
||||
//If no angle needs to resolve it from xo/yo!
|
||||
if(direct_target)
|
||||
direct_target.bullet_act(src, def_zone)
|
||||
qdel(src)
|
||||
return
|
||||
if(isnum(angle))
|
||||
setAngle(angle)
|
||||
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))
|
||||
crash_with("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))
|
||||
if(dispersion)
|
||||
setAngle(Angle + rand(-dispersion, dispersion))
|
||||
original_angle = Angle
|
||||
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
|
||||
permutated = list()
|
||||
originalRange = range
|
||||
fired = TRUE
|
||||
if(hitscan)
|
||||
process_hitscan()
|
||||
START_PROCESSING(SSprojectiles, src)
|
||||
pixel_move(1, FALSE) //move it now!
|
||||
|
||||
/obj/item/projectile/Move(atom/newloc, dir = NONE)
|
||||
. = ..()
|
||||
if(.)
|
||||
if(temporary_unstoppable_movement)
|
||||
temporary_unstoppable_movement = FALSE
|
||||
DISABLE_BITFIELD(movement_type, UNSTOPPABLE)
|
||||
if(fired && can_hit_target(original, permutated, TRUE))
|
||||
Bump(original)
|
||||
|
||||
/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/proc/before_move()
|
||||
return
|
||||
|
||||
//Checks if the projectile is eligible for embedding. Not that it necessarily will.
|
||||
/obj/item/projectile/proc/can_embed()
|
||||
//embed must be enabled and damage type must be brute
|
||||
if(embed_chance == 0 || damage_type != BRUTE)
|
||||
return 0
|
||||
return 1
|
||||
/obj/item/projectile/proc/after_move()
|
||||
return
|
||||
|
||||
/obj/item/projectile/proc/get_structure_damage()
|
||||
if(damage_type == BRUTE || damage_type == BURN)
|
||||
return damage
|
||||
return 0
|
||||
/obj/item/projectile/proc/store_hitscan_collision(datum/point/pcache)
|
||||
beam_segments[beam_index] = pcache
|
||||
beam_index = pcache
|
||||
beam_segments[beam_index] = null
|
||||
|
||||
//return 1 if the projectile should be allowed to pass through after all, 0 if not.
|
||||
/obj/item/projectile/proc/check_penetrate(var/atom/A)
|
||||
return 1
|
||||
//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)
|
||||
|
||||
/obj/item/projectile/proc/check_fire(atom/target as mob, var/mob/living/user as mob) //Checks if you can hit them or not.
|
||||
check_trajectory(target, user, pass_flags, flags)
|
||||
if(isliving(source) && params)
|
||||
var/list/calculated = calculate_projectile_angle_and_pixel_offsets(source, params)
|
||||
p_x = calculated[2]
|
||||
p_y = calculated[3]
|
||||
|
||||
//sets the click point of the projectile using mouse input params
|
||||
/obj/item/projectile/proc/set_clickpoint(var/params)
|
||||
setAngle(calculated[1] + spread)
|
||||
else if(targloc)
|
||||
yo = targloc.y - curloc.y
|
||||
xo = targloc.x - curloc.x
|
||||
setAngle(Get_Angle(src, targloc) + spread)
|
||||
else
|
||||
crash_with("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"], ",")
|
||||
|
||||
//randomize clickpoint a bit based on dispersion
|
||||
if(dispersion)
|
||||
var/radius = round((dispersion*0.443)*world.icon_size*0.8) //0.443 = sqrt(pi)/4 = 2a, where a is the side length of a square that shares the same area as a circle with diameter = dispersion
|
||||
p_x = between(0, p_x + rand(-radius, radius), world.icon_size)
|
||||
p_y = between(0, p_y + rand(-radius, radius), world.icon_size)
|
||||
//Split X+Pixel_X up into list(X, Pixel_X)
|
||||
var/list/screen_loc_X = splittext(screen_loc_params[1],":")
|
||||
|
||||
//called to launch a projectile
|
||||
/obj/item/projectile/proc/launch(atom/target, var/target_zone, var/x_offset=0, var/y_offset=0, var/angle_offset=0)
|
||||
var/turf/curloc = get_turf(src)
|
||||
var/turf/targloc = get_turf(target)
|
||||
if (!istype(targloc) || !istype(curloc))
|
||||
return 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
|
||||
|
||||
if(combustion)
|
||||
curloc.hotspot_expose(700, 5)
|
||||
//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 = user.client? getviewsize(user.client.view) : world.view
|
||||
var/screenviewX = screenview[1] * world.icon_size
|
||||
var/screenviewY = screenview[2] * world.icon_size
|
||||
|
||||
if(targloc == curloc) //Shooting something in the same turf
|
||||
target.bullet_act(src, target_zone)
|
||||
on_impact(target)
|
||||
qdel(src)
|
||||
return 0
|
||||
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/proc/redirect(x, y, starting, source)
|
||||
old_style_target(locate(x, y, z), starting? get_turf(starting) : get_turf(source))
|
||||
|
||||
/obj/item/projectile/proc/old_style_target(atom/target, atom/source)
|
||||
if(!source)
|
||||
source = get_turf(src)
|
||||
starting = source
|
||||
original = target
|
||||
def_zone = target_zone
|
||||
setAngle(Get_Angle(source, target))
|
||||
|
||||
spawn()
|
||||
setup_trajectory(curloc, targloc, x_offset, y_offset, angle_offset) //plot the initial trajectory
|
||||
process()
|
||||
/obj/item/projectile/Destroy()
|
||||
if(hitscan)
|
||||
finalize_hitscan_and_generate_tracers()
|
||||
STOP_PROCESSING(SSprojectiles, src)
|
||||
cleanup_beam_segments()
|
||||
qdel(trajectory)
|
||||
return ..()
|
||||
|
||||
return 0
|
||||
/obj/item/projectile/proc/cleanup_beam_segments()
|
||||
QDEL_LIST_ASSOC(beam_segments)
|
||||
beam_segments = list()
|
||||
qdel(beam_index)
|
||||
|
||||
//called to launch a projectile from a gun
|
||||
/obj/item/projectile/proc/launch_from_gun(atom/target, mob/user, obj/item/weapon/gun/launcher, var/target_zone, var/x_offset=0, var/y_offset=0)
|
||||
if(user == target) //Shooting yourself
|
||||
user.bullet_act(src, target_zone)
|
||||
on_impact(user)
|
||||
qdel(src)
|
||||
return 0
|
||||
|
||||
loc = get_turf(user) //move the projectile out into the world
|
||||
|
||||
firer = user
|
||||
shot_from = launcher.name
|
||||
silenced = launcher.silenced
|
||||
|
||||
return launch(target, target_zone, x_offset, y_offset)
|
||||
|
||||
//Used to change the direction of the projectile in flight.
|
||||
/obj/item/projectile/proc/redirect(var/new_x, var/new_y, var/atom/starting_loc, var/mob/new_firer=null)
|
||||
var/turf/new_target = locate(new_x, new_y, src.z)
|
||||
|
||||
original = new_target
|
||||
if(new_firer)
|
||||
firer = src
|
||||
|
||||
setup_trajectory(starting_loc, new_target)
|
||||
|
||||
//Called when the projectile intercepts a mob. Returns 1 if the projectile hit the mob, 0 if it missed and should keep flying.
|
||||
/obj/item/projectile/proc/attack_mob(var/mob/living/target_mob, var/distance, var/miss_modifier=0)
|
||||
if(!istype(target_mob))
|
||||
return
|
||||
|
||||
//roll to-hit
|
||||
miss_modifier = max(15*(distance-2) - accuracy + miss_modifier + target_mob.get_evasion(), 0)
|
||||
var/hit_zone = get_zone_with_miss_chance(def_zone, target_mob, miss_modifier, ranged_attack=(distance > 1 || original != target_mob)) //if the projectile hits a target we weren't originally aiming at then retain the chance to miss
|
||||
|
||||
var/result = PROJECTILE_FORCE_MISS
|
||||
if(hit_zone)
|
||||
def_zone = hit_zone //set def_zone, so if the projectile ends up hitting someone else later (to be implemented), it is more likely to hit the same part
|
||||
result = target_mob.bullet_act(src, def_zone)
|
||||
|
||||
if(result == PROJECTILE_FORCE_MISS)
|
||||
if(!silenced)
|
||||
visible_message("<span class='notice'>\The [src] misses [target_mob] narrowly!</span>")
|
||||
return 0
|
||||
|
||||
//hit messages
|
||||
if(silenced)
|
||||
to_chat(target_mob, "<span class='danger'>You've been hit in the [parse_zone(def_zone)] by \the [src]!</span>")
|
||||
/obj/item/projectile/proc/vol_by_damage()
|
||||
if(damage)
|
||||
return CLAMP((damage) * 0.67, 30, 100)// Multiply projectile damage by 0.67, then CLAMP the value between 30 and 100
|
||||
else
|
||||
visible_message("<span class='danger'>\The [target_mob] is hit by \the [src] in the [parse_zone(def_zone)]!</span>")//X has fired Y is now given by the guns so you cant tell who shot you if you could not see the shooter
|
||||
return 50 //if the projectile doesn't do damage, play its hitsound at 50% volume.
|
||||
|
||||
//admin logs
|
||||
if(!no_attack_log)
|
||||
if(istype(firer, /mob) && istype(target_mob))
|
||||
add_attack_logs(firer,target_mob,"Shot with \a [src.type] projectile")
|
||||
/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)
|
||||
|
||||
//sometimes bullet_act() will want the projectile to continue flying
|
||||
if (result == PROJECTILE_CONTINUE)
|
||||
return 0
|
||||
/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()
|
||||
|
||||
return 1
|
||||
//Returns true if the target atom is on our current turf and above the right layer
|
||||
//If direct target is true it's the originally clicked target.
|
||||
/obj/item/projectile/proc/can_hit_target(atom/target, list/passthrough, direct_target = FALSE, ignore_loc = FALSE)
|
||||
if(QDELETED(target))
|
||||
return FALSE
|
||||
if(!ignore_source_check && firer)
|
||||
var/mob/M = firer
|
||||
if((target == firer) || ((target == firer.loc) && istype(firer.loc, /obj/mecha)) || (target in firer.buckled_mobs) || (istype(M) && (M.buckled == target)))
|
||||
return FALSE
|
||||
if(!ignore_loc && (loc != target.loc))
|
||||
return FALSE
|
||||
if(target in passthrough)
|
||||
return FALSE
|
||||
if(target.density) //This thing blocks projectiles, hit it regardless of layer/mob stuns/etc.
|
||||
return TRUE
|
||||
if(!isliving(target))
|
||||
if(target.layer < PROJECTILE_HIT_THRESHOLD_LAYER)
|
||||
return FALSE
|
||||
else
|
||||
var/mob/living/L = target
|
||||
if(!direct_target)
|
||||
if(!L.density)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/item/projectile/Bump(atom/A as mob|obj|turf|area, forced=0)
|
||||
if(A == src)
|
||||
return 0 //no
|
||||
/obj/item/projectile/Bump(atom/A)
|
||||
if(A in permutated)
|
||||
trajectory_ignore_forcemove = TRUE
|
||||
forceMove(get_turf(A))
|
||||
trajectory_ignore_forcemove = FALSE
|
||||
return FALSE
|
||||
if(firer && !reflected)
|
||||
if(A == firer || (A == firer.loc && istype(A, /obj/mecha))) //cannot shoot yourself or your mech
|
||||
trajectory_ignore_forcemove = TRUE
|
||||
forceMove(get_turf(A))
|
||||
trajectory_ignore_forcemove = FALSE
|
||||
return FALSE
|
||||
|
||||
if(A == firer)
|
||||
loc = A.loc
|
||||
return 0 //cannot shoot yourself
|
||||
var/distance = get_dist(starting, get_turf(src))
|
||||
var/turf/target_turf = get_turf(A)
|
||||
var/passthrough = FALSE
|
||||
|
||||
if((bumped && !forced) || (A in permutated))
|
||||
return 0
|
||||
|
||||
var/passthrough = 0 //if the projectile should continue flying
|
||||
var/distance = get_dist(starting,loc)
|
||||
|
||||
bumped = 1
|
||||
if(ismob(A))
|
||||
var/mob/M = A
|
||||
if(istype(A, /mob/living))
|
||||
@@ -242,13 +517,13 @@
|
||||
var/shield_chance = min(80, (30 * (M.mob_size / 10))) //Small mobs have a harder time keeping a dead body as a shield than a human-sized one. Unathi would have an easier job, if they are made to be SIZE_LARGE in the future. -Mech
|
||||
if(prob(shield_chance))
|
||||
visible_message("<span class='danger'>\The [M] uses [G.affecting] as a shield!</span>")
|
||||
if(Bump(G.affecting, forced=1))
|
||||
if(Bump(G.affecting))
|
||||
return
|
||||
else
|
||||
visible_message("<span class='danger'>\The [M] tries to use [G.affecting] as a shield, but fails!</span>")
|
||||
else
|
||||
visible_message("<span class='danger'>\The [M] uses [G.affecting] as a shield!</span>")
|
||||
if(Bump(G.affecting, forced=1))
|
||||
if(Bump(G.affecting))
|
||||
return //If Bump() returns 0 (keep going) then we continue on to attack M.
|
||||
|
||||
passthrough = !attack_mob(M, distance)
|
||||
@@ -265,229 +540,113 @@
|
||||
//penetrating projectiles can pass through things that otherwise would not let them
|
||||
if(!passthrough && penetrating > 0)
|
||||
if(check_penetrate(A))
|
||||
passthrough = 1
|
||||
passthrough = TRUE
|
||||
penetrating--
|
||||
|
||||
//the bullet passes through a dense object!
|
||||
if(passthrough)
|
||||
//move ourselves onto A so we can continue on our way.
|
||||
if(A)
|
||||
if(istype(A, /turf))
|
||||
loc = A
|
||||
else
|
||||
loc = A.loc
|
||||
permutated.Add(A)
|
||||
bumped = 0 //reset bumped variable!
|
||||
return 0
|
||||
|
||||
//stop flying
|
||||
on_impact(A)
|
||||
|
||||
density = 0
|
||||
invisibility = 101
|
||||
trajectory_ignore_forcemove = TRUE
|
||||
forceMove(target_turf)
|
||||
permutated.Add(A)
|
||||
trajectory_ignore_forcemove = FALSE
|
||||
return FALSE
|
||||
|
||||
if(A)
|
||||
on_impact(A)
|
||||
qdel(src)
|
||||
return TRUE
|
||||
|
||||
//TODO: make it so this is called more reliably, instead of sometimes by bullet_act() and sometimes not
|
||||
/obj/item/projectile/proc/on_hit(atom/target, blocked = 0, def_zone)
|
||||
if(blocked >= 100) return 0//Full block
|
||||
if(!isliving(target)) return 0
|
||||
// if(isanimal(target)) return 0
|
||||
var/mob/living/L = target
|
||||
L.apply_effects(stun, weaken, paralyze, irradiate, stutter, eyeblur, drowsy, agony, blocked, incendiary, flammability) // add in AGONY!
|
||||
if(modifier_type_to_apply)
|
||||
L.add_modifier(modifier_type_to_apply, modifier_duration)
|
||||
return 1
|
||||
|
||||
/obj/item/projectile/ex_act()
|
||||
return //explosions probably shouldn't delete projectiles
|
||||
//called when the projectile stops flying because it Bump'd with something
|
||||
/obj/item/projectile/proc/on_impact(atom/A)
|
||||
if(damage && damage_type == BURN)
|
||||
var/turf/T = get_turf(A)
|
||||
if(T)
|
||||
T.hotspot_expose(700, 5)
|
||||
|
||||
/obj/item/projectile/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
|
||||
//Checks if the projectile is eligible for embedding. Not that it necessarily will.
|
||||
/obj/item/projectile/proc/can_embed()
|
||||
//embed must be enabled and damage type must be brute
|
||||
if(embed_chance == 0 || damage_type != BRUTE)
|
||||
return 0
|
||||
return 1
|
||||
|
||||
/obj/item/projectile/process()
|
||||
var/first_step = 1
|
||||
/obj/item/projectile/proc/get_structure_damage()
|
||||
if(damage_type == BRUTE || damage_type == BURN)
|
||||
return damage
|
||||
return 0
|
||||
|
||||
spawn while(src && src.loc)
|
||||
if(kill_count-- < 1)
|
||||
on_impact(src.loc) //for any final impact behaviours
|
||||
qdel(src)
|
||||
return
|
||||
if((!( current ) || loc == current))
|
||||
current = locate(min(max(x + xo, 1), world.maxx), min(max(y + yo, 1), world.maxy), z)
|
||||
if((x == 1 || x == world.maxx || y == 1 || y == world.maxy))
|
||||
qdel(src)
|
||||
return
|
||||
//return 1 if the projectile should be allowed to pass through after all, 0 if not.
|
||||
/obj/item/projectile/proc/check_penetrate(atom/A)
|
||||
return 1
|
||||
|
||||
trajectory.increment() // increment the current location
|
||||
location = trajectory.return_location(location) // update the locally stored location data
|
||||
update_light() //energy projectiles will look glowy and fun
|
||||
/obj/item/projectile/proc/check_fire(atom/target as mob, mob/living/user as mob) //Checks if you can hit them or not.
|
||||
check_trajectory(target, user, pass_flags, flags)
|
||||
|
||||
if(!location)
|
||||
qdel(src) // if it's left the world... kill it
|
||||
return
|
||||
/obj/item/projectile/CanPass()
|
||||
return TRUE
|
||||
|
||||
if (is_below_sound_pressure(get_turf(src)) && !vacuum_traversal) //Deletes projectiles that aren't supposed to bein vacuum if they leave pressurised areas
|
||||
qdel(src)
|
||||
return
|
||||
//Called when the projectile intercepts a mob. Returns 1 if the projectile hit the mob, 0 if it missed and should keep flying.
|
||||
/obj/item/projectile/proc/attack_mob(mob/living/target_mob, distance, miss_modifier = 0)
|
||||
if(!istype(target_mob))
|
||||
return
|
||||
|
||||
before_move()
|
||||
Move(location.return_turf())
|
||||
after_move()
|
||||
//roll to-hit
|
||||
miss_modifier = max(15*(distance-2) - accuracy + miss_modifier + target_mob.get_evasion(), 0)
|
||||
var/hit_zone = get_zone_with_miss_chance(def_zone, target_mob, miss_modifier, ranged_attack=(distance > 1 || original != target_mob)) //if the projectile hits a target we weren't originally aiming at then retain the chance to miss
|
||||
|
||||
if(!bumped && !isturf(original))
|
||||
if(loc == get_turf(original))
|
||||
if(!(original in permutated))
|
||||
if(Bump(original))
|
||||
return
|
||||
var/result = PROJECTILE_FORCE_MISS
|
||||
if(hit_zone)
|
||||
def_zone = hit_zone //set def_zone, so if the projectile ends up hitting someone else later (to be implemented), it is more likely to hit the same part
|
||||
result = target_mob.bullet_act(src, def_zone)
|
||||
|
||||
if(first_step)
|
||||
muzzle_effect(effect_transform)
|
||||
first_step = 0
|
||||
else if(!bumped)
|
||||
tracer_effect(effect_transform)
|
||||
if(result == PROJECTILE_FORCE_MISS)
|
||||
if(!silenced)
|
||||
visible_message("<span class='notice'>\The [src] misses [target_mob] narrowly!</span>")
|
||||
return FALSE
|
||||
|
||||
if(incendiary >= 2) //This should cover the bases of 'Why is there fuel here?' in a much cleaner way than previous.
|
||||
if(src && src.loc) //Safety.
|
||||
if(!src.loc.density)
|
||||
var/trail_volume = (flammability * 0.20)
|
||||
new /obj/effect/decal/cleanable/liquid_fuel/flamethrower_fuel(src.loc, trail_volume, src.dir)
|
||||
|
||||
if(!hitscan)
|
||||
sleep(step_delay) //add delay between movement iterations if it's not a hitscan weapon
|
||||
|
||||
/obj/item/projectile/proc/before_move()
|
||||
return
|
||||
|
||||
/obj/item/projectile/proc/after_move()
|
||||
return
|
||||
|
||||
/obj/item/projectile/proc/setup_trajectory(turf/startloc, turf/targloc, var/x_offset = 0, var/y_offset = 0)
|
||||
// setup projectile state
|
||||
starting = startloc
|
||||
current = startloc
|
||||
yo = targloc.y - startloc.y + y_offset
|
||||
xo = targloc.x - startloc.x + x_offset
|
||||
|
||||
// trajectory dispersion
|
||||
var/offset = 0
|
||||
if(dispersion)
|
||||
var/radius = round(dispersion*9, 1)
|
||||
offset = rand(-radius, radius)
|
||||
|
||||
// plot the initial trajectory
|
||||
trajectory = new()
|
||||
trajectory.setup(starting, original, pixel_x, pixel_y, angle_offset=offset)
|
||||
|
||||
// generate this now since all visual effects the projectile makes can use it
|
||||
effect_transform = new()
|
||||
effect_transform.Scale(trajectory.return_hypotenuse(), 1)
|
||||
effect_transform.Turn(-trajectory.return_angle()) //no idea why this has to be inverted, but it works
|
||||
|
||||
transform = turn(transform, -(trajectory.return_angle() + 90)) //no idea why 90 needs to be added, but it works
|
||||
|
||||
/obj/item/projectile/proc/muzzle_effect(var/matrix/T)
|
||||
//hit messages
|
||||
if(silenced)
|
||||
return
|
||||
to_chat(target_mob, "<span class='danger'>You've been hit in the [parse_zone(def_zone)] by \the [src]!</span>")
|
||||
else
|
||||
visible_message("<span class='danger'>\The [target_mob] is hit by \the [src] in the [parse_zone(def_zone)]!</span>")//X has fired Y is now given by the guns so you cant tell who shot you if you could not see the shooter
|
||||
|
||||
if(ispath(muzzle_type))
|
||||
var/obj/effect/projectile/M = new muzzle_type(get_turf(src))
|
||||
//admin logs
|
||||
if(!no_attack_log)
|
||||
if(istype(firer, /mob) && istype(target_mob))
|
||||
add_attack_logs(firer,target_mob,"Shot with \a [src.type] projectile")
|
||||
|
||||
if(istype(M))
|
||||
M.set_transform(T)
|
||||
M.pixel_x = location.pixel_x
|
||||
M.pixel_y = location.pixel_y
|
||||
M.update_light()
|
||||
M.activate()
|
||||
//sometimes bullet_act() will want the projectile to continue flying
|
||||
if (result == PROJECTILE_CONTINUE)
|
||||
return FALSE
|
||||
|
||||
/obj/item/projectile/proc/tracer_effect(var/matrix/M)
|
||||
if(ispath(tracer_type))
|
||||
var/obj/effect/projectile/P = new tracer_type(location.loc)
|
||||
return TRUE
|
||||
|
||||
if(istype(P))
|
||||
P.set_transform(M)
|
||||
P.pixel_x = location.pixel_x
|
||||
P.pixel_y = location.pixel_y
|
||||
P.update_light()
|
||||
if(!hitscan)
|
||||
P.activate(step_delay) //if not a hitscan projectile, remove after a single delay
|
||||
else
|
||||
P.activate()
|
||||
|
||||
/obj/item/projectile/proc/impact_effect(var/matrix/M)
|
||||
if(ispath(tracer_type) && location)
|
||||
var/obj/effect/projectile/P = new impact_type(location.loc)
|
||||
|
||||
if(istype(P))
|
||||
P.set_transform(M)
|
||||
P.pixel_x = location.pixel_x
|
||||
P.pixel_y = location.pixel_y
|
||||
P.update_light()
|
||||
P.activate()
|
||||
|
||||
//"Tracing" projectile
|
||||
/obj/item/projectile/test //Used to see if you can hit them.
|
||||
invisibility = 101 //Nope! Can't see me!
|
||||
yo = null
|
||||
xo = null
|
||||
var/result = 0 //To pass the message back to the gun.
|
||||
|
||||
/obj/item/projectile/test/Bump(atom/A as mob|obj|turf|area)
|
||||
if(A == firer)
|
||||
loc = A.loc
|
||||
return //cannot shoot yourself
|
||||
if(istype(A, /obj/item/projectile))
|
||||
return
|
||||
if(istype(A, /obj/structure/foamedmetal)) //Turrets can detect through foamed metal, but will have to blast through it. Similar to windows, if someone runs behind it, a person should probably just not shoot.
|
||||
return
|
||||
if(istype(A, /obj/structure/girder)) //They see you there.
|
||||
return
|
||||
if(istype(A, /obj/structure/door_assembly)) //And through there.
|
||||
return
|
||||
if(istype(A, /obj/structure)) //Unanchored things you can shove around will still keep the turret or other firing at your position. Aim intent still functions.
|
||||
var/obj/structure/S = A
|
||||
if(!S.anchored)
|
||||
return
|
||||
if(istype(A, /mob/living) || istype(A, /obj/mecha) || istype(A, /obj/vehicle))
|
||||
result = 2 //We hit someone, return 1!
|
||||
return
|
||||
result = 1
|
||||
return
|
||||
|
||||
/obj/item/projectile/test/launch(atom/target)
|
||||
var/turf/curloc = get_turf(src)
|
||||
var/turf/targloc = get_turf(target)
|
||||
if(!curloc || !targloc)
|
||||
return 0
|
||||
|
||||
/obj/item/projectile/proc/launch_projectile(atom/target, target_zone, mob/user, params, angle_override, forced_spread = 0)
|
||||
original = target
|
||||
def_zone = check_zone(target_zone)
|
||||
firer = user
|
||||
var/direct_target
|
||||
if(get_turf(target) == get_turf(src))
|
||||
direct_target = target
|
||||
|
||||
//plot the initial trajectory
|
||||
setup_trajectory(curloc, targloc)
|
||||
return process(targloc)
|
||||
preparePixelProjectile(target, user? user : get_turf(src), params, forced_spread)
|
||||
return fire(angle_override, direct_target)
|
||||
|
||||
/obj/item/projectile/test/process(var/turf/targloc)
|
||||
while(src) //Loop on through!
|
||||
if(result)
|
||||
return (result - 1)
|
||||
if((!( targloc ) || loc == targloc))
|
||||
targloc = locate(min(max(x + xo, 1), world.maxx), min(max(y + yo, 1), world.maxy), z) //Finding the target turf at map edge
|
||||
//called to launch a projectile from a gun
|
||||
/obj/item/projectile/proc/launch_from_gun(atom/target, target_zone, mob/user, params, angle_override, forced_spread, obj/item/weapon/gun/launcher)
|
||||
|
||||
trajectory.increment() // increment the current location
|
||||
location = trajectory.return_location(location) // update the locally stored location data
|
||||
shot_from = launcher.name
|
||||
silenced = launcher.silenced
|
||||
|
||||
Move(location.return_turf())
|
||||
|
||||
var/mob/living/M = locate() in get_turf(src)
|
||||
if(istype(M)) //If there is someting living...
|
||||
return 1 //Return 1
|
||||
else
|
||||
M = locate() in get_step(src,targloc)
|
||||
if(istype(M))
|
||||
return 1
|
||||
|
||||
//Helper proc to check if you can hit them or not.
|
||||
/proc/check_trajectory(atom/target as mob|obj, atom/firer as mob|obj, var/pass_flags=PASSTABLE|PASSGLASS|PASSGRILLE, flags=null)
|
||||
if(!istype(target) || !istype(firer))
|
||||
return 0
|
||||
|
||||
var/obj/item/projectile/test/trace = new /obj/item/projectile/test(get_turf(firer)) //Making the test....
|
||||
|
||||
//Set the flags and pass flags to that of the real projectile...
|
||||
if(!isnull(flags))
|
||||
trace.flags = flags
|
||||
trace.pass_flags = pass_flags
|
||||
|
||||
var/output = trace.launch(target) //Test it!
|
||||
qdel(trace) //No need for it anymore
|
||||
return output //Send it back to the gun!
|
||||
return launch_projectile(target, target_zone, user, params, angle_override, forced_spread)
|
||||
|
||||
@@ -13,5 +13,5 @@
|
||||
/obj/item/projectile/animate/Bump(var/atom/change)
|
||||
if((istype(change, /obj/item) || istype(change, /obj/structure)) && !is_type_in_list(change, protected_objects))
|
||||
var/obj/O = change
|
||||
new /mob/living/simple_animal/hostile/mimic/copy(O.loc, O, firer)
|
||||
new /mob/living/simple_mob/hostile/mimic/copy(O.loc, O, firer)
|
||||
..()
|
||||
|
||||
@@ -9,13 +9,17 @@
|
||||
/obj/item/projectile/arc
|
||||
name = "arcing shot"
|
||||
icon_state = "fireball" // WIP
|
||||
step_delay = 2 // Travel a bit slower, to really sell the arc visuals.
|
||||
speed = 2 // Travel a bit slower, to really sell the arc visuals.
|
||||
movement_type = UNSTOPPABLE
|
||||
plane = ABOVE_PLANE // Since projectiles are 'in the air', they might visually overlap mobs while in flight, so the projectile needs to be above their plane.
|
||||
var/target_distance = null // How many tiles the impact site is.
|
||||
var/fired_dir = null // Which direction was the projectile fired towards. Needed to invert the projectile turning based on if facing left or right.
|
||||
var/obj/effect/projectile_shadow/shadow = null // Visual indicator for the projectile's 'true' position. Needed due to being bound to two dimensions in reality.
|
||||
|
||||
/obj/item/projectile/arc/initialize()
|
||||
/obj/item/projectile/arc/Bump()
|
||||
return
|
||||
|
||||
/obj/item/projectile/arc/Initialize()
|
||||
shadow = new(get_turf(src))
|
||||
return ..()
|
||||
|
||||
@@ -23,33 +27,32 @@
|
||||
QDEL_NULL(shadow)
|
||||
return ..()
|
||||
|
||||
/obj/item/projectile/arc/Bump(atom/A, forced=0)
|
||||
return 0
|
||||
// if(get_turf(src) != original)
|
||||
// return 0
|
||||
// else
|
||||
// return ..()
|
||||
|
||||
// This is a test projectile in the sense that its testing the code to make sure it works,
|
||||
// as opposed to a 'can I hit this thing' projectile.
|
||||
/obj/item/projectile/arc/test/on_impact(turf/T)
|
||||
new /obj/effect/explosion(T)
|
||||
return ..()
|
||||
|
||||
/obj/item/projectile/arc/launch(atom/target, target_zone, x_offset=0, y_offset=0, angle_offset=0)
|
||||
var/expected_distance = get_dist(target, loc)
|
||||
kill_count = expected_distance // So the projectile "hits the ground."
|
||||
/obj/item/projectile/arc/old_style_target(target, source)
|
||||
var/source_loc = get_turf(source) || get_turf(src)
|
||||
var/expected_distance = get_dist(target, source_loc)
|
||||
range = expected_distance // So the projectile "hits the ground."
|
||||
target_distance = expected_distance
|
||||
fired_dir = get_dir(loc, target)
|
||||
..() // Does the regular launching stuff.
|
||||
fired_dir = get_dir(source_loc, target)
|
||||
..()
|
||||
if(fired_dir & EAST)
|
||||
transform = turn(transform, -45)
|
||||
else if(fired_dir & WEST)
|
||||
transform = turn(transform, 45)
|
||||
|
||||
/obj/item/projectile/arc/on_range()
|
||||
on_impact(loc)
|
||||
return ..()
|
||||
|
||||
// Visuals.
|
||||
/obj/item/projectile/arc/after_move()
|
||||
if(QDELETED(src))
|
||||
return
|
||||
// Handle projectile turning in flight.
|
||||
// This won't turn if fired north/south, as it looks weird.
|
||||
var/turn_per_step = 90 / target_distance
|
||||
@@ -73,7 +76,7 @@
|
||||
var/projectile_position = arc_progress / target_distance
|
||||
var/sine_position = projectile_position * 180
|
||||
var/pixel_z_position = arc_max_height * sin(sine_position)
|
||||
animate(src, pixel_z = pixel_z_position, time = step_delay)
|
||||
animate(src, pixel_z = pixel_z_position, time = speed)
|
||||
|
||||
// Update our shadow.
|
||||
shadow.forceMove(loc)
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
light_power = 0.5
|
||||
light_color = "#FF0D00"
|
||||
|
||||
muzzle_type = /obj/effect/projectile/laser/muzzle
|
||||
tracer_type = /obj/effect/projectile/laser/tracer
|
||||
impact_type = /obj/effect/projectile/laser/impact
|
||||
muzzle_type = /obj/effect/projectile/muzzle/laser
|
||||
tracer_type = /obj/effect/projectile/tracer/laser
|
||||
impact_type = /obj/effect/projectile/impact/laser
|
||||
|
||||
/obj/item/projectile/beam/practice
|
||||
name = "laser"
|
||||
@@ -54,9 +54,9 @@
|
||||
light_power = 1
|
||||
light_color = "#FF0D00"
|
||||
|
||||
muzzle_type = /obj/effect/projectile/laser_heavy/muzzle
|
||||
tracer_type = /obj/effect/projectile/laser_heavy/tracer
|
||||
impact_type = /obj/effect/projectile/laser_heavy/impact
|
||||
muzzle_type = /obj/effect/projectile/muzzle/laser_heavy
|
||||
tracer_type = /obj/effect/projectile/tracer/laser_heavy
|
||||
impact_type = /obj/effect/projectile/impact/laser_heavy
|
||||
|
||||
/obj/item/projectile/beam/heavylaser/fakeemitter
|
||||
name = "emitter beam"
|
||||
@@ -64,9 +64,9 @@
|
||||
fire_sound = 'sound/weapons/emitter.ogg'
|
||||
light_color = "#00CC33"
|
||||
|
||||
muzzle_type = /obj/effect/projectile/emitter/muzzle
|
||||
tracer_type = /obj/effect/projectile/emitter/tracer
|
||||
impact_type = /obj/effect/projectile/emitter/impact
|
||||
muzzle_type = /obj/effect/projectile/muzzle/emitter
|
||||
tracer_type = /obj/effect/projectile/tracer/emitter
|
||||
impact_type = /obj/effect/projectile/impact/emitter
|
||||
|
||||
/obj/item/projectile/beam/heavylaser/cannon
|
||||
damage = 80
|
||||
@@ -81,9 +81,9 @@
|
||||
armor_penetration = 50
|
||||
light_color = "#00CC33"
|
||||
|
||||
muzzle_type = /obj/effect/projectile/xray/muzzle
|
||||
tracer_type = /obj/effect/projectile/xray/tracer
|
||||
impact_type = /obj/effect/projectile/xray/impact
|
||||
muzzle_type = /obj/effect/projectile/muzzle/xray
|
||||
tracer_type = /obj/effect/projectile/tracer/xray
|
||||
impact_type = /obj/effect/projectile/impact/xray
|
||||
|
||||
/obj/item/projectile/beam/cyan
|
||||
name = "cyan beam"
|
||||
@@ -91,21 +91,21 @@
|
||||
damage = 40
|
||||
light_color = "#00C6FF"
|
||||
|
||||
muzzle_type = /obj/effect/projectile/laser_omni/muzzle
|
||||
tracer_type = /obj/effect/projectile/laser_omni/tracer
|
||||
impact_type = /obj/effect/projectile/laser_omni/impact
|
||||
muzzle_type = /obj/effect/projectile/muzzle/laser_omni
|
||||
tracer_type = /obj/effect/projectile/tracer/laser_omni
|
||||
impact_type = /obj/effect/projectile/impact/laser_omni
|
||||
|
||||
/obj/item/projectile/beam/pulse
|
||||
name = "pulse"
|
||||
icon_state = "u_laser"
|
||||
fire_sound='sound/weapons/pulse.ogg'
|
||||
fire_sound='sound/weapons/gauss_shoot.ogg' // Needs a more meaty sound than what pulse.ogg currently is; this will be a placeholder for now.
|
||||
damage = 100 //Badmin toy, don't care
|
||||
armor_penetration = 100
|
||||
light_color = "#0066FF"
|
||||
|
||||
muzzle_type = /obj/effect/projectile/laser_pulse/muzzle
|
||||
tracer_type = /obj/effect/projectile/laser_pulse/tracer
|
||||
impact_type = /obj/effect/projectile/laser_pulse/impact
|
||||
muzzle_type = /obj/effect/projectile/muzzle/laser_pulse
|
||||
tracer_type = /obj/effect/projectile/tracer/laser_pulse
|
||||
impact_type = /obj/effect/projectile/impact/laser_pulse
|
||||
|
||||
/obj/item/projectile/beam/pulse/on_hit(var/atom/target, var/blocked = 0)
|
||||
if(isturf(target))
|
||||
@@ -119,9 +119,9 @@
|
||||
damage = 0 // The actual damage is computed in /code/modules/power/singularity/emitter.dm
|
||||
light_color = "#00CC33"
|
||||
|
||||
muzzle_type = /obj/effect/projectile/emitter/muzzle
|
||||
tracer_type = /obj/effect/projectile/emitter/tracer
|
||||
impact_type = /obj/effect/projectile/emitter/impact
|
||||
muzzle_type = /obj/effect/projectile/muzzle/emitter
|
||||
tracer_type = /obj/effect/projectile/tracer/emitter
|
||||
impact_type = /obj/effect/projectile/impact/emitter
|
||||
|
||||
/obj/item/projectile/beam/lastertag/blue
|
||||
name = "lasertag beam"
|
||||
@@ -134,9 +134,9 @@
|
||||
|
||||
combustion = FALSE
|
||||
|
||||
muzzle_type = /obj/effect/projectile/laser_blue/muzzle
|
||||
tracer_type = /obj/effect/projectile/laser_blue/tracer
|
||||
impact_type = /obj/effect/projectile/laser_blue/impact
|
||||
muzzle_type = /obj/effect/projectile/muzzle/laser_blue
|
||||
tracer_type = /obj/effect/projectile/tracer/laser_blue
|
||||
impact_type = /obj/effect/projectile/impact/laser_blue
|
||||
|
||||
/obj/item/projectile/beam/lastertag/blue/on_hit(var/atom/target, var/blocked = 0)
|
||||
if(istype(target, /mob/living/carbon/human))
|
||||
@@ -173,9 +173,9 @@
|
||||
|
||||
combustion = FALSE
|
||||
|
||||
muzzle_type = /obj/effect/projectile/laser_omni/muzzle
|
||||
tracer_type = /obj/effect/projectile/laser_omni/tracer
|
||||
impact_type = /obj/effect/projectile/laser_omni/impact
|
||||
muzzle_type = /obj/effect/projectile/muzzle/laser_omni
|
||||
tracer_type = /obj/effect/projectile/tracer/laser_omni
|
||||
impact_type = /obj/effect/projectile/impact/laser_omni
|
||||
|
||||
/obj/item/projectile/beam/lastertag/omni/on_hit(var/atom/target, var/blocked = 0)
|
||||
if(istype(target, /mob/living/carbon/human))
|
||||
@@ -192,9 +192,9 @@
|
||||
armor_penetration = 10
|
||||
light_color = "#00CC33"
|
||||
|
||||
muzzle_type = /obj/effect/projectile/xray/muzzle
|
||||
tracer_type = /obj/effect/projectile/xray/tracer
|
||||
impact_type = /obj/effect/projectile/xray/impact
|
||||
muzzle_type = /obj/effect/projectile/muzzle/xray
|
||||
tracer_type = /obj/effect/projectile/tracer/xray
|
||||
impact_type = /obj/effect/projectile/impact/xray
|
||||
|
||||
/obj/item/projectile/beam/stun
|
||||
name = "stun beam"
|
||||
@@ -208,9 +208,9 @@
|
||||
|
||||
combustion = FALSE
|
||||
|
||||
muzzle_type = /obj/effect/projectile/stun/muzzle
|
||||
tracer_type = /obj/effect/projectile/stun/tracer
|
||||
impact_type = /obj/effect/projectile/stun/impact
|
||||
muzzle_type = /obj/effect/projectile/muzzle/stun
|
||||
tracer_type = /obj/effect/projectile/tracer/stun
|
||||
impact_type = /obj/effect/projectile/impact/stun
|
||||
|
||||
/obj/item/projectile/beam/stun/weak
|
||||
name = "weak stun beam"
|
||||
@@ -220,4 +220,4 @@
|
||||
/obj/item/projectile/beam/stun/med
|
||||
name = "stun beam"
|
||||
icon_state = "stun"
|
||||
agony = 35
|
||||
agony = 30
|
||||
@@ -7,9 +7,9 @@
|
||||
damage_type = HALLOSS
|
||||
light_color = "#00CECE"
|
||||
|
||||
muzzle_type = /obj/effect/projectile/laser_omni/muzzle
|
||||
tracer_type = /obj/effect/projectile/laser_omni/tracer
|
||||
impact_type = /obj/effect/projectile/laser_omni/impact
|
||||
muzzle_type = /obj/effect/projectile/muzzle/laser_omni
|
||||
tracer_type = /obj/effect/projectile/tracer/laser_omni
|
||||
impact_type = /obj/effect/projectile/impact/laser_omni
|
||||
|
||||
/obj/item/projectile/beam/stun
|
||||
agony = 35
|
||||
@@ -22,9 +22,9 @@
|
||||
damage_type = HALLOSS
|
||||
light_color = "#00CC33"
|
||||
|
||||
muzzle_type = /obj/effect/projectile/xray/muzzle
|
||||
tracer_type = /obj/effect/projectile/xray/tracer
|
||||
impact_type = /obj/effect/projectile/xray/impact
|
||||
muzzle_type = /obj/effect/projectile/muzzle/xray
|
||||
tracer_type = /obj/effect/projectile/tracer/xray
|
||||
impact_type = /obj/effect/projectile/impact/xray
|
||||
|
||||
/obj/item/projectile/beam/energy_net/on_hit(var/atom/netted)
|
||||
do_net(netted)
|
||||
@@ -38,6 +38,6 @@
|
||||
icon_state = "bluelaser"
|
||||
light_color = "#0066FF"
|
||||
|
||||
muzzle_type = /obj/effect/projectile/laser_blue/muzzle
|
||||
tracer_type = /obj/effect/projectile/laser_blue/tracer
|
||||
impact_type = /obj/effect/projectile/laser_blue/impact
|
||||
muzzle_type = /obj/effect/projectile/muzzle/laser_blue
|
||||
tracer_type = /obj/effect/projectile/tracer/laser_blue
|
||||
impact_type = /obj/effect/projectile/impact/laser_blue
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/obj/item/projectile/bullet
|
||||
name = "bullet"
|
||||
icon_state = "bullet"
|
||||
fire_sound = 'sound/weapons/gunshot/gunshot_strong.ogg'
|
||||
fire_sound = 'sound/weapons/Gunshot4.ogg'
|
||||
damage = 60
|
||||
damage_type = BRUTE
|
||||
nodamage = 0
|
||||
@@ -10,7 +10,7 @@
|
||||
sharp = 1
|
||||
var/mob_passthrough_check = 0
|
||||
|
||||
muzzle_type = /obj/effect/projectile/bullet/muzzle
|
||||
muzzle_type = /obj/effect/projectile/muzzle/bullet
|
||||
|
||||
/obj/item/projectile/bullet/on_hit(var/atom/target, var/blocked = 0)
|
||||
if (..(target, blocked))
|
||||
@@ -65,7 +65,7 @@
|
||||
/* short-casing projectiles, like the kind used in pistols or SMGs */
|
||||
|
||||
/obj/item/projectile/bullet/pistol // 9mm pistols and most SMGs. Sacrifice power for capacity.
|
||||
fire_sound = 'sound/weapons/gunshot/gunshot_pistol.ogg' // ToDo: Different shot sounds for different strength pistols. -Ace
|
||||
fire_sound = 'sound/weapons/gunshot2.ogg'
|
||||
damage = 20
|
||||
|
||||
/obj/item/projectile/bullet/pistol/ap
|
||||
@@ -77,7 +77,7 @@
|
||||
armor_penetration = -50
|
||||
|
||||
/obj/item/projectile/bullet/pistol/medium // .45 (and maybe .40 if it ever gets added) caliber security pistols. Balance between capacity and power.
|
||||
// fire_sound = 'sound/weapons/gunshot3.ogg' // ToDo: Different shot sounds for different strength pistols.
|
||||
fire_sound = 'sound/weapons/gunshot3.ogg' // Snappier sound.
|
||||
damage = 25
|
||||
|
||||
/obj/item/projectile/bullet/pistol/medium/ap
|
||||
@@ -89,11 +89,11 @@
|
||||
armor_penetration = -50
|
||||
|
||||
/obj/item/projectile/bullet/pistol/strong // .357 and .44 caliber stuff. High power pistols like the Mateba or Desert Eagle. Sacrifice capacity for power.
|
||||
fire_sound = 'sound/weapons/gunshot/gunshot_strong.ogg' // ToDo: Replace with something less ugly. I recommend weapons/gunshot3.ogg
|
||||
fire_sound = 'sound/weapons/gunshot4.ogg'
|
||||
damage = 60
|
||||
|
||||
/obj/item/projectile/bullet/pistol/rubber/strong // "Rubber" bullets for high power pistols.
|
||||
fire_sound = 'sound/weapons/gunshot/gunshot_strong.ogg' // ToDo: Same as above.
|
||||
fire_sound = 'sound/weapons/gunshot3.ogg' // Rubber shots have less powder, but these still have more punch than normal rubber shot.
|
||||
damage = 10
|
||||
agony = 60
|
||||
embed_chance = 0
|
||||
@@ -107,12 +107,13 @@
|
||||
embed_chance = 0
|
||||
sharp = 0
|
||||
check_armour = "melee"
|
||||
fire_sound ='sound/weapons/Gunshot_pathetic.ogg' // Rubber shots have less powder in the casing.
|
||||
|
||||
/* shotgun projectiles */
|
||||
|
||||
/obj/item/projectile/bullet/shotgun
|
||||
name = "slug"
|
||||
fire_sound = 'sound/weapons/gunshot/shotgun.ogg'
|
||||
fire_sound = 'sound/weapons/Gunshot_shotgun.ogg'
|
||||
damage = 50
|
||||
armor_penetration = 15
|
||||
|
||||
@@ -128,7 +129,7 @@
|
||||
//Overall less damage than slugs in exchange for more damage at very close range and more embedding
|
||||
/obj/item/projectile/bullet/pellet/shotgun
|
||||
name = "shrapnel"
|
||||
fire_sound = 'sound/weapons/gunshot/shotgun.ogg'
|
||||
fire_sound = 'sound/weapons/Gunshot_shotgun.ogg'
|
||||
damage = 13
|
||||
pellets = 6
|
||||
range_step = 1
|
||||
@@ -143,7 +144,7 @@
|
||||
//EMP shotgun 'slug', it's basically a beanbag that pops a tiny emp when it hits. //Not currently used
|
||||
/obj/item/projectile/bullet/shotgun/ion
|
||||
name = "ion slug"
|
||||
fire_sound = 'sound/weapons/Laser.ogg'
|
||||
fire_sound = 'sound/weapons/Laser.ogg' // Really? We got nothing better than this?
|
||||
damage = 15
|
||||
embed_chance = 0
|
||||
sharp = 0
|
||||
@@ -160,14 +161,18 @@
|
||||
/* "Rifle" rounds */
|
||||
|
||||
/obj/item/projectile/bullet/rifle
|
||||
fire_sound = 'sound/weapons/gunshot/gunshot3.ogg'
|
||||
fire_sound = 'sound/weapons/Gunshot_generic_rifle.ogg'
|
||||
armor_penetration = 15
|
||||
penetrating = 1
|
||||
|
||||
/obj/item/projectile/bullet/rifle/a762
|
||||
fire_sound = 'sound/weapons/gunshot/gunshot2.ogg'
|
||||
fire_sound = 'sound/weapons/Gunshot_heavy.ogg'
|
||||
damage = 35
|
||||
|
||||
/obj/item/projectile/bullet/rifle/a762/sniper // Hitscan specifically for sniper ammo; to be implimented at a later date, probably for the SVD. -Ace
|
||||
fire_sound = 'sound/weapons/Gunshot_sniper.ogg'
|
||||
hitscan = 1 //so the ammo isn't useless as a sniper weapon
|
||||
|
||||
/obj/item/projectile/bullet/rifle/a762/ap
|
||||
damage = 30
|
||||
armor_penetration = 50 // At 30 or more armor, this will do more damage than standard rounds.
|
||||
@@ -177,12 +182,13 @@
|
||||
armor_penetration = -50
|
||||
penetrating = 0
|
||||
|
||||
/obj/item/projectile/bullet/rifle/a762/hunter // Optimized for killing simple animals and not people, because Balance.
|
||||
/obj/item/projectile/bullet/rifle/a762/hunter // Optimized for killing simple animals and not people, because Balance(tm)
|
||||
damage = 20
|
||||
SA_bonus_damage = 50 // 70 total on animals.
|
||||
SA_vulnerability = SA_ANIMAL
|
||||
|
||||
/obj/item/projectile/bullet/rifle/a545
|
||||
fire_sound = 'sound/weapons/Gunshot_light.ogg'
|
||||
damage = 25
|
||||
|
||||
/obj/item/projectile/bullet/rifle/a545/ap
|
||||
@@ -199,8 +205,8 @@
|
||||
SA_bonus_damage = 35 // 50 total on animals.
|
||||
SA_vulnerability = SA_ANIMAL
|
||||
|
||||
/obj/item/projectile/bullet/rifle/a145
|
||||
fire_sound = 'sound/weapons/gunshot/sniper.ogg'
|
||||
/obj/item/projectile/bullet/rifle/a145 // 14.5×114mm is bigger than a .50 BMG round.
|
||||
fire_sound = 'sound/weapons/Gunshot_cannon.ogg' // This is literally an anti-tank rifle caliber. It better sound like a fucking cannon.
|
||||
damage = 80
|
||||
stun = 3
|
||||
weaken = 3
|
||||
@@ -251,35 +257,22 @@
|
||||
incendiary = 2
|
||||
flammability = 4
|
||||
agony = 30
|
||||
kill_count = 4
|
||||
range = 4
|
||||
vacuum_traversal = 0
|
||||
|
||||
/obj/item/projectile/bullet/incendiary/flamethrower/large
|
||||
damage = 15
|
||||
kill_count = 6
|
||||
range = 6
|
||||
|
||||
/obj/item/projectile/bullet/blank
|
||||
invisibility = 101
|
||||
damage = 1
|
||||
embed_chance = 0
|
||||
/* Practice rounds and blanks */
|
||||
|
||||
/* Practice */
|
||||
|
||||
/obj/item/projectile/bullet/pistol/practice
|
||||
/obj/item/projectile/bullet/practice
|
||||
damage = 5
|
||||
|
||||
/obj/item/projectile/bullet/rifle/practice
|
||||
damage = 5
|
||||
penetrating = 0
|
||||
|
||||
/obj/item/projectile/bullet/shotgun/practice
|
||||
name = "practice"
|
||||
damage = 5
|
||||
|
||||
/obj/item/projectile/bullet/pistol/cap
|
||||
/obj/item/projectile/bullet/pistol/cap // Just the primer, such as a cap gun.
|
||||
name = "cap"
|
||||
damage_type = HALLOSS
|
||||
fire_sound = null
|
||||
fire_sound = 'sound/effects/snap.ogg'
|
||||
damage = 0
|
||||
nodamage = 1
|
||||
embed_chance = 0
|
||||
@@ -288,5 +281,18 @@
|
||||
combustion = FALSE
|
||||
|
||||
/obj/item/projectile/bullet/pistol/cap/process()
|
||||
loc = null
|
||||
qdel(src)
|
||||
|
||||
/obj/item/projectile/bullet/blank
|
||||
name = "blank"
|
||||
damage_type = HALLOSS
|
||||
fire_sound = 'sound/weapons/Gunshot_generic_rifle.ogg' // Blanks still make loud noises.
|
||||
damage = 0
|
||||
nodamage = 1
|
||||
embed_chance = 0
|
||||
sharp = 0
|
||||
|
||||
/obj/item/projectile/bullet/blank/cap/process()
|
||||
loc = null
|
||||
qdel(src)
|
||||
@@ -9,7 +9,7 @@
|
||||
name = "chemical shell"
|
||||
icon_state = "bullet"
|
||||
damage = 10
|
||||
kill_count = 15 //if the shell hasn't hit anything after travelling this far it just explodes.
|
||||
range = 15 //if the shell hasn't hit anything after travelling this far it just explodes.
|
||||
flash_strength = 15
|
||||
brightness = 15
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
Robot.mmi = new /obj/item/device/mmi(new_mob)
|
||||
Robot.mmi.transfer_identity(M) //Does not transfer key/client.
|
||||
if("slime")
|
||||
new_mob = new /mob/living/simple_animal/slime(M.loc)
|
||||
new_mob = new /mob/living/simple_mob/slime/xenobio(M.loc)
|
||||
new_mob.universal_speak = 1
|
||||
else
|
||||
var/mob/living/carbon/human/H
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
/obj/item/projectile/energy/flash
|
||||
name = "chemical shell"
|
||||
icon_state = "bullet"
|
||||
fire_sound = 'sound/weapons/gunshot/gunshot_pistol.ogg'
|
||||
fire_sound = 'sound/weapons/gunshot_pathetic.ogg'
|
||||
damage = 5
|
||||
kill_count = 15 //if the shell hasn't hit anything after travelling this far it just explodes.
|
||||
range = 15 //if the shell hasn't hit anything after travelling this far it just explodes.
|
||||
var/flash_range = 0
|
||||
var/brightness = 7
|
||||
var/light_colour = "#ffffff"
|
||||
@@ -48,7 +48,7 @@
|
||||
|
||||
//blinds people like the flash round, but can also be used for temporary illumination
|
||||
/obj/item/projectile/energy/flash/flare
|
||||
fire_sound = 'sound/weapons/gunshot/shotgun.ogg'
|
||||
fire_sound = 'sound/weapons/grenade_launcher.ogg'
|
||||
damage = 10
|
||||
flash_range = 1
|
||||
brightness = 15
|
||||
@@ -65,7 +65,7 @@
|
||||
/obj/item/projectile/energy/electrode
|
||||
name = "electrode"
|
||||
icon_state = "spark"
|
||||
fire_sound = 'sound/weapons/Gunshot.ogg'
|
||||
fire_sound = 'sound/weapons/Gunshot2.ogg'
|
||||
taser_effect = 1
|
||||
agony = 40
|
||||
light_range = 2
|
||||
@@ -123,6 +123,7 @@
|
||||
damage_type = BURN
|
||||
agony = 10
|
||||
check_armour = "bio"
|
||||
armor_penetration = 25 // It's acid
|
||||
|
||||
combustion = FALSE
|
||||
|
||||
@@ -130,9 +131,10 @@
|
||||
name = "neurotoxic spit"
|
||||
icon_state = "neurotoxin"
|
||||
damage = 5
|
||||
damage_type = TOX
|
||||
damage_type = BIOACID
|
||||
agony = 80
|
||||
check_armour = "bio"
|
||||
armor_penetration = 25 // It's acid-based
|
||||
|
||||
combustion = FALSE
|
||||
|
||||
@@ -140,9 +142,10 @@
|
||||
name = "neurotoxic spit"
|
||||
icon_state = "neurotoxin"
|
||||
damage = 20
|
||||
damage_type = TOX
|
||||
damage_type = BIOACID
|
||||
agony = 20
|
||||
check_armour = "bio"
|
||||
armor_penetration = 25 // It's acid-based
|
||||
|
||||
/obj/item/projectile/energy/phoron
|
||||
name = "phoron bolt"
|
||||
@@ -162,7 +165,7 @@
|
||||
icon_state = "plasma_stun"
|
||||
fire_sound = 'sound/weapons/blaster.ogg'
|
||||
armor_penetration = 10
|
||||
kill_count = 4
|
||||
range = 4
|
||||
damage = 5
|
||||
agony = 55
|
||||
damage_type = BURN
|
||||
@@ -209,25 +212,25 @@
|
||||
light_color = "#0000FF"
|
||||
|
||||
embed_chance = 0
|
||||
muzzle_type = /obj/effect/projectile/pulse/muzzle
|
||||
muzzle_type = /obj/effect/projectile/muzzle/pulse
|
||||
|
||||
/obj/item/projectile/energy/phase
|
||||
name = "phase wave"
|
||||
icon_state = "phase"
|
||||
kill_count = 6
|
||||
range = 6
|
||||
damage = 5
|
||||
SA_bonus_damage = 45 // 50 total on animals
|
||||
SA_vulnerability = SA_ANIMAL
|
||||
|
||||
/obj/item/projectile/energy/phase/light
|
||||
kill_count = 4
|
||||
range = 4
|
||||
SA_bonus_damage = 35 // 40 total on animals
|
||||
|
||||
/obj/item/projectile/energy/phase/heavy
|
||||
kill_count = 8
|
||||
range = 8
|
||||
SA_bonus_damage = 55 // 60 total on animals
|
||||
|
||||
/obj/item/projectile/energy/phase/heavy/cannon
|
||||
kill_count = 10
|
||||
range = 10
|
||||
damage = 15
|
||||
SA_bonus_damage = 60 // 75 total on animals
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* File containing special 'hook' projectiles. Function is dictated by the launcher's intent.
|
||||
*/
|
||||
|
||||
/obj/item/projectile/energy/hook
|
||||
name = "graviton sphere"
|
||||
icon_state = "bluespace"
|
||||
|
||||
var/beam_state = "b_beam"
|
||||
|
||||
damage = 5
|
||||
speed = 2
|
||||
damage_type = BURN
|
||||
check_armour = "energy"
|
||||
armor_penetration = 15
|
||||
|
||||
var/impact_sound = 'sound/effects/uncloak.ogg'
|
||||
var/crack_sound = 'sound/effects/teleport.ogg'
|
||||
fire_sound = 'sound/effects/zzzt.ogg'
|
||||
|
||||
var/target_distance = null // Shamelessly stolen from arcing projectiles.
|
||||
var/my_tracking_beam = null // Beam made by the launcher. Tracked here to destroy it in time with the impact.
|
||||
var/launcher_intent = null // Stores the launcher's intent.
|
||||
|
||||
var/disarm_chance = 60 // Chance for a successful disarm hit. The inverse is a throw away from the firer.
|
||||
|
||||
var/list/help_messages = list("slaps", "pokes", "nudges", "bumps", "pinches")
|
||||
var/done_mob_unique = FALSE // Has the projectile already done something to a mob?
|
||||
|
||||
/obj/item/projectile/energy/hook/launch_projectile(atom/target, target_zone, mob/user, params, angle_override, forced_spread = 0)
|
||||
var/expected_distance = get_dist(target, loc)
|
||||
range = expected_distance // So the hook hits the ground if no mob is hit.
|
||||
target_distance = expected_distance
|
||||
if(firer) // Needed to ensure later checks in impact and on hit function.
|
||||
launcher_intent = firer.a_intent
|
||||
firer.Beam(src,icon_state=beam_state,icon='icons/effects/beam.dmi',time=60, maxdistance=10,beam_type=/obj/effect/ebeam,beam_sleep_time=1)
|
||||
|
||||
if(launcher_intent)
|
||||
switch(launcher_intent)
|
||||
if(I_HURT)
|
||||
check_armour = "bullet"
|
||||
damage *= 3
|
||||
sharp = 1
|
||||
agony = 20
|
||||
if(I_GRAB)
|
||||
check_armour = "melee"
|
||||
damage_type = HALLOSS
|
||||
if(I_DISARM)
|
||||
check_armour = "melee"
|
||||
if(prob(30)) // A chance for a successful hit to either knock someone down, or cause minor disorientation.
|
||||
weaken = 1
|
||||
else
|
||||
stun = 2
|
||||
eyeblur = 3
|
||||
if(I_HELP)
|
||||
silenced = 1
|
||||
damage_type = HALLOSS
|
||||
|
||||
..() // Does the regular launching stuff.
|
||||
|
||||
/obj/item/projectile/energy/hook/on_hit(var/atom/target, var/blocked = 0, var/def_zone = null)
|
||||
if(..())
|
||||
perform_intent_unique(target)
|
||||
|
||||
/obj/item/projectile/energy/hook/on_impact(var/atom/A)
|
||||
perform_intent_unique(get_turf(A))
|
||||
|
||||
/obj/item/projectile/energy/hook/proc/ranged_disarm(var/mob/living/carbon/human/H)
|
||||
if(istype(H))
|
||||
var/list/holding = list(H.get_active_hand() = 60, H.get_inactive_hand() = 40)
|
||||
|
||||
for(var/obj/item/weapon/gun/W in holding) // Guns are complex devices, both of a mechanical and electronic nature. A weird gravity ball or other type of object trying to pull or grab it is likely not safe.
|
||||
if(W && prob(holding[W]))
|
||||
var/list/turfs = list()
|
||||
for(var/turf/T in view())
|
||||
turfs += T
|
||||
if(turfs.len)
|
||||
var/turf/target = pick(turfs)
|
||||
visible_message("<span class='danger'>[H]'s [W] goes off due to \the [src]!</span>")
|
||||
return W.afterattack(target,H)
|
||||
|
||||
if(!(H.species.flags & NO_SLIP) && prob(50))
|
||||
var/armor_check = H.run_armor_check(def_zone, "melee")
|
||||
H.apply_effect(3, WEAKEN, armor_check)
|
||||
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
|
||||
if(armor_check < 60)
|
||||
visible_message("<span class='danger'>\The [src] has pushed [H]!</span>")
|
||||
else
|
||||
visible_message("<span class='warning'>\The [src] attempted to push [H]!</span>")
|
||||
return
|
||||
|
||||
else
|
||||
if(H.break_all_grabs(firer))
|
||||
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
|
||||
return
|
||||
|
||||
for(var/obj/item/I in holding)
|
||||
if(I)
|
||||
H.drop_from_inventory(I)
|
||||
visible_message("<span class='danger'>\The [src] has disarmed [H]!</span>")
|
||||
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
|
||||
return
|
||||
|
||||
|
||||
/obj/item/projectile/energy/hook/proc/perform_intent_unique(atom/target)
|
||||
playsound(src.loc, impact_sound, 40, 1)
|
||||
var/success = FALSE
|
||||
if(istype(target,/turf))
|
||||
if(launcher_intent)
|
||||
if(launcher_intent != I_HELP && !done_mob_unique)
|
||||
var/target_mob = pick(/mob/living in target.contents)
|
||||
|
||||
if(!target_mob)
|
||||
return
|
||||
|
||||
if(Bump(target_mob, forced=1)) //If we hit a turf, try to force an interaction with a mob on the turf.
|
||||
done_mob_unique = TRUE
|
||||
success = TRUE
|
||||
else if(firer)
|
||||
var/obj/T
|
||||
|
||||
if(original in target.contents && istype(original, /obj))
|
||||
T = original
|
||||
|
||||
var/list/possible_targets = list()
|
||||
for(var/obj/item/I in target.contents)
|
||||
if(!I.anchored)
|
||||
possible_targets += I
|
||||
for(var/obj/structure/S in target.contents)
|
||||
if(!S.anchored)
|
||||
possible_targets += S
|
||||
|
||||
if(!T)
|
||||
if(!possible_targets || !possible_targets.len)
|
||||
return
|
||||
T = pick(possible_targets)
|
||||
|
||||
spawn(2)
|
||||
playsound(target, crack_sound, 40, 1)
|
||||
visible_message("<span class='notice'>\The [T] is snatched by \the [src]!</span>")
|
||||
T.throw_at(get_turf(firer), 7, 1, src)
|
||||
success = TRUE
|
||||
else if(isliving(target) && !done_mob_unique)
|
||||
var/mob/living/L = target
|
||||
if(launcher_intent)
|
||||
switch(launcher_intent)
|
||||
if(I_HELP)
|
||||
var/message = pick(help_messages)
|
||||
if(message == "slaps")
|
||||
spawn(1)
|
||||
playsound(loc, 'sound/effects/snap.ogg', 50, 1)
|
||||
visible_message("<span class='notice'>\The [src] [message] [target].</span>")
|
||||
done_mob_unique = TRUE
|
||||
success = TRUE
|
||||
if(I_HURT)
|
||||
if(prob(10) && istype(L, /mob/living/carbon/human))
|
||||
to_chat(L, "<span class='warning'>\The [src] rips at your hands!</span>")
|
||||
ranged_disarm(L)
|
||||
success = TRUE
|
||||
done_mob_unique = TRUE
|
||||
if(I_DISARM)
|
||||
if(prob(disarm_chance) && istype(L, /mob/living/carbon/human))
|
||||
ranged_disarm(L)
|
||||
else
|
||||
L.visible_message("<span class='danger'>\The [src] sends \the [L] stumbling backwards.</span>")
|
||||
L.throw_at(get_turf(get_step(L,get_dir(firer,L))), 1, 1, src)
|
||||
done_mob_unique = TRUE
|
||||
success = TRUE
|
||||
if(I_GRAB)
|
||||
var/turf/STurf = get_turf(L)
|
||||
spawn(2)
|
||||
playsound(STurf, crack_sound, 60, 1)
|
||||
L.visible_message("<span class='critical'>\The [src] rips [L] towards \the [firer]!</span>")
|
||||
L.throw_at(get_turf(get_step(firer,get_dir(firer,L))), 6, 1, src)
|
||||
done_mob_unique = TRUE
|
||||
success = TRUE
|
||||
else if(istype(target, /obj/structure))
|
||||
var/obj/structure/S = target
|
||||
if(!S.anchored)
|
||||
S.throw_at(get_turf(get_step(firer,get_dir(firer,S))), 4, 1, src)
|
||||
success = TRUE
|
||||
qdel(my_tracking_beam)
|
||||
return success
|
||||
|
||||
/*
|
||||
* Hook subtypes.
|
||||
*/
|
||||
|
||||
/obj/item/projectile/energy/hook/ring
|
||||
name = "green orb"
|
||||
icon_state = "green_laser"
|
||||
beam_state = "n_beam"
|
||||
damage = 3
|
||||
@@ -34,7 +34,7 @@
|
||||
penetrating = 2
|
||||
embed_chance = 0
|
||||
armor_penetration = 40
|
||||
kill_count = 20
|
||||
range = 20
|
||||
|
||||
var/searing = 0 //Does this fuelrod ignore shields?
|
||||
var/detonate_travel = 0 //Will this fuelrod explode when it reaches maximum distance?
|
||||
@@ -50,7 +50,7 @@
|
||||
|
||||
if(energetic_impact)
|
||||
var/eye_coverage = 0
|
||||
for(var/mob/living/carbon/M in viewers(world.view, location))
|
||||
for(var/mob/living/carbon/M in viewers(world.view, get_turf(src)))
|
||||
eye_coverage = 0
|
||||
if(iscarbon(M))
|
||||
eye_coverage = M.eyecheck()
|
||||
@@ -103,7 +103,7 @@
|
||||
armor_penetration = 100
|
||||
penetrating = 100 //Theoretically, this shouldn't stop flying for a while, unless someone lines it up with a wall or fires it into a mountain.
|
||||
irradiate = 120
|
||||
kill_count = 75
|
||||
range = 75
|
||||
searing = 1
|
||||
detonate_travel = 1
|
||||
detonate_mob = 1
|
||||
@@ -116,4 +116,29 @@
|
||||
return ..(target, blocked, def_zone)
|
||||
|
||||
/obj/item/projectile/bullet/magnetic/fuelrod/supermatter/check_penetrate()
|
||||
return 1
|
||||
return 1
|
||||
|
||||
/obj/item/projectile/bullet/magnetic/bore
|
||||
name = "phorogenic blast"
|
||||
icon_state = "purpleemitter"
|
||||
damage = 20
|
||||
incendiary = 1
|
||||
armor_penetration = 20
|
||||
penetrating = 0
|
||||
check_armour = "melee"
|
||||
irradiate = 20
|
||||
range = 6
|
||||
|
||||
/obj/item/projectile/bullet/magnetic/bore/Bump(atom/A, forced=0)
|
||||
if(istype(A, /turf/simulated/mineral))
|
||||
var/turf/simulated/mineral/MI = A
|
||||
loc = get_turf(A) // Careful.
|
||||
permutated.Add(A)
|
||||
MI.GetDrilled(TRUE)
|
||||
return 0
|
||||
else if(istype(A, /turf/simulated/wall) || istype(A, /turf/simulated/shuttle/wall)) // Cause a loud, but relatively minor explosion on the wall it hits.
|
||||
explosion(A, -1, -1, 1, 3)
|
||||
qdel(src)
|
||||
return 1
|
||||
else
|
||||
..()
|
||||
|
||||
@@ -9,10 +9,6 @@
|
||||
var/base_spread = 90 //lower means the pellets spread more across body parts. If zero then this is considered a shrapnel explosion instead of a shrapnel cone
|
||||
var/spread_step = 10 //higher means the pellets spread more across body parts with distance
|
||||
|
||||
/obj/item/projectile/bullet/pellet/Bumped()
|
||||
. = ..()
|
||||
bumped = 0 //can hit all mobs in a tile. pellets is decremented inside attack_mob so this should be fine.
|
||||
|
||||
/obj/item/projectile/bullet/pellet/proc/get_pellets(var/distance)
|
||||
var/pellet_loss = round((distance - 1)/range_step) //pellets lost due to distance
|
||||
return max(pellets - pellet_loss, 1)
|
||||
|
||||
@@ -219,7 +219,7 @@
|
||||
embed_chance = 0 // nope
|
||||
nodamage = 1
|
||||
damage_type = HALLOSS
|
||||
muzzle_type = /obj/effect/projectile/bullet/muzzle
|
||||
muzzle_type = /obj/effect/projectile/muzzle/bullet
|
||||
|
||||
/obj/item/projectile/bola
|
||||
name = "bola"
|
||||
@@ -257,3 +257,82 @@
|
||||
visible_message("<span class='danger'>\The [src] splatters a layer of web on \the [target]!</span>")
|
||||
new /obj/effect/spider/stickyweb(target.loc)
|
||||
..()
|
||||
|
||||
/obj/item/projectile/beam/tungsten
|
||||
name = "core of molten tungsten"
|
||||
icon_state = "energy"
|
||||
fire_sound = 'sound/weapons/gauss_shoot.ogg'
|
||||
pass_flags = PASSTABLE | PASSGRILLE
|
||||
damage = 70
|
||||
damage_type = BURN
|
||||
check_armour = "laser"
|
||||
light_range = 4
|
||||
light_power = 3
|
||||
light_color = "#3300ff"
|
||||
|
||||
muzzle_type = /obj/effect/projectile/tungsten/muzzle
|
||||
tracer_type = /obj/effect/projectile/tungsten/tracer
|
||||
impact_type = /obj/effect/projectile/tungsten/impact
|
||||
|
||||
/obj/item/projectile/beam/tungsten/on_hit(var/atom/target, var/blocked = 0)
|
||||
if(isliving(target))
|
||||
var/mob/living/L = target
|
||||
L.add_modifier(/datum/modifier/grievous_wounds, 30 SECONDS)
|
||||
if(ishuman(L))
|
||||
var/mob/living/carbon/human/H = L
|
||||
|
||||
var/target_armor = H.getarmor(def_zone, check_armour)
|
||||
var/obj/item/organ/external/target_limb = H.get_organ(def_zone)
|
||||
|
||||
var/armor_special = 0
|
||||
|
||||
if(target_armor >= 60)
|
||||
var/turf/T = get_step(H, pick(alldirs - src.dir))
|
||||
H.throw_at(T, 1, 1, src)
|
||||
H.apply_damage(20, BURN, def_zone)
|
||||
if(target_limb)
|
||||
armor_special = 2
|
||||
target_limb.fracture()
|
||||
|
||||
else if(target_armor >= 45)
|
||||
H.apply_damage(15, BURN, def_zone)
|
||||
if(target_limb)
|
||||
armor_special = 1
|
||||
target_limb.dislocate()
|
||||
|
||||
else if(target_armor >= 30)
|
||||
H.apply_damage(10, BURN, def_zone)
|
||||
if(prob(30) && target_limb)
|
||||
armor_special = 1
|
||||
target_limb.dislocate()
|
||||
|
||||
else if(target_armor >= 15)
|
||||
H.apply_damage(5, BURN, def_zone)
|
||||
if(prob(15) && target_limb)
|
||||
armor_special = 1
|
||||
target_limb.dislocate()
|
||||
|
||||
if(armor_special > 1)
|
||||
target.visible_message("<span class='cult'>\The [src] slams into \the [target]'s [target_limb], reverberating loudly!</span>")
|
||||
|
||||
else if(armor_special)
|
||||
target.visible_message("<span class='cult'>\The [src] slams into \the [target]'s [target_limb] with a low rumble!</span>")
|
||||
|
||||
..()
|
||||
|
||||
/obj/item/projectile/beam/tungsten/on_impact(var/atom/A)
|
||||
if(istype(A,/turf/simulated/shuttle/wall) || istype(A,/turf/simulated/wall) || (istype(A,/turf/simulated/mineral) && A.density) || istype(A,/obj/mecha) || istype(A,/obj/machinery/door))
|
||||
var/blast_dir = src.dir
|
||||
A.visible_message("<span class='danger'>\The [A] begins to glow!</span>")
|
||||
spawn(2 SECONDS)
|
||||
var/blastloc = get_step(A, blast_dir)
|
||||
if(blastloc)
|
||||
explosion(blastloc, -1, -1, 2, 3)
|
||||
..()
|
||||
|
||||
/obj/item/projectile/beam/tungsten/Bump(atom/A, forced=0)
|
||||
if(istype(A, /obj/structure/window)) //It does not pass through windows. It pulverizes them.
|
||||
var/obj/structure/window/W = A
|
||||
W.shatter()
|
||||
return 0
|
||||
..()
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
//Helper proc to check if you can hit them or not.
|
||||
/proc/check_trajectory(atom/target as mob|obj, atom/firer as mob|obj, var/pass_flags=PASSTABLE|PASSGLASS|PASSGRILLE, flags=null)
|
||||
if(!istype(target) || !istype(firer))
|
||||
return 0
|
||||
|
||||
var/obj/item/projectile/test/trace = new /obj/item/projectile/test(get_turf(firer)) //Making the test....
|
||||
|
||||
//Set the flags and pass flags to that of the real projectile...
|
||||
if(!isnull(flags))
|
||||
trace.flags = flags
|
||||
trace.pass_flags = pass_flags
|
||||
|
||||
return trace.launch_projectile(target) //Test it!
|
||||
|
||||
/obj/item/projectile/proc/_check_fire(atom/target as mob, var/mob/living/user as mob) //Checks if you can hit them or not.
|
||||
check_trajectory(target, user, pass_flags, flags)
|
||||
|
||||
//"Tracing" projectile
|
||||
/obj/item/projectile/test //Used to see if you can hit them.
|
||||
invisibility = 101 //Nope! Can't see me!
|
||||
hitscan = TRUE
|
||||
nodamage = TRUE
|
||||
damage = 0
|
||||
var/list/hit = list()
|
||||
|
||||
/obj/item/projectile/test/process_hitscan()
|
||||
. = ..()
|
||||
if(!QDELING(src))
|
||||
qdel(src)
|
||||
return hit
|
||||
|
||||
/obj/item/projectile/test/Bump(atom/A)
|
||||
if(A != src)
|
||||
hit |= A
|
||||
return ..()
|
||||
|
||||
/obj/item/projectile/test/attack_mob()
|
||||
return
|
||||
@@ -89,7 +89,7 @@
|
||||
aiming_at = null
|
||||
owner = null
|
||||
aiming_with = null
|
||||
processing_objects -= src
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
return ..()
|
||||
|
||||
obj/aiming_overlay/proc/update_aiming_deferred()
|
||||
@@ -171,12 +171,13 @@ obj/aiming_overlay/proc/update_aiming_deferred()
|
||||
if(owner.client)
|
||||
owner.client.add_gun_icons()
|
||||
to_chat(target, "<span class='danger'>You now have a gun pointed at you. No sudden moves!</span>")
|
||||
to_chat(target, "<span class='critical'>If you fail to comply with your assailant, you accept the consequences of your actions.</span>")
|
||||
aiming_with = thing
|
||||
aiming_at = target
|
||||
if(istype(aiming_with, /obj/item/weapon/gun))
|
||||
playsound(get_turf(owner), 'sound/weapons/TargetOn.ogg', 50,1)
|
||||
forceMove(get_turf(target))
|
||||
processing_objects |= src
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
aiming_at.aimed |= src
|
||||
toggle_active(1)
|
||||
@@ -222,5 +223,5 @@ obj/aiming_overlay/proc/update_aiming_deferred()
|
||||
aiming_at.aimed -= src
|
||||
aiming_at = null
|
||||
loc = null
|
||||
processing_objects -= src
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user