Files
Bubberstation/code/datums/components/pet_commands/obeys_commands.dm

144 lines
6.1 KiB
Plaintext

/**
* # Obeys Commands Component
* Manages a list of pet command datums, allowing you to boss it around
* Creates a radial menu of pet commands when this creature is alt-clicked, if it has any
*/
#define DEFAULT_RADIAL_VIEWING_DISTANCE 9
/datum/component/obeys_commands
/// List of commands you can give to the owner of this component
var/list/available_commands = list()
///Users currently viewing our radial options
var/list/radial_viewers = list()
///radius of our radial menu
var/radial_menu_radius = 48
///after how long we shutdown radial menus
var/radial_menu_lifetime = 7 SECONDS
///offset to display the radial menu
var/list/radial_menu_offset
///should the commands move with the pet owner's screen?
var/radial_relative_to_user
/// The available_commands parameter should be passed as a list of typepaths
/datum/component/obeys_commands/Initialize(list/command_typepaths = list(), list/radial_menu_offset = list(0, 0), radial_menu_lifetime = 7 SECONDS, radial_relative_to_user = FALSE)
. = ..()
if (!isliving(parent))
return COMPONENT_INCOMPATIBLE
var/mob/living/living_parent = parent
if (!living_parent.ai_controller)
return COMPONENT_INCOMPATIBLE
if (!length(command_typepaths))
CRASH("Initialised obedience component with no commands.")
src.radial_menu_offset = radial_menu_offset
src.radial_relative_to_user = radial_relative_to_user
src.radial_menu_lifetime = radial_menu_lifetime
for (var/command_path in command_typepaths)
var/datum/pet_command/new_command = new command_path(parent)
available_commands[new_command.command_name] = new_command
/datum/component/obeys_commands/Destroy(force)
QDEL_LIST_ASSOC_VAL(available_commands)
return ..()
/datum/component/obeys_commands/RegisterWithParent()
RegisterSignal(parent, COMSIG_LIVING_BEFRIENDED, PROC_REF(add_friend))
RegisterSignal(parent, COMSIG_LIVING_UNFRIENDED, PROC_REF(remove_friend))
RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
/datum/component/obeys_commands/UnregisterFromParent()
UnregisterSignal(parent, list(COMSIG_LIVING_BEFRIENDED, COMSIG_LIVING_UNFRIENDED, COMSIG_ATOM_EXAMINE, COMSIG_CLICK_ALT))
/// Add someone to our friends list
/datum/component/obeys_commands/proc/add_friend(datum/source, mob/living/new_friend)
SIGNAL_HANDLER
RegisterSignal(new_friend, COMSIG_KB_LIVING_VIEW_PET_COMMANDS, PROC_REF(on_key_pressed))
RegisterSignal(new_friend, DEACTIVATE_KEYBIND(COMSIG_KB_LIVING_VIEW_PET_COMMANDS), PROC_REF(on_key_unpressed))
for (var/command_name in available_commands)
var/datum/pet_command/command = available_commands[command_name]
INVOKE_ASYNC(command, TYPE_PROC_REF(/datum/pet_command, add_new_friend), new_friend)
/datum/component/obeys_commands/proc/on_key_unpressed(mob/living/source)
SIGNAL_HANDLER
UnregisterSignal(source, COMSIG_ATOM_MOUSE_ENTERED)
remove_from_viewers(source)
/datum/component/obeys_commands/proc/remove_from_viewers(mob/living/source)
radial_viewers -= REF(source)
/// Remove someone from our friends list
/datum/component/obeys_commands/proc/remove_friend(datum/source, mob/living/old_friend)
SIGNAL_HANDLER
UnregisterSignal(old_friend, list(
COMSIG_KB_LIVING_VIEW_PET_COMMANDS,
DEACTIVATE_KEYBIND(COMSIG_KB_LIVING_VIEW_PET_COMMANDS),
))
for (var/command_name in available_commands)
var/datum/pet_command/command = available_commands[command_name]
INVOKE_ASYNC(command, TYPE_PROC_REF(/datum/pet_command, remove_friend), old_friend)
/// Add a note about whether they will follow the instructions of the inspecting mob
/datum/component/obeys_commands/proc/on_examine(mob/living/source, mob/user, list/examine_list)
SIGNAL_HANDLER
if (IS_DEAD_OR_INCAP(source))
return
if (!(user in source.ai_controller?.blackboard[BB_FRIENDS_LIST]))
return
examine_list += span_notice("[source.p_They()] seem[source.p_s()] happy to see you!")
/datum/component/obeys_commands/proc/on_key_pressed(mob/living/friend)
SIGNAL_HANDLER
RegisterSignal(friend, COMSIG_ATOM_MOUSE_ENTERED, PROC_REF(on_mouse_hover))
/datum/component/obeys_commands/proc/on_mouse_hover(mob/living/friend, atom/mouse_hovered)
SIGNAL_HANDLER
if(mouse_hovered == parent)
display_menu(friend)
return
var/mob/living/owner = parent
if(isliving(mouse_hovered) && mouse_hovered.loc != owner.loc)
remove_from_viewers(friend)
/// Displays a radial menu of commands
/datum/component/obeys_commands/proc/display_menu(mob/living/friend)
var/mob/living/living_parent = parent
if (IS_DEAD_OR_INCAP(living_parent) || friend.stat != CONSCIOUS)
return
if (!(friend in living_parent.ai_controller?.blackboard[BB_FRIENDS_LIST]))
return // Not our friend, can't boss us around
if(radial_viewers[REF(friend)])
return
if(!can_see(friend, parent, DEFAULT_RADIAL_VIEWING_DISTANCE))
return
INVOKE_ASYNC(src, PROC_REF(display_radial_menu), friend)
/// Actually display the radial menu and then do something with the result
/datum/component/obeys_commands/proc/display_radial_menu(mob/living/friend)
var/list/radial_options = list()
for (var/command_name in available_commands)
var/datum/pet_command/command = available_commands[command_name]
var/datum/radial_menu_choice/choice = command.provide_radial_data()
if (!choice)
continue
radial_options += choice
radial_viewers[REF(friend)] = world.time + radial_menu_lifetime
var/pick = show_radial_menu(friend, parent, radial_options, radius = radial_menu_radius, button_animation_flags = BUTTON_FADE_IN | BUTTON_FADE_OUT, custom_check = CALLBACK(src, PROC_REF(check_menu_viewer), friend), check_delay = 0.15 SECONDS, display_close_button = FALSE, radial_menu_offset = radial_menu_offset, user_space = radial_relative_to_user)
remove_from_viewers(friend)
if(!pick)
return
var/datum/pet_command/picked_command = available_commands[pick]
picked_command.try_activate_command(friend, radial_command = TRUE)
/datum/component/obeys_commands/proc/check_menu_viewer(mob/living/user)
if(QDELETED(user) || !radial_viewers[REF(user)])
return FALSE
if(world.time > radial_viewers[REF(user)])
return FALSE
var/viewing_distance = DEFAULT_RADIAL_VIEWING_DISTANCE
if(!can_see(user, parent, viewing_distance))
return FALSE
return TRUE
#undef DEFAULT_RADIAL_VIEWING_DISTANCE