refactors guns a bit

This commit is contained in:
kevinz000
2020-01-27 17:39:49 -07:00
parent 5e10d297e1
commit 0eb13d8f23
9 changed files with 142 additions and 105 deletions

View File

@@ -191,7 +191,7 @@
else if(istype(I, /obj/item/gun/energy))
var/obj/item/gun/energy/EG = I
if(EG.cell?.charge < EG.cell.maxcharge)
var/obj/item/ammo_casing/energy/S = EG.ammo_type[EG.select]
var/obj/item/ammo_casing/energy/S = EG.ammo_type[EG.current_firemode_index]
EG.cell.give(S.e_cost * coeff)
if(!EG.chambered)
EG.recharge_newshot(TRUE)

View File

@@ -197,8 +197,6 @@ GLOBAL_LIST_INIT(protected_objects, list(/obj/structure/table, /obj/structure/ca
return 0
return ..()
/mob/living/simple_animal/hostile/mimic/copy/ranged
var/obj/item/gun/TrueGun = null
var/obj/item/gun/magic/Zapstick
@@ -229,14 +227,13 @@ GLOBAL_LIST_INIT(protected_objects, list(/obj/structure/table, /obj/structure/ca
casingtype = initial(M.ammo_type)
if(istype(G, /obj/item/gun/energy))
Zapgun = G
var/selectfiresetting = Zapgun.select
var/obj/item/ammo_casing/energy/E = Zapgun.ammo_type[selectfiresetting]
var/obj/item/ammo_casing/energy/E = Zapgun.ammo_type[Zapgun.current_firemode_index]
projectiletype = initial(E.projectile_type)
/mob/living/simple_animal/hostile/mimic/copy/ranged/OpenFire(the_target)
if(Zapgun)
if(Zapgun.cell)
var/obj/item/ammo_casing/energy/shot = Zapgun.ammo_type[Zapgun.select]
var/obj/item/ammo_casing/energy/shot = Zapgun.ammo_type[Zapgun.current_firemode_index]
if(Zapgun.cell.charge >= shot.e_cost)
Zapgun.cell.use(shot.e_cost)
Zapgun.update_icon()

View File

@@ -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.
var/fire_delay = 0
/// Last world.time this was fired
var/last_fire = 0
/// don't fire two bursts at the same time
var/firing_burst = 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.
@@ -110,7 +119,6 @@
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)
@@ -136,6 +144,9 @@
/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)
@@ -162,7 +173,6 @@
handle_suicide(user, target, params)
return
//Exclude lasertag guns from the TRAIT_CLUMSY check.
if(clumsy_check)
if(istype(user))
@@ -194,8 +204,6 @@
process_fire(target, user, TRUE, params, null, bonus_spread)
/obj/item/gun/can_trigger_gun(mob/living/user)
. = ..()
if(!.)
@@ -220,6 +228,9 @@
/obj/item/gun/proc/recharge_newshot()
return
/obj/item/gun/proc/on_cooldown()
return busy_action || firing_burst || (last_fire > world.time + fire_delay)
/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
@@ -260,14 +271,14 @@
/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
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
@@ -276,8 +287,12 @@
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))
process_burst(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
process_burst(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))
@@ -294,8 +309,8 @@
return
process_chamber()
update_icon()
semicd = TRUE
addtimer(CALLBACK(src, .proc/reset_semicd), fire_delay)
last_fire = world.time
if(user)
user.update_inv_hands()
@@ -303,13 +318,6 @@
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)
@@ -429,7 +437,7 @@
if(!ishuman(user) || !ishuman(target))
return
if(semicd)
if(on_cooldown())
return
if(user == target)
@@ -439,7 +447,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)
@@ -447,10 +455,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>")

View File

@@ -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

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"
@@ -8,7 +21,8 @@
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.
/// 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
@@ -46,16 +60,6 @@
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)
@@ -81,13 +85,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)
@@ -97,11 +102,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)
@@ -124,19 +129,60 @@
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]
if(istype(v, /obj/item/ammo_casing/energy)) //already set
continue
else
C = new v //if you put non energycasing/type stuff in here you deserve the runtime
ammo_type[i] = C
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 set_firemode_to_next(user)
/obj/item/gun/energy/proc/can_select_fire(mob/living/user)
return TRUE
/obj/item/gun/energy/update_icon(force_update)
if(QDELETED(src))
@@ -156,7 +202,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)
@@ -175,6 +221,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))
@@ -184,7 +233,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)
@@ -208,11 +257,11 @@
/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)
. = ""

View File

@@ -189,7 +189,7 @@
if(!ishuman(user) || !ishuman(target))
return
if(semicd)
if(on_cooldown())
return
if(user == target)
@@ -199,7 +199,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)
@@ -207,10 +207,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>")

View File

@@ -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")
@@ -183,7 +183,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()
@@ -318,7 +318,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

View File

@@ -46,34 +46,22 @@
cell.use(shot.e_cost)//... drain the cell cell
chambered = 0 //either way, released the prepared shot
/obj/item/gun/energy/pumpaction/select_fire(mob/living/user) //makes it so that it doesn't rack itself when changing firing modes unless already racked
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>")
if(chambered)
chambered = 0
recharge_newshot(1)
update_icon()
if(ismob(loc)) //forces inhands to update
var/mob/M = loc
M.update_inv_hands()
return
/obj/item/gun/energy/pumpaction/post_set_firemode()
var/has_shot = chambered
. = ..(recharge_newshot = FALSE)
if(has_shot)
recharge_newshot(TRUE)
/obj/item/gun/energy/pumpaction/update_icon() //adds racked indicators
..()
var/obj/item/ammo_casing/energy/shot = ammo_type[select]
var/obj/item/ammo_casing/energy/shot = ammo_type[current_firemode_index]
if(chambered)
add_overlay("[icon_state]_rack_[shot.select_name]")
else
add_overlay("[icon_state]_rack_empty")
/obj/item/gun/energy/pumpaction/proc/pump(mob/M) //pumping proc. Checks if the gun is empty and plays a different sound if it is.
var/obj/item/ammo_casing/energy/shot = ammo_type[select]
var/obj/item/ammo_casing/energy/shot = ammo_type[current_firemode_index]
if(cell.charge < shot.e_cost)
playsound(M, 'sound/weapons/laserPumpEmpty.ogg', 100, 1) //Ends with three beeps made from highly processed knife honing noises
else
@@ -102,7 +90,7 @@
/obj/item/gun/energy/pumpaction/worn_overlays(isinhands, icon_file, style_flags = NONE) //ammo counter for inhands
. = ..()
var/ratio = CEILING((cell.charge / cell.maxcharge) * charge_sections, 1)
var/obj/item/ammo_casing/energy/shot = ammo_type[select]
var/obj/item/ammo_casing/energy/shot = ammo_type[current_firemode_index]
if(isinhands)
if(cell.charge < shot.e_cost)
var/mutable_appearance/ammo_inhand = mutable_appearance(icon_file, "[item_state]_empty")