Thank you mod authors for making this mod! It makes my Morrowind playtrough much easier on my fingers and on my wrist.
I cant imagine Morrowind designers thought that this is the best way to play the game - repetady for 30seconds to minute spam a mouse button on each enemy. And when skills get higher then the same is true for packs of 2-3-4 enemies at a time.
I get the 3D video game implementation into D&D combat system - where you need to roll the dice to know if your attack has been successful or not and that is great, but after a while my fingers start hurting and if i lay back in to the chair, my wrist also start hurting.
This mod solves this problem. And also works perfectly so far, past ~ 20 hours of gameplay i havent noticed anything that wouldnt work perfectly.
I've edited the mod with the following changes: 1) Expands the hotkeys to include mouse buttons in the combination 2) Allows the option to auto-attack when holding the hotkey, instead of toggling the auto-attack on and off. 3) Allows the option to ignore control keys (shift, alt, ctrl, and super). This allows one to auto-attack while holding shift to sprint or when not holding shift.
This allows me to, for example, bind my auto-attack key to the mouse, and just hold the mouse button when I want to attack, and release it to stop attacking. I can attack while sprinting, walking, sneaking, or standing still without worrying about whether I'm holding shift or ctrl.
Users who wish to use these changes will need to replace their config.lua, main.lua, mcm.lua, and util.lua files in the mod with the following:
config.lua:
Spoiler:
Show
local inMemConfig
local this = {}
--Static Config (stored right here) this.static = { modName = "Auto Attack", modDescription = [[A mod that allows you to attack automatically with a hotkey.]], useKeyOptionIndex = 5, deviceMouse = 1, deviceKeyboard = 0, buttonDown = 128, }
this.mcm = setmetatable({}, { __index = function(_, key) inMemConfig = inMemConfig or mwse.loadConfig(this.configPath, this.mcmDefault) return inMemConfig[key] end, __newindex = function(_, key, value) inMemConfig = inMemConfig or mwse.loadConfig(this.configPath, this.mcmDefault) inMemConfig[key] = value mwse.saveConfig(this.configPath, inMemConfig) end })
-- local persistentDefault = -- }
-- this.persistent = setmetatable({}, { -- __index = function(_, key) -- if not tes3.player then return end -- tes3.player.data[this.configPath] = tes3.player.data[this.configPath] or persistentDefault -- return tes3.player.data[this.configPath][key] -- end, -- __newindex = function(_, key, value) -- if not tes3.player then return end -- tes3.player.data[this.configPath] = tes3.player.data[this.configPath] or persistentDefault -- tes3.player.data[this.configPath][key] = value -- end -- })
return this
main.lua:
Spoiler:
Show
require("mer.autoAttack.mcm")
local util = require("mer.autoAttack.util") local config = util.config local logger = util.createLogger("main") local attackToggled = false
--[[ --original function, edited below local function onKeyDown(e) if not config.mcm.enabled then return end if not tes3.player then return end if not tes3.player.mobile then return end if not tes3.player.mobile.weaponDrawn then return end if util.isKeyPressed(e, config.mcm.hotKey) then attackToggled = not attackToggled logger:debug("Toggling Auto-Attack %s", attackToggled and "on" or "off") if config.mcm.displayMessages then tes3.messageBox("Auto-Attack %s.", attackToggled and "On" or "Off") end end end ]]--
--[[ plan: add separate isKeyUp to detect when hotkey is released then change onKeyDown to just turn on, not toggle attack and add onKeyUp to turn off attack ]]--
---@param e keyDownEventData|mouseButtonDownEventData local function onKeyDown(e) if not config.mcm.enabled then return end if not tes3.player then return end if not tes3.player.mobile then return end if not tes3.player.mobile.weaponDrawn then return end if tes3.menuMode() then return end if util.isKeyPressed(e, config.mcm.hotKey) then --attackToggled = not attackToggled if config.mcm.holding then attackToggled = true --turned false using onKeyUp else attackToggled = not attackToggled end logger:debug("Toggling Auto-Attack %s", attackToggled and "on" or "off") if config.mcm.displayMessages then tes3.messageBox("Auto-Attack %s.", attackToggled and "On" or "Off") end end end
---@param e keyUpEventData|mouseButtonUpEventData local function onKeyUp(e) if not config.mcm.enabled then return end if not config.mcm.holding then return end --if you run this code when holding isn't enabled it still toggles off your attack, so this needs to be disabled if not tes3.player then return end if not tes3.player.mobile then return end if not tes3.player.mobile.weaponDrawn then return end if tes3.menuMode() then return end if util.isKeyReleased(e, config.mcm.hotKey) then --attackToggled = not attackToggled attackToggled = false logger:debug("Toggling Auto-Attack %s", attackToggled and "on" or "off") if config.mcm.displayMessages then tes3.messageBox("Auto-Attack %s.", attackToggled and "On" or "Off") end end end
local function getAttackKeyConfig() local inputController = tes3.worldController.inputController ---@type tes3inputConfig local attackConfig = inputController.inputMaps[config.static.useKeyOptionIndex] return attackConfig end
local function getPlayerAtMaxSwing() local attackSwing = tes3.mobilePlayer.animationController:calculateAttackSwing() local atMaxSwing = attackSwing >= math.max(1, config.mcm.maxSwing) / 100 logger:trace("At Max Swing: %s", atMaxSwing) return atMaxSwing end
---@param e simulateEventData local function onSimulate(e) if not config.mcm.enabled then return end if not attackToggled then return end if not tes3.player.mobile then return end if not tes3.player.mobile.weaponDrawn then return end
local inputController = tes3.worldController.inputController local attackConfig = getAttackKeyConfig() local attackSwing = tes3.player.mobile.actionData.attackSwing logger:trace("AttackSwing: %s", attackSwing)
--Mouse if attackConfig.device == config.static.deviceMouse then local state = getPlayerAtMaxSwing() and 0 or config.static.buttonDown logger:trace("Setting state to %s", state) inputController.mouseState.buttons[attackConfig.code + 1] = state --Keyboard elseif attackConfig.device == config.static.deviceKeyboard then local state = getPlayerAtMaxSwing() and 0 or config.static.buttonDown logger:trace("Setting state to %s", state) inputController.keyboardState[attackConfig.code + 1] = state end end
local function initialise() event.register(tes3.event.keyDown, onKeyDown) event.register(tes3.event.mouseButtonDown, onKeyDown) event.register(tes3.event.keyUp, onKeyUp) event.register(tes3.event.mouseButtonUp, onKeyUp) event.register(tes3.event.simulate, onSimulate) logger:info("Initialised: %s", util.getVersion()) end event.register(tes3.event.initialized, initialise)
mcm.lua:
Spoiler:
Show
local util = require("mer.autoAttack.util") local config = util.config local mcmConfig = mwse.loadConfig(config.configPath, config.mcmDefault)
local LINKS_LIST = { { text = "Release history", url = "https://github.com/jhaakma/auto-attack/releases" }, -- { -- text = "Wiki", -- url = "https://github.com/jhaakma/auto-attack/wiki" -- }, { text = "Nexus Page", url = "https://www.nexusmods.com/morrowind/mods/51348" }, { text = "Buy me a coffee", url = "https://ko-fi.com/merlord" }, } local CREDITS_LIST = { { text = "Made by Merlord", url = "https://www.nexusmods.com/users/3040468?tab=user+files", }, }
local function addSideBar(component) local versionText = string.format(config.static.modName) component.sidebar:createCategory(versionText) component.sidebar:createInfo{ text = config.static.modDescription}
local linksCategory = component.sidebar:createCategory("Links") for _, link in ipairs(LINKS_LIST) do linksCategory:createHyperLink{ text = link.text, url = link.url } end local creditsCategory = component.sidebar:createCategory("Credits") for _, credit in ipairs(CREDITS_LIST) do creditsCategory:createHyperLink{ text = credit.text, url = credit.url } end end
local function registerMCM() local template = mwse.mcm.createTemplate{ name = config.static.modName,} template.onClose = function() config.save(mcmConfig) end template:register()
local page = template:createSideBarPage{ label = "Settings"} addSideBar(page)
page:createYesNoButton{ label = "Enable Mod", description = "Turn this mod on or off", variable = mwse.mcm.createTableVariable{ id = "enabled", table = mcmConfig } }
page:createYesNoButton{ label = "Enable Holding", description = "If enabled, auto-attack by holding hotkey. If disabled, auto-attack by toggling hotkey.", variable = mwse.mcm.createTableVariable{ id = "holding", table = mcmConfig } }
page:createYesNoButton{ label = "Enable Control Keys", description = "If enabled, can use shift, control, alt, and super as part of hotkeys. If disabled, these are ignored, only main key is used.", variable = mwse.mcm.createTableVariable{ id = "controlKeysMatter", table = mcmConfig } }
page:createOnOffButton{ label = "Display Messages", description = "Displays a message box whenever auto-attack is toggled on or off.", variable = mwse.mcm.createTableVariable{ id = "displayMessages", table = mcmConfig } }
page:createKeyBinder{ label = "Toggle Auto Attack Hot key", description = "The key and/or mouse combo to toggle auto attacking on or off.", allowMouse = true, allowCombinations = true, ---@type mwseKeyMouseCombo variable = mwse.mcm.createTableVariable{ id = "hotKey", table = mcmConfig } }
page:createSlider{ label = "Max Swing: %s%%", description = "Determines how far back to pull the weapon before releasing while auto-attacking.", variable = mwse.mcm.createTableVariable{ id = "maxSwing", table = mcmConfig }, min = 0, max = 100, jump = 10, step = 1 }
page:createDropdown{ label = "Log Level", description = "Set the logging level for mwse.log. Keep on INFO unless you are debugging.", options = { { label = "TRACE", value = "TRACE"}, { label = "DEBUG", value = "DEBUG"}, { label = "INFO", value = "INFO"}, { label = "ERROR", value = "ERROR"}, { label = "NONE", value = "NONE"}, }, variable = mwse.mcm.createTableVariable{ id = "logLevel", table = mcmConfig }, callback = function(self) for _, logger in pairs(util.loggers) do logger:setLogLevel(self.variable.value) end end } end event.register("modConfigReady", registerMCM)
util.lua:
Spoiler:
Show
local Util = {}
Util.config = require("mer.autoAttack.config") Util.messageBox = require("mer.autoAttack.messageBox") Util.loggers = {} do --logger local logLevel = Util.config.mcm.logLevel local logger = require("logging.logger") Util.log = logger.new{ name = Util.config.static.modName, logLevel = logLevel } Util.createLogger = function(serviceName) local logger = logger.new{ name = string.format("%s: %s", Util.config.static.modName, serviceName), logLevel = logLevel } Util.loggers[serviceName] = logger return logger end end
---@param pressed keyDownEventData|mouseButtonDownEventData ---@param stored keyDownEventData|mouseButtonDownEventData function Util.isKeyPressed(pressed, stored) if Util.config.mcm.controlKeysMatter then return ( tes3.isKeyEqual({ expected = stored, actual = pressed }) ) --[[ return ( pressed.keyCode == stored.keyCode and not not pressed.isShiftDown == not not stored.isShiftDown and not not pressed.isControlDown == not not stored.isControlDown and not not pressed.isAltDown == not not stored.isAltDown and not not pressed.isSuperDown == not not stored.isSuperDown ) ]]-- else pressed.isShiftDown = false pressed.isControlDown = false pressed.isAltDown = false pressed.isSuperDown = false stored.isShiftDown = false stored.isControlDown = false stored.isAltDown = false stored.isSuperDown = false return ( tes3.isKeyEqual({ expected = stored, actual = pressed }) ) --[[ return ( pressed.keyCode == stored.keyCode ) ]]-- end end
---@param released keyUpEventData|mouseButtonUpEventData ---@param stored keyUpEventData|mouseButtonUpEventData function Util.isKeyReleased(released, stored) if Util.config.mcm.controlKeysMatter then return ( tes3.isKeyEqual({ expected = stored, actual = released }) ) --[[ return ( released.keyCode == stored.keyCode and not not released.isShiftDown == not not stored.isShiftDown and not not released.isControlDown == not not stored.isControlDown and not not released.isAltDown == not not stored.isAltDown and not not released.isSuperDown == not not stored.isSuperDown ) ]]-- else released.isShiftDown = false released.isControlDown = false released.isAltDown = false released.isSuperDown = false stored.isShiftDown = false stored.isControlDown = false stored.isAltDown = false stored.isSuperDown = false return ( tes3.isKeyEqual({ expected = stored, actual = released }) ) --[[ return ( released.keyCode == stored.keyCode ) ]]-- end end
function Util.getVersion() local versionFile = io.open("Data Files/MWSE/mods/mer/autoAttack/version.txt", "r") if not versionFile then return end local version = "" for line in versionFile:lines() do -- Loops over all the lines in an open text file version = line end return version end
In further testing, I've noted that using a mouse button for your hotkey doesn't get saved correctly. autoAttack.json, the config file, correctly saves the hotkey, but when loaded it defaults to shift-Q, the default value. I didn't actually change anything in the MCM code for the hotkey except for adding allowMouse = true, so I'm not sure why this isn't saving correctly. The type of config saved would be a mwseKeyMouseCombo instead of a mwseKeyCombo but that's never actually mentioned in any of the lua or .json files and mwseKeyMouseCombo inherits mwseKeyCombo.
For now, for users, if you use a mouse button in your hotkey, you'll need to reset it every time you run the game. You can change this by manually coding it into config.lua but most people aren't going to do this.
Fixed, see post below. Above code doesn't have this error.
For users, I've edited my comments above with an updated version of the files. The underlying base MCM bug is fixed, and now the version above does everything as intended. You do need to run MWSE-Update.exe to make sure your MWSE.exe is updated to today's, 8/15/2024, version of MWSE. Earlier versions before 8/15/2024 will have a bug that makes the MCM not correctly save a hotkey using a mouse button.
I asked ChatGPT to change the script so that auto-attack goes on hold instead of switch, so I could use the AutoHotkey to assign the key to the left mouse button for comfortable fighting with low-damage weapons like short blades. And it worked, lol. You can ask a ChatGPT to customize mods without coding knowledge. But it couldn't add exceptions for throwing weapons to the mod. It would be awesome if you could add a mod version that works by holding down a key, with the option to assign a mouse button and an exception for long-range weapons.
Thanks for this, do you think it is possible to do this with casting (such as the hotkey casting in Morrowind Code Patch)? I would do it myself if it isn't a dead end.
15 comments
I cant imagine Morrowind designers thought that this is the best way to play the game - repetady for 30seconds to minute spam a mouse button on each enemy. And when skills get higher then the same is true for packs of 2-3-4 enemies at a time.
I get the 3D video game implementation into D&D combat system - where you need to roll the dice to know if your attack has been successful or not and that is great, but after a while my fingers start hurting and if i lay back in to the chair, my wrist also start hurting.
This mod solves this problem. And also works perfectly so far, past ~ 20 hours of gameplay i havent noticed anything that wouldnt work perfectly.
1) Expands the hotkeys to include mouse buttons in the combination
2) Allows the option to auto-attack when holding the hotkey, instead of toggling the auto-attack on and off.
3) Allows the option to ignore control keys (shift, alt, ctrl, and super). This allows one to auto-attack while holding shift to sprint or when not holding shift.
This allows me to, for example, bind my auto-attack key to the mouse, and just hold the mouse button when I want to attack, and release it to stop attacking. I can attack while sprinting, walking, sneaking, or standing still without worrying about whether I'm holding shift or ctrl.
Users who wish to use these changes will need to replace their config.lua, main.lua, mcm.lua, and util.lua files in the mod with the following:
config.lua:
local inMemConfig
local this = {}
--Static Config (stored right here)
this.static = {
modName = "Auto Attack",
modDescription =
[[A mod that allows you to attack automatically with a hotkey.]],
useKeyOptionIndex = 5,
deviceMouse = 1,
deviceKeyboard = 0,
buttonDown = 128,
}
--MCM Config (stored as JSON)
this.configPath = "autoAttack"
this.mcmDefault = {
enabled = true,
holding = false,
controlKeysMatter = true,
logLevel = "INFO",
---@type mwseKeyMouseCombo
hotKey = {
keyCode = tes3.scanCode.q,
isAltDown = true
--mouseButton = 0
},
maxSwing = 100,
displayMessages = true
}
this.save = function(newConfig)
inMemConfig = newConfig
mwse.saveConfig(this.configPath, inMemConfig)
end
this.mcm = setmetatable({}, {
__index = function(_, key)
inMemConfig = inMemConfig or mwse.loadConfig(this.configPath, this.mcmDefault)
return inMemConfig[key]
end,
__newindex = function(_, key, value)
inMemConfig = inMemConfig or mwse.loadConfig(this.configPath, this.mcmDefault)
inMemConfig[key] = value
mwse.saveConfig(this.configPath, inMemConfig)
end
})
-- local persistentDefault =
-- }
-- this.persistent = setmetatable({}, {
-- __index = function(_, key)
-- if not tes3.player then return end
-- tes3.player.data[this.configPath] = tes3.player.data[this.configPath] or persistentDefault
-- return tes3.player.data[this.configPath][key]
-- end,
-- __newindex = function(_, key, value)
-- if not tes3.player then return end
-- tes3.player.data[this.configPath] = tes3.player.data[this.configPath] or persistentDefault
-- tes3.player.data[this.configPath][key] = value
-- end
-- })
return this
main.lua:
require("mer.autoAttack.mcm")
local util = require("mer.autoAttack.util")
local config = util.config
local logger = util.createLogger("main")
local attackToggled = false
--[[
--original function, edited below
local function onKeyDown(e)
if not config.mcm.enabled then return end
if not tes3.player then return end
if not tes3.player.mobile then return end
if not tes3.player.mobile.weaponDrawn then return end
if util.isKeyPressed(e, config.mcm.hotKey) then
attackToggled = not attackToggled
logger:debug("Toggling Auto-Attack %s", attackToggled and "on" or "off")
if config.mcm.displayMessages then
tes3.messageBox("Auto-Attack %s.", attackToggled and "On" or "Off")
end
end
end
]]--
--[[
plan:
add separate isKeyUp to detect when hotkey is released
then change onKeyDown to just turn on, not toggle attack
and add onKeyUp to turn off attack
]]--
---@param e keyDownEventData|mouseButtonDownEventData
local function onKeyDown(e)
if not config.mcm.enabled then return end
if not tes3.player then return end
if not tes3.player.mobile then return end
if not tes3.player.mobile.weaponDrawn then return end
if tes3.menuMode() then return end
if util.isKeyPressed(e, config.mcm.hotKey) then
--attackToggled = not attackToggled
if config.mcm.holding then
attackToggled = true --turned false using onKeyUp
else
attackToggled = not attackToggled
end
logger:debug("Toggling Auto-Attack %s", attackToggled and "on" or "off")
if config.mcm.displayMessages then
tes3.messageBox("Auto-Attack %s.", attackToggled and "On" or "Off")
end
end
end
---@param e keyUpEventData|mouseButtonUpEventData
local function onKeyUp(e)
if not config.mcm.enabled then return end
if not config.mcm.holding then return end --if you run this code when holding isn't enabled it still toggles off your attack, so this needs to be disabled
if not tes3.player then return end
if not tes3.player.mobile then return end
if not tes3.player.mobile.weaponDrawn then return end
if tes3.menuMode() then return end
if util.isKeyReleased(e, config.mcm.hotKey) then
--attackToggled = not attackToggled
attackToggled = false
logger:debug("Toggling Auto-Attack %s", attackToggled and "on" or "off")
if config.mcm.displayMessages then
tes3.messageBox("Auto-Attack %s.", attackToggled and "On" or "Off")
end
end
end
local function getAttackKeyConfig()
local inputController = tes3.worldController.inputController
---@type tes3inputConfig
local attackConfig = inputController.inputMaps[config.static.useKeyOptionIndex]
return attackConfig
end
local function getPlayerAtMaxSwing()
local attackSwing = tes3.mobilePlayer.animationController:calculateAttackSwing()
local atMaxSwing = attackSwing >= math.max(1, config.mcm.maxSwing) / 100
logger:trace("At Max Swing: %s", atMaxSwing)
return atMaxSwing
end
---@param e simulateEventData
local function onSimulate(e)
if not config.mcm.enabled then return end
if not attackToggled then return end
if not tes3.player.mobile then return end
if not tes3.player.mobile.weaponDrawn then return end
local inputController = tes3.worldController.inputController
local attackConfig = getAttackKeyConfig()
local attackSwing = tes3.player.mobile.actionData.attackSwing
logger:trace("AttackSwing: %s", attackSwing)
--Mouse
if attackConfig.device == config.static.deviceMouse then
local state = getPlayerAtMaxSwing() and 0 or config.static.buttonDown
logger:trace("Setting state to %s", state)
inputController.mouseState.buttons[attackConfig.code + 1] = state
--Keyboard
elseif attackConfig.device == config.static.deviceKeyboard then
local state = getPlayerAtMaxSwing() and 0 or config.static.buttonDown
logger:trace("Setting state to %s", state)
inputController.keyboardState[attackConfig.code + 1] = state
end
end
local function initialise()
event.register(tes3.event.keyDown, onKeyDown)
event.register(tes3.event.mouseButtonDown, onKeyDown)
event.register(tes3.event.keyUp, onKeyUp)
event.register(tes3.event.mouseButtonUp, onKeyUp)
event.register(tes3.event.simulate, onSimulate)
logger:info("Initialised: %s", util.getVersion())
end
event.register(tes3.event.initialized, initialise)
mcm.lua:
local util = require("mer.autoAttack.util")
local config = util.config
local mcmConfig = mwse.loadConfig(config.configPath, config.mcmDefault)
local LINKS_LIST = {
{
text = "Release history",
url = "https://github.com/jhaakma/auto-attack/releases"
},
-- {
-- text = "Wiki",
-- url = "https://github.com/jhaakma/auto-attack/wiki"
-- },
{
text = "Nexus Page",
url = "https://www.nexusmods.com/morrowind/mods/51348"
},
{
text = "Buy me a coffee",
url = "https://ko-fi.com/merlord"
},
}
local CREDITS_LIST = {
{
text = "Made by Merlord",
url = "https://www.nexusmods.com/users/3040468?tab=user+files",
},
}
local function addSideBar(component)
local versionText = string.format(config.static.modName)
component.sidebar:createCategory(versionText)
component.sidebar:createInfo{ text = config.static.modDescription}
local linksCategory = component.sidebar:createCategory("Links")
for _, link in ipairs(LINKS_LIST) do
linksCategory:createHyperLink{ text = link.text, url = link.url }
end
local creditsCategory = component.sidebar:createCategory("Credits")
for _, credit in ipairs(CREDITS_LIST) do
creditsCategory:createHyperLink{ text = credit.text, url = credit.url }
end
end
local function registerMCM()
local template = mwse.mcm.createTemplate{ name = config.static.modName,}
template.onClose = function()
config.save(mcmConfig)
end
template:register()
local page = template:createSideBarPage{ label = "Settings"}
addSideBar(page)
page:createYesNoButton{
label = "Enable Mod",
description = "Turn this mod on or off",
variable = mwse.mcm.createTableVariable{ id = "enabled", table = mcmConfig }
}
page:createYesNoButton{
label = "Enable Holding",
description = "If enabled, auto-attack by holding hotkey. If disabled, auto-attack by toggling hotkey.",
variable = mwse.mcm.createTableVariable{ id = "holding", table = mcmConfig }
}
page:createYesNoButton{
label = "Enable Control Keys",
description = "If enabled, can use shift, control, alt, and super as part of hotkeys. If disabled, these are ignored, only main key is used.",
variable = mwse.mcm.createTableVariable{ id = "controlKeysMatter", table = mcmConfig }
}
page:createOnOffButton{
label = "Display Messages",
description = "Displays a message box whenever auto-attack is toggled on or off.",
variable = mwse.mcm.createTableVariable{ id = "displayMessages", table = mcmConfig }
}
page:createKeyBinder{
label = "Toggle Auto Attack Hot key",
description = "The key and/or mouse combo to toggle auto attacking on or off.",
allowMouse = true,
allowCombinations = true,
---@type mwseKeyMouseCombo
variable = mwse.mcm.createTableVariable{ id = "hotKey", table = mcmConfig }
}
page:createSlider{
label = "Max Swing: %s%%",
description = "Determines how far back to pull the weapon before releasing while auto-attacking.",
variable = mwse.mcm.createTableVariable{ id = "maxSwing", table = mcmConfig },
min = 0,
max = 100,
jump = 10,
step = 1
}
page:createDropdown{
label = "Log Level",
description = "Set the logging level for mwse.log. Keep on INFO unless you are debugging.",
options = {
{ label = "TRACE", value = "TRACE"},
{ label = "DEBUG", value = "DEBUG"},
{ label = "INFO", value = "INFO"},
{ label = "ERROR", value = "ERROR"},
{ label = "NONE", value = "NONE"},
},
variable = mwse.mcm.createTableVariable{ id = "logLevel", table = mcmConfig },
callback = function(self)
for _, logger in pairs(util.loggers) do
logger:setLogLevel(self.variable.value)
end
end
}
end
event.register("modConfigReady", registerMCM)
util.lua:
local Util = {}
Util.config = require("mer.autoAttack.config")
Util.messageBox = require("mer.autoAttack.messageBox")
Util.loggers = {}
do --logger
local logLevel = Util.config.mcm.logLevel
local logger = require("logging.logger")
Util.log = logger.new{
name = Util.config.static.modName,
logLevel = logLevel
}
Util.createLogger = function(serviceName)
local logger = logger.new{
name = string.format("%s: %s", Util.config.static.modName, serviceName),
logLevel = logLevel
}
Util.loggers[serviceName] = logger
return logger
end
end
---@param pressed keyDownEventData|mouseButtonDownEventData
---@param stored keyDownEventData|mouseButtonDownEventData
function Util.isKeyPressed(pressed, stored)
if Util.config.mcm.controlKeysMatter then
return ( tes3.isKeyEqual({ expected = stored, actual = pressed }) )
--[[
return (
pressed.keyCode == stored.keyCode
and not not pressed.isShiftDown == not not stored.isShiftDown
and not not pressed.isControlDown == not not stored.isControlDown
and not not pressed.isAltDown == not not stored.isAltDown
and not not pressed.isSuperDown == not not stored.isSuperDown
)
]]--
else
pressed.isShiftDown = false
pressed.isControlDown = false
pressed.isAltDown = false
pressed.isSuperDown = false
stored.isShiftDown = false
stored.isControlDown = false
stored.isAltDown = false
stored.isSuperDown = false
return ( tes3.isKeyEqual({ expected = stored, actual = pressed }) )
--[[
return (
pressed.keyCode == stored.keyCode
)
]]--
end
end
---@param released keyUpEventData|mouseButtonUpEventData
---@param stored keyUpEventData|mouseButtonUpEventData
function Util.isKeyReleased(released, stored)
if Util.config.mcm.controlKeysMatter then
return ( tes3.isKeyEqual({ expected = stored, actual = released }) )
--[[
return (
released.keyCode == stored.keyCode
and not not released.isShiftDown == not not stored.isShiftDown
and not not released.isControlDown == not not stored.isControlDown
and not not released.isAltDown == not not stored.isAltDown
and not not released.isSuperDown == not not stored.isSuperDown
)
]]--
else
released.isShiftDown = false
released.isControlDown = false
released.isAltDown = false
released.isSuperDown = false
stored.isShiftDown = false
stored.isControlDown = false
stored.isAltDown = false
stored.isSuperDown = false
return ( tes3.isKeyEqual({ expected = stored, actual = released }) )
--[[
return (
released.keyCode == stored.keyCode
)
]]--
end
end
function Util.getVersion()
local versionFile = io.open("Data Files/MWSE/mods/mer/autoAttack/version.txt", "r")
if not versionFile then return end
local version = ""
for line in versionFile:lines() do -- Loops over all the lines in an open text file
version = line
end
return version
end
return Util
In further testing, I've noted that using a mouse button for your hotkey doesn't get saved correctly. autoAttack.json, the config file, correctly saves the hotkey, but when loaded it defaults to shift-Q, the default value. I didn't actually change anything in the MCM code for the hotkey except for adding allowMouse = true, so I'm not sure why this isn't saving correctly. The type of config saved would be a mwseKeyMouseCombo instead of a mwseKeyCombo but that's never actually mentioned in any of the lua or .json files and mwseKeyMouseCombo inherits mwseKeyCombo.
For now, for users, if you use a mouse button in your hotkey, you'll need to reset it every time you run the game. You can change this by manually coding it into config.lua but most people aren't going to do this.
Fixed, see post below. Above code doesn't have this error.