diff --git a/code/datums/components/pet_commands/pet_command.dm b/code/datums/components/pet_commands/pet_command.dm
index aa8ff403c95..a3adcd53a12 100644
--- a/code/datums/components/pet_commands/pet_command.dm
+++ b/code/datums/components/pet_commands/pet_command.dm
@@ -15,7 +15,7 @@
/// If true, command will not appear in radial menu and can only be accessed through speech
var/hidden = FALSE
/// Speech strings to listen out for
- var/list/speech_commands = list()
+ VAR_PROTECTED/list/speech_commands = list()
/// 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
@@ -26,10 +26,14 @@
var/targeting_strategy_key = BB_PET_TARGETING_STRATEGY
/// our pointed reaction we play
var/pointed_reaction
+ /// The regex for finding our commands in speech
+ VAR_PRIVATE/regex/command_regex
/datum/pet_command/New(mob/living/parent)
. = ..()
parent_uid = parent.UID()
+ if(length(speech_commands))
+ command_regex = regex("\\b([speech_commands.Join("|")])\\b", "i")
/// Register a new guy we want to listen to
/datum/pet_command/proc/add_new_friend(mob/living/tamer)
@@ -73,11 +77,8 @@
* 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
+ var/valid_length = check_verbosity ? length(spoken_text) > MAX_NAME_LEN : TRUE
+ if(command_regex.Find(spoken_text) && valid_length)
return TRUE
return FALSE
@@ -173,7 +174,6 @@
parent.ai_controller.cancel_actions()
if(!look_for_target(friend, potential_target) || !set_command_target(parent, potential_target))
return FALSE
- parent.visible_message("[parent] follows [friend]'s gesture towards [potential_target] [pointed_reaction]!")
+ var/suffix = pointed_reaction ? " [pointed_reaction]" : ""
+ parent.visible_message("[parent] follows [friend]'s gesture towards [potential_target][suffix]!")
return TRUE
-
-
diff --git a/code/datums/components/pet_commands/pet_commands_basic.dm b/code/datums/components/pet_commands/pet_commands_basic.dm
index 1003deb3c5e..3017cd731e9 100644
--- a/code/datums/components/pet_commands/pet_commands_basic.dm
+++ b/code/datums/components/pet_commands/pet_commands_basic.dm
@@ -8,7 +8,7 @@
command_name = "Stay"
command_desc = "Command your pet to stay idle in this location."
speech_commands = list("sit", "stay", "stop")
- command_feedback = "sits"
+ command_feedback = "sits."
/datum/pet_command/idle/execute_action(datum/ai_controller/controller)
return SUBTREE_RETURN_FINISH_PLANNING // This cancels further AI planning
diff --git a/code/modules/mob/living/basic/minebots/minebot_ai.dm b/code/modules/mob/living/basic/minebots/minebot_ai.dm
index 4289622d227..6463b91f966 100644
--- a/code/modules/mob/living/basic/minebots/minebot_ai.dm
+++ b/code/modules/mob/living/basic/minebots/minebot_ai.dm
@@ -172,7 +172,11 @@
living_pawn.face_atom(target)
- RegisterSignal(target, COMSIG_MINE_EXPOSE_GIBTONITE, PROC_REF(on_mine_expose_gibtonite), override = TRUE)
+ var/turf/simulated/mineral/mineral_target = target
+ if(istype(mineral_target) && istype(mineral_target.ore, /datum/ore/gibtonite))
+ // Duplicate signals can occur when a minebot previously tried to mine a
+ // turf and failed (e.g. something else was hit by the kpa projectile)
+ RegisterSignal(mineral_target, COMSIG_MINE_EXPOSE_GIBTONITE, PROC_REF(on_mine_expose_gibtonite), override = TRUE)
living_pawn.RangedAttack(target)
living_pawn.a_intent = old_intent
@@ -182,6 +186,7 @@
/datum/ai_behavior/minebot_mine_turf/proc/on_mine_expose_gibtonite(datum/source, mob/living/instigator)
SIGNAL_HANDLER // COMSIG_MINE_EXPOSE_GIBTONITE
+ UnregisterSignal(source, COMSIG_MINE_EXPOSE_GIBTONITE)
instigator.emote("me", EMOTE_VISIBLE, "yelps!")
instigator.ai_controller.set_blackboard_key(BB_MINEBOT_GIBTONITE_RUN, source)