diff --git a/MWSE/mods/celediel/MoreAttentiveGuards/combat.lua b/MWSE/mods/celediel/MoreAttentiveGuards/combat.lua index 9ddbabd..e8cd13f 100644 --- a/MWSE/mods/celediel/MoreAttentiveGuards/combat.lua +++ b/MWSE/mods/celediel/MoreAttentiveGuards/combat.lua @@ -9,9 +9,7 @@ local function log(...) if config.debug then common.log(...) end end local function isFriendlyActor(actor) for friend in tes3.iterate(tes3.mobilePlayer.friendlyActors) do - if actor.object.id == friend.object.id or actor.object.baseObject.id == friend.object.baseObject.id then - return true - end + if actor.object.id == friend.object.id or actor.object.baseObject.id == friend.object.baseObject.id then return true end end return false end @@ -80,11 +78,13 @@ local function alertGuards(aggressor, cell) if not npc.disabled and npc.object.isGuard and npc.mobile and distance <= config.combatDistance then log("Alerting %s, %s units away, to the combat!", npc.object.name, distance) - if config.combatDialogue then - local response = common.guardDialogue(npc.object.name, - table.choice(common.dialogues[config.language].join_combat), + if config.combatDialogue == common.dialogueMode.text then + local response = common.playGuardText(npc.object.name, table.choice(common.dialogues.text[config.language].join_combat), aggressor) log(response) + elseif config.combatDialogue == common.dialogueMode.voice then + local response = common.playGuardVoice(npc.mobile, "join_combat") + log("Playing sound file: %s", response) end npc.mobile:startCombat(aggressor) diff --git a/MWSE/mods/celediel/MoreAttentiveGuards/common.lua b/MWSE/mods/celediel/MoreAttentiveGuards/common.lua index a52108e..014f3e2 100644 --- a/MWSE/mods/celediel/MoreAttentiveGuards/common.lua +++ b/MWSE/mods/celediel/MoreAttentiveGuards/common.lua @@ -5,17 +5,20 @@ local this = {} this.modName = "More Attentive Guards" -- or something this.author = "Celediel" this.version = "1.1.6" -this.modInfo = "Guards with some actual spatial awareness!\n\n" .. -"Guards who catch you sneaking will follow you for a bit of" .. -"time, and will also come to the player's rescue if attacked unprovoked." -this.dialogues = require("celediel.MoreAttentiveGuards.dialogues") +this.modInfo = "Guards with some actual spatial awareness!\n\nGuards who catch you sneaking will follow you for a bit of" .. + "time, and will also come to the player's rescue if attacked unprovoked." +this.dialogues = { + text = require("celediel.MoreAttentiveGuards.dialogue.text"), + voice = require("celediel.MoreAttentiveGuards.dialogue.voice") +} +this.dialogueMode = { none = 0, text = 1, voice = 2 } this.configString = string.gsub(this.modName, "%s+", "") -- }}} -- {{{ NPC stuff or whatever -this.basicIdles = {60, 20, 20, 20, 0, 0, 0, 0} +this.basicIdles = { 60, 20, 20, 20, 0, 0, 0, 0 } -- }}} @@ -38,9 +41,9 @@ this.generateWanderRange = function(cell) return (cell.isInterior and not cell.behavesAsExterior) and 200 or 2000 end -this.guardDialogue = function(npc, str, target) - -- target of the dialogue, either an NPC/Creature, or the player's class or race - -- this is what %s is replaced with in the dialogue string; npc/creature for combat, player for sneak +this.playGuardText = function(npc, str, target) + -- target of the dialogue, either an NPC/Creature, or the player's class or race. + -- This is what %s is replaced with in the dialogue string; npc/creature for combat, player for sneak local targetOrPlayer if target == tes3.mobilePlayer then targetOrPlayer = math.random() >= 0.5 and target.object.class.name or target.object.race.name @@ -55,6 +58,41 @@ this.guardDialogue = function(npc, str, target) return output end +-- Plays a random sound of specified type and returns the path of the sound file that was played +this.playGuardVoice = function(mobile, type) + local distanceCap = 2500 -- sounds further away than this are too quiet to be heard + local ref = mobile.reference + local sex = ref.baseObject.female and "f" or "m" + local race = ref.baseObject.race.id:lower() + local directory, soundPath, sound + this.log("before: ref:%s sex:%s race:%s soundPath:%s type:%s", ref.id, sex, race, soundPath, type) + + -- make sure the race/sex/type combo exists in the voice data + if this.dialogues.voice[race] and this.dialogues.voice[race][sex] and this.dialogues.voice[race][sex][type] then + directory = string.format("vo\\%s\\%s\\", this.dialogues.voice[race].dir, sex) + sound = table.choice(this.dialogues.voice[race][sex][type]) + -- sound will be nil if the race/sex/type combo is an empty table + if sound then soundPath = directory .. sound.file .. ".mp3" end + end + this.log("after: ref:%s sex:%s race:%s soundPath:%s type:%s", ref.id, sex, race, soundPath, type) + + local distanceFromPlayer = math.clamp(mobile.position:distance(tes3.mobilePlayer.position), 0, distanceCap) or 0 + local volume = 1 - (distanceFromPlayer / distanceCap) + + -- LuaFormatter off + if soundPath then + tes3.say({ + soundPath = soundPath, + subtitle = sound.subtitle, + volume = volume, + reference = mobile + }) + end + -- LuaFormatter on + + return soundPath +end + -- }}} return this diff --git a/MWSE/mods/celediel/MoreAttentiveGuards/config.lua b/MWSE/mods/celediel/MoreAttentiveGuards/config.lua index 18bf2df..ddcdd7c 100644 --- a/MWSE/mods/celediel/MoreAttentiveGuards/config.lua +++ b/MWSE/mods/celediel/MoreAttentiveGuards/config.lua @@ -9,13 +9,13 @@ this.default = { debug = false, -- sneak sneakEnable = true, - sneakDialogue = true, + sneakDialogue = common.dialogueMode.voice, sneakDialogueTimer = 5, sneakDialogueChance = 67, -- combat combatEnable = true, combatDistance = 850, - combatDialogue = true, + combatDialogue = common.dialogueMode.voice, ignored = { ["mer_tgw_guar"] = true, ["mer_tgw_guar_w"] = true diff --git a/MWSE/mods/celediel/MoreAttentiveGuards/dialogues.lua b/MWSE/mods/celediel/MoreAttentiveGuards/dialogue/text.lua similarity index 100% rename from MWSE/mods/celediel/MoreAttentiveGuards/dialogues.lua rename to MWSE/mods/celediel/MoreAttentiveGuards/dialogue/text.lua diff --git a/MWSE/mods/celediel/MoreAttentiveGuards/dialogue/voice.lua b/MWSE/mods/celediel/MoreAttentiveGuards/dialogue/voice.lua new file mode 100644 index 0000000..4c39fa4 --- /dev/null +++ b/MWSE/mods/celediel/MoreAttentiveGuards/dialogue/voice.lua @@ -0,0 +1,318 @@ +--[[ + {file = "filename without MP3", subtitle = "what it says"}, +]] + +-- LuaFormatter off +local voices = { + argonian = { + f = { + ["sneaking"] = { + {file = "Hlo_AF000a", subtitle = "What?"}, + {file = "Idl_AF007", subtitle = "What was that?"}, + {file = "Idl_AF001", subtitle = "Sniff."}, + }, + ["stop_sneaking"] = { + {file = "Srv_AF003", subtitle = "You should leave."}, + {file = "Srv_AF012", subtitle = "Leave! Before I eat it!"}, + {file = "Srv_AF010", subtitle = "It should go away and die!"}, + {file = "Hlo_AF000c", subtitle = "Humph."}, + {file = "Hlo_AF000b", subtitle = "Humph."}, + {file = "Thf_AF003", subtitle = "Hiss."}, + }, + ["stop_following"] = { + {file = "Hlo_AF019", subtitle = "You hardly seem worth the trouble, criminal."}, + {file = "Hlo_AF000b", subtitle = "Humph."}, + {file = "Hlo_AF000c", subtitle = "Humph."}, + {file = "Hlo_AF000d", subtitle = "I won't waste my time on the likes of you."}, + {file = "Hlo_AF000e", subtitle = "Get out of here!"}, + {file = "Thf_AF003", subtitle = "Hiss."}, + }, + ["join_combat"] = { + {file = "Hlo_AF017", subtitle = "Your life is mine!"}, + {file = "Hlo_AF014", subtitle = "Kill it!"}, + {file = "Hlo_AF014", subtitle = "Rip it apart!"}, + {file = "Hlo_AF012", subtitle = "Bleed!"}, + {file = "Atk_AF010", subtitle = "Hahahaha."}, + } + }, + m = { + ["sneaking"] = { + {file = "Flw_AM001", subtitle = "Where are you going?"}, + {file = "Thf_AM005", subtitle = "I see you!"}, + {file = "Hlo_AM106", subtitle = "You make a name for yourself, criminal."}, + {file = "Hlo_AM107", subtitle = "Your crimes are known to us."}, + {file = "Hlo_AM056", subtitle = "Sniff. This scent is new."}, + {file = "Hlo_AM040", subtitle = "Is there nothing for you to do?"}, + {file = "Hlo_AM027", subtitle = "Must you make a pest of yourself?"}, + }, + ["stop_sneaking"] = { + {file = "Srv_AM012", subtitle = "Leave! Before I eat it!"}, + {file = "Srv_AM009", subtitle = "It should go away and die!"}, + {file = "Hlo_AM046", subtitle = "Crime doesn't suit you, friend."}, + {file = "Hlo_AM022", subtitle = "Be gone!"}, + }, + ["stop_following"] = { + {file = "Hlo_AM019", subtitle = "You hardly seem worth the trouble, criminal."}, + {file = "Hlo_AM018", subtitle = "Get away, criminal."}, + {file = "Hlo_AM022", subtitle = "Be gone!"}, + }, + ["join_combat"] = { + {file = "bAtk_AM002", subtitle = "Your head will be my new trophy!"}, + {file = "bAtk_AM005", subtitle = "Your cursed bloodline ends here!"}, + {file = "Atk_AM010", subtitle = "Bash!"}, + {file = "Atk_AM011", subtitle = "Kill!"}, + {file = "Atk_AM012", subtitle = "It will die!"}, + {file = "Atk_AM013", subtitle = "Suffer!"}, + {file = "Atk_AM014", subtitle = "Die!"}, + } + }, + dir = "a" + }, + breton = { + f = { + ["sneaking"] = { + }, + ["stop_sneaking"] = { + }, + ["stop_following"] = { + }, + ["join_combat"] = { + } + }, + m = { + ["sneaking"] = { + }, + ["stop_sneaking"] = { + }, + ["stop_following"] = { + }, + ["join_combat"] = { + } + }, + dir = "b" + }, + ["dark elf"] = { + f = { + ["sneaking"] = { + }, + ["stop_sneaking"] = { + }, + ["stop_following"] = { + }, + ["join_combat"] = { + } + }, + m = { + ["sneaking"] = { + {file = "Flw_DM001", subtitle = "Where are you going?"}, + {file = "Idl_DM007", subtitle = "What was that?"}, + {file = "Hlo_DM165", subtitle = "There are better ways than theft to earn a coin, outlander."}, + }, + ["stop_sneaking"] = { + {file = "Hlo_DM021", subtitle = "Bothersome creature."}, + {file = "Hlo_DM001", subtitle = "Go away."}, + {file = "Hlo_DM000b", subtitle ="Humph."}, + {file = "Hlo_DM000c", subtitle = "Hmmph."}, + }, + ["stop_following"] = { + {file = "Hlo_DM111", subtitle = "Move along, outlander."}, + {file = "Hlo_DM035", subtitle = "Keep moving, scum."}, + {file = "Hlo_DM021", subtitle = "Bothersome creature."}, + {file = "Hlo_DM000b", subtitle ="Humph."}, + {file = "Hlo_DM000c", subtitle = "Hmmph."}, + }, + ["join_combat"] = { + } + }, + dir = "d" + }, + ["high elf"] = { + f = { + ["sneaking"] = { + }, + ["stop_sneaking"] = { + }, + ["stop_following"] = { + }, + ["join_combat"] = { + } + }, + m = { + ["sneaking"] = { + }, + ["stop_sneaking"] = { + }, + ["stop_following"] = { + }, + ["join_combat"] = { + } + }, + dir = "h" + }, + imperial = { + f = { + ["sneaking"] = { + }, + ["stop_sneaking"] = { + }, + ["stop_following"] = { + }, + ["join_combat"] = { + } + }, + m = { + ["sneaking"] = { + {file = "Hlo_IM007", subtitle = "Are you here to start trouble, or are you just stupid?"}, + {file = "Flw_IM001", subtitle = "Where are you going?"}, + {file = "Hlo_IM057", subtitle = "Stay out of trouble and you won't get hurt."}, + }, + ["stop_sneaking"] = { + {file = "bIdl_IM028", subtitle = "Just as well..."}, + {file = "Hlo_IM000e", subtitle = "Get out of here."}, + {file = "Srv_IM027", subtitle = "You are a nuisance to me. Please leave."} + }, + ["stop_following"] = { + {file = "Hlo_IM000e", subtitle = "Get out of here."}, + {file = "Srv_IM027", subtitle = "You are a nuisance to me. Please leave."}, + {file = "Hlo_IM006", subtitle = "What a pathetic excuse for a criminal!"} + }, + ["join_combat"] = { + {file = "Atk_IM009", subtitle = "Die, scoundrel!"}, + {file = "CrAtk_IM005", subtitle = "Die!"}, + {file = "Atk_IM010", subtitle = "You're hardly a match for me!"}, + {file = "Atk_IM007", subtitle = "Let's see what you're made of!"}, + {file = "Hlo_IM004", subtitle = "Since you're already on death's door, may I open it for you?"}, + {file = "Hlo_IM018", subtitle = "You're a disgrace to the Empire."}, + {file = "Hlo_IM000d", subtitle = "You're about to find more trouble than you can possibly imagine."} + } + }, + dir = "i" + }, + khajiit = { + f = { + ["sneaking"] = { + }, + ["stop_sneaking"] = { + }, + ["stop_following"] = { + }, + ["join_combat"] = { + } + }, + m = { + ["sneaking"] = { + }, + ["stop_sneaking"] = { + }, + ["stop_following"] = { + }, + ["join_combat"] = { + } + }, + dir = "k" + }, + nord = { + f = { + ["sneaking"] = { + }, + ["stop_sneaking"] = { + }, + ["stop_following"] = { + }, + ["join_combat"] = { + } + }, + m = { + ["sneaking"] = { + }, + ["stop_sneaking"] = { + }, + ["stop_following"] = { + }, + ["join_combat"] = { + } + }, + dir = "n" + }, + orc = { + f = { + ["sneaking"] = { + }, + ["stop_sneaking"] = { + }, + ["stop_following"] = { + }, + ["join_combat"] = { + } + }, + m = { + ["sneaking"] = { + }, + ["stop_sneaking"] = { + }, + ["stop_following"] = { + }, + ["join_combat"] = { + } + }, + dir = "o" + }, + redguard = { + f = { + ["sneaking"] = { + }, + ["stop_sneaking"] = { + }, + ["stop_following"] = { + }, + ["join_combat"] = { + } + }, + m = { + ["sneaking"] = { + }, + ["stop_sneaking"] = { + }, + ["stop_following"] = { + }, + ["join_combat"] = { + } + }, + dir = "r" + }, + ["wood elf"] = { + f = { + ["sneaking"] = { + }, + ["stop_sneaking"] = { + }, + ["stop_following"] = { + }, + ["join_combat"] = { + } + }, + m = { + ["sneaking"] = { + }, + ["stop_sneaking"] = { + }, + ["stop_following"] = { + }, + ["join_combat"] = { + } + }, + dir = "w" + } +} +-- LuaFormatter on + +-- TR voices +voices["t_els_cathay"] = voices.khajiit +voices["t_els_cathay-raht"] = voices.khajiit +voices["t_els_ohmes"] = voices.khajiit +voices["t_els_ohmes-raht"] = voices.khajiit +voices["t_els_suthay"] = voices.khajiit +voices["t_sky_reachman"] = voices.breton -- todo: combine Nord + Breton +voices["t_pya_seaelf"] = voices["high elf"] -- todo: something better + +return voices diff --git a/MWSE/mods/celediel/MoreAttentiveGuards/main.lua b/MWSE/mods/celediel/MoreAttentiveGuards/main.lua index 8f78678..104d0a2 100644 --- a/MWSE/mods/celediel/MoreAttentiveGuards/main.lua +++ b/MWSE/mods/celediel/MoreAttentiveGuards/main.lua @@ -2,7 +2,7 @@ local sneak = require("celediel.MoreAttentiveGuards.sneak") local combat = require("celediel.MoreAttentiveGuards.combat") local common = require("celediel.MoreAttentiveGuards.common") --- in order for this to work, functions in returned table must follow pattern: onEventName +-- in order for this to work, functions in returned table must follow naming pattern: onEventName local function registerFunctionEvents(t) for name, func in pairs(t) do if type(func) == "function" then event.register(name:gsub("on(%u)", string.lower), func) end diff --git a/MWSE/mods/celediel/MoreAttentiveGuards/mcm.lua b/MWSE/mods/celediel/MoreAttentiveGuards/mcm.lua index 1850fc0..d6e39b1 100644 --- a/MWSE/mods/celediel/MoreAttentiveGuards/mcm.lua +++ b/MWSE/mods/celediel/MoreAttentiveGuards/mcm.lua @@ -3,15 +3,12 @@ local common = require("celediel.MoreAttentiveGuards.common") -- {{{ helper functions -local function createTableVar(id) return mwse.mcm.createTableVariable({id = id, table = config}) end +local function createTableVar(id) return mwse.mcm.createTableVariable({ id = id, table = config }) end local function createLanguageOptions() local options = {} - -- I guess I don't know how ipairs works - local i = 1 - for name, _ in pairs(common.dialogues) do - options[i] = {label = name:gsub("^%l", string.upper), value = name} - i = i + 1 -- wtf lua + for name, _ in pairs(common.dialogues.text) do + options[#options + 1] = { label = name:gsub("^%l", string.upper), value = name } end return options end @@ -37,7 +34,8 @@ local mainCategory = page:createCategory(common.modName) local generalCategory = mainCategory:createCategory("Common settings") generalCategory:createDropdown({ - label = "Language", + label = "Text Language", + description = "If dialogue mode is set to text, this language will be used.", options = createLanguageOptions(), variable = createTableVar("language") }) @@ -60,10 +58,15 @@ sneakCategory:createYesNoButton({ variable = createTableVar("sneakEnable") }) -sneakCategory:createYesNoButton({ +sneakCategory:createDropdown({ label = "Sneak dialogue", description = "Guards sometimes say things to you when you sneak.", - variable = createTableVar("sneakDialogue") + variable = createTableVar("sneakDialogue"), + options = { + { label = "Text", value = common.dialogueMode.text }, + { label = "Voice", value = common.dialogueMode.voice }, + { label = "None", value = common.dialogueMode.none } + } }) sneakCategory:createSlider({ @@ -108,10 +111,15 @@ combatCategory:createSlider({ variable = createTableVar("combatDistance") }) -combatCategory:createYesNoButton({ - label = "Enable combat dialogue", +combatCategory:createDropdown({ + label = "Combat dialogue", description = "Guards have things to say when they come to the rescue of a player who is attacked unprovoked.", - variable = createTableVar("combatDialogue") + variable = createTableVar("combatDialogue"), + options = { + { label = "Text", value = common.dialogueMode.text }, + { label = "Voice", value = common.dialogueMode.voice }, + { label = "None", value = common.dialogueMode.none } + } }) -- }}} @@ -121,9 +129,9 @@ template:createExclusionsPage({ description = "Guards will not respond to these NPCs or creatures attacking the player.", showAllBlocked = false, filters = { - {label = "Plugins", type = "Plugin"}, - {label = "NPCs", type = "Object", objectType = tes3.objectType.npc}, - {label = "Creatures", type = "Object", objectType = tes3.objectType.creature} + { label = "Plugins", type = "Plugin" }, + { label = "NPCs", type = "Object", objectType = tes3.objectType.npc }, + { label = "Creatures", type = "Object", objectType = tes3.objectType.creature } }, variable = createTableVar("ignored") }) diff --git a/MWSE/mods/celediel/MoreAttentiveGuards/sneak.lua b/MWSE/mods/celediel/MoreAttentiveGuards/sneak.lua index f0b0fba..87373a0 100644 --- a/MWSE/mods/celediel/MoreAttentiveGuards/sneak.lua +++ b/MWSE/mods/celediel/MoreAttentiveGuards/sneak.lua @@ -50,16 +50,21 @@ end -- {{{ timer functions -local function startDialogue() +local function startDialogue(chance) if not follower then return end - local dialogue = table.choice(common.dialogues[config.language].sneaking) - local roll = math.random(0, 100) + local roll = type(chance) == "number" and chance or math.random(0, 100) - log("Dialogue roll = %s > %s", config.sneakDialogueChance, roll) + log("Dialogue roll = %s > %s == %s", config.sneakDialogueChance, roll, config.sneakDialogueChance > roll) if config.sneakDialogueChance > roll then - local response = common.guardDialogue(follower.object.name, dialogue, tes3.mobilePlayer) - log(response) + if config.sneakDialogue == common.dialogueMode.text then + local response = common.playGuardText(follower.object.name, table.choice(common.dialogues.text[config.language].sneaking), + tes3.mobilePlayer) + log(response) + elseif config.sneakDialogue == common.dialogueMode.voice then + local response = common.playGuardVoice(follower, "sneaking") + log("Playing sound file: %s", response) + end end end @@ -73,9 +78,8 @@ local function stopFollowing(onTimer) local wanderRange = common.generateWanderRange(tes3.getPlayerCell()) local idles = common.generateIdles() - log("%s has probably reached their original destination, resuming %s range wander...", follower.object.name, - wanderRange) - tes3.setAIWander({reference = follower, range = wanderRange, reset = true, idles = idles}) + log("%s has probably reached their original destination, resuming %s range wander...", follower.object.name, wanderRange) + tes3.setAIWander({ reference = follower, range = wanderRange, reset = true, idles = idles }) follower = nil interop.setGuardFollower(follower) @@ -91,23 +95,27 @@ local function stopFollowing(onTimer) duration = duration > 0 and duration or 1 log("%s has decided that %s isn't doing anything suspicious, heading back to %s... " .. - "(which is %s distance units away... it'll probably take %s seconds to get there)", - follower.object.name, tes3.player.object.name, ogPosition, distance, duration) + "(which is %s distance units away... it'll probably take %s seconds to get there)", follower.object.name, + tes3.player.object.name, ogPosition, distance, duration) -- send a dialogue to let player know guard doesn't care any more - if onTimer and config.sneakDialogue then - local response = common.guardDialogue(follower.object.name, - table.choice(common.dialogues[config.language].stop_following), - tes3.mobilePlayer) - log(response) + if onTimer then + if config.sneakDialogue == common.dialogueMode.text then + local response = common.playGuardText(follower.object.name, table.choice(common.dialogues[config.language].stop_following), + tes3.mobilePlayer) + log(response) + elseif config.sneakDialogue == common.dialogueMode.voice then + local response = common.playGuardVoice(follower, "stop_following") + log("Playing sound file: %s", response) + end end if dialogueTimer and dialogueTimer.state == timer.active then dialogueTimer:cancel() end - tes3.setAITravel({reference = follower, destination = ogPosition}) + tes3.setAITravel({ reference = follower, destination = ogPosition }) ogPosition = nil - timer.start({duration = duration, iterations = 1, callback = startWander}) + timer.start({ duration = duration, iterations = 1, callback = startWander }) end timer.delayOneFrame(startTravel) @@ -121,17 +129,13 @@ local function startFollowing() if followTime <= 0 then return end log("%s starting to follow %s for %s time units", follower.object.name, tes3.player.object.name, followTime) - tes3.setAIFollow({reference = follower, target = tes3.mobilePlayer}) + tes3.setAIFollow({ reference = follower, target = tes3.mobilePlayer }) - followTimer = timer.start({duration = followTime, callback = stopFollowing}) + followTimer = timer.start({ duration = followTime, callback = stopFollowing }) - if config.sneakDialogue then - startDialogue() - dialogueTimer = timer.start({ - duration = config.sneakDialogueTimer, - iterations = -1, - callback = startDialogue - }) + if config.sneakDialogue ~= common.dialogueMode.none then + startDialogue(0) -- always say something the first time + dialogueTimer = timer.start({ duration = config.sneakDialogueTimer, iterations = -1, callback = startDialogue }) end isFollowing = true @@ -143,11 +147,14 @@ local function startFollowing() end local function abortFollow() - if config.sneakDialogue then - local response = common.guardDialogue(follower.object.name, - table.choice(common.dialogues[config.language].stop_sneaking), + -- send a dialogue to let player know guard doesn't care any more + if config.sneakDialogue == common.dialogueMode.text then + local response = common.playGuardText(follower.object.name, table.choice(common.dialogues.text[config.language].stop_sneaking), tes3.mobilePlayer) log(response) + elseif config.sneakDialogue == common.dialogueMode.voice then + local response = common.playGuardVoice(follower, "stop_sneaking") + log("Playing sound file: %s", response) end stopFollowing(false) end