Files
Bubberstation/code/datums/components/callouts.dm
SmArtKar 523dc774f2 Callouts and MODsuit quick module pickers now track user (#85418)
## About The Pull Request
Callout and MODsuit quick picker (Ctrl + MMB) radials are now
user-bound, meaning that they won't change their screen position if you
move.

## Why It's Good For The Game

Those aren't radials bound to specific objects and rather appear at your
cursor purely for convenience. In case of callouts this is especially
important as you're most likely running while casting them which will
make your mouse move over and trigger a random option as you don't have
to click to use them.

## Changelog
🆑
qol: Callouts and MODsuit quick module pickers now track user
/🆑
2024-08-01 15:24:09 +01:00

178 lines
5.6 KiB
Plaintext

#define CALLOUT_TIME (5 SECONDS)
#define CALLOUT_COOLDOWN 3 SECONDS
/// Component that allows its owner/owner's wearer to use callouts system - their pointing is replaced with a fancy radial which allows them to summon glowing markers
/datum/component/callouts
/// If parent is clothing, slot on which this component activates
var/item_slot
/// If we are currently active
var/active = TRUE
/// Current user of this component
var/mob/cur_user
/// Whenever the user should shout the voiceline
var/voiceline = FALSE
/// If voiceline is true, what prefix the user should use
var/radio_prefix = null
/// List of all callout options
var/static/list/callout_options = typecacheof(subtypesof(/datum/callout_option))
/// Text displayed when parent is examined
var/examine_text = null
/// Cooldown for callouts
COOLDOWN_DECLARE(callout_cooldown)
/datum/component/callouts/Initialize(item_slot = null, voiceline = FALSE, radio_prefix = null, examine_text = null)
if (!isitem(parent) && !ismob(parent))
return COMPONENT_INCOMPATIBLE
src.item_slot = item_slot
src.voiceline = voiceline
src.radio_prefix = radio_prefix
src.examine_text = examine_text
if (ismob(parent))
cur_user = parent
return
var/atom/atom_parent = parent
if (!ismob(atom_parent.loc))
return
var/mob/user = atom_parent.loc
if (!isnull(item_slot) && user.get_item_by_slot(item_slot) != parent)
return
RegisterSignal(atom_parent.loc, COMSIG_MOB_CLICKON, PROC_REF(on_click))
cur_user = atom_parent.loc
/datum/component/callouts/Destroy(force)
cur_user = null
. = ..()
/datum/component/callouts/RegisterWithParent()
RegisterSignal(parent, COMSIG_MOB_CLICKON, PROC_REF(on_click))
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(on_equipped))
RegisterSignal(parent, COMSIG_ITEM_DROPPED, PROC_REF(on_dropped))
RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examines))
RegisterSignal(parent, COMSIG_CLICK_CTRL, PROC_REF(on_ctrl_click))
/datum/component/callouts/UnregisterFromParent()
UnregisterSignal(parent, list(COMSIG_MOB_CLICKON, COMSIG_ITEM_EQUIPPED, COMSIG_ITEM_DROPPED, COMSIG_ATOM_EXAMINE, COMSIG_CLICK_CTRL))
/datum/component/callouts/proc/on_ctrl_click(datum/source, mob/living/user)
SIGNAL_HANDLER
if(!isitem(parent))
return
var/obj/item/item_parent = parent
active = !active
item_parent.balloon_alert(user, active ? "callouts enabled" : "callouts disabled")
/datum/component/callouts/proc/on_equipped(datum/source, mob/equipper, slot)
SIGNAL_HANDLER
if (item_slot & slot)
RegisterSignal(equipper, COMSIG_MOB_CLICKON, PROC_REF(on_click))
cur_user = equipper
else if (cur_user == equipper)
UnregisterSignal(cur_user, COMSIG_MOB_CLICKON, PROC_REF(on_click))
cur_user = null
/datum/component/callouts/proc/on_dropped(datum/source, mob/user)
SIGNAL_HANDLER
if (cur_user == user)
UnregisterSignal(cur_user, COMSIG_MOB_CLICKON, PROC_REF(on_click))
cur_user = null
/datum/component/callouts/proc/on_examines(mob/source, mob/user, list/examine_list)
SIGNAL_HANDLER
if (!isnull(examine_text))
examine_list += examine_text
/datum/component/callouts/proc/on_click(mob/user, atom/clicked_atom, list/modifiers)
SIGNAL_HANDLER
if (!LAZYACCESS(modifiers, SHIFT_CLICK) || !LAZYACCESS(modifiers, MIDDLE_CLICK))
return
if (!active)
return
if (!COOLDOWN_FINISHED(src, callout_cooldown))
clicked_atom.balloon_alert(user, "callout is on cooldown!")
return COMSIG_MOB_CANCEL_CLICKON
INVOKE_ASYNC(src, PROC_REF(callout_picker), user, clicked_atom)
return COMSIG_MOB_CANCEL_CLICKON
/datum/component/callouts/proc/callout_picker(mob/user, atom/clicked_atom)
var/list/callout_items = list()
for(var/datum/callout_option/callout_option as anything in callout_options)
callout_items[callout_option] = image(icon = 'icons/hud/radial.dmi', icon_state = callout_option::icon_state)
var/datum/callout_option/selection = show_radial_menu(user, get_turf(clicked_atom), callout_items, entry_animation = FALSE, click_on_hover = TRUE, user_space = TRUE)
if (!selection)
return
COOLDOWN_START(src, callout_cooldown, CALLOUT_COOLDOWN)
new /obj/effect/temp_visual/callout(get_turf(user), user, selection, clicked_atom)
SEND_SIGNAL(user, COMSIG_MOB_CREATED_CALLOUT, selection, clicked_atom)
if (voiceline)
user.say((!isnull(radio_prefix) ? radio_prefix : "") + selection::voiceline, forced = src)
/obj/effect/temp_visual/callout
name = "callout"
icon = 'icons/effects/callouts.dmi'
icon_state = "point"
plane = ABOVE_LIGHTING_PLANE
duration = CALLOUT_TIME
/obj/effect/temp_visual/callout/Initialize(mapload, mob/creator, datum/callout_option/callout, atom/target)
. = ..()
if (isnull(creator))
return
icon_state = callout::icon_state
color = colorize_string(creator.GetVoice(), 2, 0.9)
update_appearance()
var/turf/target_loc = get_turf(target)
animate(src, pixel_x = (target_loc.x - loc.x) * world.icon_size + target.pixel_x, pixel_y = (target_loc.y - loc.y) * world.icon_size + target.pixel_y, time = 0.2 SECONDS, easing = EASE_OUT)
/datum/callout_option
var/name = "ERROR"
var/icon_state = "point"
var/voiceline = "Something has gone wrong!"
/datum/callout_option/point
name = "Point"
icon_state = "point"
voiceline = "Here!"
/datum/callout_option/danger
name = "Danger"
icon_state = "danger"
voiceline = "Danger there!"
/datum/callout_option/guard
name = "Guard"
icon_state = "guard"
voiceline = "Hold this position!"
/datum/callout_option/attack
name = "Attack"
icon_state = "attack"
voiceline = "Attack there!"
/datum/callout_option/mine
name = "Mine"
icon_state = "mine"
voiceline = "Dig here!"
/datum/callout_option/move
name = "Move"
icon_state = "move"
voiceline = "Reposition there!"
#undef CALLOUT_TIME
#undef CALLOUT_COOLDOWN