Files
FenodyreeAv d8e02149e0 Fixes EMPs affecting carried items twice, fixes EMPs not disabling energy guns. (#22482)
Human_defense was calling it's parent, which also EMP'd all carried
items, so carried items were taking two EMP acts.
This mean that PDAs always exploded from light EMPs.

Energy guns used sleep() in their EMP act and didn't multiply by *
SECONDS.
EMP_LIGHT is 2 and EMP_HEAVY is 1, previous implementation was
multiplying where it should divide.
Also added some feedback about why the gun won't fire.

Now decrements a timer in process, 10 seconds for a light EMP, 20
seconds for a heavy EMP.
2026-05-26 17:58:27 +00:00

338 lines
11 KiB
Plaintext

ABSTRACT_TYPE(/obj/item/gun/energy)
name = "energy gun"
desc = "A basic energy-based gun."
icon = 'icons/obj/guns/faction/nanotrasen_corporation/ecarbine.dmi'
icon_state = "energykill100"
item_state = "energykill100"
fire_sound = 'sound/weapons/Taser.ogg'
fire_sound_text = "laser blast"
update_icon_on_init = TRUE
safetyon_sound = 'sound/weapons/laser_safetyon.ogg'
safetyoff_sound = 'sound/weapons/laser_safetyoff.ogg'
/// Does this gun use the ratio system to modify its icon_state?
var/has_icon_ratio = TRUE
/// Does this gun use the ratio system to paint its item_states?
var/has_item_ratio = TRUE
/// What type of power cell this uses.
var/obj/item/cell/power_supply
/// How much energy is needed to fire.
var/charge_cost = 200
/// Determines the capacity of the weapon's power cell. Specifying a cell_type overrides this value.
var/max_shots = 10
var/cell_type = null
/// Also passed to turrets.
var/projectile_type = /obj/projectile/beam/practice
var/modifystate
/// If set, the icon state will be chosen based on the current charge.
var/charge_meter = 1
/// This list matches with firemode index, used to determine which firemodes get unlocked with what level of authorization.
var/list/required_firemode_auth
///How long the gun is disabled for after an emp, is set in emp_act and counted down in process.
var/emp_disable_time
///Initial charge
var/initial_charge
//self-recharging
/// If set, the weapon will recharge itself.
var/self_recharge = FALSE
/// If set, the weapon will look for an external power source to draw from, otherwise it recharges magically
var/use_external_power = FALSE
var/recharge_time = 4
/// Multiplies by charge cost to determine how much charge should be returned
var/recharge_multiplier = 1
var/charge_tick = 0
/// External power source for the gun. Checked by get_external_power_supply()
var/obj/item/recharger
// Vars passed to turrets
/// If TRUE, allows you to attach the gun on a turret.
var/can_turret = FALSE
/// If null, turret defaults to projectile_type.
var/secondary_projectile_type = null
/// If null, turret defaults to fire_sound.
var/secondary_fire_sound = null
/// If TRUE, allows switching lethal and stun modes.
var/can_switch_modes = FALSE
/// Set of sprites to use for the turret gun.
var/turret_sprite_set = "carbine"
/// Is the gun in lethal (secondary) mode by default?
var/turret_is_lethal = TRUE
/obj/item/gun/energy/mechanics_hints(mob/user, distance, is_adjacent)
. += ..()
. += "This is an energy weapon. Most energy weapons can fire through windows harmlessly. Energy weapons must be recharged once depleted."
if(can_turret)
. += "This weapon can be installed on a turret mount."
/obj/item/gun/energy/feedback_hints(mob/user, distance, is_adjacent)
. += ..()
if(distance > 1)
return
var/shots_remaining = round(power_supply.charge / charge_cost)
. += "It has [shots_remaining] shot\s remaining."
/obj/item/gun/energy/switch_firemodes()
. = ..()
if(.)
update_icon()
/obj/item/gun/energy/emp_act(severity)
. = ..()
if(!power_supply)
return
if(!emp_disable_time)
var/mob/M
if(ismob(loc))
M = loc
if(power_supply.charge > 0)
to_chat(M, SPAN_DANGER("\The [src] locks up as the EMP scrambles it!"))
playsound(M, 'sound/weapons/smg_empty_alarm.ogg', 30)
initial_charge = power_supply.charge
power_supply.charge = 0
SSicon_update.add_to_queue(src)
START_PROCESSING(SSprocessing, src)
emp_disable_time = max(emp_disable_time, 20 / severity)
/obj/item/gun/energy/process(seconds_per_tick)
emp_disable_time = max(0, emp_disable_time - seconds_per_tick)
if (!emp_disable_time)
STOP_PROCESSING(SSprocessing, src)
restore_gun_charge()
/obj/item/gun/energy/proc/restore_gun_charge()
power_supply.give(initial_charge)
var/mob/M
if(ismob(loc))
M = loc
if (power_supply.charge > charge_cost)
playsound(M, 'sound/weapons/laser_safetyoff.ogg', 30)
to_chat(M, SPAN_NOTICE("\The [src] clicks as it resets, ready to fire again!"))
update_maptext()
update_icon()
/obj/item/gun/energy/get_cell()
return power_supply
/obj/item/gun/energy/Initialize()
. = ..()
if(cell_type)
power_supply = new cell_type(src)
else
power_supply = new /obj/item/cell/device/variable(src, max_shots*charge_cost)
update_maptext()
/obj/item/gun/energy/Destroy()
if(recharger)
disconnect()
QDEL_NULL(power_supply)
return ..()
/obj/item/gun/energy/proc/try_recharge()
. = 1
if (!power_supply || power_supply.charge >= power_supply.maxcharge || !self_recharge)
return 0 // check if we actually need to recharge
if (use_external_power)
var/obj/item/cell/external = get_external_power_supply()
if(!external || !external.use(charge_cost)) //Take power from the borg...
return 0
power_supply.give(charge_cost * recharge_multiplier) //... to recharge the shot
update_maptext()
update_icon()
addtimer(CALLBACK(src, PROC_REF(try_recharge)), recharge_time * 2 SECONDS, TIMER_UNIQUE)
/obj/item/gun/energy/consume_next_projectile()
if(!power_supply)
return null
if(!ispath(projectile_type))
return null
if(!power_supply.checked_use(charge_cost))
return null
if(self_recharge)
addtimer(CALLBACK(src, PROC_REF(try_recharge)), recharge_time * 2 SECONDS, TIMER_UNIQUE)
return new projectile_type(src)
/obj/item/gun/energy/proc/get_external_power_supply()
if(isrobot(src.loc))
var/mob/living/silicon/robot/R = src.loc
return R.cell
if(isrobot(src.loc.loc)) // for things inside a robot's module
var/mob/living/silicon/robot/R = src.loc.loc
return R.cell
if(recharger)
return recharger.get_cell()
if(istype(src.loc, /obj/item/rig_module))
var/obj/item/rig_module/module = src.loc
if(module.holder && module.holder.wearer)
var/mob/living/carbon/human/H = module.holder.wearer
if(istype(H) && H.back)
var/obj/item/rig/suit = H.back
if(istype(suit))
return suit.cell
if(istype(loc, /obj/item/mecha_equipment))
return loc.get_cell()
return null
/**
* Connects the energy gun to an external power supply
*
* * powersource - the power supply in question. Can either be `/obj/item/rig_module/recharger` or `/obj/item/recharger_backpack`.
*
* Returns `TRUE` if the connection was successful, `FALSE` otherwise
*/
/obj/item/gun/energy/proc/connect(obj/item/powersource)
SHOULD_NOT_SLEEP(TRUE)
//Validate that the type is allowed first of all
if(!is_type_in_list(powersource, list(/obj/item/rig_module/recharger, /obj/item/recharger_backpack)))
stack_trace("Wrong type to connect the energy gun to!")
return FALSE
//If we're already connected with something, we can't connect with something else
if(recharger)
to_chat(usr, SPAN_WARNING("\The [src] is already connected to \the [recharger]!"))
return FALSE
if(istype(powersource, /obj/item/rig_module/recharger))
var/obj/item/rig_module/recharger/rigcharge = powersource
if(!rigcharge.holder || !rigcharge.holder.wearer)
to_chat(usr, SPAN_WARNING("\The [rigcharge] must be installed in a rig and active!"))
return FALSE
to_chat(usr, SPAN_NOTICE("You neatly plug \the [src] into \the [powersource]."))
playsound(get_turf(src), 'sound/machines/click.ogg', 30, 0)
rigcharge.connected = src
recharger = rigcharge.holder
self_recharge = TRUE
use_external_power = TRUE
else if(istype(powersource, /obj/item/recharger_backpack))
var/obj/item/recharger_backpack/back_charge = powersource
if(!ismob(loc))
to_chat(usr, SPAN_WARNING("\The [back_charge] must be worn on the back before a weapon can be connected!"))
return FALSE
to_chat(usr, SPAN_NOTICE("You neatly plug \the [src] into \the [powersource]."))
playsound(get_turf(src), 'sound/machines/click.ogg', 30, 0)
back_charge.connect(src)
recharger = back_charge
self_recharge = TRUE
use_external_power = TRUE
return TRUE
///Disconnects the energy gun from its external power source if one exists.
/obj/item/gun/energy/proc/disconnect()
SHOULD_NOT_SLEEP(TRUE)
if(!recharger)
to_chat(usr, SPAN_WARNING("\The [src] lacks an external power source to disconnect!"))
return
if(istype(recharger, /obj/item/rig))
var/obj/item/rig/rig = recharger
var/obj/item/rig_module/recharger/rigcharger = locate() in rig.installed_modules
to_chat(usr, SPAN_NOTICE("With a snap, \the [src] is disconnected from \the [recharger]."))
playsound(get_turf(src), 'sound/machines/click.ogg', 30, 0)
if(rigcharger.active)
rigcharger.deactivate()
rigcharger.connected = null
else if(istype(recharger, /obj/item/recharger_backpack))
var/obj/item/recharger_backpack/backcharger = recharger
to_chat(usr, SPAN_NOTICE("With a snap, \the [src] is disconnected from \the [recharger]."))
playsound(get_turf(src), 'sound/machines/click.ogg', 30, 0)
backcharger.disconnect(src)
recharger = null
self_recharge = initial(self_recharge)
use_external_power = initial(use_external_power)
/obj/item/gun/energy/mouse_drop_dragged(atom/over, mob/user, src_location, over_location, params)
. = ..()
if(istype(over, /obj/item/rig))
var/obj/item/rig/rig = over
var/obj/item/rig_module/recharger/rigcharge = locate() in rig.installed_modules
if(rigcharge)
connect(rigcharge)
else if(istype(over, /obj/item/recharger_backpack))
var/obj/item/recharger_backpack/backcharge = over
connect(backcharge)
/obj/item/gun/energy/dropped(mob/user)
. = ..()
if(recharger)
disconnect()
/obj/item/gun/energy/update_icon()
if(charge_meter && power_supply && power_supply.maxcharge)
var/ratio = power_supply.charge / power_supply.maxcharge
var/icon_state_ratio = ""
var/item_state_ratio = ""
//make sure that rounding down will not give us the empty state even if we have charge for a shot left.
if(power_supply.charge < charge_cost)
ratio = 0
else
ratio = max(round(ratio, 0.25) * 100, 25)
if(has_icon_ratio)
icon_state_ratio = ratio
if(has_item_ratio)
item_state_ratio = ratio
if(modifystate)
icon_state = "[modifystate][icon_state_ratio]"
item_state = "[modifystate][item_state_ratio]"
else
icon_state = "[initial(icon_state)][icon_state_ratio]"
item_state = "[initial(item_state)][item_state_ratio]"
..()
/obj/item/gun/energy/handle_post_fire()
..()
update_maptext()
/obj/item/gun/energy/get_ammo()
if(!power_supply)
return 0
return round(power_supply.charge / charge_cost)
/obj/item/gun/energy/get_print_info()
. = ""
. += "Max Shots: [initial(max_shots)]<br>"
. += "Recharge Type: [initial(self_recharge) ? "self recharging" : "not self recharging"]<br>"
if(initial(self_recharge))
. += "Recharge Time: [initial(recharge_time)]<br>"
. += "<br><b>Primary Projectile</b><br>"
var/obj/projectile/P = new projectile_type
. += P.get_print_info()
if(secondary_projectile_type)
. += "<br><b>Secondary Projectile</b><br>"
var/obj/projectile/P_second = new secondary_projectile_type
. += P_second.get_print_info()
. += "<br>"
. += ..(FALSE)