Files
VMSolidus 201592f6bd Mob Destroy Refactoring (#22573)
Refactoring the entire destroy proc path from Mob Human all the way down
to Atom while trying to find the causes for the damn mob human hard
deletes. This PR comprehensively reorganizes every single stray
snowflake var used by /atom/ all the way to /mob/living/carbon/human,
and makes sure that every var that COULD store a reference, is now
cleared during the entirety of the Mob Destroy() parent hierarchy.

This may very well be the end of the lag war. 

In total, I've hunted down and cleared 39 hanging references between
/atom and /mob/living/carbon/human

---------

Signed-off-by: VMSolidus <evilexecutive@gmail.com>
2026-06-03 15:15:56 +00:00

486 lines
14 KiB
Plaintext

/*
Click code cleanup
~Sayu
*/
/**
* Before anything else, defer these calls to a per-mobtype handler. This allows us to
* remove istype() spaghetti code, but requires the addition of other handler procs to simplify it.
*
* Alternately, you could hardcode every mob's variation in a flat [/mob/proc/ClickOn] proc; however,
* that's a lot of code duplication and is hard to maintain.
*
* Note that this proc can be overridden, and is in the case of screen objects.
*/
/atom/Click(location, control, params)
if(flags_1 & INITIALIZED_1)
SEND_SIGNAL(src, COMSIG_CLICK, location, control, params, usr)
//Aurora snowflake click handler system
var/datum/click_handler/click_handler = usr.GetClickHandler()
click_handler.OnClick(src, params)
/atom/DblClick(location,control,params)
if(flags_1 & INITIALIZED_1)
//Aurora snowflake click handler system
var/datum/click_handler/click_handler = usr.GetClickHandler()
click_handler.OnDblClick(src, params)
//Why god why
if(istype(usr.machine, /obj/structure/machinery/computer/security))
var/obj/structure/machinery/computer/security/console = usr.machine
console.jump_on_click(usr,src)
/atom/MouseWheel(delta_x,delta_y,location,control,params)
if(flags_1 & INITIALIZED_1)
usr.MouseWheelOn(src, delta_x, delta_y, params)
/**
* Standard mob ClickOn()
* Handles exceptions: middle click, modified clicks, mech actions
*
* After that, mostly just check your state, check whether you're holding an item,
* check whether you're adjacent to the target, then pass off the click to whoever
* is receiving it.
* The most common are:
* * [mob/proc/UnarmedAttack] (atom,adjacent) - used here only when adjacent, with no item in hand; in the case of humans, checks gloves
* * [atom/proc/attackby] (item,user) - used only when adjacent
* * [obj/item/proc/afterattack] (atom,user,adjacent,params) - used both ranged and adjacent
* * [mob/proc/RangedAttack] (atom,modifiers) - used only ranged, only used for tk and laser eyes but could be changed
*/
/mob/proc/ClickOn(atom/A, params)
if(world.time <= next_click) // Hard check, before anything else, to avoid crashing
return
next_click = world.time + 1
var/list/modifiers = params2list(params)
if(SEND_SIGNAL(src, COMSIG_MOB_CLICKON, A, modifiers) & COMSIG_MOB_CANCEL_CLICKON)
return
/* START Aurora snowflake */
if(istype(loc, /mob/living/heavy_vehicle) && !(A in src.contents))
var/mob/living/heavy_vehicle/M = loc
return M.ClickOn(A, params, src)
// pAI handling
if(istype(loc.loc, /mob/living/bot))
var/mob/living/bot/B = loc.loc
if(!B.on)
to_chat(src, SPAN_WARNING("\The [B] isn't turned on!"))
return
return B.ClickOn(A, params)
/* END Aurora snowflake */
if(LAZYACCESS(modifiers, SHIFT_CLICK))
if(LAZYACCESS(modifiers, MIDDLE_CLICK))
ShiftMiddleClickOn(A)
return
if(LAZYACCESS(modifiers, CTRL_CLICK))
CtrlShiftClickOn(A)
return
if(LAZYACCESS(modifiers, ALT_CLICK))
AltShiftClickOn(A)
return
ShiftClickOn(A)
return
if(LAZYACCESS(modifiers, MIDDLE_CLICK))
if(LAZYACCESS(modifiers, CTRL_CLICK))
CtrlMiddleClickOn(A)
else
MiddleClickOn(A, params)
return
if(LAZYACCESS(modifiers, ALT_CLICK)) // alt and alt-gr (rightalt)
if(LAZYACCESS(modifiers, RIGHT_CLICK))
AltClickSecondaryOn(A)
else
AltClickOn(A)
return
if(LAZYACCESS(modifiers, CTRL_CLICK))
CtrlClickOn(A)
return
if(stat || paralysis || stunned || weakened) // In TG it's if(INCAPACITATED_IGNORING(src, INCAPABLE_RESTRAINTS|INCAPABLE_STASIS))
return
face_atom(A) // change direction to face what you clicked on
/* START Aurora snowflake */
if(LAZYACCESS(modifiers, RIGHT_CLICK))
RightClickOn(A)
return TRUE
/* END Aurora snowflake */
if(!canClick()) // in the year 2000...
return
if(restrained())
setClickCooldown(10)
RestrainedClickOn(A)
return 1
if(in_throw_mode && (isturf(A) || isturf(A.loc)) && throw_item(A))
trigger_aiming(TARGET_CAN_CLICK)
throw_mode_off()
return TRUE
var/obj/item/W = get_active_hand()
if(W == A)
if(LAZYACCESS(modifiers, RIGHT_CLICK))
W.attack_self_secondary(src, modifiers)
/* START Aurora snowflake */
trigger_aiming(TARGET_CAN_CLICK)
if(hand)
update_inv_l_hand(FALSE)
else
update_inv_r_hand(FALSE)
/* END Aurora snowflake */
return
else
W.attack_self(src, modifiers)
/* START Aurora snowflake */
trigger_aiming(TARGET_CAN_CLICK)
if(hand)
update_inv_l_hand(FALSE)
else
update_inv_r_hand(FALSE)
/* END Aurora snowflake */
return
/* START AURORA SNOWFLAKE CODE */
//Atoms on your person
// A is your location but is not a turf; or is on you (backpack); or is on something on you (box in backpack); sdepth is needed here because contents depth does not equate inventory storage depth.
var/sdepth = A.storage_depth(src)
if((!isturf(A) && A == loc) || (sdepth != -1 && sdepth <= 1))
if(W)
var/resolved = W.resolve_attackby(A, src, params)
if(!resolved && A && W)
W.afterattack(A, src, 1, params) // 1 indicates adjacency
else
if(ismob(A)) // No instant mob attacking
setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
UnarmedAttack(A, TRUE, modifiers)
trigger_aiming(TARGET_CAN_CLICK)
return 1
if(!isturf(loc)) // This is going to stop you from telekinesing from inside a closet, but I don't shed many tears for that
return
var/mob/living/simple_animal/simple_mob = istype(src, /mob/living/simple_animal) ? src : null
//Atoms on turfs (not on your person)
// A is a turf or is on a turf, or in something on a turf (pen in a box); but not something in something on a turf (pen in a box in a backpack)
sdepth = A.storage_depth_turf()
if(isturf(A) || isturf(A.loc) || (sdepth != -1 && sdepth <= 1))
if(A.Adjacent(src) || (W && W.attack_can_reach(src, A, W.reach)) || (simple_mob && simple_mob.attack_can_reach(src, A, simple_mob.melee_reach))) // see adjacent.dm
if(W)
// Return 1 in attackby() to prevent afterattack() effects (when safely moving items for example)
var/resolved = W.resolve_attackby(A,src, params)
if(!resolved && A && W)
W.afterattack(A, src, 1, params) // 1: clicking something Adjacent
else
if(ismob(A)) // No instant mob attacking
setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
UnarmedAttack(A, TRUE, modifiers)
trigger_aiming(TARGET_CAN_CLICK)
return
else // non-adjacent click
if(W)
W.afterattack(A, src, 0, params) // 0: not Adjacent
else
RangedAttack(A, params)
trigger_aiming(TARGET_CAN_CLICK)
return 1
/* END AURORA SNOWFLAKE CODE */
/mob/proc/setClickCooldown(var/timeout)
next_move = max(world.time + timeout, next_move)
/mob/proc/canClick()
if(GLOB.config.no_click_cooldown || next_move <= world.time)
return 1
return 0
/// Default behavior: ignore double clicks (the second click that makes the doubleclick call already calls for a normal click)
/mob/proc/DblClickOn(atom/A, params)
return
/**
* UnarmedAttack: The higest level of mob click chain discounting click itself.
*
* This handles, just "clicking on something" without an item. It translates
* into [atom/proc/attack_hand], [atom/proc/attack_animal] etc.
*
* Note: proximity_flag here is used to distinguish between normal usage (flag=1),
* and usage when clicking on things telekinetically (flag=0). This proc will
* not be called at ranged except with telekinesis.
*
* proximity_flag is not currently passed to attack_hand, and is instead used
* in human click code to allow glove touches only at melee range.
*
* modifiers is a lazy list of click modifiers this attack had,
* used for figuring out different properties of the click, mostly right vs left and such.
*/
/mob/proc/UnarmedAttack(atom/A, proximity_flag, list/modifiers)
return
/mob/living/UnarmedAttack(var/atom/A, var/proximity_flag)
if(!(GAME_STATE & RUNLEVELS_PLAYING))
to_chat(src, "You cannot attack people before the game has started.")
return FALSE
if(stat)
return FALSE
if(check_sting(src, A))
return FALSE
return 1
/**
* Ranged unarmed attack:
*
* This currently is just a default for all mobs, involving
* laser eyes and telekinesis. You could easily add exceptions
* for things like ranged glove touches, spitting alien acid/neurotoxin,
* animals lunging, etc.
*/
/mob/proc/RangedAttack(atom/A, modifiers)
if(SEND_SIGNAL(src, COMSIG_MOB_ATTACK_RANGED, A, modifiers) & COMPONENT_CANCEL_ATTACK_CHAIN)
return TRUE
if((mutations & LASER_EYES) && a_intent == I_HURT)
LaserEyes(A, modifiers) // moved into a proc below
return
A.attack_ranged(src, modifiers)
/*
Restrained ClickOn
Used when you are handcuffed and click things.
Not currently used by anything but could easily be.
*/
/mob/proc/RestrainedClickOn(var/atom/A)
return
/**
* Middle click
* Mainly used for swapping hands
*/
/mob/proc/MiddleClickOn(atom/A, params)
. = SEND_SIGNAL(src, COMSIG_MOB_MIDDLECLICKON, A, params)
if(. & COMSIG_MOB_CANCEL_CLICKON)
return
if(A.handle_middle_mouse_click(src))
return
swap_hand()
/mob/living/carbon/human/MiddleClickOn(atom/A, params)
if(species.handle_middle_mouse_click(src, A))
return
return ..()
/**
* Shift click
* For most mobs, examine.
* This is overridden in ai.dm
*/
/mob/proc/ShiftClickOn(atom/A)
A.ShiftClick(src)
return
/atom/proc/ShiftClick(mob/user)
SEND_SIGNAL(src, COMSIG_SHIFT_CLICKED_ON, user)
var/flags = SEND_SIGNAL(user, COMSIG_CLICK_SHIFT, src)
if(flags & COMSIG_MOB_CANCEL_CLICKON)
return
if(user.can_examine() || flags & COMPONENT_ALLOW_EXAMINATE)
examinate(user, src)
/mob/proc/ShiftMiddleClickOn(atom/A)
src.pointed(A)
return
/*
Alt click
Unused except for AI
*/
/mob/proc/AltClickOn(atom/A)
A.AltClick(src)
return
/atom/proc/AltClick(mob/user)
var/turf/T = get_turf(src)
if(!T || !user.TurfAdjacent(T))
return FALSE
if(T && (isturf(loc) || isturf(src)) && user.TurfAdjacent(T))
user.set_listed_turf(T)
/mob/proc/TurfAdjacent(turf/T)
return T.AdjacentQuick(src)
/**
* Alt Shift click
* Currently only used for removing accessories from clothing.
*/
/mob/proc/AltShiftClickOn(atom/A)
A.AltShiftClick(src)
/atom/proc/AltShiftClick(mob/user)
return
/mob/proc/RightClickOn(atom/A)
A.RightClick(src)
/atom/proc/RightClick(mob/user)
return
///Main proc for secondary alt click
/mob/proc/AltClickSecondaryOn(atom/target)
base_click_alt_secondary(target)
/**
* ### Base proc for alt click interaction right click.
*
* If you wish to add custom `click_alt_secondary` behavior for a single type, use that proc.
*/
/mob/proc/base_click_alt_secondary(atom/target)
SHOULD_NOT_OVERRIDE(TRUE)
//Hook on the mob to intercept the click
if(SEND_SIGNAL(src, COMSIG_MOB_ALTCLICKON_SECONDARY, target) & COMSIG_MOB_CANCEL_CLICKON)
return
//Hook on the atom to intercept the click
if(SEND_SIGNAL(target, COMSIG_CLICK_ALT_SECONDARY, src) & COMPONENT_CANCEL_CLICK_ALT_SECONDARY)
return
// If it has a custom click_alt_secondary then do that
//if(can_perform_action(target, target.interaction_flags_click | SILENT_ADJACENCY))
target.click_alt_secondary(src)
/**
* ## Custom alt click secondary interaction
* Override this to change default alt right click behavior.
*
* ### Guard clauses
* Consider adding `interaction_flags_click` before adding unique guard clauses.
**/
/atom/proc/click_alt_secondary(mob/user)
SHOULD_CALL_PARENT(FALSE)
return NONE
/*
Misc helpers
Laser Eyes: as the name implies, handles this since nothing else does currently
face_atom: turns the mob towards what you clicked on
*/
/mob/proc/LaserEyes(atom/A, params)
return
/mob/living/LaserEyes(atom/A, params)
setClickCooldown(4)
var/turf/T = get_turf(src)
src.visible_message(SPAN_DANGER("\The [src]'s eyes flare with ruby light!"))
fire_projectile(/obj/projectile/beam, T, 'sound/weapons/wave.ogg', firer = src)
/mob/living/carbon/human/LaserEyes(atom/A, params)
if(nutrition <= 0)
to_chat(src, SPAN_WARNING("You're out of energy! You need food!"))
return
..()
adjustNutritionLoss(rand(1,5))
handle_regular_hud_updates()
// Simple helper to face what you clicked on, in case it should be needed in more than one place
/mob/proc/face_atom(var/atom/A, var/force_face = FALSE)
if(!A || !x || !y || !A.x || !A.y) return
var/dx = A.x - x
var/dy = A.y - y
var/direction
if (loc == A.loc && A.atom_flags & ATOM_FLAG_CHECKS_BORDER)
direction = A.dir
else if (!dx && !dy)
return
else if(abs(dx) < abs(dy))
if(dy > 0)
direction = NORTH
else
direction = SOUTH
else
if(dx > 0)
direction = EAST
else
direction = WEST
if(direction != dir)
facedir(direction, force_face)
GLOBAL_LIST(click_catchers)
/atom/movable/screen/click_catcher
icon = 'icons/hud/mob/screen_gen.dmi'
icon_state = "click_catcher"
plane = CLICKCATCHER_PLANE
mouse_opacity = MOUSE_OPACITY_OPAQUE
screen_loc = "CENTER-7,CENTER-7"
/atom/movable/screen/click_catcher/Destroy()
SHOULD_CALL_PARENT(FALSE)
return QDEL_HINT_LETMELIVE
/proc/create_click_catcher()
. = list()
for(var/i = 0, i<15, i++)
for(var/j = 0, j<15, j++)
var/atom/movable/screen/click_catcher/CC = new()
CC.screen_loc = "NORTH-[i],EAST-[j]"
. += CC
/atom/movable/screen/click_catcher/Click(location, control, params)
var/list/modifiers = params2list(params)
if(LAZYACCESS(modifiers, MIDDLE_CLICK) && iscarbon(usr))
var/mob/living/carbon/C = usr
C.swap_hand()
else
var/turf/click_turf = parse_caught_click_modifiers(modifiers, get_turf(usr.client ? usr.client.eye : usr), usr.client)
if (click_turf)
modifiers["catcher"] = TRUE
click_turf.Click(click_turf, control, list2params(modifiers))
. = 1
/// MouseWheelOn
/mob/proc/MouseWheelOn(atom/A, delta_x, delta_y, params)
SEND_SIGNAL(src, COMSIG_MOUSE_SCROLL_ON, A, delta_x, delta_y, params)
// Suppress the mouse macros
/client/var/has_mouse_macro_warning
/mob/proc/LogMouseMacro(verbused, params)
if(!client)
return
if(!client.has_mouse_macro_warning) // Log once
log_admin("[key_name(usr)] attempted to use a mouse macro: [verbused] [params]")
/mob/verb/ClickSubstitute(params as command_text)
set hidden = 1
set name = ".click"
LogMouseMacro(".click", params)
/mob/verb/DblClickSubstitute(params as command_text)
set hidden = 1
set name = ".dblclick"
LogMouseMacro(".dblclick", params)
/mob/verb/MouseSubstitute(params as command_text)
set hidden = 1
set name = ".mouse"
LogMouseMacro(".mouse", params)