Files
Aurora.3/code/_onclick/click.dm
Matt Atlas 556f1aa86f Fullscreen overlay rework, full-black unconsciousness overlay, overlay fadeout animations + more. (#10053)
Ported the clickcatcher from tg/bay. You can now click black spots to turn. Isn't that handy, especially with vision cones.

    Reworked fullscreen overlays into an easier and better system, courtesy of PsiOmegaDelta.

    Similiarly, added fadeout animations to all pain overlays, plus unconsciousness.

    Fixed the vampire frenzy overlay never showing.

    Unconsciousness is now FULL darkness.

    Flashing mobs is now an inbuilt proc.
2020-10-02 08:53:14 +03:00

396 lines
10 KiB
Plaintext

/*
Click code cleanup
~Sayu
*/
// 1 decisecond click delay (above and beyond mob/next_move)
/mob/var/next_click = 0
/*
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 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(src)
usr.ClickOn(src, params)
/atom/DblClick(var/location, var/control, var/params)
if(src)
usr.DblClickOn(src, params)
/atom/proc/allow_click_through(var/atom/A, var/params, var/mob/user)
return FALSE
/turf/allow_click_through(var/atom/A, var/params, var/mob/user)
return TRUE
/atom/proc/RelayMouseDrag(src_object, over_object, src_location, over_location, src_control, over_control, params, var/mob/user)
return FALSE
/mob/proc/OnMouseDrag(src_object, over_object, src_location, over_location, src_control, over_control, params)
if(istype(loc, /atom))
var/atom/A = loc
if(client && client.buildmode)
build_click(src, client.buildmode, params, A)
return
if(A.RelayMouseDrag(src_object, over_object, src_location, over_location, src_control, over_control, params, src))
return
if(over_object)
if(!incapacitated())
var/obj/item/gun/gun = get_active_hand()
if(istype(gun))
set_dir(get_dir(src, over_object))
gun.Fire(get_turf(over_object), src, params, (get_dist(over_object, src) <= 1), FALSE)
/*
Standard mob ClickOn()
Handles exceptions: Buildmode, 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 recieving it.
The most common are:
* mob/UnarmedAttack(atom,adjacent) - used here only when adjacent, with no item in hand; in the case of humans, checks gloves
* atom/attackby(item,user) - used only when adjacent
* item/afterattack(atom,user,adjacent,params) - used both ranged and adjacent
* mob/RangedAttack(atom,params) - used only ranged, only used for tk and laser eyes but could be changed
*/
/mob/proc/ClickOn(var/atom/A, var/params)
if(world.time <= next_click) // Hard check, before anything else, to avoid crashing
return
next_click = world.time + 1
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)
if(client && client.buildmode)
build_click(src, client.buildmode, params, A)
return
var/list/modifiers = params2list(params)
if(modifiers["shift"] && modifiers["ctrl"])
CtrlShiftClickOn(A)
return TRUE
if(modifiers["ctrl"] && modifiers["middle"])
pointed(A)
return TRUE
if(modifiers["middle"])
MiddleClickOn(A)
return TRUE
if(modifiers["shift"])
ShiftClickOn(A)
return FALSE
if(modifiers["alt"]) // alt and alt-gr (rightalt)
AltClickOn(A)
return
if(modifiers["ctrl"])
CtrlClickOn(A)
return TRUE
if(stat || paralysis || stunned || weakened)
return
face_atom(A) // change direction to face what you clicked on
if(!canClick()) // in the year 2000...
return
if(restrained())
setClickCooldown(10)
RestrainedClickOn(A)
return 1
if(in_throw_mode)
if(isturf(A) || isturf(A.loc))
throw_item(A)
return 1
throw_mode_off()
var/obj/item/W = get_active_hand()
if(W == A) // Handle attack_self
W.attack_self(src)
if(hand)
update_inv_l_hand(0)
else
update_inv_r_hand(0)
return 1
//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))
// faster access to objects already on you
if(A.loc != src)
setMoveCooldown(10) //getting something out of a backpack
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, 1)
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
//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)) ) // 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, 1)
return
else // non-adjacent click
if(W)
W.afterattack(A, src, 0, params) // 0: not Adjacent
else
RangedAttack(A, params)
return 1
/mob/proc/setClickCooldown(var/timeout)
next_move = max(world.time + timeout, next_move)
/mob/proc/canClick()
if(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(var/atom/A, var/params)
return
/*
Translates into attack_hand, 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.
*/
/mob/proc/UnarmedAttack(var/atom/A, var/proximity_flag)
return
/mob/living/UnarmedAttack(var/atom/A, var/proximity_flag)
if(!Master.round_started)
to_chat(src, "You cannot attack people before the game has started.")
return 0
if(stat)
return 0
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(var/atom/A, var/params)
if(!mutations.len) return
if((LASER_EYES in mutations) && a_intent == I_HURT)
LaserEyes(A, params) // moved into a proc below
/*
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
Only used for swapping hands
*/
/mob/proc/MiddleClickOn(var/atom/A)
swap_hand()
return
// In case of use break glass
/*
/atom/proc/MiddleClick(var/mob/M as mob)
return
*/
/*
Shift click
For most mobs, examine.
This is overridden in ai.dm
*/
/mob/proc/ShiftClickOn(var/atom/A)
A.ShiftClick(src)
return
/atom/proc/ShiftClick(var/mob/user)
if(user.client && user.client.eye == user)
user.examinate(src)
return
/*
Ctrl click
For most objects, pull
*/
/mob/proc/CtrlClickOn(var/atom/A)
A.CtrlClick(src)
return
/atom/proc/CtrlClick(var/mob/user)
return
/atom/movable/CtrlClick(var/mob/user)
if(Adjacent(user))
user.start_pulling(src)
/*
Alt click
Unused except for AI
*/
/mob/proc/AltClickOn(var/atom/A)
A.AltClick(src)
return
/atom/proc/AltClick(var/mob/user)
var/turf/T = get_turf(src)
if(T && user.TurfAdjacent(T))
if(user.listed_turf == T)
user.listed_turf = null
else
user.listed_turf = T
user.client.statpanel = "Turf"
return 1
/mob/proc/TurfAdjacent(var/turf/T)
return T.AdjacentQuick(src)
/*
Control+Shift click
Unused except for AI
*/
/mob/proc/CtrlShiftClickOn(var/atom/A)
A.CtrlShiftClick(src)
return
/atom/proc/CtrlShiftClick(var/mob/user)
return
/*
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 class='danger'>\The [src]'s eyes flare with ruby light!</span>")
var/obj/item/projectile/beam/LE = new (T)
LE.muzzle_type = /obj/effect/projectile/muzzle/eyelaser
LE.tracer_type = /obj/effect/projectile/tracer/eyelaser
LE.impact_type = /obj/effect/projectile/impact/eyelaser
playsound(usr.loc, 'sound/weapons/wave.ogg', 75, 1)
LE.launch_projectile(A, zone_sel? zone_sel.selecting : null, src, params)
/mob/living/carbon/human/LaserEyes(atom/A, params)
if(nutrition <= 0)
to_chat(src, "<span class='warning'>You're out of energy! You need food!</span>")
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)
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.flags & ON_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, TRUE)
var/global/list/click_catchers
/obj/screen/click_catcher
icon = 'icons/mob/screen_gen.dmi'
icon_state = "click_catcher"
plane = CLICKCATCHER_PLANE
mouse_opacity = 2
screen_loc = "CENTER-7,CENTER-7"
/obj/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/obj/screen/click_catcher/CC = new()
CC.screen_loc = "NORTH-[i],EAST-[j]"
. += CC
/obj/screen/click_catcher/Click(location, control, params)
var/list/modifiers = params2list(params)
if(modifiers["middle"] && istype(usr, /mob/living/carbon))
var/mob/living/carbon/C = usr
C.swap_hand()
else
var/turf/T = screen_loc2turf(screen_loc, get_turf(usr))
if(T)
T.Click(location, control, params)
. = 1