Files
Wildkins 5fec421dae Destroy several sources of lag (#21422)
Destroys a ton of sources of lag:

- /obj/machinery/biogenerator/interact and
/obj/machinery/computer/rdconsole/attack_hand

Both of these call REF(src) constantly on their UI update, which also
happens constantly. Death by a thousand cuts

- /obj/machinery/button/ignition/attack_hand and
/obj/machinery/button/switch/holosign/attack_hand

Sleeps rather than timers, iterating the entire machinery list rather
than just storing the IDs we need at roundstart

- /obj/machinery/telecomms/update_icon and /obj/machinery/meter/process

Calling overlay updates WAY TOO MUCH. UpdateOverlays is a significant
overhead for the server at this point and the two of these combined are
responsible for a little over 20% of updates. They now should only
update overlays when necessary rather than every tick.

- NEW! /obj/machinery/disposal/proc/update and
/obj/machinery/portable_atmospherics/hydroponics/update_icon

These two were also offenders, worse than telecomms but better than
meters. They have also been brought into line.

- /mob/living/carbon/slime

Hard-del'd if grinded into slime extract less than two minutes after
being "born". Fixed by adding TIMER_DELETE_ME flag.

---------

Co-authored-by: Matt Atlas <liermattia@gmail.com>
2025-10-04 11:05:07 +00:00

206 lines
6.1 KiB
Plaintext

/obj/item/ammo_pile
name = "ammo pile"
desc = "A handful of some kind of ammunition."
w_class = WEIGHT_CLASS_SMALL
var/list/ammo = list()
var/list/image/ammo_overlays = list()
var/ammo_type // the type of ammo this ammo pile accepts
var/max_ammo = 5
/obj/item/ammo_pile/Destroy()
ammo.Cut()
ammo_overlays.Cut()
. = ..()
/obj/item/ammo_pile/feedback_hints(mob/user, distance, is_adjacent)
. += ..()
if(is_adjacent)
. += SPAN_NOTICE("It contains [length(ammo)] rounds.")
/obj/item/ammo_pile/Initialize(mapload, var/list/provided_ammo)
. = ..()
if(islist(provided_ammo))
var/obj/first_round = provided_ammo[1]
add_ammo(first_round)
check_name_and_ammo()
provided_ammo -= first_round
for(var/obj/rounds in provided_ammo)
add_ammo(rounds)
else if(ammo_type)
var/obj/first_round = new ammo_type()
add_ammo(first_round)
check_name_and_ammo()
for(var/i = 1, i <= max_ammo - 1, i++)
var/obj/C = new ammo_type()
add_ammo(C)
addtimer(CALLBACK(src, PROC_REF(check_ammo)), 5) // if we don't have any ammo in 5 deciseconds, we're an empty pile, which is worthless, so self-delete
/obj/item/ammo_pile/attack(mob/living/target_mob, mob/living/user, target_zone)
return
/obj/item/ammo_pile/afterattack(atom/A, mob/living/user, proximity_flag)
if(!proximity_flag)
return
if(!length(ammo))
check_ammo()
if(A.type == ammo_type)
if(length(ammo) >= max_ammo)
to_chat(user, SPAN_WARNING("\The [src] is already fully stacked."))
return
var/obj/item/ammo_casing/B = A
if(!B.BB)
to_chat(user, SPAN_WARNING("\The [B] is spent!"))
return
to_chat(user, SPAN_NOTICE("You add \the [A] to \the [src]."))
add_ammo(A)
else if(istype(A, /obj/item/ammo_pile))
if(length(ammo) >= max_ammo)
to_chat(user, SPAN_WARNING("\The [src] is already fully stacked."))
return
var/obj/item/ammo_pile/target_pile = A
if(target_pile.ammo_type != src.ammo_type)
to_chat(user, SPAN_WARNING("\The [target_pile] has a different type of ammunition!"))
return
var/amount_taken = 0
for(var/obj/bullet in target_pile.ammo)
if(length(src.ammo) >= src.max_ammo)
break
src.add_ammo(bullet)
target_pile.remove_ammo()
amount_taken++
to_chat(user, SPAN_NOTICE("You take [amount_taken] rounds from the other pile and add it to yours."))
else if(istype(A, /obj/item/gun) || istype(A, /obj/item/ammo_magazine))
var/obj/bullet = get_next_ammo()
A.attackby(bullet, user)
if(!(bullet in src)) // if the gun / mag accepted the bullet, it will no longer be in our pile
remove_ammo()
/obj/item/ammo_pile/attackby(obj/item/attacking_item, mob/user)
if(istype(attacking_item, /obj/item/ammo_casing))
if(attacking_item.type != ammo_type)
to_chat(user, SPAN_WARNING("\The [attacking_item] has a different type of ammunition!"))
return TRUE
if(length(ammo) >= max_ammo)
to_chat(user, SPAN_WARNING("\The [src] is already fully stacked."))
return TRUE
var/obj/item/ammo_casing/B = attacking_item
if(!B.BB)
to_chat(user, SPAN_WARNING("\The [B] is spent!"))
return TRUE
to_chat(user, SPAN_NOTICE("You add \the [attacking_item] to \the [src]."))
add_ammo(attacking_item)
return TRUE
return ..()
/obj/item/ammo_pile/attack_hand(mob/user)
if(user.get_inactive_hand() == src)
var/obj/item/ammo_casing/C = get_next_ammo()
user.put_in_hands(C)
remove_ammo()
else
..()
/obj/item/ammo_pile/proc/check_name_and_ammo()
if(length(ammo))
var/obj/item/ammo_casing/first_round = ammo[1]
name = "[first_round.caliber] pile"
desc = "A pile of [first_round.caliber] rounds."
ammo_type = first_round.type
max_ammo = first_round.max_stack
/obj/item/ammo_pile/proc/get_next_ammo() //Returns the next shell to be used.
if(!length(ammo))
qdel(src)
return null
var/obj/item/ammo_casing/to_load = ammo[1]
return to_load
/obj/item/ammo_pile/proc/check_ammo()
var/ammo_amount = length(ammo)
switch(ammo_amount)
if(0)
qdel(src)
if(1)
var/obj/bullet = ammo[1]
bullet.forceMove(get_turf(src))
var/mob/gunman
if(ismob(loc))
gunman = loc
qdel(src)
if(gunman)
gunman.put_in_hands(bullet)
if(ammo_amount)
var/obj/first_round = ammo[1]
if(ammo_amount > 7)
w_class = first_round.w_class + 2 // too many to fit in your pockets, generally
else
w_class = first_round.w_class + 1
/obj/item/ammo_pile/proc/add_ammo(var/obj/item/ammo_casing/bullet)
if(!bullet.BB)
return
RegisterSignal(bullet, COMSIG_QDELETING, PROC_REF(clear_ammo))
if(ismob(bullet.loc))
var/mob/gunman = bullet.loc
gunman.drop_from_inventory(bullet, src)
if(istype(bullet.loc, /obj/item/storage))
var/obj/item/storage/S = bullet.loc
S.remove_from_storage(bullet, src)
bullet.forceMove(src)
ammo += bullet
var/image/ammo_picture = image(bullet.icon, bullet.icon_state, dir = pick(GLOB.alldirs))
ammo_picture.pixel_x = rand(-6, 6)
ammo_picture.pixel_y = rand(-6, 6)
ammo_overlays[bullet] = ammo_picture
AddOverlays(ammo_overlays[bullet])
/// Called when one of our contained bullets is qdel'd -- why does this happen? it is a mystery
/obj/item/ammo_pile/proc/clear_ammo(datum/source)
SIGNAL_HANDLER
CutOverlays(ammo_overlays[source])
ammo -= source
UnregisterSignal(source, COMSIG_QDELETING)
/obj/item/ammo_pile/proc/remove_ammo(var/atom/target)
var/obj/bullet = ammo[1]
if(target)
bullet.forceMove(target)
CutOverlays(ammo_overlays[bullet])
ammo -= bullet
UnregisterSignal(bullet, COMSIG_QDELETING)
check_ammo()
/obj/item/ammo_pile/proc/scatter()
for(var/thing in ammo)
var/obj/bullet = thing
bullet.forceMove(get_turf(src))
bullet.throw_at_random(FALSE, 2, 7)
remove_ammo(bullet)
/obj/item/ammo_pile/throw_at()
..()
scatter()
/obj/item/ammo_pile/slug
ammo_type = /obj/item/ammo_casing/shotgun
/obj/item/ammo_pile/shotgun_blanks
ammo_type = /obj/item/ammo_casing/shotgun/blank
/obj/item/ammo_pile/shotgun_beanbag
ammo_type = /obj/item/ammo_casing/shotgun/beanbag
/obj/item/ammo_pile/shotgun_flash
ammo_type = /obj/item/ammo_casing/shotgun/flash
/obj/item/ammo_pile/shotgun_pellet
ammo_type = /obj/item/ammo_casing/shotgun/pellet
/obj/item/ammo_pile/shotgun_stunshell
ammo_type = /obj/item/ammo_casing/shotgun/stunshell
/obj/item/ammo_pile/fourty_five
ammo_type = /obj/item/ammo_casing/c45