Laggy DragNDrop fix (#21052)

* added dragndrop click methods for atoms

* some updating, little changing MouseDrop logic

* added INVOKE_ASYNC to delayed procs

* cleanup

* cleanup

* cleanup

* no cheesing

* more comments, removed  variable from atom

* whoopsie

* added inventory screen objects support, disabling tooltips now disables all tooltips, not only items

* fixing table hitting self issue
This commit is contained in:
HMBGERDO
2023-06-22 17:19:52 +01:00
committed by GitHub
parent 70541d997c
commit fc3dcb1254
30 changed files with 138 additions and 58 deletions
+35 -4
View File
@@ -1,3 +1,21 @@
/*
This function is called every time we cross other turf(i guess) while dragging atom
It means that while one dragNdrop if you cross multiple turfs this function will be called multiple times
*/
/atom/MouseDrag()
if(drag_start == 0) // we want to capture only moment of start dragging
drag_start = world.time
/*
This function is exist to check of our current dragNdrop could be just laggy click
Currently it just checks how long our dragNdrop was. If it was lag, it would be extremely small, otherwise it would be long
returns
TRUE if current dragNdrop is lesser than 0.2 sec
FALSE if not
*/
/atom/proc/could_be_click_lag()
return world.time - drag_start < 0.2 SECONDS // should be enough to lag
/*
MouseDrop:
@@ -5,15 +23,28 @@
recieving object instead, so that's the default action. This allows you to drag
almost anything into a trash can.
*/
/atom/MouseDrop(atom/over)
/atom/MouseDrop(atom/over, src_location, over_location, src_control, over_control, params)
if(!usr || !over)
return
var/lagging = could_be_click_lag()
drag_start = 0
if(!(istype(over, /obj/screen) || (loc && loc == over.loc)))
if(!Adjacent(usr) || !over.Adjacent(usr)) // should stop you from dragging through windows
if(lagging)
usr.ClickOn(src, params)
return
INVOKE_ASYNC(over, PROC_REF(MouseDrop_T), src, usr)
var/datum/callback/mousedrop = new(over, PROC_REF(MouseDrop_T), src, usr, params)
var/result = mousedrop.InvokeAsync() // if it gets TRUE in return, we think that all is fine
if(!result && lagging)
usr.ClickOn(src, params) // if not, we click object
// recieve a mousedrop
/atom/proc/MouseDrop_T(atom/dropping, mob/user)
/*
recieve a mousedrop
called on object which was under the object you dragged and dropped
return TRUE if you want to prevent us click the object
actually if you do something in that proc like changing user location or whatever, you expected to return TRUE
to inform the game this action was expected and its fine
*/
/atom/proc/MouseDrop_T(atom/dropping, mob/user, params) // return TRUE if you want to prevent us click the object after it
return
+9 -4
View File
@@ -5,11 +5,16 @@
var/ordered = TRUE
/obj/screen/movable/action_button/MouseDrop(over_object)
if(locked && could_be_click_lag()) // in case something bad happend and game realised we dragged our ability instead of pressing it
Click()
drag_start = 0
return
drag_start = 0
if(locked)
to_chat(usr, "<span class='warning'>Action button \"[name]\" is locked, unlock it first.</span>")
closeToolTip(usr)
return
if((istype(over_object, /obj/screen/movable/action_button) && !istype(over_object, /obj/screen/movable/action_button/hide_toggle)))
if(locked)
to_chat(usr, "<span class='warning'>Action button \"[name]\" is locked, unlock it first.</span>")
closeToolTip(usr)
return
var/obj/screen/movable/action_button/B = over_object
var/list/actions = usr.actions
actions.Swap(actions.Find(linked_action), actions.Find(B.linked_action))
+10
View File
@@ -217,6 +217,7 @@
S.show_to(user)
else // If it's not in the storage, try putting it inside
S.attackby(I, user)
return TRUE
/obj/screen/zone_sel
name = "damage zone"
@@ -403,6 +404,15 @@
object_overlays += item_overlay
add_overlay(object_overlays)
/obj/screen/inventory/MouseDrop(atom/over)
cut_overlay(object_overlays)
object_overlays.Cut()
if(could_be_click_lag())
Click()
drag_start = 0
return
return ..()
/obj/screen/inventory/Click(location, control, params)
// At this point in client Click() code we have passed the 1/10 sec check and little else
// We don't even know if it's a middle click
+2
View File
@@ -71,6 +71,8 @@
var/list/alternate_appearances //the alternate appearances we own
var/list/viewing_alternate_appearances //the alternate appearances we're viewing, stored here to reestablish them after Logout()s
//these lists are built as necessary, so atoms aren't all lugging around empty lists
var/drag_start = 0 /*whenever we start dragging atom, this variable will contain world.time() of the moment we started dragging atom, otherwile will be 0
It is required to check how long dragNdrop was to prevent abusing the feature of laggy dragNdrop click */
/*
Builds an alternate_appearance datum for the supplied args, optionally displaying it straight away
+4 -3
View File
@@ -194,16 +194,16 @@
return
if(occupant)
to_chat(user, "<span class='boldnotice'>[src] is already occupied!</span>")
return
return TRUE
var/mob/living/L = O
if(!istype(L) || L.buckled)
return
if(L.abiotic())
to_chat(user, "<span class='danger'>Subject may not hold anything in their hands.</span>")
return
return TRUE
if(L.has_buckled_mobs()) //mob attached to us
to_chat(user, "<span class='warning'>[L] will not fit into [src] because [L.p_they()] [L.p_have()] a slime latched onto [L.p_their()] head.</span>")
return
return TRUE
if(L == user)
visible_message("<span class='notice'>[user] climbs into [src].</span>")
else
@@ -211,6 +211,7 @@
put_in(L)
if(user.pulling == L)
user.stop_pulling()
return TRUE
/obj/machinery/dna_scannernew/attackby(obj/item/I, mob/user, params)
if(exchange_parts(user, I))
@@ -30,18 +30,19 @@
return
if(occupant)
to_chat(user, "<span class='notice'>[src] is already occupied.</span>")
return //occupied
return TRUE
if(target.buckled)
return
if(target.has_buckled_mobs()) //mob attached to us
to_chat(user, "<span class='warning'>[target] will not fit into [src] because [target.p_they()] [target.p_have()] a slime latched onto [target.p_their()] head.</span>")
return
return TRUE
visible_message("<span class='notice'>[user] puts [target] into [src].</span>")
target.forceMove(src)
occupant = target
update_icon(UPDATE_ICON_STATE)
add_fingerprint(user)
return TRUE
/obj/machinery/abductor/experiment/attack_hand(mob/user)
if(..())
+1
View File
@@ -64,6 +64,7 @@
if(!user_buckle_mob(O, user, check_loc = FALSE))
return
take_patient(O, user)
return TRUE
/**
* Updates the `patient` var to be the mob occupying the table
+4 -2
View File
@@ -490,6 +490,10 @@
visible_message("[user] starts climbing into the sleeper.")
else
visible_message("[user] starts putting [L.name] into the sleeper.")
INVOKE_ASYNC(src, TYPE_PROC_REF(/obj/machinery/sleeper, put_in), O, user, L)
return TRUE
/obj/machinery/sleeper/proc/put_in(atom/movable/O, mob/user, mob/living/L) // need this proc to use INVOKE_ASYNC in other proc. You're not recommended to use that one
if(do_after(user, 20, target = L))
if(!permitted_check(O, user))
return
@@ -504,8 +508,6 @@
if(user.pulling == L)
user.stop_pulling()
SStgui.update_uis(src)
return
return
/obj/machinery/sleeper/proc/permitted_check(atom/movable/O, mob/user)
if(O.loc == user) //no you can't pull things out of your ass
+5 -4
View File
@@ -119,18 +119,18 @@
return FALSE //not a borg or human
if(panel_open)
to_chat(user, "<span class='notice'>Close the maintenance panel first.</span>")
return FALSE //panel open
return TRUE //panel open
if(occupant)
to_chat(user, "<span class='notice'>[src] is already occupied.</span>")
return FALSE //occupied
return TRUE //occupied
if(H.buckled)
return FALSE
if(H.abiotic())
to_chat(user, "<span class='notice'>Subject may not hold anything in their hands.</span>")
return FALSE
return TRUE
if(H.has_buckled_mobs()) //mob attached to us
to_chat(user, "<span class='warning'>[H] will not fit into [src] because [H.p_they()] [H.p_have()] a slime latched onto [H.p_their()] head.</span>")
return
return TRUE
if(H == user)
visible_message("[user] climbs into [src].")
@@ -143,6 +143,7 @@
update_icon(UPDATE_ICON_STATE)
add_fingerprint(user)
SStgui.update_uis(src)
return TRUE
/obj/machinery/bodyscanner/attack_ai(user)
return attack_hand(user)
+8 -9
View File
@@ -518,7 +518,7 @@
return
if(occupant)
to_chat(user, "<span class='boldnotice'>The cryo pod is already occupied!</span>")
return
return TRUE
var/mob/living/L = O
@@ -527,13 +527,14 @@
if(L.stat == DEAD)
to_chat(user, "<span class='notice'>Dead people can not be put into cryo.</span>")
return
return TRUE
if(L.has_buckled_mobs()) //mob attached to us
to_chat(user, "<span class='warning'>[L] will not fit into [src] because [L.p_they()] [L.p_have()] a slime latched onto [L.p_their()] head.</span>")
return
INVOKE_ASYNC(src, TYPE_PROC_REF(/obj/machinery/cryopod, put_in), user, L)
return TRUE
/obj/machinery/cryopod/proc/put_in(mob/user, mob/living/L) // need this proc to use INVOKE_ASYNC in other proc. You're not recommended to use that one
var/willing = null //We don't want to allow people to be forced into despawning.
time_till_despawn = initial(time_till_despawn)
@@ -547,18 +548,16 @@
if(willing)
if(!Adjacent(L) && !Adjacent(user))
to_chat(user, "<span class='boldnotice'>You're not close enough to [src].</span>")
return
return TRUE
if(L == user)
visible_message("[user] starts climbing into the cryo pod.")
else
visible_message("[user] starts putting [L] into the cryo pod.")
if(do_after(user, 20, target = L))
if(!L) return
if(!L) return TRUE
if(occupant)
to_chat(user, "<span class='boldnotice'>\The [src] is in use.</span>")
return
return TRUE
take_occupant(L, willing)
else
to_chat(user, "<span class='notice'>You stop [L == user ? "climbing into the cryo pod." : "putting [L] into the cryo pod."]</span>")
+6 -3
View File
@@ -431,19 +431,22 @@
var/mob/living/target = A
if(!state_open)
to_chat(user, "<span class='warning'>[src]'s doors are shut!</span>")
return
return TRUE
if(!is_operational())
to_chat(user, "<span class='warning'>[src] is not operational!</span>")
return
return TRUE
if(occupant || helmet || suit || storage)
to_chat(user, "<span class='warning'>It's too cluttered inside to fit in!</span>")
return
return TRUE
if(target == user)
user.visible_message("<span class='warning'>[user] starts squeezing into [src]!</span>", "<span class='notice'>You start working your way into [src]...</span>")
else
target.visible_message("<span class='warning'>[user] starts shoving [target] into [src]!</span>", "<span class='userdanger'>[user] starts shoving you into [src]!</span>")
INVOKE_ASYNC(src, TYPE_PROC_REF(/obj/machinery/suit_storage_unit, put_in), user, target)
return TRUE
/obj/machinery/suit_storage_unit/proc/put_in(mob/user, mob/living/target)
if(do_mob(user, target, 30))
if(occupant || helmet || suit || storage)
return
+8 -5
View File
@@ -1096,7 +1096,7 @@
/obj/mecha/MouseDrop_T(mob/M, mob/user)
if(frozen)
to_chat(user, "<span class='warning'>Do not enter Admin-Frozen mechs.</span>")
return
return TRUE
if(user.incapacitated())
return
if(user != M)
@@ -1105,7 +1105,7 @@
if(occupant)
to_chat(user, "<span class='warning'>[src] is already occupied!</span>")
log_append_to_last("Permission denied.")
return
return TRUE
var/passed
if(dna)
if(ishuman(user))
@@ -1116,17 +1116,20 @@
if(!passed)
to_chat(user, "<span class='warning'>Access denied.</span>")
log_append_to_last("Permission denied.")
return
return TRUE
if(user.buckled)
to_chat(user, "<span class='warning'>You are currently buckled and cannot move.</span>")
log_append_to_last("Permission denied.")
return
return TRUE
if(user.has_buckled_mobs()) //mob attached to us
to_chat(user, "<span class='warning'>You can't enter the exosuit with other creatures attached to you!</span>")
return
return TRUE
visible_message("<span class='notice'>[user] starts to climb into [src]")
INVOKE_ASYNC(src, TYPE_PROC_REF(/obj/mecha, put_in), user)
return TRUE
/obj/mecha/proc/put_in(mob/user) // need this proc to use INVOKE_ASYNC in other proc. You're not recommended to use that one
if(do_after(user, 40, target = src))
if(obj_integrity <= 0)
to_chat(user, "<span class='warning'>You cannot get in the [name], it has been destroyed!</span>")
+2
View File
@@ -751,6 +751,8 @@ GLOBAL_DATUM_INIT(welding_sparks, /mutable_appearance, mutable_appearance('icons
if(loc && I.loc == loc && isstorage(loc) && loc.Adjacent(user)) // Are we trying to swap two items in the storage?
var/obj/item/storage/S = loc
S.swap_items(src, I, user)
remove_outline()
return TRUE
remove_outline() //get rid of the hover effect in case the mouse exit isn't called if someone drags and drops an item and somthing goes wrong
/obj/item/proc/apply_outline(mob/user, outline_color = null)
+2 -1
View File
@@ -59,13 +59,14 @@
. += "bodybag_label"
/obj/structure/closet/body_bag/MouseDrop(over_object, src_location, over_location)
. = ..()
if(over_object == usr && (in_range(src, usr) || usr.contents.Find(src)))
if(!ishuman(usr) || opened || length(contents))
return FALSE
visible_message("<span class='notice'>[usr] folds up [src].</span>")
new item_path(get_turf(src))
qdel(src)
return
. = ..()
/obj/structure/closet/body_bag/relaymove(mob/user)
if(user.stat)
+3 -2
View File
@@ -46,9 +46,10 @@
/obj/structure/MouseDrop_T(atom/movable/C, mob/user as mob)
if(..())
return
return TRUE
if(C == user)
do_climb(user)
INVOKE_ASYNC(src, TYPE_PROC_REF(/obj/structure, do_climb), user)
return TRUE
/obj/structure/proc/density_check()
for(var/obj/O in orange(0, src))
@@ -255,6 +255,7 @@
if(user != O)
user.visible_message("<span class='danger'>[user] stuffs [O] into [src]!</span>", "<span class='danger'>You stuff [O] into [src]!</span>")
add_fingerprint(user)
return TRUE
/obj/structure/closet/attack_ai(mob/user)
if(isrobot(user) && Adjacent(user)) //Robots can open/close it, but not the AI
@@ -89,7 +89,8 @@
if(isanimal(user) && victim != user)
return // animals cannot put mobs other than themselves onto spikes
add_fingerprint(user)
start_spike(victim, user)
INVOKE_ASYNC(src, TYPE_PROC_REF(/obj/structure/kitchenspike, start_spike), victim, user)
return TRUE
/obj/structure/kitchenspike/proc/start_spike(mob/living/victim, mob/user)
if(has_buckled_mobs())
+2 -1
View File
@@ -244,6 +244,7 @@
if(user != O)
user.visible_message("<span class='warning'>[user] stuffs [O] into [src]!</span>")
return TRUE
/obj/structure/m_tray/Destroy()
@@ -527,7 +528,7 @@ GLOBAL_LIST_EMPTY(crematoriums)
if(user != O)
user.visible_message("<span class='warning'>[user] stuffs [O] into [src]!</span>")
//Foreach goto(99)
return
return TRUE
/obj/structure/c_tray/Destroy()
if(connected && connected.connected == src)
@@ -216,7 +216,6 @@
return
/obj/structure/bed/roller/MouseDrop(over_object, src_location, over_location)
..()
if(over_object == usr && Adjacent(usr) && (in_range(src, usr) || usr.contents.Find(src)))
if(!ishuman(usr) || usr.incapacitated())
return
@@ -225,6 +224,8 @@
usr.visible_message("<span class='notice'>[usr] collapses \the [name].</span>", "<span class='notice'>You collapse \the [name].</span>")
new folded(get_turf(src))
qdel(src)
return
..()
/obj/item/roller_holder
name = "roller bed rack"
@@ -63,7 +63,6 @@
..()
/obj/structure/chair/MouseDrop(over_object, src_location, over_location)
. = ..()
if(over_object == usr && Adjacent(usr))
if(!item_chair || has_buckled_mobs())
return
@@ -82,6 +81,8 @@
var/C = new item_chair(loc)
usr.put_in_hands(C)
qdel(src)
return
. = ..()
/obj/structure/chair/attack_tk(mob/user as mob)
if(!anchored || has_buckled_mobs() || !isturf(user.loc))
+4 -2
View File
@@ -176,7 +176,8 @@
return 1
/obj/structure/table/MouseDrop_T(obj/O, mob/user)
..()
if(..())
return TRUE
if((!( isitem(O) ) || user.get_active_hand() != O))
return
if(isrobot(user))
@@ -185,7 +186,7 @@
return
if(O.loc != src.loc)
step(O, get_dir(O, src))
return
return TRUE
/obj/structure/table/proc/tablepush(obj/item/grab/G, mob/user)
if(HAS_TRAIT(user, TRAIT_PACIFISM))
@@ -878,6 +879,7 @@
return
if(O.loc != src.loc)
step(O, get_dir(O, src))
return TRUE
/obj/structure/rack/attackby(obj/item/W, mob/user, params)
if(isrobot(user))
@@ -133,16 +133,16 @@
return
if(occupant)
to_chat(user, "<span class='boldnotice'>The cryo cell is already occupied!</span>")
return
return TRUE
var/mob/living/L = O
if(!istype(L) || L.buckled)
return
if(L.abiotic())
to_chat(user, "<span class='danger'>Subject may not hold anything in their hands.</span>")
return
return TRUE
if(L.has_buckled_mobs()) //mob attached to us
to_chat(user, "<span class='warning'>[L] will not fit into [src] because [L.p_they()] [L.p_have()] a slime latched onto [L.p_their()] head.</span>")
return
return TRUE
if(put_mob(L))
if(L == user)
visible_message("[user] climbs into the cryo cell.")
@@ -152,6 +152,7 @@
if(user.pulling == L)
user.stop_pulling()
SStgui.update_uis(src)
return TRUE
/obj/machinery/atmospherics/unary/cryo_cell/process()
..()
@@ -124,7 +124,8 @@
if(targetl.buckled)
return
move_into_gibber(user,target)
INVOKE_ASYNC(src, TYPE_PROC_REF(/obj/machinery/gibber, move_into_gibber), user, target)
return TRUE
/obj/machinery/gibber/proc/move_into_gibber(mob/user, mob/living/victim)
if(occupant)
@@ -228,12 +228,12 @@
return
if(stat & (BROKEN|NOPOWER))
to_chat(user, "<span class='notice'>\The [src] is unpowered and useless.</span>")
return
return TRUE
var/obj/item/storage/box/pillbottles/P = over_object
if(!length(P.contents))
to_chat(user, "<span class='notice'>\The [P] is empty.</span>")
return
return TRUE
var/items_loaded = 0
for(var/obj/G in P.contents)
@@ -245,6 +245,7 @@
var/failed = length(P.contents)
if(failed)
to_chat(user, "<span class='notice'>[failed] item\s [failed == 1 ? "is" : "are"] refused.</span>")
return TRUE
/obj/machinery/smartfridge/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
@@ -366,6 +366,7 @@
return
load(AM)
return TRUE
// called to load a crate
/mob/living/simple_animal/bot/mulebot/proc/load(atom/movable/AM)
@@ -263,6 +263,7 @@
var/mob/living/Food = A
if(CanFeedon(Food))
Feedon(Food)
return
return ..()
/mob/living/simple_animal/slime/unEquip(obj/item/I, force, silent = FALSE)
+4 -3
View File
@@ -910,9 +910,10 @@ GLOBAL_LIST_INIT(slot_equipment_priority, list( \
/mob/proc/stripPanelEquip(obj/item/what, mob/who)
return
/mob/MouseDrop(mob/M as mob)
..()
if(M != usr) return
/mob/MouseDrop(mob/M as mob, src_location, over_location, src_control, over_control, params)
if((M != usr) || !istype(M))
..()
return
if(isliving(M))
var/mob/living/L = M
if(L.mob_size <= MOB_SIZE_SMALL)
+1
View File
@@ -513,6 +513,7 @@
playsound(loc, 'sound/machines/ping.ogg', 50, 0)
atom_say("Attention: Posterior Placed on Printing Plaque!")
SStgui.update_uis(src)
return TRUE
/obj/machinery/photocopier/Destroy()
QDEL_LIST_CONTENTS(saved_documents)
+6 -3
View File
@@ -189,14 +189,18 @@
if(isanimal(user) && target != user)
return //animals cannot put mobs other than themselves into disposal
src.add_fingerprint(user)
var/target_loc = target.loc
var/msg
for(var/mob/V in viewers(usr))
if(target == user && !user.stat && !user.IsWeakened() && !user.IsStunned() && !user.IsParalyzed())
V.show_message("[usr] starts climbing into the disposal.", 3)
if(target != user && !user.restrained() && !user.stat && !user.IsWeakened() && !user.IsStunned() && !user.IsParalyzed())
if(target.anchored) return
V.show_message("[usr] starts stuffing [target.name] into the disposal.", 3)
INVOKE_ASYNC(src, TYPE_PROC_REF(/obj/machinery/disposal, put_in), target, user)
return TRUE
/obj/machinery/disposal/proc/put_in(mob/living/target, mob/living/user) // need this proc to use INVOKE_ASYNC in other proc. You're not recommended to use that one
var/msg
var/target_loc = target.loc
if(!do_after(usr, 20, target = target))
return
if(QDELETED(src) || target_loc != target.loc)
@@ -223,7 +227,6 @@
C.show_message(msg, 3)
update()
return
// attempt to move while inside
/obj/machinery/disposal/relaymove(mob/user as mob)
+1 -1
View File
@@ -101,7 +101,7 @@ Notes:
//Includes sanity.checks
/proc/openToolTip(mob/user = null, atom/movable/tip_src = null, params = null, title = "", content = "", theme = "")
if(istype(user))
if(user.client && user.client.tooltips)
if(user.client && user.client.tooltips && !(user.client.prefs.toggles2 & PREFTOGGLE_2_HIDE_ITEM_TOOLTIPS))
if(!theme && user.client.prefs && user.client.prefs.UI_style)
theme = lowertext(user.client.prefs.UI_style)
if(!theme)