Revert "PLASE"

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