This commit is contained in:
Ghommie
2020-02-22 00:55:32 +01:00
1009 changed files with 20505 additions and 13193 deletions
@@ -0,0 +1,26 @@
/obj/item/ammo_casing/caseless/arrow
name = "wooden arrow"
desc = "An arrow made of wood, typically fired from a bow."
projectile_type = /obj/item/projectile/bullet/reusable/arrow
caliber = "arrow"
icon_state = "arrow"
throwforce = 3 //good luck hitting someone with the pointy end of the arrow
throw_speed = 3
/obj/item/ammo_casing/caseless/arrow/ashen
name = "ashen arrow"
desc = "Fire harderned wooden arrow."
icon_state = "asharrow"
projectile_type = /obj/item/projectile/bullet/reusable/arrow/ashen
/obj/item/ammo_casing/caseless/arrow/bone
name = "bone arrow"
desc = "Arrow made of bone and sinew. The tip is sharp enough to pierce into a goliath plate."
icon_state = "bonearrow"
projectile_type = /obj/item/projectile/bullet/reusable/arrow/bone
/obj/item/ammo_casing/caseless/arrow/bronze
name = "bronze arrow"
desc = "Bronze tipped arrow."
icon_state = "bronzearrow"
projectile_type = /obj/item/projectile/bullet/reusable/arrow/bronze
@@ -15,15 +15,15 @@
. = ..()
/obj/item/ammo_casing/energy/gravity/repulse
projectile_type = /obj/item/projectile/gravityrepulse
projectile_type = /obj/item/projectile/gravity/repulse
select_name = "repulse"
/obj/item/ammo_casing/energy/gravity/attract
projectile_type = /obj/item/projectile/gravityattract
projectile_type = /obj/item/projectile/gravity/attract
select_name = "attract"
/obj/item/ammo_casing/energy/gravity/chaos
projectile_type = /obj/item/projectile/gravitychaos
projectile_type = /obj/item/projectile/gravity/chaos
select_name = "chaos"
@@ -12,4 +12,4 @@
/obj/item/ammo_casing/energy/plasma/weak
projectile_type = /obj/item/projectile/plasma/weak
e_cost = 100
e_cost = 100
@@ -5,6 +5,10 @@
e_cost = 200
harmful = FALSE
/obj/item/ammo_casing/energy/electrode/security
projectile_type = /obj/item/projectile/energy/electrode/security
e_cost = 100
/obj/item/ammo_casing/energy/electrode/spec
e_cost = 100
@@ -13,6 +17,7 @@
e_cost = 100
/obj/item/ammo_casing/energy/electrode/hos
projectile_type = /obj/item/projectile/energy/electrode/security/hos
e_cost = 200
/obj/item/ammo_casing/energy/electrode/old
@@ -27,4 +32,4 @@
click_cooldown_override = 3.5
/obj/item/ammo_casing/energy/disabler/secborg
e_cost = 50
e_cost = 50
@@ -13,7 +13,7 @@
name = "magpistol magazine (non-lethal disabler)"
icon_state = "smallmagmag"
ammo_type = /obj/item/ammo_casing/caseless/magnetic/weak/disabler
max_ammo = 15
max_ammo = 16
/obj/item/ammo_box/magazine/mmag/small/lethal
name = "magpistol magazine (lethal)"
@@ -0,0 +1,6 @@
/obj/item/ammo_box/magazine/internal/bow
name = "bow... magazine?" //shouldn't be seeing this
ammo_type = /obj/item/ammo_casing/caseless/arrow
caliber = "arrow"
max_ammo = 1
start_empty = 1
+98 -72
View File
@@ -15,7 +15,7 @@
throw_speed = 3
throw_range = 5
force = 5
item_flags = NEEDS_PERMIT
item_flags = NEEDS_PERMIT | NO_ATTACK_CHAIN_SOFT_STAMCRIT
attack_verb = list("struck", "hit", "bashed")
var/fire_sound = "gunshot"
@@ -28,10 +28,19 @@
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
/// Weapon is burst fire if this is above 1
var/burst_size = 1
/// The time between shots in burst.
var/burst_shot_delay = 3
/// The time between firing actions, this means between bursts if this is burst weapon. The reason this is 0 is because you are still, by default, limited by clickdelay.
var/fire_delay = 0
/// Last world.time this was fired
var/last_fire = 0
/// Currently firing, whether or not it's a burst or not.
var/firing = FALSE
/// Used in gun-in-mouth execution/suicide and similar, while TRUE nothing should work on this like firing or modification and so on and so forth.
var/busy_action = FALSE
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.
@@ -42,6 +51,7 @@
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/no_pin_required = FALSE //whether the gun can be fired without a pin
var/obj/item/flashlight/gun_light
var/can_flashlight = 0
@@ -70,17 +80,24 @@
/obj/item/gun/Initialize()
. = ..()
if(pin)
pin = new pin(src)
if(no_pin_required)
pin = null
else
pin = new pin(src)
if(gun_light)
alight = new (src)
if(zoomable)
azoom = new (src)
/obj/item/gun/Destroy()
QDEL_NULL(pin)
QDEL_NULL(gun_light)
QDEL_NULL(bayonet)
QDEL_NULL(chambered)
if(pin)
QDEL_NULL(pin)
if(gun_light)
QDEL_NULL(gun_light)
if(bayonet)
QDEL_NULL(bayonet)
if(chambered)
QDEL_NULL(chambered)
return ..()
/obj/item/gun/CheckParts(list/parts_list)
@@ -94,6 +111,8 @@
/obj/item/gun/examine(mob/user)
. = ..()
if(no_pin_required)
return
if(pin)
. += "It has \a [pin] installed."
else
@@ -117,13 +136,12 @@
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
user.adjustStaminaLossBuffered(getstamcost(user)*(firing && burst_size >= 2 ? 1/burst_size : 1)) //CIT CHANGE - ditto
if(suppressed)
playsound(user, fire_sound, 10, 1)
@@ -143,9 +161,15 @@
/obj/item/gun/afterattack(atom/target, mob/living/user, flag, params)
. = ..()
process_afterattack(target, user, flag, params)
/obj/item/gun/proc/process_afterattack(atom/target, mob/living/user, flag, params)
if(!target)
return
if(firing_burst)
if(firing)
return
if(user.getStaminaLoss() >= STAMINA_SOFTCRIT) //respect stamina softcrit
to_chat(user, "<span class='warning'>You are too exhausted to fire [src]!</span>")
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.
@@ -169,7 +193,6 @@
handle_suicide(user, target, params)
return
//Exclude lasertag guns from the TRAIT_CLUMSY check.
if(clumsy_check)
if(istype(user))
@@ -201,8 +224,6 @@
process_fire(target, user, TRUE, params, null, bonus_spread)
/obj/item/gun/can_trigger_gun(mob/living/user)
. = ..()
if(!.)
@@ -214,6 +235,8 @@
return FALSE
/obj/item/gun/proc/handle_pins(mob/living/user)
if(no_pin_required)
return TRUE
if(pin)
if(pin.pin_auth(user) || (pin.obj_flags & EMAGGED))
return TRUE
@@ -227,54 +250,29 @@
/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)
before_firing(target,user)
if(!chambered.fire_casing(target, user, params, ,suppressed, zone_override, sprd, src))
shoot_with_empty_chamber(user)
firing_burst = FALSE
return FALSE
else
if(get_dist(user, target) <= 1) //Making sure whether the target is in vicinity for the pointblank shot
shoot_live_shot(user, 1, target, message)
else
shoot_live_shot(user, 0, target, message)
if (iteration >= burst_size)
firing_burst = FALSE
else
shoot_with_empty_chamber(user)
firing_burst = FALSE
return FALSE
process_chamber()
update_icon()
return TRUE
/obj/item/gun/proc/on_cooldown()
return busy_action || firing || ((last_fire + fire_delay) > world.time)
/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)
if(on_cooldown())
return
firing = TRUE
. = do_fire(target, user, message, params, zone_override, bonus_spread)
firing = FALSE
last_fire = world.time
if(user)
user.update_inv_hands()
SEND_SIGNAL(user, COMSIG_LIVING_GUN_PROCESS_FIRE, target, params, zone_override)
/obj/item/gun/proc/do_fire(atom/target, mob/living/user, message = TRUE, params, zone_override = "", bonus_spread = 0)
var/sprd = 0
var/randomized_gun_spread = 0
var/rand_spr = rand()
if(spread)
randomized_gun_spread = rand(0, 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
@@ -282,9 +280,12 @@
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))
do_burst_shot(user, target, message, params, zone_override, sprd, randomized_gun_spread, randomized_bonus_spread, rand_spr, 1)
for(var/i in 2 to burst_size)
sleep(burst_shot_delay)
if(QDELETED(src))
break
do_burst_shot(user, target, message, params, zone_override, sprd, randomized_gun_spread, randomized_bonus_spread, rand_spr, i)
else
if(chambered)
sprd = round((rand() - 0.5) * DUALWIELD_PENALTY_EXTRA_MULTIPLIER * (randomized_gun_spread + randomized_bonus_spread))
@@ -302,21 +303,46 @@
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/proc/do_burst_shot(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)
firing = FALSE
return FALSE
if(!issilicon(user))
if(iteration > 1 && !(user.is_holding(src))) //for burst firing
firing = 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)
before_firing(target,user)
if(!chambered.fire_casing(target, user, params, ,suppressed, zone_override, sprd, src))
shoot_with_empty_chamber(user)
firing = 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 = FALSE
else
shoot_with_empty_chamber(user)
firing = FALSE
return FALSE
process_chamber()
update_icon()
return TRUE
/obj/item/gun/attack(mob/M as mob, mob/user)
if(user.a_intent == INTENT_HARM) //Flogging
@@ -437,7 +463,7 @@
if(!ishuman(user) || !ishuman(target))
return
if(semicd)
if(on_cooldown())
return
if(user == target)
@@ -447,7 +473,7 @@
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
busy_action = TRUE
if(!bypass_timer && (!do_mob(user, target, 120) || user.zone_selected != BODY_ZONE_PRECISE_MOUTH))
if(user)
@@ -455,10 +481,10 @@
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
busy_action = FALSE
return
semicd = FALSE
busy_action = FALSE
target.visible_message("<span class='warning'>[user] pulls the trigger!</span>", "<span class='userdanger'>[user] pulls the trigger!</span>")
@@ -5,7 +5,7 @@
var/automatic_burst_overlay = TRUE
can_suppress = TRUE
burst_size = 3
fire_delay = 2
burst_shot_delay = 2
actions_types = list(/datum/action/item_action/toggle_firemode)
/obj/item/gun/ballistic/automatic/proto
@@ -77,11 +77,9 @@
/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()
@@ -100,7 +98,7 @@
item_state = "c20r"
mag_type = /obj/item/ammo_box/magazine/smgm45
fire_sound = 'sound/weapons/gunshot_smg.ogg'
fire_delay = 2
burst_shot_delay = 2
burst_size = 2
pin = /obj/item/firing_pin/implant/pindicate
can_bayonet = TRUE
@@ -130,7 +128,7 @@
mag_type = /obj/item/ammo_box/magazine/wt550m9
can_suppress = FALSE
burst_size = 2
fire_delay = 1
burst_shot_delay = 1
can_bayonet = TRUE
knife_x_offset = 25
knife_y_offset = 12
@@ -165,7 +163,7 @@
can_suppress = FALSE
var/obj/item/gun/ballistic/revolver/grenadelauncher/underbarrel
burst_size = 3
fire_delay = 2
burst_shot_delay = 2
pin = /obj/item/firing_pin/implant/pindicate
/obj/item/gun/ballistic/automatic/m90/Initialize()
@@ -212,7 +210,6 @@
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
@@ -220,7 +217,6 @@
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()
@@ -237,7 +233,7 @@
fire_sound = 'sound/weapons/gunshot_smg.ogg'
can_suppress = FALSE
burst_size = 4
fire_delay = 1
burst_shot_delay = 1
/obj/item/gun/ballistic/automatic/ar
name = "\improper NT-ARG 'Boarder'"
@@ -249,7 +245,7 @@
fire_sound = 'sound/weapons/gunshot_smg.ogg'
can_suppress = FALSE
burst_size = 3
fire_delay = 1
burst_shot_delay = 1
// Bulldog shotgun //
@@ -264,7 +260,6 @@
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()
@@ -301,7 +296,7 @@
var/cover_open = FALSE
can_suppress = FALSE
burst_size = 3
fire_delay = 1
burst_shot_delay = 1
spread = 7
pin = /obj/item/firing_pin/implant/pindicate
@@ -421,7 +416,7 @@
mag_type = /obj/item/ammo_box/magazine/recharge
fire_delay = 2
can_suppress = FALSE
burst_size = 0
burst_size = 1
actions_types = list()
fire_sound = 'sound/weapons/laser.ogg'
casing_ejector = FALSE
@@ -0,0 +1,68 @@
/obj/item/gun/ballistic/bow
name = "wooden bow"
desc = "Some sort of primitive projectile weapon. Used to fire arrows."
icon_state = "bow"
item_state = "bow"
w_class = WEIGHT_CLASS_BULKY
weapon_weight = WEAPON_HEAVY //need both hands to fire
force = 5
mag_type = /obj/item/ammo_box/magazine/internal/bow
fire_sound = 'sound/weapons/bowfire.wav'
slot_flags = ITEM_SLOT_BACK
item_flags = NONE
casing_ejector = FALSE
inaccuracy_modifier = 0.33 //to counteract the innaccuracy from WEAPON_HEAVY, bows are supposed to be accurate but only able to be fired with both hands
pin = null
no_pin_required = TRUE
trigger_guard = TRIGGER_GUARD_NONE //so ashwalkers can use it
/obj/item/gun/ballistic/bow/shoot_with_empty_chamber()
return
/obj/item/gun/ballistic/bow/chamber_round()
chambered = magazine.get_round(1)
/obj/item/gun/ballistic/bow/afterattack()
. = ..()
if (chambered)
chambered = null
magazine.get_round(0)
update_icon()
/obj/item/gun/ballistic/bow/attack_self(mob/living/user)
if (chambered)
var/obj/item/ammo_casing/AC = magazine.get_round(0)
user.put_in_hands(AC)
chambered = null
to_chat(user, "<span class='notice'>You gently release the bowstring, removing the arrow.</span>")
else if (get_ammo())
to_chat(user, "<span class='notice'>You draw back the bowstring.</span>")
playsound(src, 'sound/weapons/bowdraw.wav', 75, 0) //gets way too high pitched if the freq varies
chamber_round()
update_icon()
/obj/item/gun/ballistic/bow/attackby(obj/item/I, mob/user, params)
if (magazine.attackby(I, user, params, 1))
to_chat(user, "<span class='notice'>You notch the arrow.</span>")
update_icon()
/obj/item/gun/ballistic/bow/update_icon()
icon_state = "bow_[get_ammo() ? (chambered ? "firing" : "loaded") : "unloaded"]"
/obj/item/gun/ballistic/bow/can_shoot()
return chambered
/obj/item/gun/ballistic/bow/ashen
name = "bone bow"
desc = "Some sort of primitive projectile weapon made of bone and sinew. Used to fire arrows."
icon_state = "ashenbow"
item_state = "ashenbow"
force = 8
/obj/item/gun/ballistic/bow/pipe
name = "pipe bow"
desc = "Some sort of pipe made projectile weapon made of a silk string and lots of bending. Used to fire arrows."
icon_state = "pipebow"
item_state = "pipebow"
inaccuracy_modifier = 1.1 //Made of pipe and in a rush
force = 0
@@ -61,7 +61,6 @@
/obj/item/gun/ballistic/automatic/magrifle/nopin
pin = null
spawnwithmagazine = FALSE
cell_type = /obj/item/stock_parts/cell/magnetic/empty
/obj/item/gun/ballistic/automatic/magrifle/hyperburst
name = "\improper Hyper-Burst Rifle"
@@ -100,4 +99,3 @@
/obj/item/gun/ballistic/automatic/magrifle/pistol/nopin
pin = null
spawnwithmagazine = FALSE
cell_type = /obj/item/stock_parts/cell/magnetic/pistol/empty
@@ -79,6 +79,7 @@
/obj/item/gun/ballistic/automatic/pistol/stickman
name = "flat gun"
desc = "A 2 dimensional gun.. what?"
can_suppress = FALSE
icon_state = "flatgun"
/obj/item/gun/ballistic/automatic/pistol/stickman/pickup(mob/living/user)
+139 -40
View File
@@ -1,3 +1,16 @@
/*
* Energy guns that draw from a cell to fire.
*
* This is a bit weird but this is how it currently works:
* When switching shots, it clears the chamber, and loads the correct energy ammo casing if there is enough energy to fire it.
* If there's no projectile in the casing, it creates it now.
* Otherwise the chamber stays null.
* After firing, it actually deducts the energy and then clears the chamber and does the above again.
* It detects if a successful fire is done by checking if the chambered energy ammo casing still has its projectile intact.
*
* It might be good in the future to move away from ammo casinsgs and instead use a datum-firemode system, but that would make handling firing,
* which the casing does as of now, a little interesting to implement.
*/
/obj/item/gun/energy
icon_state = "energy"
name = "energy gun"
@@ -7,8 +20,10 @@
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.
/// = TRUE/FALSE decides if the user can switch to it of their own accord
var/list/ammo_type = list(/obj/item/ammo_casing/energy = TRUE)
/// The index of the ammo_types/firemodes which we're using right now
var/current_firemode_index = 1
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
@@ -21,6 +36,9 @@
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
/// SET THIS TO TRUE IF YOU OVERRIDE altafterattack() or ANY right click action! If this is FALSE, the gun will show in examine its default right click behavior, which is to switch modes.
var/right_click_overridden = FALSE
/obj/item/gun/energy/emp_act(severity)
. = ..()
if(!(. & EMP_PROTECT_CONTENTS))
@@ -46,22 +64,19 @@
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)
QDEL_LIST(ammo_type)
STOP_PROCESSING(SSobj, src)
if(flags_1 & INITIALIZED_1)
QDEL_NULL(cell)
if(!(flags_1 & HOLOGRAM_1)) //holodeck stuff.
QDEL_LIST(ammo_type)
STOP_PROCESSING(SSobj, src)
return ..()
/obj/item/gun/energy/examine(mob/user)
. = ..()
if(!right_click_overridden)
. += "<span class='notice'>Right click in combat mode to switch modes.</span>"
/obj/item/gun/energy/process()
if(selfcharge && cell?.charge < cell.maxcharge)
charge_tick++
@@ -82,13 +97,14 @@
recharge_newshot(TRUE)
update_icon()
/obj/item/gun/energy/attack_self(mob/living/user as mob)
if(ammo_type.len > 1)
// ATTACK SELF IGNORING PARENT RETURN VALUE
/obj/item/gun/energy/attack_self(mob/living/user)
. = ..()
if(can_select_fire(user))
select_fire(user)
update_icon()
/obj/item/gun/energy/can_shoot()
var/obj/item/ammo_casing/energy/shot = ammo_type[select]
var/obj/item/ammo_casing/energy/shot = ammo_type[current_firemode_index]
return !QDELETED(cell) ? (cell.charge >= shot.e_cost) : FALSE
/obj/item/gun/energy/recharge_newshot(no_cyborg_drain)
@@ -98,11 +114,11 @@
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
var/obj/item/ammo_casing/energy/shot = ammo_type[current_firemode_index] //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]
var/obj/item/ammo_casing/energy/AC = ammo_type[current_firemode_index]
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)
@@ -115,29 +131,104 @@
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)
/obj/item/gun/energy/do_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)
/obj/item/gun/energy/do_burst_shot(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)
// Firemodes/Ammotypes
/obj/item/gun/energy/proc/update_ammo_types()
var/obj/item/ammo_casing/energy/C
for(var/i in 1 to length(ammo_type))
var/v = ammo_type[i]
var/user_can_select = ammo_type[v]
if(istype(v, /obj/item/ammo_casing/energy)) //already set
ammo_type[v] = isnull(user_can_select)? TRUE : user_can_select
else
C = new v(src) //if you put non energycasing/type stuff in here you deserve the runtime
ammo_type[i] = C
ammo_type[C] = isnull(user_can_select)? TRUE : user_can_select
set_firemode_index(initial(current_firemode_index))
/obj/item/gun/energy/proc/set_firemode_index(index, mob/user_for_feedback)
chambered = null //unchamber whatever we have chambered
if(index > length(ammo_type))
index = 1
else if(index < 1)
index = length(ammo_type)
var/obj/item/ammo_casing/energy/C = ammo_type[index] //energy weapons should not have no casings, if it does you deserve the runtime.
current_firemode_index = index
fire_sound = C.fire_sound
fire_delay = C.delay
if(user_for_feedback)
to_chat(user_for_feedback, "<span class='notice'>[src] is now set to [C.select_name || C].</span>")
post_set_firemode()
update_icon(TRUE)
return
/obj/item/gun/energy/proc/post_set_firemode(recharge_newshot = TRUE)
if(recharge_newshot)
recharge_newshot(TRUE)
/obj/item/gun/energy/proc/set_firemode_to_next(mob/user_for_feedback)
return set_firemode_index(++current_firemode_index, user_for_feedback)
/obj/item/gun/energy/proc/set_firemode_to_prev(mob/user_for_feedback)
return set_firemode_index(--current_firemode_index, user_for_feedback)
/obj/item/gun/energy/proc/get_firemode_index(casing_type)
var/obj/item/ammo_casing/energy/E = locate(casing_type) in ammo_type
if(E)
return ammo_type.Find(E)
/obj/item/gun/energy/proc/set_firemode_to_type(casing_type)
var/index = get_firemode_index(casing_type)
if(index)
set_firemode_index(index)
/// This is the proc used in general for when a user switches firemodes. Just goes to next firemode by default.
/obj/item/gun/energy/proc/select_fire(mob/living/user)
return user_set_firemode_to_next(user)
/obj/item/gun/energy/proc/can_select_fire(mob/living/user)
return TRUE
#define INCREMENT_OR_WRAP(i) i = (i >= length(ammo_type))? 1 : (i + 1)
#define DECREMENT_OR_WRAP(i) i = (i <= 1)? length(ammo_type) : (i - 1)
#define IS_VALID_INDEX(i) (ammo_type[ammo_type[i]])
/obj/item/gun/energy/proc/user_set_firemode_to_next(mob/user_for_feedback)
var/current_index = current_firemode_index
var/new_index = current_index
INCREMENT_OR_WRAP(new_index)
if(!IS_VALID_INDEX(new_index))
var/initial_index = new_index
while(!IS_VALID_INDEX(new_index) && (new_index != initial_index))
new_index = INCREMENT_OR_WRAP(new_index)
if(initial_index == new_index) //cycled through without finding another
new_index = current_index
set_firemode_index(new_index, user_for_feedback)
/obj/item/gun/energy/proc/user_set_firemode_to_prev(mob/user_for_feedback)
var/current_index = current_firemode_index
var/new_index = current_index
DECREMENT_OR_WRAP(new_index)
if(!IS_VALID_INDEX(new_index))
var/initial_index = new_index
while(!IS_VALID_INDEX(new_index) && (new_index != initial_index))
new_index = DECREMENT_OR_WRAP(new_index)
if(initial_index == new_index) //cycled through without finding another
new_index = current_index
set_firemode_index(new_index, user_for_feedback)
#undef INCREMENT_OR_WRAP
#undef DECREMENT_OR_WRAP
#undef IS_VALID_INDEX
/obj/item/gun/energy/update_icon(force_update)
if(QDELETED(src))
@@ -157,7 +248,7 @@
if(!initial(item_state))
itemState = icon_state
if (modifystate)
var/obj/item/ammo_casing/energy/shot = ammo_type[select]
var/obj/item/ammo_casing/energy/shot = ammo_type[current_firemode_index]
add_overlay("[icon_state]_[shot.select_name]")
iconState += "_[shot.select_name]"
if(itemState)
@@ -176,6 +267,9 @@
if(itemState)
itemState += "[ratio]"
item_state = itemState
if(ismob(loc)) //forces inhands to update
var/mob/M = loc
M.update_inv_hands()
/obj/item/gun/energy/suicide_act(mob/living/user)
if (istype(user) && can_shoot() && can_trigger_gun(user) && user.get_bodypart(BODY_ZONE_HEAD))
@@ -185,7 +279,7 @@
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]
var/obj/item/ammo_casing/energy/shot = ammo_type[current_firemode_index]
cell.use(shot.e_cost)
update_icon()
return(FIRELOSS)
@@ -207,13 +301,12 @@
STOP_PROCESSING(SSobj, src)
. = ..()
/obj/item/gun/energy/ignition_effect(atom/A, mob/living/user)
if(!can_shoot() || !ammo_type[select])
if(!can_shoot() || !ammo_type[current_firemode_index])
shoot_with_empty_chamber()
. = ""
else
var/obj/item/ammo_casing/energy/E = ammo_type[select]
var/obj/item/ammo_casing/energy/E = ammo_type[current_firemode_index]
var/obj/item/projectile/energy/BB = E.BB
if(!BB)
. = ""
@@ -235,3 +328,9 @@
playsound(user, BB.hitsound, 50, 1)
cell.use(E.e_cost)
. = "<span class='danger'>[user] casually lights their [A.name] with [src]. Damn.</span>"
/obj/item/gun/energy/altafterattack(atom/target, mob/user, proximity_flags, params)
if(!right_click_overridden)
select_fire(user)
return TRUE
return ..()
@@ -320,10 +320,10 @@
if(C.setting == counter_setting)
return BULLET_ACT_BLOCK
var/mob/living/L = target
if(!istype(target))
if(!isliving(target))
return BULLET_ACT_BLOCK
var/mob/living/L = target
switch(hugbox)
if(DUEL_HUGBOX_NONE)
var/obj/item/bodypart/B = L.get_bodypart(BODY_ZONE_HEAD)
@@ -97,7 +97,7 @@
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)
ammo_type = list(/obj/item/ammo_casing/energy/disabler, /obj/item/ammo_casing/energy/laser)
selfcharge = EGUN_SELFCHARGE
var/fail_tick = 0
var/fail_chance = 0
@@ -113,18 +113,21 @@
..()
/obj/item/gun/energy/e_gun/nuclear/proc/failcheck()
if(prob(fail_chance) && isliving(loc))
var/mob/living/M = loc
if(prob(fail_chance))
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>")
radiation_pulse(src, 50)
var/mob/M = (ismob(loc) && loc) || (ismob(loc.loc) && loc.loc) //thank you short circuiting. if you powergame and nest these guns deeply you get to suffer no-warning radiation death.
if(M)
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>")
radiation_pulse(src, 200)
crit_fail = TRUE
var/mob/M = (ismob(loc) && loc) || (ismob(loc.loc) && loc.loc)
if(M)
to_chat(M, "<span class='userdanger'>Your [name]'s reactor overloads!</span>")
/obj/item/gun/energy/e_gun/nuclear/emp_act(severity)
. = ..()
@@ -11,6 +11,7 @@
/obj/item/gun/energy/laser/practice
name = "practice laser gun"
icon_state = "laser-p"
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
@@ -202,7 +203,7 @@
if(!ishuman(user) || !ishuman(target))
return
if(semicd)
if(on_cooldown())
return
if(user == target)
@@ -212,7 +213,7 @@
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
busy_action = TRUE
if(!bypass_timer && (!do_mob(user, target, 120) || user.zone_selected != BODY_ZONE_PRECISE_MOUTH))
if(user)
@@ -220,10 +221,10 @@
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
busy_action = FALSE
return
semicd = FALSE
busy_action = FALSE
target.visible_message("<span class='warning'>[user] pulls the trigger!</span>", "<span class='userdanger'>[user] pulls the trigger!</span>")
@@ -36,7 +36,7 @@
/obj/item/gun/energy/decloner/update_icon()
..()
var/obj/item/ammo_casing/energy/shot = ammo_type[select]
var/obj/item/ammo_casing/energy/shot = ammo_type[current_firemode_index]
if(!QDELETED(cell) && (cell.charge > shot.e_cost))
add_overlay("decloner_spin")
@@ -185,7 +185,7 @@
var/atmos_link = FALSE
/obj/item/gun/energy/wormhole_projector/update_icon()
icon_state = "[initial(icon_state)][select]"
icon_state = "[initial(icon_state)][current_firemode_index]"
item_state = icon_state
/obj/item/gun/energy/wormhole_projector/update_ammo_types()
@@ -320,7 +320,7 @@
/obj/item/gun/energy/emitter/update_icon()
..()
var/obj/item/ammo_casing/energy/shot = ammo_type[select]
var/obj/item/ammo_casing/energy/shot = ammo_type[current_firemode_index]
if(!QDELETED(cell) && (cell.charge > shot.e_cost))
add_overlay("emitter_carbine_empty")
else
+15 -2
View File
@@ -18,10 +18,23 @@
/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."
desc = "A dual-mode taser designed to fire both short-range high-power electrodes and long-range disabler beams. <span class='boldnotice'>Right click in combat mode to fire a taser shot with a cooldown.</span>"
icon_state = "advtaser"
ammo_type = list(/obj/item/ammo_casing/energy/disabler, /obj/item/ammo_casing/energy/electrode)
ammo_type = list(/obj/item/ammo_casing/energy/disabler, /obj/item/ammo_casing/energy/electrode/security = FALSE)
ammo_x_offset = 2
// Not enough guns have altfire systems like this yet for this to be a universal framework.
var/last_altfire = 0
var/altfire_delay = 15
/obj/item/gun/energy/e_gun/advtaser/altafterattack(atom/target, mob/user, proximity_flag, params)
. = TRUE
if(last_altfire + altfire_delay > world.time)
return
var/current_index = current_firemode_index
set_firemode_to_type(/obj/item/ammo_casing/energy/electrode)
process_afterattack(target, user, proximity_flag, params)
set_firemode_index(current_index)
last_altfire = world.time
/obj/item/gun/energy/e_gun/advtaser/cyborg
name = "cyborg taser"
@@ -73,6 +73,12 @@
user.adjustOxyLoss(500)
charges--
/obj/item/gun/magic/wand/death/debug
desc = "In some obscure circles, this is known as the 'cloning tester's friend'."
max_charges = 500
variable_charges = FALSE
can_charge = TRUE
recharge_rate = 1
/////////////////////////////////////
//WAND OF HEALING
@@ -43,6 +43,7 @@
var/lastangle = 0
var/aiming_lastangle = 0
var/last_aimbeam = 0
var/mob/current_user = null
var/list/obj/effect/projectile/tracer/current_tracers
@@ -186,8 +187,9 @@
/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)
if(!check_user())
return
if(((diff < AIMING_BEAM_ANGLE_CHANGE_THRESHOLD) || ((last_aimbeam + 1) > world.time)) && !force_update)
return
aiming_lastangle = lastangle
var/obj/item/projectile/beam/beam_rifle/hitscan/aiming_beam/P = new
@@ -208,6 +210,7 @@
targloc = get_turf_in_angle(lastangle, curloc, 10)
P.preparePixelProjectile(targloc, current_user, current_user.client.mouseParams, 0)
P.fire(lastangle)
last_aimbeam = world.time
/obj/item/gun/energy/beam_rifle/process()
if(!aiming)
@@ -296,27 +299,17 @@
if(istype(object, /obj/screen) && !istype(object, /obj/screen/click_catcher))
return
process_aim()
if(aiming_time_left <= aiming_time_fire_threshold && check_user())
if(aiming_time_left <= aiming_time_fire_threshold && check_user() && ((lastfire + delay) <= world.time))
sync_ammo()
afterattack(M.client.mouseObject, M, FALSE, M.client.mouseParams, passthrough = TRUE)
do_fire(M.client.mouseObject, M, FALSE, M.client.mouseParams, M.zone_selected)
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
/obj/item/gun/energy/beam_rifle/do_fire(atom/target, mob/living/user, message = TRUE, params, zone_override = "", bonus_spread = 0)
. = ..()
if(.)
lastfire = world.time
stop_aiming()
/obj/item/gun/energy/beam_rifle/proc/sync_ammo()
+15 -3
View File
@@ -23,6 +23,8 @@
if(proximity_flag)
if(istype(target, /obj/item/gun))
var/obj/item/gun/G = target
if(G.no_pin_required)
return
if(G.pin && (force_replace || G.pin.pin_removeable))
G.pin.forceMove(get_turf(G))
G.pin.gun_remove(user)
@@ -69,13 +71,10 @@
if(gun)
qdel(gun)
/obj/item/firing_pin/magic
name = "magic crystal shard"
desc = "A small enchanted shard which allows magical weapons to fire."
// Test pin, works only near firing range.
/obj/item/firing_pin/test_range
name = "test-range firing pin"
@@ -229,3 +228,16 @@
if(gun)
gun.pin = null
return ..()
//Station Locked
/obj/item/firing_pin/away
name = "station locked pin"
desc = "A firing pin that only will fire when off the station."
/obj/item/firing_pin/away/pin_auth(mob/living/user)
var/area/station_area = get_area(src)
if(!station_area || is_station_level(station_area.z))
to_chat(user, "<span class='warning'>The pin beeps, refusing to fire.</span>")
return FALSE
return TRUE
+13 -16
View File
@@ -52,7 +52,6 @@
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
@@ -90,6 +89,10 @@
var/decayedRange //stores original range
var/reflect_range_decrease = 5 //amount of original range that falls off when reflecting, so it doesn't go forever
var/is_reflectable = FALSE // Can it be reflected or not?
/// factor to multiply by for zone accuracy percent.
var/zone_accuracy_factor = 1
//Effects
var/stun = 0
var/knockdown = 0
@@ -185,10 +188,6 @@
else
new /obj/effect/temp_visual/dir_setting/bloodsplatter(target_loca, splatter_dir, bloodtype_to_color())
if(iscarbon(L) && !HAS_TRAIT(L, TRAIT_NOMARROW))
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)
@@ -211,11 +210,8 @@
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.type + " ("
reagent_note += num2text(R.volume) + ") "
if(reagents)
reagent_note = reagents.log_list()
if(ismob(firer))
log_combat(firer, L, "shot", src, reagent_note)
@@ -253,7 +249,8 @@
return TRUE
var/distance = get_dist(T, 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(check_zone(def_zone) != BODY_ZONE_CHEST)
def_zone = ran_zone(def_zone, max(100-(7*distance), 5) * zone_accuracy_factor) //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)
@@ -494,8 +491,6 @@
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))
@@ -508,14 +503,16 @@
forceMove(T)
trajectory_ignore_forcemove = FALSE
after_z_change(old, loc)
forcemoved = TRUE
if(QDELETED(src))
return
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(QDELETED(src))
return
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
@@ -6,7 +6,7 @@
/obj/item/projectile/bullet/dart/Initialize()
. = ..()
create_reagents(50, NO_REACT)
create_reagents(50, NO_REACT, NO_REAGENTS_VALUE)
/obj/item/projectile/bullet/dart/on_hit(atom/target, blocked = FALSE, skip = FALSE)
if(iscarbon(target))
@@ -61,7 +61,7 @@
/obj/item/projectile/bullet/pellet/shotgun_buckshot
name = "buckshot pellet"
damage = 10
damage = 12.5
/obj/item/projectile/bullet/pellet/shotgun_rubbershot
name = "rubbershot pellet"
@@ -7,6 +7,7 @@
knockdown = 100
dismemberment = 50
armour_penetration = 50
zone_accuracy_factor = 100 //guarunteed 100%
var/breakthings = TRUE
/obj/item/projectile/bullet/p50/on_hit(atom/target, blocked = 0)
@@ -2,11 +2,11 @@
name = "electrode"
icon_state = "spark"
color = "#FFFF00"
nodamage = 1
nodamage = TRUE
knockdown = 60
knockdown_stamoverride = 36
knockdown_stam_max = 50
stutter = 5
stutter = 10
jitter = 20
hitsound = 'sound/weapons/taserhit.ogg'
range = 7
@@ -14,6 +14,7 @@
muzzle_type = /obj/effect/projectile/muzzle/stun
impact_type = /obj/effect/projectile/impact/stun
var/tase_duration = 50
var/strong_tase = TRUE
/obj/item/projectile/energy/electrode/on_hit(atom/target, blocked = FALSE)
. = ..()
@@ -26,10 +27,24 @@
C.IgniteMob()
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)
else if(tase_duration && (C.status_flags & CANKNOCKDOWN) && !HAS_TRAIT(C, TRAIT_STUNIMMUNE))
C.apply_status_effect(strong_tase? STATUS_EFFECT_TASED : STATUS_EFFECT_TASED_WEAK, 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)
..()
/obj/item/projectile/energy/electrode/security
tase_duration = 30
knockdown = 0
stamina = 10
knockdown_stamoverride = 0
knockdown_stam_max = 0
strong_tase = FALSE
range = 12
/obj/item/projectile/energy/electrode/security/hos
knockdown = 100
knockdown_stamoverride = 30
knockdown_stam_max = null
@@ -7,10 +7,11 @@
var/zap_range = 3
var/power = 10000
/obj/item/projectile/energy/tesla/fire(setAngle)
if(firer)
chain = firer.Beam(src, icon_state = "lightning[rand(1, 12)]", time = INFINITY, maxdistance = INFINITY)
..()
/obj/item/projectile/energy/tesla/fire(setAngle, atom/direct_target)
var/atom/source = fired_from || firer
if(source)
chain = source.Beam(src, icon_state = "lightning[rand(1, 12)]", time = INFINITY, maxdistance = INFINITY)
return ..()
/obj/item/projectile/energy/tesla/on_hit(atom/target)
. = ..()
@@ -0,0 +1,25 @@
/obj/item/projectile/bullet/reusable/arrow
name = "wooden arrow"
desc = "Woosh!"
damage = 15
icon_state = "arrow"
ammo_type = /obj/item/ammo_casing/caseless/arrow
/obj/item/projectile/bullet/reusable/arrow/ashen
name = "ashen arrow"
desc = "Fire harderned arrow."
damage = 25
ammo_type = /obj/item/ammo_casing/caseless/arrow/ashen
/obj/item/projectile/bullet/reusable/arrow/bone //AP for ashwalkers
name = "bone arrow"
desc = "Arrow made of bone and sinew."
damage = 35
armour_penetration = 40
ammo_type = /obj/item/ammo_casing/caseless/arrow/bone
/obj/item/projectile/bullet/reusable/arrow/bronze //Just some AP shots
name = "bronze arrow"
desc = "Bronze tipped arrow."
armour_penetration = 10
ammo_type = /obj/item/ammo_casing/caseless/arrow/bronze
@@ -1,90 +1,65 @@
/obj/item/projectile/gravityrepulse
/obj/item/projectile/gravity
name = "gravity bolt"
icon = 'icons/effects/effects.dmi'
icon_state = "chronofield"
hitsound = 'sound/weapons/wave.ogg'
damage = 0
damage_type = BRUTE
nodamage = TRUE
var/power = 4
var/list/thrown = list() //normally we wouldn't need this but incase on_hit is called multiple times, yeah.. this is a good idea.
/obj/item/projectile/gravity/Initialize(mapload)
. = ..()
var/obj/item/ammo_casing/energy/gravity/G = loc
if(istype(G))
power = min(G.gun.power, 15)
/obj/item/projectile/gravity/on_hit()
. = ..()
var/turf/T = get_turf(src)
var/list/tothrow = list()
var/list/cachedrange = range(T, power)
for(var/mob/M in cachedrange)
tothrow += M
for(var/obj/O in cachedrange)
tothrow += O
var/safety = 50
for(var/i in tothrow)
if(!safety)
break
var/atom/movable/AM = i
if((AM == src) || (AM == firer) || (AM.move_resist > MOVE_FORCE_EXTREMELY_STRONG) || AM.anchored || thrown[AM])
continue
thrown[AM] = TRUE
safety--
var/target = get_target(T, AM)
if(!target)
return FALSE
AM.throw_at(target, power + 1, 1)
for(var/turf/F in range(T,power))
new /obj/effect/temp_visual/gravpush(F)
/obj/item/projectile/gravity/proc/get_target(turf/origin, atom/movable/AM)
return origin
/obj/item/projectile/gravity/repulse
name = "repulsion bolt"
icon = 'icons/effects/effects.dmi'
icon_state = "chronofield"
hitsound = 'sound/weapons/wave.ogg'
damage = 0
damage_type = BRUTE
nodamage = 1
color = "#33CCFF"
var/turf/T
var/power = 4
var/list/thrown_items = list()
/obj/item/projectile/gravityrepulse/Initialize()
. = ..()
var/obj/item/ammo_casing/energy/gravity/repulse/C = loc
if(istype(C)) //Hard-coded maximum power so servers can't be crashed by trying to throw the entire Z level's items
power = min(C.gun.power, 15)
/obj/item/projectile/gravity/repulse/get_target(turf/origin, atom/movable/AM)
return get_turf_in_angle(Get_Angle(origin, AM), origin, power)
/obj/item/projectile/gravityrepulse/on_hit()
. = ..()
T = get_turf(src)
for(var/atom/movable/A in range(T, power))
if(A == src || (firer && A == src.firer) || A.anchored || thrown_items[A])
continue
var/throwtarget = get_edge_target_turf(src, get_dir(src, get_step_away(A, src)))
A.throw_at(throwtarget,power+1,1)
thrown_items[A] = A
for(var/turf/F in range(T,power))
new /obj/effect/temp_visual/gravpush(F)
/obj/item/projectile/gravityattract
/obj/item/projectile/gravity/attract
name = "attraction bolt"
icon = 'icons/effects/effects.dmi'
icon_state = "chronofield"
hitsound = 'sound/weapons/wave.ogg'
damage = 0
damage_type = BRUTE
nodamage = 1
color = "#FF6600"
var/turf/T
var/power = 4
var/list/thrown_items = list()
/obj/item/projectile/gravityattract/Initialize()
. = ..()
var/obj/item/ammo_casing/energy/gravity/attract/C = loc
if(istype(C)) //Hard-coded maximum power so servers can't be crashed by trying to throw the entire Z level's items
power = min(C.gun.power, 15)
/obj/item/projectile/gravity/attract/get_target(turf/origin, atom/movable/AM)
return origin
/obj/item/projectile/gravityattract/on_hit()
. = ..()
T = get_turf(src)
for(var/atom/movable/A in range(T, power))
if(A == src || (firer && A == src.firer) || A.anchored || thrown_items[A])
continue
A.throw_at(T, power+1, 1)
thrown_items[A] = A
for(var/turf/F in range(T,power))
new /obj/effect/temp_visual/gravpush(F)
/obj/item/projectile/gravitychaos
/obj/item/projectile/gravity/chaos
name = "gravitational blast"
icon = 'icons/effects/effects.dmi'
icon_state = "chronofield"
hitsound = 'sound/weapons/wave.ogg'
damage = 0
damage_type = BRUTE
nodamage = 1
color = "#101010"
var/turf/T
var/power = 4
var/list/thrown_items = list()
/obj/item/projectile/gravitychaos/Initialize()
. = ..()
var/obj/item/ammo_casing/energy/gravity/chaos/C = loc
if(istype(C)) //Hard-coded maximum power so servers can't be crashed by trying to throw the entire Z level's items
power = min(C.gun.power, 15)
/obj/item/projectile/gravitychaos/on_hit()
. = ..()
T = get_turf(src)
for(var/atom/movable/A in range(T, power))
if(A == src|| (firer && A == src.firer) || A.anchored || thrown_items[A])
continue
A.throw_at(get_edge_target_turf(A, pick(GLOB.cardinals)), power+1, 1)
thrown_items[A] = A
for(var/turf/Z in range(T,power))
new /obj/effect/temp_visual/gravpush(Z)
/obj/item/projectile/gravity/chaos/get_target(turf/origin, atom/movable/AM)
return get_turf_in_angle(rand(0, 359), origin, power)
@@ -43,4 +43,34 @@
explosion(target, 0, 1, 2, 4)
else
explosion(target, 0, 0, 2, 4)
return BULLET_ACT_HIT
return BULLET_ACT_HIT
/obj/item/projectile/bullet/a84mm_br
name ="\improper HE missile"
desc = "Boom."
icon_state = "missile"
damage = 30
ricochets_max = 0 //it's a MISSILE
var/sturdy = list(
/turf/closed,
/obj/mecha,
/obj/machinery/door/,
/obj/machinery/door/poddoor/shutters
)
/obj/item/broken_missile
name = "\improper broken missile"
desc = "A missile that did not detonate. The tail has snapped and it is in no way fit to be used again."
icon = 'icons/obj/projectiles.dmi'
icon_state = "missile_broken"
w_class = WEIGHT_CLASS_TINY
/obj/item/projectile/bullet/a84mm_br/on_hit(atom/target, blocked=0)
..()
for(var/i in sturdy)
if(istype(target, i))
explosion(target, 0, 1, 1, 2)
return BULLET_ACT_HIT
//if(istype(target, /turf/closed) || ismecha(target))
new /obj/item/broken_missile(get_turf(src), 1)