mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-19 06:03:14 +00:00
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
208 lines
7.6 KiB
Plaintext
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
|