So I edited the lua script to work with the "x" key instead of the right mouse button - this is useful for folks like me that play on a Macbook were left and right click alternation is kinda funky @Xerxse feel free to upload it as an optional file it might help out some people local core = require('openmw.core') local input = require('openmw.input') local self = require('openmw.self') local time = require('openmw_aux.time') local types = require('openmw.types') local ui = require('openmw.ui') local anim = require('openmw.animation') local Controls = require('openmw.interfaces').Controls local storage = require('openmw.storage') -- For settings local async = require('openmw.async') -- For settings updates
local I = require('openmw.interfaces')
local isBlocking = false local hasShieldEquipped = false local isInWeaponStance = false local hasOneHandedWeapon = false local isBlockDebuffed = false -- Track if the debuff is currently applied
-- Skill and Attribute IDs local skills = types.NPC.stats.skills local attributes = types.Actor.stats.attributes
-- Default Settings local blockBuffPercent_default = 65 local weaponDebuffPercent_default = 35 local speedDebuffPercent_default = 35
-- Settings Keys local settingsGroupKey = 'Settings_ShieldBuffDebuff'
-- Runtime Settings local blockBuffPercent = blockBuffPercent_default local weaponDebuffPercent = weaponDebuffPercent_default local speedDebuffPercent = speedDebuffPercent_default
-- Store modifier values for saving/loading local savedBlockModifier = 0 local savedAxeModifier = 0 local savedBluntModifier = 0 local savedLongBladeModifier = 0 local savedShortBladeModifier = 0 local savedSpeedModifier = 0
-- Function to apply skill/attribute modifiers local function modifySkill(skill, percent, isBuff) if percent == 0 then return end local modValue = math.floor(skill.base * (percent / 100)) skill.modifier = skill.modifier + (isBuff and modValue or -modValue) return modValue -- Return the modifier value for saving end
local function modifyAttribute(attribute, percent, isBuff) if percent == 0 then return end local modValue = math.floor(attribute.base * (percent / 100)) attribute.modifier = attribute.modifier + (isBuff and modValue or -modValue) return modValue -- Return the modifier value for saving end
local function updateEquipmentStatus() local shield = types.Actor.getEquipment(self, types.Actor.EQUIPMENT_SLOT.CarriedLeft) hasShieldEquipped = shield ~= nil and shield.type == types.Armor
local weapon = types.Actor.getEquipment(self, types.Actor.EQUIPMENT_SLOT.CarriedRight) local weaponRecord = weapon and types.Weapon.records[weapon.recordId] local weaponTypes = types.Weapon.TYPE hasOneHandedWeapon = weaponRecord and ( weaponRecord.type == weaponTypes.AxeOneHand or weaponRecord.type == weaponTypes.ShortBladeOneHand or weaponRecord.type == weaponTypes.LongBladeOneHand or weaponRecord.type == weaponTypes.BluntOneHand ) end
local function updateCombatState() isInWeaponStance = types.Actor.getStance(self) == types.Actor.STANCE.Weapon end
local function shouldApplyBlockDebuff() return isInWeaponStance and hasShieldEquipped and hasOneHandedWeapon and not isBlocking end
local function applyBlockDebuff() if shouldApplyBlockDebuff() and not isBlockDebuffed then savedBlockModifier = modifySkill(skills.block(self), 100, false) -- Set Block skill to 0 isBlockDebuffed = true elseif not shouldApplyBlockDebuff() and isBlockDebuffed then modifySkill(skills.block(self), 100, true) -- Restore Block skill isBlockDebuffed = false end end
local function removeBlockDebuff() if isBlockDebuffed then modifySkill(skills.block(self), 100, true) -- Restore Block skill isBlockDebuffed = false end end
local function canBlock() return isInWeaponStance and hasShieldEquipped and hasOneHandedWeapon and not isBlocking end
local function blockBegin(key) local isPaused = core.isWorldPaused() local mode = I.UI and I.UI:getMode() -- Corrected way to access UI mode
if isPaused or mode == "Dialogue" or mode == "Inventory" then return -- Do nothing if paused or in dialogue/inventory end
updateEquipmentStatus() -- Ensure shield status is up-to-date if key.symbol == 'x' and canBlock() and hasShieldEquipped then -- Added hasShieldEquipped check isBlocking = true Controls.overrideCombatControls(true) removeBlockDebuff() -- Ensure the debuff is removed before applying the buff
local function onKeyRelease(key) if key.symbol == 'x' then local isPaused = core.isWorldPaused() local mode = I.UI and I.UI:getMode() -- Corrected way to access UI mode
if isPaused or mode == "Dialogue" or mode == "Inventory" then return -- Do nothing if paused or in dialogue/inventory end
I.Settings.registerGroup({ key = settingsGroupKey, page = 'ShieldsUp', l10n = 'ShieldsUp', name = 'Settings', permanentStorage = true, settings = { { key = 'blockBuffPercent', default = blockBuffPercent_default, renderer = 'number', name = 'Block Skill Buff (%)', description = 'Percentage to buff the Block skill when blocking. Default: 65', argument = { integer = true, min = 0, max = 100 }, }, { key = 'weaponDebuffPercent', default = weaponDebuffPercent_default, renderer = 'number', name = 'Weapon Skill Debuff (%)', description = 'Percentage to debuff weapon skills when blocking. Default: 35', argument = { integer = true, min = 0, max = 100 }, }, { key = 'speedDebuffPercent', default = speedDebuffPercent_default, renderer = 'number', name = 'Speed Attribute Debuff (%)', description = 'Percentage to debuff the Speed attribute when blocking. Default: 35', argument = { integer = true, min = 0, max = 100 }, }, }, })
-- Settings Update Function local settingsGroup = storage.playerSection(settingsGroupKey)
local function updateSettings() blockBuffPercent = settingsGroup:get('blockBuffPercent') weaponDebuffPercent = settingsGroup:get('weaponDebuffPercent') speedDebuffPercent = settingsGroup:get('speedDebuffPercent') end
Is a system that allows blocking possible with hand-to-hand/no shield and just a weapon? Or do you know if OpenMW wouldn't be able to support that yet?
how did you remove vanilla blocking? if you want you can build on the features of my mod too, it has its own animation for blocking and blocks based on a button. (edit I see you already have those features, great mod! still curious about how you removed vanilla block as it was an issue for me)
hey man, I used a tweaked version of your mod for some time and yeah I recall the auto blocking. It works by setting the min block chance to 0 and temporarily debuffing Block skill to 0
Probably out of scope for this mod, but I'd love to be able to block without a shield, with just a weapon alone, similar to the later games. Would probably involve a lot more work, though, so I get it if you're not interested.
Regardless, I really like this as it is, easy endorse from me :)
Instead of a speed debuff, is it possible to consume fatigue? Tanky classes are already played slow; speed usually isn't a favored attribute, so the sacrifice is sort of pointless. Melee's only hairy when you're pacing fatigue, so you don't get knocked down.
It's set to RMB for me. Where can I change that? It'd be compatible if UI-modes would only open its widget when the weapon is sheathed. Now it activates when raising a shield too. But UI-modes doesn't have a Nexus page, so can't really ask for this fix :)
Your mods are awesome dude. If you'll forgive me for being a bit cheeky, you wouldn't be able to make a death knock back mod a'la "rag n'wahs" (or bomberman for magic) could you? It's the only mod left that vanilla morrowind still has over openmw. Just in case you were looking for ideas
21 comments
local core = require('openmw.core')
local input = require('openmw.input')
local self = require('openmw.self')
local time = require('openmw_aux.time')
local types = require('openmw.types')
local ui = require('openmw.ui')
local anim = require('openmw.animation')
local Controls = require('openmw.interfaces').Controls
local storage = require('openmw.storage') -- For settings
local async = require('openmw.async') -- For settings updates
local I = require('openmw.interfaces')
local isBlocking = false
local hasShieldEquipped = false
local isInWeaponStance = false
local hasOneHandedWeapon = false
local isBlockDebuffed = false -- Track if the debuff is currently applied
-- Skill and Attribute IDs
local skills = types.NPC.stats.skills
local attributes = types.Actor.stats.attributes
-- Default Settings
local blockBuffPercent_default = 65
local weaponDebuffPercent_default = 35
local speedDebuffPercent_default = 35
-- Settings Keys
local settingsGroupKey = 'Settings_ShieldBuffDebuff'
-- Runtime Settings
local blockBuffPercent = blockBuffPercent_default
local weaponDebuffPercent = weaponDebuffPercent_default
local speedDebuffPercent = speedDebuffPercent_default
-- Store modifier values for saving/loading
local savedBlockModifier = 0
local savedAxeModifier = 0
local savedBluntModifier = 0
local savedLongBladeModifier = 0
local savedShortBladeModifier = 0
local savedSpeedModifier = 0
-- Function to apply skill/attribute modifiers
local function modifySkill(skill, percent, isBuff)
if percent == 0 then return end
local modValue = math.floor(skill.base * (percent / 100))
skill.modifier = skill.modifier + (isBuff and modValue or -modValue)
return modValue -- Return the modifier value for saving
end
local function modifyAttribute(attribute, percent, isBuff)
if percent == 0 then return end
local modValue = math.floor(attribute.base * (percent / 100))
attribute.modifier = attribute.modifier + (isBuff and modValue or -modValue)
return modValue -- Return the modifier value for saving
end
local function updateEquipmentStatus()
local shield = types.Actor.getEquipment(self, types.Actor.EQUIPMENT_SLOT.CarriedLeft)
hasShieldEquipped = shield ~= nil and shield.type == types.Armor
local weapon = types.Actor.getEquipment(self, types.Actor.EQUIPMENT_SLOT.CarriedRight)
local weaponRecord = weapon and types.Weapon.records[weapon.recordId]
local weaponTypes = types.Weapon.TYPE
hasOneHandedWeapon = weaponRecord and (
weaponRecord.type == weaponTypes.AxeOneHand or
weaponRecord.type == weaponTypes.ShortBladeOneHand or
weaponRecord.type == weaponTypes.LongBladeOneHand or
weaponRecord.type == weaponTypes.BluntOneHand
)
end
local function updateCombatState()
isInWeaponStance = types.Actor.getStance(self) == types.Actor.STANCE.Weapon
end
local function shouldApplyBlockDebuff()
return isInWeaponStance and hasShieldEquipped and hasOneHandedWeapon and not isBlocking
end
local function applyBlockDebuff()
if shouldApplyBlockDebuff() and not isBlockDebuffed then
savedBlockModifier = modifySkill(skills.block(self), 100, false) -- Set Block skill to 0
isBlockDebuffed = true
elseif not shouldApplyBlockDebuff() and isBlockDebuffed then
modifySkill(skills.block(self), 100, true) -- Restore Block skill
isBlockDebuffed = false
end
end
local function removeBlockDebuff()
if isBlockDebuffed then
modifySkill(skills.block(self), 100, true) -- Restore Block skill
isBlockDebuffed = false
end
end
local function canBlock()
return isInWeaponStance and hasShieldEquipped and hasOneHandedWeapon and not isBlocking
end
local function blockBegin(key)
local isPaused = core.isWorldPaused()
local mode = I.UI and I.UI:getMode() -- Corrected way to access UI mode
if isPaused or mode == "Dialogue" or mode == "Inventory" then
return -- Do nothing if paused or in dialogue/inventory
end
updateEquipmentStatus() -- Ensure shield status is up-to-date
if key.symbol == 'x' and canBlock() and hasShieldEquipped then -- Added hasShieldEquipped check
isBlocking = true
Controls.overrideCombatControls(true)
removeBlockDebuff() -- Ensure the debuff is removed before applying the buff
-- Apply Buffs/Debuffs
savedBlockModifier = modifySkill(skills.block(self), blockBuffPercent, true)
savedAxeModifier = modifySkill(skills.axe(self), weaponDebuffPercent, false)
savedBluntModifier = modifySkill(skills.bluntweapon(self), weaponDebuffPercent, false)
savedLongBladeModifier = modifySkill(skills.longblade(self), weaponDebuffPercent, false)
savedShortBladeModifier = modifySkill(skills.shortblade(self), weaponDebuffPercent, false)
savedSpeedModifier = modifyAttribute(attributes.speed(self), speedDebuffPercent, false)
I.AnimationController.playBlendedAnimation('shieldraise', {
startKey = 'start',
stopKey = 'stop',
priority = {
[anim.BONE_GROUP.LeftArm] = anim.PRIORITY.Weapon,
},
autoDisable = false,
blendMask = anim.BLEND_MASK.LeftArm + anim.BLEND_MASK.Torso + anim.BLEND_MASK.RightArm,
})
end
end
local function onKeyRelease(key)
if key.symbol == 'x' then
local isPaused = core.isWorldPaused()
local mode = I.UI and I.UI:getMode() -- Corrected way to access UI mode
if isPaused or mode == "Dialogue" or mode == "Inventory" then
return -- Do nothing if paused or in dialogue/inventory
end
isBlocking = false
Controls.overrideCombatControls(false)
-- Remove Buffs/Debuffs
modifySkill(skills.block(self), blockBuffPercent, false)
modifySkill(skills.axe(self), weaponDebuffPercent, true)
modifySkill(skills.bluntweapon(self), weaponDebuffPercent, true)
modifySkill(skills.longblade(self), weaponDebuffPercent, true)
modifySkill(skills.shortblade(self), weaponDebuffPercent, true)
modifyAttribute(attributes.speed(self), speedDebuffPercent, true)
-- Reapply the debuff *after* the buffs are removed and blocking has stopped
applyBlockDebuff()
I.AnimationController.playBlendedAnimation('idle1', {
startKey = 'loop start',
stopKey = 'loop stop',
priority = {
[anim.BONE_GROUP.LeftArm] = anim.PRIORITY.Weapon,
},
autoDisable = true,
})
end
end
-- Settings Registration
I.Settings.registerPage({
key = 'ShieldsUp',
l10n = 'ShieldsUp',
name = 'Shields Up',
description = 'Improved vanilla block experience. By Xe',
})
I.Settings.registerGroup({
key = settingsGroupKey,
page = 'ShieldsUp',
l10n = 'ShieldsUp',
name = 'Settings',
permanentStorage = true,
settings = {
{
key = 'blockBuffPercent',
default = blockBuffPercent_default,
renderer = 'number',
name = 'Block Skill Buff (%)',
description = 'Percentage to buff the Block skill when blocking. Default: 65',
argument = { integer = true, min = 0, max = 100 },
},
{
key = 'weaponDebuffPercent',
default = weaponDebuffPercent_default,
renderer = 'number',
name = 'Weapon Skill Debuff (%)',
description = 'Percentage to debuff weapon skills when blocking. Default: 35',
argument = { integer = true, min = 0, max = 100 },
},
{
key = 'speedDebuffPercent',
default = speedDebuffPercent_default,
renderer = 'number',
name = 'Speed Attribute Debuff (%)',
description = 'Percentage to debuff the Speed attribute when blocking. Default: 35',
argument = { integer = true, min = 0, max = 100 },
},
},
})
-- Settings Update Function
local settingsGroup = storage.playerSection(settingsGroupKey)
local function updateSettings()
blockBuffPercent = settingsGroup:get('blockBuffPercent')
weaponDebuffPercent = settingsGroup:get('weaponDebuffPercent')
speedDebuffPercent = settingsGroup:get('speedDebuffPercent')
end
settingsGroup:subscribe(async:callback(updateSettings))
-- Save and Load Handlers
local function onSave()
return {
isBlocking = isBlocking,
isBlockDebuffed = isBlockDebuffed,
savedBlockModifier = savedBlockModifier,
savedAxeModifier = savedAxeModifier,
savedBluntModifier = savedBluntModifier,
savedLongBladeModifier = savedLongBladeModifier,
savedShortBladeModifier = savedShortBladeModifier,
savedSpeedModifier = savedSpeedModifier,
}
end
local function onLoad(data)
if data then
isBlocking = data.isBlocking
isBlockDebuffed = data.isBlockDebuffed
-- Reapply modifiers (undo previous and then reapply)
if data.savedBlockModifier ~= 0 then
modifySkill(skills.block(self), data.savedBlockModifier * 100 /
math.floor(skills.block(self).base * (100 / 100)), false) -- Undo
modifySkill(skills.block(self), data.savedBlockModifier * 100 /
math.floor(skills.block(self).base * (100 / 100)), true) -- Reapply
savedBlockModifier = data.savedBlockModifier
end
if data.savedAxeModifier ~= 0 then
modifySkill(skills.axe(self), data.savedAxeModifier * 100 /
math.floor(skills.axe(self).base * (100 / 100)), true) -- Undo
modifySkill(skills.axe(self), data.savedAxeModifier * 100 /
math.floor(skills.axe(self).base * (100 / 100)), false) -- Reapply
savedAxeModifier = data.savedAxeModifier
end
if data.savedBluntModifier ~= 0 then
modifySkill(skills.bluntweapon(self), data.savedBluntModifier * 100 /
math.floor(skills.bluntweapon(self).base * (100 / 100)), true) -- Undo
modifySkill(skills.bluntweapon(self), data.savedBluntModifier * 100 /
math.floor(skills.bluntweapon(self).base * (100 / 100)), false) --
Reapply
savedBluntModifier = data.savedBluntModifier
end
if data.savedLongBladeModifier ~= 0 then
modifySkill(skills.longblade(self), data.savedLongBladeModifier * 100 /
math.floor(skills.longblade(self).base * (100 / 100)), true) -- Undo
modifySkill(skills.longblade(self), data.savedLongBladeModifier * 100 /
math.floor(skills.longblade(self).base * (100 / 100)), false) --
Reapply
savedLongBladeModifier = data.savedLongBladeModifier
end
if data.savedShortBladeModifier ~= 0 then
modifySkill(skills.shortblade(self), data.savedShortBladeModifier * 100
/ math.floor(skills.shortblade(self).base * (100 / 100)), true) -- Undo
modifySkill(skills.shortblade(self), data.savedShortBladeModifier * 100
/ math.floor(skills.shortblade(self).base * (100 / 100)), false) --
Reapply
savedShortBladeModifier = data.savedShortBladeModifier
end
if data.savedSpeedModifier ~= 0 then
modifyAttribute(attributes.speed(self), data.savedSpeedModifier * 100 /
math.floor(attributes.speed(self).base * (100 / 100)), true) -- Undo
modifyAttribute(attributes.speed(self), data.savedSpeedModifier * 100 /
math.floor(attributes.speed(self).base * (100 / 100)), false) --
Reapply
savedSpeedModifier = data.savedSpeedModifier
end
end
end
return {
engineHandlers = {
onKeyPress = blockBegin,
onKeyRelease = onKeyRelease,
onEquip = function()
updateEquipmentStatus()
applyBlockDebuff()
end,
onUnequip = function()
updateEquipmentStatus()
applyBlockDebuff()
end,
onStanceChange = function()
updateCombatState()
updateEquipmentStatus()
applyBlockDebuff()
end,
onActive = function()
updateSettings() -- Initialize settings on activation
updateCombatState()
updateEquipmentStatus()
applyBlockDebuff()
end,
onDeactivate = function()
removeBlockDebuff()
end,
onUpdate = function(dt)
updateCombatState()
updateEquipmentStatus()
-- Only apply the debuff if *not* actively blocking
if not isBlocking then
applyBlockDebuff()
end
end,
onSave = onSave,
onLoad = onLoad,
},
}
Regardless, I really like this as it is, easy endorse from me :)
but if I could somehow dream up an elaborate stunt then lol why not