Merge branch 'master' into upstream-merge-5944

This commit is contained in:
Novacat
2019-03-28 19:19:44 -04:00
committed by GitHub
1570 changed files with 370927 additions and 63521 deletions
+2 -4
View File
@@ -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
+22 -6
View File
@@ -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()
-291
View File
@@ -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"
+34 -38
View File
@@ -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
+207 -205
View File
@@ -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
+35 -1
View File
@@ -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>")
+1 -1
View File
@@ -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)
+7 -3
View File
@@ -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)
+9 -9
View File
@@ -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"
+519 -360
View File
@@ -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)
..()
+18 -15
View File
@@ -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)
+35 -35
View File
@@ -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
+39 -33
View File
@@ -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
+15 -12
View File
@@ -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
+193
View File
@@ -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)
+80 -1
View File
@@ -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)