Files
Bubberstation/code/datums/components/pet_commands/pet_command.dm
Waterpig bb70889f6e TG Upstream Part 1
3591 individual conflicts

Update build.js

Update install_node.sh

Update byond.js

oh my fucking god

hat

slow

huh

holy shit

we all fall down

2 more I missed

2900 individual conflicts

2700 Individual conflicts

replaces yarn file with tg version, bumping us down to 2200-ish

Down to 2000 individual conflicts

140 down

mmm

aaaaaaaaaaaaaaaaaaa

not yt

575

soon

900 individual conflicts

600 individual conflicts, 121 file conflicts

im not okay

160 across 19 files

29 in 4 files

0 conflicts, compiletime fix time

some minor incap stuff

missed ticks

weird dupe definition stuff

missed ticks 2

incap fixes

undefs and pie fix

Radio update and some extra minor stuff

returns a single override

no more dupe definitions, 175 compiletime errors

Unticked file fix

sound and emote stuff

honk and more radio stuff
2024-10-19 08:04:33 -07:00

208 lines
7.6 KiB
Plaintext

/**
* # Pet Command
* Set some AI blackboard commands in response to receiving instructions
* This is abstract and should be extended for actual behaviour
*/
/datum/pet_command
/// Weak reference to who follows this command
var/datum/weakref/weak_parent
/// Unique name used for radial selection, should not be shared with other commands on one mob
var/command_name
/// Description to display in radial menu
var/command_desc
/// 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
/// Icon state to display in radial menu
var/radial_icon_state
/// Speech strings to listen out for
var/list/speech_commands = list()
/// Callout that triggers this command
var/callout_type
/// Shown above the mob's head when it hears you
var/command_feedback
/// How close a mob needs to be to a target to respond to a command
var/sense_radius = 7
/datum/pet_command/New(mob/living/parent)
. = ..()
weak_parent = WEAKREF(parent)
/// Register a new guy we want to listen to
/datum/pet_command/proc/add_new_friend(mob/living/tamer)
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))
/// 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))
/// 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)
SIGNAL_HANDLER
if(mute_type == MUTE_IC && find_command_in_text(last_message, check_verbosity = TRUE))
return WAIVE_AUTOMUTE_CHECK
return NONE
/// 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
if (!can_see(parent, speaker, sense_radius)) // Basically the same rules as hearing
return
var/spoken_text = speech_args[SPEECH_MESSAGE]
if (!find_command_in_text(spoken_text))
return
try_activate_command(speaker)
/// Respond to a callout
/datum/pet_command/proc/respond_to_callout(mob/living/caller, datum/callout_option/callout, atom/target)
SIGNAL_HANDLER
if (isnull(callout_type) || !ispath(callout, callout_type))
return
var/mob/living/parent = weak_parent.resolve()
if (!parent)
return
if (!valid_callout_target(caller, callout, target))
var/found_new_target = FALSE
for (var/atom/new_target in range(2, target))
if (valid_callout_target(caller, callout, new_target))
target = new_target
found_new_target = TRUE
if (!found_new_target)
return
if (try_activate_command(caller))
look_for_target(parent, target)
/// Does this callout with this target trigger this command?
/datum/pet_command/proc/valid_callout_target(mob/living/caller, datum/callout_option/callout, atom/target)
return TRUE
/**
* Returns true if we find any of our spoken commands in the text.
* if check_verbosity is true, skip the match if there spoken_text is way longer than the match
*/
/datum/pet_command/proc/find_command_in_text(spoken_text, check_verbosity = FALSE)
for (var/command as anything in speech_commands)
if (!findtext(spoken_text, command))
continue
if(check_verbosity && length(spoken_text) > length(command) + MAX_NAME_LEN)
continue
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)
var/mob/living/parent = weak_parent.resolve()
if (!parent)
return FALSE
if (!parent.ai_controller) // We stopped having a brain at some point
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)
var/mob/living/parent = weak_parent.resolve()
if (!parent)
return FALSE
if (!parent.ai_controller)
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))
return FALSE
parent.ai_controller.CancelActions()
set_command_target(parent, pointed_atom)
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)
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
/// Store the target for the AI blackboard
/datum/pet_command/proc/set_command_target(mob/living/parent, atom/target)
parent.ai_controller.set_blackboard_key(BB_CURRENT_PET_TARGET, target)
return TRUE
/// Provide information about how to display this command in a radial menu
/datum/pet_command/proc/provide_radial_data()
if (hidden)
return
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)
/**
* Execute an AI action on the provided controller, what we should actually do when this command is active.
* This should basically always be called from a planning subtree which passes its own controller.
* Return SUBTREE_RETURN_FINISH_PLANNING to pass that instruction on to the controller, or don't if you don't want that.
*/
/datum/pet_command/proc/execute_action(datum/ai_controller/controller)
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
var/mob/living/parent = weak_parent.resolve()
if (!parent)
return FALSE
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