Pet Commands QOL . makes pet commands easier to use (#88495)

## About The Pull Request
this PR improves the UX of pet commands a bit. i decided to expand on
their radial menu. You can now hold shift and hover over your pet to
display a menu of commands which you can choose from. alternatively, you
can still type out commands in chat


https://github.com/user-attachments/assets/9da7f7ea-58a3-4fd6-b040-45cc05cda51d



## Why It's Good For The Game
makes pet commands easier to give out when you're managing more than 1
pet. also fixes the fishing command not working.

## Changelog
🆑
qol: holding shift and hovering over your pet will display a list of
commands you can click from
fix: fixes the fishing pet command not working
/🆑
This commit is contained in:
Ben10Omintrix
2024-12-22 03:05:30 +01:00
committed by GitHub
parent d39dd980b8
commit efe62c5a72
50 changed files with 426 additions and 220 deletions
@@ -92,3 +92,6 @@
/// from /obj/projectile/energy/fisher/on_hit() or /obj/item/gun/energy/recharge/fisher when striking a target
#define COMSIG_ATOM_SABOTEUR_ACT "hit_by_saboteur"
#define COMSIG_SABOTEUR_SUCCESS 1
/// signal sent when a mouse is hovering over us, sent by atom/proc/on_mouse_entered
#define COMSIG_ATOM_MOUSE_ENTERED "mouse_entered"
+4
View File
@@ -4,6 +4,9 @@
#define COMSIG_KB_ACTIVATED (1<<0)
#define COMSIG_KB_EMOTE "keybinding_emote_down"
///Signal sent when a keybind is deactivated
#define DEACTIVATE_KEYBIND(A) "[A]_DEACTIVATED"
//Admin
#define COMSIG_KB_ADMIN_ASAY_DOWN "keybinding_admin_asay_down"
#define COMSIG_KB_ADMIN_DSAY_DOWN "keybinding_admin_dsay_down"
@@ -54,6 +57,7 @@
#define COMSIG_KB_LIVING_DISABLE_COMBAT_DOWN "keybinding_living_disable_combat_down"
#define COMSIG_KB_LIVING_TOGGLEMOVEINTENT_DOWN "keybinding_mob_togglemoveintent_down"
#define COMSIG_KB_LIVING_TOGGLEMOVEINTENTALT_DOWN "keybinding_mob_togglemoveintentalt_down"
#define COMSIG_KB_LIVING_VIEW_PET_COMMANDS "keybinding_living_view_pet_commands"
//Mob
#define COMSIG_KB_MOB_FACENORTH_DOWN "keybinding_mob_facenorth_down"
+6
View File
@@ -0,0 +1,6 @@
#define NEXT_PAGE_ID "__next__"
#define DEFAULT_CHECK_DELAY 2 SECONDS
#define BUTTON_SLIDE_IN (1<<0)
#define BUTTON_FADE_IN (1<<1)
#define BUTTON_FADE_OUT (1<<2)
+36 -19
View File
@@ -1,6 +1,3 @@
#define NEXT_PAGE_ID "__next__"
#define DEFAULT_CHECK_DELAY 20
GLOBAL_LIST_EMPTY(radial_menus)
/atom/movable/screen/radial
@@ -113,7 +110,7 @@ GLOBAL_LIST_EMPTY(radial_menus)
var/hudfix_method = TRUE //TRUE to change anchor to user, FALSE to shift by py_shift
var/py_shift = 0
var/entry_animation = TRUE
var/button_animation_flags = BUTTON_SLIDE_IN
///A replacement icon state for the generic radial slice bg icon. Doesn't affect the next page nor the center buttons
var/radial_slice_icon
@@ -163,6 +160,8 @@ GLOBAL_LIST_EMPTY(radial_menus)
var/atom/movable/screen/radial/slice/new_element = new /atom/movable/screen/radial/slice
new_element.tooltips = use_tooltips
new_element.set_parent(src)
if(button_animation_flags & BUTTON_FADE_IN)
new_element.alpha = 0
elements += new_element
var/page = 1
@@ -186,9 +185,9 @@ GLOBAL_LIST_EMPTY(radial_menus)
page_data[page] = current
pages = page
current_page = clamp(set_page, 1, pages)
update_screen_objects(entry_animation, click_on_hover)
update_screen_objects(button_animation_flags, click_on_hover)
/datum/radial_menu/proc/update_screen_objects(anim = FALSE, click_on_hover = FALSE)
/datum/radial_menu/proc/update_screen_objects(anim_flag = NONE, click_on_hover = FALSE)
var/list/page_choices = page_data[current_page]
var/angle_per_element = round(zone / page_choices.len)
for(var/i in 1 to elements.len)
@@ -198,11 +197,11 @@ GLOBAL_LIST_EMPTY(radial_menus)
HideElement(element)
element.click_on_hover = FALSE
else
SetElement(element,page_choices[i],angle,anim = anim,anim_order = i)
SetElement(element,page_choices[i],angle,anim_flag = anim_flag,anim_order = i)
// Only activate click on hover after the animation plays
if (!click_on_hover)
continue
if (anim)
if (anim_flag)
addtimer(VARSET_CALLBACK(element, click_on_hover, TRUE), i * 0.5)
else
element.click_on_hover = TRUE
@@ -217,11 +216,11 @@ GLOBAL_LIST_EMPTY(radial_menus)
E.choice = null
E.next_page = FALSE
/datum/radial_menu/proc/SetElement(atom/movable/screen/radial/slice/E,choice_id,angle,anim,anim_order)
/datum/radial_menu/proc/SetElement(atom/movable/screen/radial/slice/E, choice_id, angle, anim_flag, anim_order)
//Position
var/py = round(cos(angle) * radius) + py_shift
var/px = round(sin(angle) * radius)
if(anim)
if(anim_flag & BUTTON_SLIDE_IN)
var/timing = anim_order * 0.5
var/matrix/starting = matrix()
starting.Scale(0.1,0.1)
@@ -232,8 +231,11 @@ GLOBAL_LIST_EMPTY(radial_menus)
E.pixel_y = py
E.pixel_x = px
//Visuals
E.alpha = 255
if(anim_flag & BUTTON_FADE_IN)
animate(E, alpha = 255, time = 0.15 SECONDS, easing = EASE_OUT)
else
E.alpha = 255
E.mouse_opacity = MOUSE_OPACITY_ICON
E.cut_overlays()
E.vis_contents.Cut()
@@ -266,7 +268,9 @@ GLOBAL_LIST_EMPTY(radial_menus)
info_button.layer = RADIAL_CONTENT_LAYER
E.vis_contents += info_button
/datum/radial_menu/New()
/datum/radial_menu/New(display_close_button)
if(!display_close_button)
return
close_button = new
close_button.set_parent(src)
@@ -327,7 +331,9 @@ GLOBAL_LIST_EMPTY(radial_menus)
menu_holder = image(icon='icons/effects/effects.dmi',loc=anchor,icon_state="nothing", layer = RADIAL_BACKGROUND_LAYER, pixel_x = offset_x, pixel_y = offset_y)
SET_PLANE_EXPLICIT(menu_holder, ABOVE_HUD_PLANE, M)
menu_holder.appearance_flags |= KEEP_APART|RESET_ALPHA|RESET_COLOR|RESET_TRANSFORM
menu_holder.vis_contents += elements + close_button
menu_holder.vis_contents += elements
if(!isnull(close_button))
menu_holder.vis_contents += close_button
current_user.images += menu_holder
/datum/radial_menu/proc/hide()
@@ -345,6 +351,14 @@ GLOBAL_LIST_EMPTY(radial_menus)
next_check = world.time + check_delay
stoplag(1)
/datum/radial_menu/proc/remove_menu()
if(!(button_animation_flags & BUTTON_FADE_OUT))
qdel(src)
return
for(var/atom/movable/element as anything in elements)
animate(element, alpha = 0, time = 0.15 SECONDS)
QDEL_IN(src, 0.5 SECONDS)
/datum/radial_menu/Destroy()
Reset()
hide()
@@ -356,11 +370,11 @@ GLOBAL_LIST_EMPTY(radial_menus)
Choices should be a list where list keys are movables or text used for element names and return value
and list values are movables/icons/images used for element icons
*/
/proc/show_radial_menu(mob/user, atom/anchor, list/choices, uniqueid, radius, datum/callback/custom_check, require_near = FALSE, tooltips = FALSE, no_repeat_close = FALSE, radial_slice_icon = "radial_slice", autopick_single_option = TRUE, entry_animation = TRUE, click_on_hover = FALSE, user_space = FALSE)
/proc/show_radial_menu(mob/user, atom/anchor, list/choices, uniqueid, radius, datum/callback/custom_check, require_near = FALSE, tooltips = FALSE, no_repeat_close = FALSE, radial_slice_icon = "radial_slice", autopick_single_option = TRUE, button_animation_flags = BUTTON_SLIDE_IN, click_on_hover = FALSE, user_space = FALSE, check_delay = DEFAULT_CHECK_DELAY, display_close_button = TRUE, radial_menu_offset = list(0, 0))
if(!user || !anchor || !length(choices))
return
if(length(choices)==1 && autopick_single_option)
if(length(choices) == 1 && autopick_single_option)
return choices[1]
if(!uniqueid)
@@ -372,8 +386,9 @@ GLOBAL_LIST_EMPTY(radial_menus)
menu.finished = TRUE
return
var/datum/radial_menu/menu = new
menu.entry_animation = entry_animation
var/datum/radial_menu/menu = new(display_close_button)
menu.button_animation_flags = button_animation_flags
menu.check_delay = check_delay
GLOB.radial_menus[uniqueid] = menu
if(radius)
menu.radius = radius
@@ -390,10 +405,12 @@ GLOBAL_LIST_EMPTY(radial_menus)
var/turf/anchor_turf = get_turf(anchor)
offset_x = (anchor_turf.x - user_turf.x) * ICON_SIZE_X + anchor.pixel_x - user.pixel_x
offset_y = (anchor_turf.y - user_turf.y) * ICON_SIZE_Y + anchor.pixel_y - user.pixel_y
offset_x += radial_menu_offset[1]
offset_y += radial_menu_offset[2]
menu.show_to(user, offset_x, offset_y)
menu.wait(user, anchor, require_near)
var/answer = menu.selected_choice
qdel(menu)
menu.remove_menu()
GLOB.radial_menus -= uniqueid
if(require_near && !in_range(anchor, user))
return
+1 -1
View File
@@ -43,7 +43,7 @@
/datum/radial_menu/persistent/proc/change_choices(list/newchoices, tooltips = FALSE, animate = FALSE, keep_same_page = FALSE)
if(!newchoices.len)
return
entry_animation = FALSE
button_animation_flags = NONE
var/target_page = keep_same_page ? current_page : 1 //Stores the current_page value before it's set back to 1 on Reset()
Reset()
set_choices(newchoices,tooltips, set_page = target_page)
+1 -1
View File
@@ -111,7 +111,7 @@
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)
var/datum/callout_option/selection = show_radial_menu(user, get_turf(clicked_atom), callout_items, button_animation_flags = NONE, click_on_hover = TRUE, user_space = TRUE)
if (!selection)
return
+13 -10
View File
@@ -3,11 +3,11 @@
* Watch for someone throwing or pointing at something and then go get it and bring it back.
* If it's food we might eat it instead.
*/
/datum/pet_command/point_targeting/fetch
/datum/pet_command/fetch
command_name = "Fetch"
command_desc = "Command your pet to retrieve something you throw or point at."
radial_icon = 'icons/mob/actions/actions_spells.dmi'
radial_icon_state = "summons"
radial_icon_state = "fetch"
requires_pointing = TRUE
speech_commands = list("fetch")
command_feedback = "bounces"
pointed_reaction = "with great interest"
@@ -16,22 +16,25 @@
/// If true, this is a poorly trained pet who will eat food you throw instead of bringing it back
var/will_eat_targets = TRUE
/datum/pet_command/point_targeting/fetch/New(mob/living/parent)
/datum/pet_command/fetch/New(mob/living/parent)
. = ..()
if(isnull(parent))
return
parent.AddElement(/datum/element/ai_held_item) // We don't remove this on destroy because they might still be holding something
/datum/pet_command/point_targeting/fetch/add_new_friend(mob/living/tamer)
/datum/pet_command/fetch/add_new_friend(mob/living/tamer)
. = ..()
RegisterSignal(tamer, COMSIG_MOB_THROW, PROC_REF(listened_throw))
/datum/pet_command/point_targeting/fetch/remove_friend(mob/living/unfriended)
/datum/pet_command/fetch/remove_friend(mob/living/unfriended)
. = ..()
UnregisterSignal(unfriended, COMSIG_MOB_THROW)
/datum/pet_command/fetch/retrieve_command_text(atom/living_pet, atom/target)
return isnull(target) ? null : "signals [living_pet] to fetch [target]!"
/// A friend has thrown something, if we're listening or at least not busy then go get it
/datum/pet_command/point_targeting/fetch/proc/listened_throw(mob/living/carbon/thrower)
/datum/pet_command/fetch/proc/listened_throw(mob/living/carbon/thrower)
SIGNAL_HANDLER
var/mob/living/parent = weak_parent.resolve()
@@ -57,7 +60,7 @@
RegisterSignal(thrown_thing, COMSIG_MOVABLE_THROW_LANDED, PROC_REF(listen_throw_land))
/// A throw we were listening to has finished, see if it's in range for us to try grabbing it
/datum/pet_command/point_targeting/fetch/proc/listen_throw_land(obj/item/thrown_thing, datum/thrownthing/throwingdatum)
/datum/pet_command/fetch/proc/listen_throw_land(obj/item/thrown_thing, datum/thrownthing/throwingdatum)
SIGNAL_HANDLER
UnregisterSignal(thrown_thing, COMSIG_MOVABLE_THROW_LANDED)
@@ -76,7 +79,7 @@
parent.ai_controller.set_blackboard_key(BB_FETCH_DELIVER_TO, thrower)
// Don't try and fetch turfs or anchored objects if someone points at them
/datum/pet_command/point_targeting/fetch/look_for_target(mob/living/pointing_friend, obj/item/pointed_atom)
/datum/pet_command/fetch/look_for_target(mob/living/pointing_friend, obj/item/pointed_atom)
if (!istype(pointed_atom))
return FALSE
if (pointed_atom.anchored)
@@ -89,7 +92,7 @@
parent.ai_controller.set_blackboard_key(BB_FETCH_DELIVER_TO, pointing_friend)
// Finally, plan our actions
/datum/pet_command/point_targeting/fetch/execute_action(datum/ai_controller/controller)
/datum/pet_command/fetch/execute_action(datum/ai_controller/controller)
controller.queue_behavior(/datum/ai_behavior/forget_failed_fetches)
var/atom/target = controller.blackboard[BB_CURRENT_PET_TARGET]
@@ -3,12 +3,23 @@
* 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())
/datum/component/obeys_commands/Initialize(list/command_typepaths = list(), list/radial_menu_offset = list(0, 0), radial_relative_to_user = FALSE)
. = ..()
if (!isliving(parent))
return COMPONENT_INCOMPATIBLE
@@ -17,7 +28,8 @@
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
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
@@ -30,7 +42,6 @@
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))
RegisterSignal(parent, COMSIG_CLICK_ALT, PROC_REF(display_menu))
/datum/component/obeys_commands/UnregisterFromParent()
UnregisterSignal(parent, list(COMSIG_LIVING_BEFRIENDED, COMSIG_LIVING_UNFRIENDED, COMSIG_ATOM_EXAMINE, COMSIG_CLICK_ALT))
@@ -38,15 +49,26 @@
/// 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 as anything 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)
/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 as anything 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)
@@ -61,21 +83,34 @@
return
examine_list += span_notice("[source.p_They()] seem[source.p_s()] happy to see you!")
/// Displays a radial menu of commands
/datum/component/obeys_commands/proc/display_menu(datum/source, mob/living/clicker)
/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
if(isliving(mouse_hovered))
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) || !clicker.can_perform_action(living_parent))
if (IS_DEAD_OR_INCAP(living_parent) || friend.stat != CONSCIOUS)
return
if (!(clicker in living_parent.ai_controller?.blackboard[BB_FRIENDS_LIST]))
if (!(friend in living_parent.ai_controller?.blackboard[BB_FRIENDS_LIST]))
return // Not our friend, can't boss us around
INVOKE_ASYNC(src, PROC_REF(display_radial_menu), clicker)
return CLICK_ACTION_SUCCESS
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/clicker)
/datum/component/obeys_commands/proc/display_radial_menu(mob/living/friend)
var/list/radial_options = list()
for (var/command_name as anything in available_commands)
var/datum/pet_command/command = available_commands[command_name]
@@ -83,9 +118,22 @@
if (!choice)
continue
radial_options += choice
var/pick = show_radial_menu(clicker, clicker, radial_options, tooltips = TRUE)
if (!pick)
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(clicker)
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
@@ -13,7 +13,7 @@
/// If true, command will not appear in radial menu and can only be accessed through speech
var/hidden = FALSE
/// Icon to display in radial menu
var/icon/radial_icon
var/icon/radial_icon = 'icons/hud/radial_pets.dmi'
/// Icon state to display in radial menu
var/radial_icon_state
/// Speech strings to listen out for
@@ -24,6 +24,12 @@
var/command_feedback
/// How close a mob needs to be to a target to respond to a command
var/sense_radius = 7
/// does this pet command need a point to activate?
var/requires_pointing = FALSE
/// Blackboard key for targeting strategy, this is likely going to need it
var/targeting_strategy_key = BB_PET_TARGETING_STRATEGY
///our pointed reaction we play
var/pointed_reaction
/datum/pet_command/New(mob/living/parent)
. = ..()
@@ -34,10 +40,17 @@
RegisterSignal(tamer, COMSIG_MOB_SAY, PROC_REF(respond_to_command))
RegisterSignal(tamer, COMSIG_MOB_AUTOMUTE_CHECK, PROC_REF(waive_automute))
RegisterSignal(tamer, COMSIG_MOB_CREATED_CALLOUT, PROC_REF(respond_to_callout))
if(requires_pointing)
RegisterSignal(tamer, COMSIG_MOVABLE_POINTED, PROC_REF(point_on_target))
/// Stop listening to a guy
/datum/pet_command/proc/remove_friend(mob/living/unfriended)
UnregisterSignal(unfriended, list(COMSIG_MOB_SAY, COMSIG_MOB_AUTOMUTE_CHECK, COMSIG_MOB_CREATED_CALLOUT))
UnregisterSignal(unfriended, list(
COMSIG_MOB_SAY,
COMSIG_MOB_AUTOMUTE_CHECK,
COMSIG_MOB_CREATED_CALLOUT,
COMSIG_MOVABLE_POINTED,
))
/// Stop the automute from triggering for commands (unless the spoken text is suspiciously longer than the command)
/datum/pet_command/proc/waive_automute(mob/living/speaker, client/client, last_message, mute_type)
@@ -49,7 +62,6 @@
/// Respond to something that one of our friends has asked us to do
/datum/pet_command/proc/respond_to_command(mob/living/speaker, speech_args)
SIGNAL_HANDLER
var/mob/living/parent = weak_parent.resolve()
if (!parent)
return
@@ -60,7 +72,7 @@
if (!find_command_in_text(spoken_text))
return
try_activate_command(speaker)
try_activate_command(commander = speaker, radial_command = FALSE)
/// Respond to a callout
/datum/pet_command/proc/respond_to_callout(mob/living/caller, datum/callout_option/callout, atom/target)
@@ -83,7 +95,7 @@
if (!found_new_target)
return
if (try_activate_command(caller))
if (try_activate_command(commander = caller, radial_command = FALSE))
look_for_target(parent, target)
/// Does this callout with this target trigger this command?
@@ -103,48 +115,77 @@
return TRUE
return FALSE
/// Apply a command state if conditions are right, return command if successful
/datum/pet_command/proc/try_activate_command(mob/living/commander)
/datum/pet_command/proc/pet_able_to_respond()
var/mob/living/parent = weak_parent.resolve()
if (!parent)
if(isnull(parent) || isnull(parent.ai_controller))
return FALSE
if (!parent.ai_controller) // We stopped having a brain at some point
if(IS_DEAD_OR_INCAP(parent)) // Probably can't hear them if we're dead
return FALSE
if (IS_DEAD_OR_INCAP(parent)) // Probably can't hear them if we're dead
return FALSE
if (parent.ai_controller.blackboard[BB_ACTIVE_PET_COMMAND] == src) // We're already doing it
return FALSE
set_command_active(parent, commander)
return TRUE
/// Target the pointed atom for actions
/datum/pet_command/proc/look_for_target(mob/living/friend, atom/pointed_atom)
/// Apply a command state if conditions are right, return command if successful
/datum/pet_command/proc/try_activate_command(mob/living/commander, radial_command)
if(!pet_able_to_respond())
return FALSE
var/mob/living/parent = weak_parent.resolve()
if (!parent)
set_command_active(parent, commander, radial_command)
return TRUE
/datum/pet_command/proc/generate_emote_command(atom/target)
var/mob/living/living_pet = weak_parent?.resolve()
return isnull(living_pet) ? null : retrieve_command_text(living_pet, target)
/datum/pet_command/proc/retrieve_command_text(atom/living_pet, atom/target)
return "signals [living_pet] to spring into action!"
/// Target the pointed atom for actions
/datum/pet_command/proc/look_for_target(mob/living/friend, atom/potential_target)
var/mob/living/parent = weak_parent.resolve()
if(!pet_able_to_respond())
return FALSE
if (!parent.ai_controller)
if (parent.ai_controller.blackboard[BB_CURRENT_PET_TARGET] == potential_target) // That's already our target
return FALSE
if (IS_DEAD_OR_INCAP(parent))
return FALSE
if (parent.ai_controller.blackboard[BB_ACTIVE_PET_COMMAND] != src) // We're not listening right now
return FALSE
if (parent.ai_controller.blackboard[BB_CURRENT_PET_TARGET] == pointed_atom) // That's already our target
return FALSE
if (!can_see(parent, pointed_atom, sense_radius))
if (!can_see(parent, potential_target, sense_radius))
return FALSE
parent.ai_controller.CancelActions()
set_command_target(parent, pointed_atom)
set_command_target(parent, potential_target)
return TRUE
/// Activate the command, extend to add visible messages and the like
/datum/pet_command/proc/set_command_active(mob/living/parent, mob/living/commander)
set_command_target(parent, null)
/datum/pet_command/proc/set_command_active(mob/living/parent, mob/living/commander, radial_command = FALSE)
parent.ai_controller.clear_blackboard_key(BB_CURRENT_PET_TARGET)
parent.ai_controller.CancelActions() // Stop whatever you're doing and do this instead
parent.ai_controller.set_blackboard_key(BB_ACTIVE_PET_COMMAND, src)
if (command_feedback)
parent.balloon_alert_to_viewers("[command_feedback]") // If we get a nicer runechat way to do this, refactor this
if(!radial_command)
return
if(!requires_pointing)
var/manual_emote_text = generate_emote_command()
commander.manual_emote(manual_emote_text)
return
RegisterSignal(commander, COMSIG_MOB_CLICKON, PROC_REF(click_on_target))
commander.client?.mouse_override_icon = 'icons/effects/mouse_pointers/pet_paw.dmi'
commander.update_mouse_pointer()
/datum/pet_command/proc/click_on_target(mob/living/source, atom/target, list/modifiers)
SIGNAL_HANDLER
if(!can_see(source, target, 9))
return COMSIG_MOB_CANCEL_CLICKON
on_target_set(source, target)
UnregisterSignal(source, COMSIG_MOB_CLICKON)
source.client?.mouse_override_icon = source.client::mouse_override_icon
source.update_mouse_pointer()
var/manual_emote_text = generate_emote_command(target)
if(!isnull(manual_emote_text))
INVOKE_ASYNC(source, TYPE_PROC_REF(/atom, manual_emote), manual_emote_text)
return COMSIG_MOB_CANCEL_CLICKON
/datum/pet_command/proc/point_on_target(mob/living/friend, atom/potential_target)
SIGNAL_HANDLER
on_target_set(friend, potential_target)
/// Store the target for the AI blackboard
/datum/pet_command/proc/set_command_target(mob/living/parent, atom/target)
@@ -158,11 +199,6 @@
var/datum/radial_menu_choice/choice = new()
choice.name = command_name
choice.image = icon(icon = radial_icon, icon_state = radial_icon_state)
var/tooltip = command_desc
if (length(speech_commands))
tooltip += "<br>Speak this command with the words [speech_commands.Join(", ")]."
choice.info = tooltip
return list("[command_name]" = choice)
/**
@@ -174,34 +210,15 @@
SHOULD_CALL_PARENT(FALSE)
CRASH("Pet command execute action not implemented.")
/**
* # Point Targeting Pet Command
* As above but also listens for you pointing at something and marks it as a target
*/
/datum/pet_command/point_targeting
/// Text describing an action we perform upon receiving a new target
var/pointed_reaction
/// Blackboard key for targeting strategy, this is likely going to need it
var/targeting_strategy_key = BB_PET_TARGETING_STRATEGY
/datum/pet_command/point_targeting/add_new_friend(mob/living/tamer)
. = ..()
RegisterSignal(tamer, COMSIG_MOVABLE_POINTED, PROC_REF(on_point))
/datum/pet_command/point_targeting/remove_friend(mob/living/unfriended)
. = ..()
UnregisterSignal(unfriended, COMSIG_MOVABLE_POINTED)
/// Target the pointed atom for actions
/datum/pet_command/point_targeting/proc/on_point(mob/living/friend, atom/pointed_atom, obj/effect/temp_visual/point/point)
SIGNAL_HANDLER
/datum/pet_command/proc/on_target_set(mob/living/friend, atom/potential_target)
var/mob/living/parent = weak_parent.resolve()
if (!parent)
return FALSE
return
parent.ai_controller.CancelActions()
if (look_for_target(friend, pointed_atom) && set_command_target(parent, pointed_atom))
parent.visible_message(span_warning("[parent] follows [friend]'s gesture towards [pointed_atom] [pointed_reaction]!"))
return TRUE
return FALSE
if(!look_for_target(friend, potential_target) || !set_command_target(parent, potential_target))
return
parent.visible_message(span_warning("[parent] follows [friend]'s gesture towards [potential_target] [pointed_reaction]!"))
@@ -7,14 +7,16 @@
/datum/pet_command/idle
command_name = "Stay"
command_desc = "Command your pet to stay idle in this location."
radial_icon = 'icons/obj/bed.dmi'
radial_icon_state = "dogbed"
radial_icon_state = "halt"
speech_commands = list("sit", "stay", "stop")
command_feedback = "sits"
/datum/pet_command/idle/execute_action(datum/ai_controller/controller)
return SUBTREE_RETURN_FINISH_PLANNING // This cancels further AI planning
/datum/pet_command/idle/retrieve_command_text(atom/living_pet, atom/target)
return "signals [living_pet] to stay idle!"
/**
* # Pet Command: Stop
* Tells a pet to exit command mode and resume its normal behaviour, which includes regular target-seeking and what have you
@@ -22,8 +24,7 @@
/datum/pet_command/free
command_name = "Loose"
command_desc = "Allow your pet to resume its natural behaviours."
radial_icon = 'icons/mob/actions/actions_spells.dmi'
radial_icon_state = "repulse"
radial_icon_state = "free"
speech_commands = list("free", "loose")
command_feedback = "relaxes"
@@ -31,6 +32,9 @@
controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND)
return // Just move on to the next planning subtree.
/datum/pet_command/free/retrieve_command_text(atom/living_pet, atom/target)
return "signals [living_pet] to go free!"
/**
* # Pet Command: Follow
* Tells a pet to follow you until you tell it to do something else
@@ -38,8 +42,7 @@
/datum/pet_command/follow
command_name = "Follow"
command_desc = "Command your pet to accompany you."
radial_icon = 'icons/testing/turf_analysis.dmi'
radial_icon_state = "red_arrow"
radial_icon_state = "follow"
speech_commands = list("heel", "follow")
callout_type = /datum/callout_option/move
///the behavior we use to follow
@@ -49,6 +52,9 @@
. = ..()
set_command_target(parent, commander)
/datum/pet_command/follow/retrieve_command_text(atom/living_pet, atom/target)
return "signals [living_pet] to follow!"
/datum/pet_command/follow/execute_action(datum/ai_controller/controller)
controller.queue_behavior(follow_behavior, BB_CURRENT_PET_TARGET)
return SUBTREE_RETURN_FINISH_PLANNING
@@ -60,14 +66,16 @@
/datum/pet_command/play_dead
command_name = "Play Dead"
command_desc = "Play a macabre trick."
radial_icon = 'icons/mob/simple/pets.dmi'
radial_icon_state = "puppy_dead"
radial_icon_state = "play_dead"
speech_commands = list("play dead") // Don't get too creative here, people talk about dying pretty often
/datum/pet_command/play_dead/execute_action(datum/ai_controller/controller)
controller.queue_behavior(/datum/ai_behavior/play_dead)
return SUBTREE_RETURN_FINISH_PLANNING
/datum/pet_command/play_dead/retrieve_command_text(atom/living_pet, atom/target)
return "signals [living_pet] to play dead!"
/**
* # Pet Command: Good Boy
* React if complimented
@@ -115,16 +123,18 @@
controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND)
return SUBTREE_RETURN_FINISH_PLANNING
/datum/pet_command/untargeted_ability/retrieve_command_text(atom/living_pet, atom/target)
return "signals [living_pet] to use an ability!"
/**
* # Pet Command: Attack
* Tells a pet to chase and bite the next thing you point at
*/
/datum/pet_command/point_targeting/attack
/datum/pet_command/attack
command_name = "Attack"
command_desc = "Command your pet to attack things that you point out to it."
radial_icon = 'icons/effects/effects.dmi'
radial_icon_state = "bite"
radial_icon_state = "attack"
requires_pointing = TRUE
callout_type = /datum/callout_option/attack
speech_commands = list("attack", "sic", "kill")
command_feedback = "growl"
@@ -135,7 +145,7 @@
var/attack_behaviour = /datum/ai_behavior/basic_melee_attack
// Refuse to target things we can't target, chiefly other friends
/datum/pet_command/point_targeting/attack/set_command_target(mob/living/parent, atom/target)
/datum/pet_command/attack/set_command_target(mob/living/parent, atom/target)
if (!target)
return
var/mob/living/living_parent = parent
@@ -149,28 +159,32 @@
return
return ..()
/datum/pet_command/attack/retrieve_command_text(atom/living_pet, atom/target)
return isnull(target) ? null : "signals [living_pet] to attack [target]!"
/// Display feedback about not targeting something
/datum/pet_command/point_targeting/attack/proc/refuse_target(mob/living/parent, atom/target)
/datum/pet_command/attack/proc/refuse_target(mob/living/parent, atom/target)
var/mob/living/living_parent = parent
living_parent.balloon_alert_to_viewers("[refuse_reaction]")
living_parent.visible_message(span_notice("[living_parent] refuses to attack [target]."))
/datum/pet_command/point_targeting/attack/execute_action(datum/ai_controller/controller)
/datum/pet_command/attack/execute_action(datum/ai_controller/controller)
controller.queue_behavior(attack_behaviour, BB_CURRENT_PET_TARGET, targeting_strategy_key)
return SUBTREE_RETURN_FINISH_PLANNING
/**
* # Breed command. breed with a partner!
*/
/datum/pet_command/point_targeting/breed
/datum/pet_command/breed
command_name = "Breed"
command_desc = "Command your pet to attempt to breed with a partner."
radial_icon = 'icons/mob/simple/animal.dmi'
radial_icon_state = "heart"
requires_pointing = TRUE
radial_icon_state = "breed"
speech_commands = list("breed", "consummate")
///the behavior we use to make babies
var/datum/ai_behavior/reproduce_behavior = /datum/ai_behavior/make_babies
/datum/pet_command/point_targeting/breed/set_command_target(mob/living/parent, atom/target)
/datum/pet_command/breed/set_command_target(mob/living/parent, atom/target)
if(isnull(target) || !isliving(target))
return
if(!HAS_TRAIT(parent, TRAIT_MOB_BREEDER) || !HAS_TRAIT(target, TRAIT_MOB_BREEDER))
@@ -184,21 +198,25 @@
return
return ..()
/datum/pet_command/point_targeting/breed/execute_action(datum/ai_controller/controller)
/datum/pet_command/breed/execute_action(datum/ai_controller/controller)
if(is_type_in_list(controller.blackboard[BB_CURRENT_PET_TARGET], controller.blackboard[BB_BABIES_PARTNER_TYPES]))
controller.queue_behavior(reproduce_behavior, BB_CURRENT_PET_TARGET)
controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND)
return SUBTREE_RETURN_FINISH_PLANNING
/datum/pet_command/breed/retrieve_command_text(atom/living_pet, atom/target)
return isnull(target) ? null : "signals [living_pet] to breed with [target]!"
/**
* # Pet Command: Targetted Ability
* Tells a pet to use some kind of ability on the next thing you point at
*/
/datum/pet_command/point_targeting/use_ability
/datum/pet_command/use_ability
command_name = "Use ability"
command_desc = "Command your pet to use one of its special skills on something that you point out to it."
radial_icon = 'icons/mob/actions/actions_spells.dmi'
radial_icon_state = "projectile"
requires_pointing = TRUE
speech_commands = list("shoot", "blast", "cast")
command_feedback = "growl"
pointed_reaction = "and growls"
@@ -207,7 +225,7 @@
/// The AI behavior to use for the ability
var/ability_behavior = /datum/ai_behavior/pet_use_ability
/datum/pet_command/point_targeting/use_ability/execute_action(datum/ai_controller/controller)
/datum/pet_command/use_ability/execute_action(datum/ai_controller/controller)
if (!pet_ability_key)
return
var/datum/action/cooldown/using_action = controller.blackboard[pet_ability_key]
@@ -218,6 +236,9 @@
controller.queue_behavior(ability_behavior, pet_ability_key, BB_CURRENT_PET_TARGET)
return SUBTREE_RETURN_FINISH_PLANNING
/datum/pet_command/use_ability/retrieve_command_text(atom/living_pet, atom/target)
return isnull(target) ? null : "signals [living_pet] to use an ability on [target]!"
/datum/pet_command/protect_owner
command_name = "Protect owner"
command_desc = "Your pet will run to your aid."
@@ -278,25 +299,39 @@
/**
* # Fish command: command the mob to fish at the next fishing spot you point at. Requires the profound fisher component
*/
/datum/pet_command/point_targeting/fish
/datum/pet_command/fish
command_name = "Fish"
command_desc = "Command your pet to try fishing at a nearby fishing spot."
radial_icon = 'icons/obj/aquarium/fish.dmi'
radial_icon_state = "goldfish"
requires_pointing = TRUE
radial_icon_state = "fish"
speech_commands = list("fish")
// Refuse to target things we can't target, chiefly other friends
/datum/pet_command/point_targeting/fish/set_command_target(mob/living/parent, atom/target)
if (!target)
return
if(!parent.ai_controller || !HAS_TRAIT(parent, TRAIT_PROFOUND_FISHER))
return
var/datum/targeting_strategy/targeter = GET_TARGETING_STRATEGY(/datum/targeting_strategy/fishing)
if (!targeter?.can_attack(parent, target))
parent.balloon_alert_to_viewers("shakes head!")
return
/datum/pet_command/fish/execute_action(datum/ai_controller/controller)
if(controller.blackboard_key_exists(BB_CURRENT_PET_TARGET))
controller.queue_behavior(/datum/ai_behavior/interact_with_target/fishing, BB_CURRENT_PET_TARGET)
return SUBTREE_RETURN_FINISH_PLANNING
/datum/pet_command/fish/retrieve_command_text(atom/living_pet, atom/target)
return "signals [living_pet] to go fish!"
/datum/pet_command/move
command_name = "Move"
command_desc = "Command your pet to move to a location!"
requires_pointing = TRUE
radial_icon_state = "move"
speech_commands = list("move", "walk")
///the behavior we use to walk towards targets
var/datum/ai_behavior/walk_behavior = /datum/ai_behavior/travel_towards
/datum/pet_command/move/set_command_target(mob/living/parent, atom/target)
if(isnull(target) || !can_see(parent, target, 9))
return FALSE
return ..()
/datum/pet_command/point_targeting/fish/execute_action(datum/ai_controller/controller)
controller.queue_behavior(/datum/ai_behavior/hunt_target/interact_with_target/reset_target_combat_mode_off, BB_CURRENT_PET_TARGET)
/datum/pet_command/move/execute_action(datum/ai_controller/controller)
if(controller.blackboard_key_exists(BB_CURRENT_PET_TARGET))
controller.queue_behavior(walk_behavior, BB_CURRENT_PET_TARGET)
return SUBTREE_RETURN_FINISH_PLANNING
/datum/pet_command/move/retrieve_command_text(atom/living_pet, atom/target)
return "signals [living_pet] to move!"
+2 -1
View File
@@ -90,7 +90,8 @@
source.ai_controller.set_blackboard_key(BB_CULT_TEAM, team)
var/static/list/new_pet_commands = list(
/datum/pet_command/point_targeting/attack,
/datum/pet_command/move,
/datum/pet_command/attack,
/datum/pet_command/follow,
/datum/pet_command/free,
/datum/pet_command/idle,
+2
View File
@@ -21,6 +21,8 @@
return SEND_SIGNAL(user.mob, keybind_signal) & COMSIG_KB_ACTIVATED
/datum/keybinding/proc/up(client/user)
SHOULD_CALL_PARENT(TRUE)
SEND_SIGNAL(user.mob, DEACTIVATE_KEYBIND(keybind_signal))
return FALSE
/datum/keybinding/proc/can_use(client/user)
+3
View File
@@ -46,6 +46,7 @@
return TRUE
/datum/keybinding/living/look_up/up(client/user)
. = ..()
var/mob/living/L = user.mob
L.end_look_up()
return TRUE
@@ -66,6 +67,7 @@
return TRUE
/datum/keybinding/living/look_down/up(client/user)
. = ..()
var/mob/living/L = user.mob
L.end_look_down()
return TRUE
@@ -144,6 +146,7 @@
return TRUE
/datum/keybinding/living/toggle_move_intent/up(client/user)
. = ..()
var/mob/living/M = user.mob
M.toggle_move_intent()
return TRUE
+7
View File
@@ -191,3 +191,10 @@
if(.)
return
user.movement_locked = FALSE
/datum/keybinding/living/view_pet_data
hotkey_keys = list("Shift")
name = "view_pet_commands"
full_name = "View Pet Commands"
description = "Hold down to see all the commands you can give your pets!"
keybind_signal = COMSIG_KB_LIVING_VIEW_PET_COMMANDS
+2
View File
@@ -864,6 +864,8 @@
if (isnull(user))
return
SEND_SIGNAL(user, COMSIG_ATOM_MOUSE_ENTERED, src)
// Screentips
var/datum/hud/active_hud = user.hud_used
if(!active_hud)
@@ -226,7 +226,7 @@
/datum/pet_command/idle,
/datum/pet_command/free,
/datum/pet_command/follow,
/datum/pet_command/point_targeting/attack/star_gazer
/datum/pet_command/attack/star_gazer
)
/datum/heretic_knowledge/ultimate/cosmic_final/is_valid_sacrifice(mob/living/carbon/human/sacrifice)
@@ -242,7 +242,7 @@
star_gazer_mob.maxHealth = INFINITY
star_gazer_mob.health = INFINITY
user.AddComponent(/datum/component/death_linked, star_gazer_mob)
star_gazer_mob.AddComponent(/datum/component/obeys_commands, star_gazer_commands)
star_gazer_mob.AddComponent(/datum/component/obeys_commands, star_gazer_commands, radial_menu_lifetime = 15 SECONDS, radial_relative_to_user = TRUE)
star_gazer_mob.AddComponent(/datum/component/damage_aura, range = 7, burn_damage = 0.5, simple_damage = 0.5, immune_factions = list(FACTION_HERETIC), current_owner = user)
star_gazer_mob.befriend(user)
var/datum/action/cooldown/open_mob_commands/commands_action = new /datum/action/cooldown/open_mob_commands()
@@ -127,7 +127,7 @@
var/static/list/pet_commands = list(
/datum/pet_command/idle,
/datum/pet_command/free,
/datum/pet_command/point_targeting/clean,
/datum/pet_command/clean,
)
/mob/living/basic/bot/cleanbot/Initialize(mapload)
@@ -200,14 +200,15 @@
return
return ..()
/datum/pet_command/point_targeting/clean
/datum/pet_command/clean
command_name = "Clean"
command_desc = "Command a cleanbot to clean the mess."
requires_pointing = TRUE
radial_icon = 'icons/obj/service/janitor.dmi'
radial_icon_state = "mop"
speech_commands = list("clean", "mop")
/datum/pet_command/point_targeting/clean/set_command_target(mob/living/parent, atom/target)
/datum/pet_command/clean/set_command_target(mob/living/parent, atom/target)
if(isnull(target) || !istype(target, /obj/effect/decal/cleanable))
return
if(isnull(parent.ai_controller))
@@ -216,7 +217,7 @@
return
return ..()
/datum/pet_command/point_targeting/clean/execute_action(datum/ai_controller/basic_controller/bot/controller)
/datum/pet_command/clean/execute_action(datum/ai_controller/basic_controller/bot/controller)
if(controller.blackboard_key_exists(BB_CURRENT_PET_TARGET))
controller.queue_behavior(/datum/ai_behavior/execute_clean, BB_CURRENT_PET_TARGET)
return SUBTREE_RETURN_FINISH_PLANNING
@@ -57,7 +57,7 @@
/datum/pet_command/beehive/enter,
/datum/pet_command/beehive/exit,
/datum/pet_command/follow/bee,
/datum/pet_command/point_targeting/attack/swirl,
/datum/pet_command/attack/swirl,
/datum/pet_command/scatter,
)
@@ -113,8 +113,9 @@
required_distance = 0
///swirl around the owner in menacing fashion
/datum/pet_command/point_targeting/attack/swirl
/datum/pet_command/attack/swirl
command_name = "Swirl"
requires_pointing = TRUE
command_desc = "Your pets will swirl around you and attack whoever you point at!"
speech_commands = list("swirl", "spiral", "swarm")
pointed_reaction = null
@@ -123,7 +124,7 @@
///the owner we will swarm around
var/key_to_swarm = BB_SWARM_TARGET
/datum/pet_command/point_targeting/attack/swirl/try_activate_command(mob/living/commander)
/datum/pet_command/attack/swirl/try_activate_command(mob/living/commander, radial_command)
var/mob/living/living_pawn = weak_parent.resolve()
if(isnull(living_pawn))
return
@@ -134,7 +135,7 @@
controller.set_blackboard_key(key_to_swarm, commander)
return ..()
/datum/pet_command/point_targeting/attack/swirl/execute_action(datum/ai_controller/controller)
/datum/pet_command/attack/swirl/execute_action(datum/ai_controller/controller)
if(controller.blackboard_key_exists(BB_CURRENT_PET_TARGET))
return ..()
controller.queue_behavior(/datum/ai_behavior/swirl_around_target, BB_SWARM_TARGET)
@@ -186,7 +187,7 @@
radial_icon = 'icons/obj/service/hydroponics/equipment.dmi'
radial_icon_state = "beebox"
/datum/pet_command/beehive/try_activate_command(mob/living/commander)
/datum/pet_command/beehive/try_activate_command(mob/living/commander, radial_command)
var/mob/living/living_pawn = weak_parent.resolve()
if(isnull(living_pawn))
return
@@ -103,7 +103,7 @@
can_attack_turfs = TRUE
can_attack_dense_objects = TRUE
/datum/pet_command/point_targeting/attack/star_gazer
/datum/pet_command/attack/star_gazer
speech_commands = list("attack", "sic", "kill", "slash them")
command_feedback = "stares!"
pointed_reaction = "stares intensely!"
@@ -43,11 +43,12 @@
//commands to give when tamed
var/static/list/pet_commands = list(
/datum/pet_command/idle,
/datum/pet_command/move,
/datum/pet_command/free,
/datum/pet_command/good_boy/wolf,
/datum/pet_command/follow/wolf,
/datum/pet_command/point_targeting/attack,
/datum/pet_command/point_targeting/fetch,
/datum/pet_command/attack,
/datum/pet_command/fetch,
/datum/pet_command/play_dead,
/datum/pet_command/protect_owner,
)
@@ -40,13 +40,14 @@
///list of pet commands we can issue
var/list/pet_commands = list(
/datum/pet_command/idle,
/datum/pet_command/move,
/datum/pet_command/free,
/datum/pet_command/follow,
/datum/pet_command/untargeted_ability/blood_rain,
/datum/pet_command/untargeted_ability/summon_toad,
/datum/pet_command/point_targeting/attack,
/datum/pet_command/point_targeting/use_ability/flop,
/datum/pet_command/point_targeting/use_ability/bubble,
/datum/pet_command/attack,
/datum/pet_command/use_ability/flop,
/datum/pet_command/use_ability/bubble,
)
/mob/living/basic/leaper/Initialize(mapload)
@@ -39,7 +39,7 @@
ability_key = BB_LEAPER_SUMMON
finish_planning = FALSE
/datum/pet_command/point_targeting/use_ability/flop
/datum/pet_command/use_ability/flop
command_name = "Flop"
command_desc = "Command your pet to belly flop your target!"
radial_icon = 'icons/mob/actions/actions_items.dmi'
@@ -47,7 +47,7 @@
speech_commands = list("flop", "crush")
pet_ability_key = BB_LEAPER_FLOP
/datum/pet_command/point_targeting/use_ability/bubble
/datum/pet_command/use_ability/bubble
command_name = "Poison Bubble"
command_desc = "Launch poisonous bubbles at your target!"
radial_icon = 'icons/obj/weapons/guns/projectiles.dmi'
@@ -55,6 +55,9 @@
speech_commands = list("bubble", "shoot")
pet_ability_key = BB_LEAPER_BUBBLE
/datum/pet_command/use_ability/bubble/retrieve_command_text(atom/living_pet, atom/target)
return isnull(target) ? null : "signals [living_pet] to shoot a bubble towards [target]!"
/datum/pet_command/untargeted_ability/blood_rain
command_name = "Blood Rain"
command_desc = "Let it rain poisonous blood!"
@@ -63,6 +66,8 @@
speech_commands = list("blood", "rain", "volley")
ability_key = BB_LEAPER_VOLLEY
/datum/pet_command/untargeted_ability/blood_rain/retrieve_command_text(atom/living_pet, atom/target)
return "signals [living_pet] to unleash a volley of rain!"
/datum/pet_command/untargeted_ability/summon_toad
command_name = "Summon Toads"
@@ -71,3 +76,6 @@
radial_icon_state = "frog_trash"
speech_commands = list("frogs", "bombers")
ability_key = BB_LEAPER_SUMMON
/datum/pet_command/untargeted_ability/summon_toad/retrieve_command_text(atom/living_pet, atom/target)
return "signals [living_pet] to summon some explosive frogs!"
@@ -214,9 +214,9 @@
/datum/pet_command/idle,
/datum/pet_command/free,
/datum/pet_command/follow,
/datum/pet_command/point_targeting/attack,
/datum/pet_command/point_targeting/use_ability/solarbeam,
/datum/pet_command/point_targeting/use_ability/rapidseeds,
/datum/pet_command/attack,
/datum/pet_command/use_ability/solarbeam,
/datum/pet_command/use_ability/rapidseeds,
)
//abilities
@@ -166,7 +166,7 @@
finish_planning = FALSE
///pet commands
/datum/pet_command/point_targeting/use_ability/solarbeam
/datum/pet_command/use_ability/solarbeam
command_name = "Launch solarbeam"
command_desc = "Command your pet to launch a solarbeam at your target!"
radial_icon = 'icons/effects/beam.dmi'
@@ -174,10 +174,17 @@
speech_commands = list("beam", "solar")
pet_ability_key = BB_SOLARBEAM_ABILITY
/datum/pet_command/point_targeting/use_ability/rapidseeds
/datum/pet_command/use_ability/solarbeam/retrieve_command_text(atom/living_pet, atom/target)
return isnull(target) ? null : "signals [living_pet] to use a solar beam on [target]!"
/datum/pet_command/use_ability/rapidseeds
command_name = "Rapid seeds"
command_desc = "Command your pet to launch a volley of seeds at your target!"
radial_icon = 'icons/obj/weapons/guns/projectiles.dmi'
radial_icon_state = "seedling"
speech_commands = list("rapid", "seeds", "volley")
pet_ability_key = BB_RAPIDSEEDS_ABILITY
/datum/pet_command/use_ability/rapidseeds/retrieve_command_text(atom/living_pet, atom/target)
return isnull(target) ? null : "signals [living_pet] to unleash a volley of seeds on [target]!"
@@ -36,7 +36,7 @@
/datum/pet_command/free,
/datum/pet_command/grub_spit,
/datum/pet_command/follow,
/datum/pet_command/point_targeting/fetch,
/datum/pet_command/fetch,
)
/mob/living/basic/mining/goldgrub/Initialize(mapload)
@@ -211,6 +211,8 @@
/datum/pet_command/grub_spit
command_name = "Spit"
radial_icon = 'icons/obj/ore.dmi'
radial_icon_state = "uranium"
command_desc = "Ask your grub pet to spit out its ores."
speech_commands = list("spit", "ores")
@@ -222,4 +224,7 @@
controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND)
return SUBTREE_RETURN_FINISH_PLANNING
/datum/pet_command/grub_spit/retrieve_command_text(atom/living_pet, atom/target)
return "signals [living_pet] to spit its ores!"
#undef BURROW_RANGE
@@ -61,7 +61,7 @@
if(isnull(ore_food))
balloon_alert(src, "no food!")
else
melee_attack(ore_food)
UnarmedAttack(ore_food, TRUE, modifiers)
return FALSE
/mob/living/basic/mining/gutlunch/proc/after_birth(mob/living/basic/mining/gutlunch/grub/baby, mob/living/partner)
@@ -111,11 +111,12 @@
//pet commands when we tame the gutluncher
var/static/list/pet_commands = list(
/datum/pet_command/idle,
/datum/pet_command/move,
/datum/pet_command/free,
/datum/pet_command/point_targeting/attack,
/datum/pet_command/point_targeting/breed/gutlunch,
/datum/pet_command/attack,
/datum/pet_command/breed/gutlunch,
/datum/pet_command/follow,
/datum/pet_command/point_targeting/fetch,
/datum/pet_command/fetch,
/datum/pet_command/mine_walls,
)
@@ -102,10 +102,11 @@
/datum/pet_command/mine_walls
command_name = "Mine"
radial_icon_state = "mine"
command_desc = "Command your pet to mine down walls."
speech_commands = list("mine", "smash")
/datum/pet_command/mine_walls/try_activate_command(mob/living/commander)
/datum/pet_command/mine_walls/try_activate_command(mob/living/commander, radial_command)
var/mob/living/parent = weak_parent.resolve()
if(isnull(parent))
return
@@ -121,10 +122,13 @@
return SUBTREE_RETURN_FINISH_PLANNING
controller.queue_behavior(/datum/ai_behavior/find_mineral_wall, BB_CURRENT_PET_TARGET)
//pet commands
/datum/pet_command/point_targeting/breed/gutlunch
/datum/pet_command/mine_walls/retrieve_command_text(atom/living_pet, atom/target)
return "signals [living_pet] to start mining!"
/datum/pet_command/point_targeting/breed/gutlunch/set_command_target(mob/living/parent, atom/target)
//pet commands
/datum/pet_command/breed/gutlunch
/datum/pet_command/breed/gutlunch/set_command_target(mob/living/parent, atom/target)
if(GLOB.gutlunch_count >= MAXIMUM_GUTLUNCH_POP)
parent.balloon_alert_to_viewers("can't reproduce anymore!")
return
@@ -32,7 +32,7 @@
/// The type of charging ability we give this mob
var/charge_type = /datum/action/cooldown/mob_cooldown/charge/basic_charge/lobster
/// The pet command for the charging ability we give this mob
var/charge_command = /datum/pet_command/point_targeting/use_ability/lob_charge
var/charge_command = /datum/pet_command/use_ability/lob_charge
/// At which speed do we amputate limbs
var/snip_speed = 5 SECONDS
///Lobstrosities are natural anglers. This rapresent their proficiency at fishing when not mindless
@@ -72,10 +72,11 @@
var/list/pet_commands = list(
/datum/pet_command/idle,
/datum/pet_command/free,
/datum/pet_command/point_targeting/attack,
/datum/pet_command/move,
/datum/pet_command/attack,
charge_command,
/datum/pet_command/follow,
/datum/pet_command/point_targeting/fish,
/datum/pet_command/fish,
)
AddComponent(/datum/component/happiness)
AddComponent(/datum/component/obeys_commands, pet_commands)
@@ -153,7 +154,7 @@
ai_controller = /datum/ai_controller/basic_controller/lobstrosity/juvenile
snip_speed = 6.5 SECONDS
charge_type = /datum/action/cooldown/mob_cooldown/charge/basic_charge/lobster/shrimp
charge_command = /datum/pet_command/point_targeting/use_ability/lob_charge/shrimp
charge_command = /datum/pet_command/use_ability/lob_charge/shrimp
base_fishing_level = SKILL_LEVEL_NOVICE
/// What do we become when we grow up?
var/mob/living/basic/mining/lobstrosity/grow_type = /mob/living/basic/mining/lobstrosity
@@ -254,7 +255,7 @@
charger.apply_status_effect(/datum/status_effect/tired_post_charge/lesser)
///Command the lobster to charge at someone.
/datum/pet_command/point_targeting/use_ability/lob_charge
/datum/pet_command/use_ability/lob_charge
command_name = "Charge"
command_desc = "Command your lobstrosity to charge against someone."
radial_icon = 'icons/mob/actions/actions_items.dmi'
@@ -265,7 +266,7 @@
pet_ability_key = BB_TARGETED_ACTION
ability_behavior = /datum/ai_behavior/pet_use_ability/then_attack/long_ranged
/datum/pet_command/point_targeting/use_ability/lob_charge/set_command_target(mob/living/parent, atom/target)
/datum/pet_command/use_ability/lob_charge/set_command_target(mob/living/parent, atom/target)
if (!target)
return
var/datum/targeting_strategy/targeter = GET_TARGETING_STRATEGY(parent.ai_controller.blackboard[targeting_strategy_key])
@@ -274,5 +275,5 @@
return FALSE
return ..()
/datum/pet_command/point_targeting/use_ability/lob_charge/shrimp
/datum/pet_command/use_ability/lob_charge/shrimp
ability_behavior = /datum/ai_behavior/pet_use_ability/then_attack/short_ranged
@@ -41,8 +41,8 @@
var/list/pet_commands = list(
/datum/pet_command/idle,
/datum/pet_command/free,
/datum/pet_command/point_targeting/attack,
/datum/pet_command/point_targeting/fetch,
/datum/pet_command/attack,
/datum/pet_command/fetch,
)
/mob/living/basic/mining/mook/Initialize(mapload)
@@ -1,7 +1,7 @@
///commands the chief can pick from
GLOBAL_LIST_INIT(mook_commands, list(
new /datum/pet_command/point_targeting/attack,
new /datum/pet_command/point_targeting/fetch,
new /datum/pet_command/attack,
new /datum/pet_command/fetch,
))
/datum/ai_controller/basic_controller/mook
@@ -346,7 +346,7 @@ GLOBAL_LIST_INIT(mook_commands, list(
if(!locate(/mob/living/basic/mining/mook) in oview(command_distance, controller.pawn))
return
if(controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET))
controller.queue_behavior(/datum/ai_behavior/issue_commands, BB_BASIC_MOB_CURRENT_TARGET, /datum/pet_command/point_targeting/attack)
controller.queue_behavior(/datum/ai_behavior/issue_commands, BB_BASIC_MOB_CURRENT_TARGET, /datum/pet_command/attack)
return
var/atom/ore_target = controller.blackboard[BB_ORE_TARGET]
@@ -356,7 +356,7 @@ GLOBAL_LIST_INIT(mook_commands, list(
if(get_dist(ore_target, living_pawn) <= 1)
return
controller.queue_behavior(/datum/ai_behavior/issue_commands, BB_ORE_TARGET, /datum/pet_command/point_targeting/fetch)
controller.queue_behavior(/datum/ai_behavior/issue_commands, BB_ORE_TARGET, /datum/pet_command/fetch)
/datum/ai_behavior/issue_commands
action_cooldown = 5 SECONDS
@@ -47,11 +47,13 @@ GLOBAL_LIST_EMPTY(raptor_population)
var/ridable_component = /datum/component/riding/creature/raptor
//pet commands when we tame the raptor
var/static/list/pet_commands = list(
/datum/pet_command/breed,
/datum/pet_command/idle,
/datum/pet_command/move,
/datum/pet_command/free,
/datum/pet_command/point_targeting/attack,
/datum/pet_command/attack,
/datum/pet_command/follow,
/datum/pet_command/point_targeting/fetch,
/datum/pet_command/fetch,
)
///things we inherited from our parent
var/datum/raptor_inheritance/inherited_stats
@@ -158,7 +160,7 @@ GLOBAL_LIST_EMPTY(raptor_population)
if(isnull(ore_food))
balloon_alert(src, "no food!")
else
melee_attack(ore_food)
UnarmedAttack(ore_food, TRUE, modifiers)
return FALSE
/mob/living/basic/raptor/melee_attack(mob/living/target, list/modifiers, ignore_cooldown)
@@ -42,13 +42,14 @@
///the commands our owner can give us
var/static/list/pet_commands = list(
/datum/pet_command/idle/minebot,
/datum/pet_command/move,
/datum/pet_command/protect_owner/minebot,
/datum/pet_command/minebot_ability/light,
/datum/pet_command/minebot_ability/dump,
/datum/pet_command/automate_mining,
/datum/pet_command/free/minebot,
/datum/pet_command/follow,
/datum/pet_command/point_targeting/attack/minebot,
/datum/pet_command/attack/minebot,
)
///possible colors the bot can have
var/static/list/possible_colors= list(
@@ -281,14 +281,16 @@
/datum/pet_command/automate_mining
command_name = "Automate mining"
command_desc = "Make your minebot automatically mine!"
radial_icon = 'icons/obj/mining.dmi'
radial_icon_state = "pickaxe"
radial_icon_state = "mine"
speech_commands = list("mine")
callout_type = /datum/callout_option/mine
/datum/pet_command/automate_mining/valid_callout_target(mob/living/caller, datum/callout_option/callout, atom/target)
return ismineralturf(target)
/datum/pet_command/automate_mining/retrieve_command_text(atom/living_pet, atom/target)
return "signals [living_pet] to start mining!"
/datum/pet_command/automate_mining/execute_action(datum/ai_controller/controller)
controller.set_blackboard_key(BB_AUTOMATED_MINING, TRUE)
controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND)
@@ -315,6 +317,9 @@
radial_icon_state = "mech_lights_off"
ability_key = BB_MINEBOT_LIGHT_ABILITY
/datum/pet_command/minebot_ability/light/retrieve_command_text(atom/living_pet, atom/target)
return "signals [living_pet] to toggle its lights!"
/datum/pet_command/minebot_ability/dump
command_name = "Dump ore"
command_desc = "Make your minebot dump all its ore!"
@@ -322,10 +327,13 @@
radial_icon_state = "mech_eject"
ability_key = BB_MINEBOT_DUMP_ABILITY
/datum/pet_command/point_targeting/attack/minebot
/datum/pet_command/minebot_ability/light/retrieve_command_text(atom/living_pet, atom/target)
return "signals [living_pet] to dump its ore!"
/datum/pet_command/attack/minebot
attack_behaviour = /datum/ai_behavior/basic_ranged_attack/minebot
/datum/pet_command/point_targeting/attack/minebot/execute_action(datum/ai_controller/controller)
/datum/pet_command/attack/minebot/execute_action(datum/ai_controller/controller)
controller.set_blackboard_key(BB_AUTOMATED_MINING, FALSE)
var/mob/living/living_pawn = controller.pawn
if(!living_pawn.combat_mode)
@@ -7,10 +7,10 @@
speech_commands = list("good dog")
// Set correct attack behaviour
/datum/pet_command/point_targeting/attack/dog
/datum/pet_command/attack/dog
attack_behaviour = /datum/ai_behavior/basic_melee_attack/dog
/datum/pet_command/point_targeting/attack/dog/set_command_active(mob/living/parent, mob/living/commander)
/datum/pet_command/attack/dog/set_command_active(mob/living/parent, mob/living/commander)
. = ..()
parent.ai_controller.set_blackboard_key(BB_DOG_HARASS_HARM, TRUE)
@@ -38,11 +38,12 @@
var/static/list/pet_commands = list(
/datum/pet_command/idle,
/datum/pet_command/free,
/datum/pet_command/move,
/datum/pet_command/good_boy/dog,
/datum/pet_command/follow/dog,
/datum/pet_command/perform_trick_sequence,
/datum/pet_command/point_targeting/attack/dog,
/datum/pet_command/point_targeting/fetch,
/datum/pet_command/attack/dog,
/datum/pet_command/fetch,
/datum/pet_command/play_dead,
)
///icon state of the collar we can wear
+2 -1
View File
@@ -31,9 +31,10 @@
///list of our pet commands we follow
var/static/list/pet_commands = list(
/datum/pet_command/idle,
/datum/pet_command/move,
/datum/pet_command/free,
/datum/pet_command/follow,
/datum/pet_command/point_targeting/attack,
/datum/pet_command/attack,
/datum/pet_command/perform_trick_sequence,
)
@@ -39,8 +39,9 @@
var/static/list/pet_commands = list(
/datum/pet_command/idle,
/datum/pet_command/free,
/datum/pet_command/move,
/datum/pet_command/untargeted_ability/pet_lights,
/datum/pet_command/point_targeting/use_ability/take_photo,
/datum/pet_command/use_ability/take_photo,
/datum/pet_command/follow/orbie,
/datum/pet_command/perform_trick_sequence,
)
@@ -123,17 +123,24 @@
return SUBTREE_RETURN_FINISH_PLANNING
return ..()
/datum/pet_command/point_targeting/use_ability/take_photo
/datum/pet_command/use_ability/pet_lights/retrieve_command_text(atom/living_pet, atom/target)
return "signals [living_pet] to toggle its lights!"
/datum/pet_command/use_ability/take_photo
command_name = "Photo"
command_desc = "Make your pet take a photo!"
radial_icon = 'icons/mob/simple/pets.dmi'
radial_icon_state = "orbie_lights_action"
radial_icon = 'icons/obj/art/camera.dmi'
radial_icon_state = "camera"
speech_commands = list("photo", "picture", "image")
command_feedback = "Readys camera mode"
pet_ability_key = BB_PHOTO_ABILITY
targeting_strategy_key = BB_TARGETING_STRATEGY
/datum/pet_command/point_targeting/use_ability/take_photo/execute_action(datum/ai_controller/controller)
/datum/pet_command/use_ability/take_photo/retrieve_command_text(atom/living_pet, atom/target)
return isnull(target) ? null : "signals [living_pet] to take a photo of [target]!"
/datum/pet_command/use_ability/take_photo/execute_action(datum/ai_controller/controller)
if(controller.blackboard[BB_VIRTUAL_PET_LEVEL] < 3)
controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND)
return SUBTREE_RETURN_FINISH_PLANNING
@@ -152,6 +159,9 @@
return FALSE
return findtext(spoken_text, text_command)
/datum/pet_command/perform_trick_sequence/light/retrieve_command_text(atom/living_pet, atom/target)
return "signals [living_pet] to dance!"
/datum/pet_command/perform_trick_sequence/execute_action(datum/ai_controller/controller)
var/mob/living/living_pawn = controller.pawn
var/list/trick_sequence = controller.blackboard[BB_TRICK_SEQUENCE]
@@ -236,3 +236,6 @@
radial_icon_state = "1"
speech_commands = list("rune", "revival")
ability_key = BB_RUNE_ABILITY
/datum/pet_command/untargeted_ability/draw_rune/retrieve_command_text(atom/living_pet, atom/target)
return "signals [living_pet] to draw a rune!"
@@ -1,4 +1,4 @@
/datum/pet_command/point_targeting/attack/slime
/datum/pet_command/attack/slime
speech_commands = list("attack", "sic", "kill", "eat", "feed")
command_feedback = "blorbles"
pointed_reaction = "and blorbles"
@@ -6,7 +6,7 @@
var/hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/slime
/datum/pet_command/point_targeting/attack/slime/execute_action(datum/ai_controller/controller)
/datum/pet_command/attack/slime/execute_action(datum/ai_controller/controller)
var/mob/living/basic/slime/slime_pawn = controller.pawn
if(isslime(slime_pawn) && slime_pawn.can_feed_on(controller.blackboard[BB_CURRENT_PET_TARGET], check_friendship = TRUE))
+1 -1
View File
@@ -97,7 +97,7 @@
/datum/pet_command/idle,
/datum/pet_command/free,
/datum/pet_command/follow,
/datum/pet_command/point_targeting/attack/slime,
/datum/pet_command/attack/slime,
)
/// Our evolve action
@@ -59,7 +59,7 @@
/datum/pet_command/idle,
/datum/pet_command/free,
/datum/pet_command/follow,
/datum/pet_command/point_targeting/attack
/datum/pet_command/attack
)
/// Carp want to eat raw meat
var/static/list/desired_food = list(/obj/item/food/meat/slab, /obj/item/food/meat/rawcutlet)
@@ -1,6 +1,6 @@
#define MAGICARP_SPELL_TARGET_SEEK_RANGE 4
/datum/pet_command/point_targeting/use_ability/magicarp
/datum/pet_command/use_ability/magicarp
pet_ability_key = BB_MAGICARP_SPELL
/datum/ai_planning_subtree/attack_obstacle_in_path/carp
@@ -56,8 +56,8 @@ GLOBAL_LIST_INIT(magicarp_spell_colours, list(
/datum/pet_command/idle,
/datum/pet_command/free,
/datum/pet_command/follow,
/datum/pet_command/point_targeting/attack,
/datum/pet_command/point_targeting/use_ability/magicarp,
/datum/pet_command/attack,
/datum/pet_command/use_ability/magicarp,
)
/// List of all projectiles we can fire.
/// Non-static, because subtypes can have their own lists.
@@ -65,7 +65,7 @@
/datum/pet_command/free,
/datum/pet_command/protect_owner,
/datum/pet_command/follow,
/datum/pet_command/point_targeting/attack/mouse
/datum/pet_command/attack/mouse
)
/// Commands you can give to glockroaches
var/static/list/glockroach_commands = list(
@@ -73,7 +73,7 @@
/datum/pet_command/free,
/datum/pet_command/protect_owner/glockroach,
/datum/pet_command/follow,
/datum/pet_command/point_targeting/attack/glockroach
/datum/pet_command/attack/glockroach
)
/datum/action/cooldown/mob_cooldown/riot/IsAvailable(feedback = FALSE)
@@ -211,7 +211,7 @@
return TRUE
// Command you can give to a mouse to make it kill someone
/datum/pet_command/point_targeting/attack/mouse
/datum/pet_command/attack/mouse
speech_commands = list("attack", "sic", "kill", "cheese em")
command_feedback = "squeak!" // Frogs and roaches can squeak too it's fine
pointed_reaction = "and squeaks aggressively"
@@ -219,7 +219,7 @@
attack_behaviour = /datum/ai_behavior/basic_melee_attack
// Command you can give to a mouse to make it kill someone
/datum/pet_command/point_targeting/attack/glockroach
/datum/pet_command/attack/glockroach
speech_commands = list("attack", "sic", "kill", "cheese em")
command_feedback = "squeak!"
pointed_reaction = "and cocks its gun"
Binary file not shown.

After

Width:  |  Height:  |  Size: 372 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

+1
View File
@@ -187,6 +187,7 @@
#include "code\__DEFINES\pronouns.dm"
#include "code\__DEFINES\qdel.dm"
#include "code\__DEFINES\quirks.dm"
#include "code\__DEFINES\radial_defines.dm"
#include "code\__DEFINES\radiation.dm"
#include "code\__DEFINES\radio.dm"
#include "code\__DEFINES\radioactive_nebula.dm"