diff --git a/Data Files/MWSE/mods/mer/ashfall/activators/config/activatorConfig.lua b/Data Files/MWSE/mods/mer/ashfall/activators/config/activatorConfig.lua index 42e87d4c..7398f936 100644 --- a/Data Files/MWSE/mods/mer/ashfall/activators/config/activatorConfig.lua +++ b/Data Files/MWSE/mods/mer/ashfall/activators/config/activatorConfig.lua @@ -1,11 +1,15 @@ local Activator = require("mer.ashfall.activators.Activator") local this = {} ---[[ - TODO: - Move configs into integrations, move tables into Activator, - then remove this file. -]] +---@class Ashfall.Activator.Config +---@field name? string The name of the activator +---@field type string The type of the activator +---@field mcmSetting? string The name of the MCM setting that controls whether this activator is active +---@field ids? table A table of ids to register for this activator. Key: id, Value: table of data +---@field patterns? table A table of patterns to register for this activator. Key: pattern, Value: true +---@field isStewer boolean Whether this activator is a stewer +---@field owned? boolean Whether this activator is owned by NPCs, such as kegs + this.types = { waterSource = "waterSource", diff --git a/Data Files/MWSE/mods/mer/ashfall/harvest/init.lua b/Data Files/MWSE/mods/mer/ashfall/harvest/init.lua index 8b08d6df..ee43465a 100644 --- a/Data Files/MWSE/mods/mer/ashfall/harvest/init.lua +++ b/Data Files/MWSE/mods/mer/ashfall/harvest/init.lua @@ -1,186 +1,117 @@ -local activatorController = require("mer.ashfall.activators.activatorController") -local harvestConfigs = require("mer.ashfall.harvest.config") local common = require("mer.ashfall.common.common") local logger = common.createLogger("harvestController") local config = require("mer.ashfall.config").config local service = require("mer.ashfall.harvest.service") - - -event.register("loaded", function() - timer.start{ - type = timer.real, - iterations = -1, - duration = 0.1, - callback = service.updateDisabledHarvestables - } -end) - +--- Attempt a harvest on attack swing ---@param e attackEventData local function harvestOnAttack(e) logger:debug("harvestOnAttack() ENTRY") - --Get the necessary objects and check conditions-- - --Filter to player if e.mobile.reference ~= tes3.player then logger:debug("Harvest: Not player") return end - - --Get player target Activator - local activator = activatorController.getCurrentActivator() - if not activator then - logger:debug("Harvest: No activator") - return - end - - --Get activator Ref - local reference = activatorController.getCurrentActivatorReference() - if not reference then - logger:debug("Harvest: No reference") + local data = service.getCurrentHarvestData() + if not data then + logger:debug("Harvest: No current harvest data") return end - - --Get harvest config from activator - ---@type Ashfall.Harvest.Config - local harvestConfig = harvestConfigs.activatorHarvestData[activator.type] - if not harvestConfig then - logger:debug("Harvest: No harvest config") - logger:debug("activatorType: %s", activator.type) - return - end - - --Get Player Weapon - local weapon = tes3.player.mobile.readiedWeapon - if not weapon then - logger:debug("Harvest: No weapon") - return - end - - --Get harvest data from weapon - local weaponData = service.getWeaponHarvestData(weapon, harvestConfig) - if not weaponData then - logger:debug("Harvest: No weapon data") - return - end - - --Check if Activator is active - local activatorActive = config[activator.mcmSetting] ~= false - if not activatorActive then - logger:debug("Harvest: Activator not active") - return - end - - --Return if illegal to harvest - if service.checkIllegalToHarvest() then - service.showIllegalToHarvestMessage(harvestConfig) - logger:debug("Harvest: Illegal to harvest") - return - end - - --Check attack direction - if not service.validAttackDirection(harvestConfig) then - logger:debug("Harvest: Invalid attack direction") - return - end - - --Check if activator is already harvested - if service.checkHarvested(reference) then - logger:debug("Harvest: Can't harvest, already harvested") - return - end - logger:debug("Checks passed, swinging") --CHECKS PASS, we are swinging at something - service.playSound(harvestConfig) - + service.playSound(data.harvestConfig) --Get strength of swing - local swingStrength = service.getSwingStrength(weapon, weaponData) - + local swingStrength = service.getSwingStrength(data.weapon, data.weaponData) --Degrade weapon and exit if it breaks - local weaponBroke = service.degradeWeapon(weapon, swingStrength, weaponData.degradeMulti) + local weaponBroke = service.degradeWeapon(data.weapon, swingStrength, data.weaponData.degradeMulti) if weaponBroke then logger:debug("Weapon broke") return end - --Accumulate swings and check if it's enough to harvest - local didHarvest = service.attemptSwing(swingStrength, reference, harvestConfig.swingsNeeded) + local didHarvest = service.attemptSwing(swingStrength, data.reference, data.harvestConfig.swingsNeeded) if not didHarvest then return end logger:debug("Enough swings, harvesting") --Harvest the resources - service.harvest(reference, harvestConfig) + service.harvest(data.reference, data.harvestConfig) --Disable if exhausted - if harvestConfig.destructionLimitConfig and config.disableHarvested then + if data.harvestConfig.destructionLimitConfig and config.disableHarvested then logger:debug("Disabling exhausted harvestable") - service.disableExhaustedHarvestable(reference, harvestConfig) + service.disableExhaustedHarvestable(data.reference, data.harvestConfig) end logger:debug("harvestOnAttack() EXIT") end event.register("attack", harvestOnAttack ) --- Force a chop action if looking at a harvestable + +--- Force a chop action if looking at a harvestable with a valid weapon ---@param e attackStartEventData event.register("attackStart", function(e) - --Get player target Activator - local activator = activatorController.getCurrentActivator() - if not activator then - logger:debug("Harvest: No activator") - return - end - - - logger:debug("Activator type: %s", activator.type) - - --Get harvest config from activator - ---@type Ashfall.Harvest.Config - local harvestConfig = harvestConfigs.activatorHarvestData[activator.type] - if not harvestConfig then - logger:debug("Harvest: No harvest config") - return - end - - --Get Player Weapon - local weapon = tes3.player.mobile.readiedWeapon - if not weapon then - logger:debug("Harvest: No weapon") + --Filter to player + if e.reference ~= tes3.player then + logger:debug("Harvest: Not player") return end - - --Get harvest data from weapon - local weaponData = service.getWeaponHarvestData(weapon, harvestConfig) - if not weaponData then - logger:debug("Harvest: No weapon data") + local data = service.getCurrentHarvestData({ ignoreAttackDirection = true}) + if not data then + logger:debug("Harvest: Not ready to harvest") return end - - if not (harvestConfig.attackDirections and table.size(harvestConfig.attackDirections) > 0) then + local hasAttackDirections = data.harvestConfig.attackDirections + and table.size(data.harvestConfig.attackDirections) > 0 + if not hasAttackDirections then logger:debug("No attack directions") return end - --Check if current attack direction doesn't match valid directions for this harvestable - if not harvestConfig.attackDirections[e.attackType] then - if harvestConfig.defaultAttackDirection then - logger:debug("Forcing attack type %s", table.find(tes3.physicalAttackType, harvestConfig.defaultAttackDirection)) - e.attackType = harvestConfig.defaultAttackDirection - else - --set to first one in list - for attackType, _ in pairs(harvestConfig.attackDirections) do - logger:debug("Forcing attack type %s", table.find(tes3.physicalAttackType, attackType)) - e.attackType = attackType - break - end + if data.harvestConfig.attackDirections[e.attackType] then + --already a valid direction + return + end + if data.harvestConfig.defaultAttackDirection then + logger:debug("Forcing default attack type %s", table.find(tes3.physicalAttackType, data.harvestConfig.defaultAttackDirection)) + e.attackType = data.harvestConfig.defaultAttackDirection + else + --set to first one in list + for attackType, _ in pairs(data.harvestConfig.attackDirections) do + logger:debug("Forcing attack type %s", table.find(tes3.physicalAttackType, attackType)) + e.attackType = attackType + break end end - end) +--- Block swing sounds when harvesting +---@param e addSoundEventData +event.register("addSound", function(e) + --filter to player + if e.reference ~= tes3.player then return end + local data = service.getCurrentHarvestData() + if not data then return end + local swishSounds = { + ["swishl"] = true, + ["swishm"] = true, + ["swishs"] = true, + ["weapon swish"] = true, + ["miss"] = true, + } + if swishSounds[e.sound.id:lower()] then + logger:debug("Blocking vanilla weapon swish sound") + return false + end +end, { priority = 500}) + --- Reset harvestables on load. event.register("loaded", function() service.destroyedHarvestables:iterate(function(reference) service.enableHarvestable(reference) end) + timer.start{ + type = timer.real, + iterations = -1, + duration = 0.1, + callback = service.updateDisabledHarvestables + } end) --- Clear any data added when an item was felled from a tree. diff --git a/Data Files/MWSE/mods/mer/ashfall/harvest/service.lua b/Data Files/MWSE/mods/mer/ashfall/harvest/service.lua index eff7250c..80edc9ca 100644 --- a/Data Files/MWSE/mods/mer/ashfall/harvest/service.lua +++ b/Data Files/MWSE/mods/mer/ashfall/harvest/service.lua @@ -34,6 +34,81 @@ function HarvestService.showIllegalToHarvestMessage(harvestConfig) tes3.messageBox("You must be in the wilderness to harvest.") end +---@class Ashfall.HarvestService.getCurrentHarvestData.params +---@field ignoreAttackDirection boolean If true, will ignore the attack direction check + +--Get the config for the current harvestable +---@return Ashfall.Harvest.CurrentHarvestData|nil +---@param e Ashfall.HarvestService.getCurrentHarvestData.params|nil +function HarvestService.getCurrentHarvestData(e) + e = e or {} + --Get player target Activator + local activator = ActivatorController.getCurrentActivator() + if not activator then + logger:trace("Harvest: No activator") + return + end + --Get activator Ref + local reference = ActivatorController.getCurrentActivatorReference() + if not reference then + logger:debug("Harvest: No reference") + return + end + --Get harvest config from activator + ---@type Ashfall.Harvest.Config + local harvestConfig = harvestConfigs.activatorHarvestData[activator.type] + if not harvestConfig then + logger:trace("Harvest: No harvest config") + return + end + --Get player Weapon + local weapon = tes3.player.mobile.readiedWeapon + if not weapon then + logger:debug("Harvest: No weapon") + return + end + --Get harvest data from weapon + local weaponData = HarvestService.getWeaponHarvestData(weapon, harvestConfig) + if not weaponData then + logger:debug("Harvest: No weapon data") + return + end + --Check if Activator is active + local activatorActive = config[activator.mcmSetting] ~= false + if not activatorActive then + logger:debug("Harvest: Activator not active") + return + end + --Return if illegal to harvest + if HarvestService.checkIllegalToHarvest() then + HarvestService.showIllegalToHarvestMessage(harvestConfig) + logger:debug("Harvest: Illegal to harvest") + return + end + if not e.ignoreAttackDirection then + --Check attack direction + if not HarvestService.validAttackDirection(harvestConfig) then + logger:debug("Harvest: Invalid attack direction") + return + end + end + --Check if activator is already harvested + if HarvestService.checkHarvested(reference) then + logger:debug("Harvest: Can't harvest, already harvested") + return + end + --All checks pass, return the harvest data + local currentHarvestData = { + reference = reference, + activator = activator, + harvestConfig = harvestConfig, + weapon = weapon, + weaponData = weaponData + } + return currentHarvestData +end + + ---@param weapon tes3equipmentStack ---@param harvestConfig Ashfall.Harvest.Config ---@return Ashfall.Harvest.WeaponData | nil @@ -77,15 +152,19 @@ function HarvestService.getWeaponHarvestData(weapon, harvestConfig) end end +function HarvestService.getAttackDirection() + return tes3.mobilePlayer.actionData.attackDirection ---@diagnostic disable-line +end + function HarvestService.validAttackDirection(harvestConfig) - local attackDirection = tes3.mobilePlayer.actionData.attackDirection + local attackDirection = HarvestService.getAttackDirection() return harvestConfig.attackDirections[attackDirection] end ---@param weapon tes3equipmentStack ---@return number damageEffect function HarvestService.getDamageEffect(weapon) - local attackDirection = tes3.mobilePlayer.actionData.attackDirection + local attackDirection = HarvestService.getAttackDirection() local maxField = harvestConfigs.attackDirectionMapping[attackDirection].max local maxDamage = weapon.object[maxField] logger:trace("maxDamage: %s", maxDamage) @@ -158,10 +237,10 @@ function HarvestService.degradeWeapon(weapon, swingStrength, degradeMulti) degradeMulti = degradeMulti or 1.0 logger:trace("degrade multiplier: %s", degradeMulti) --Weapon degradation - weapon.variables.condition = weapon.variables.condition - (4 * swingStrength * degradeMulti) + weapon.itemData.condition = weapon.itemData.condition - (4 * swingStrength * degradeMulti) --weapon is broken, unequip - if weapon.variables.condition <= 0 then - weapon.variables.condition = 0 + if weapon.itemData.condition <= 0 then + weapon.itemData.condition = 0 tes3.mobilePlayer:unequip{ type = tes3.objectType.weapon } return true end diff --git a/Data Files/MWSE/mods/mer/ashfall/harvest/types.lua b/Data Files/MWSE/mods/mer/ashfall/harvest/types.lua index cec0274d..8b3e32c1 100644 --- a/Data Files/MWSE/mods/mer/ashfall/harvest/types.lua +++ b/Data Files/MWSE/mods/mer/ashfall/harvest/types.lua @@ -28,3 +28,10 @@ ---@field fallSound string The sound to play when the harvestable is destroyed ---@field clutter table A list of clutter items that are destroyed alongside this harvestable. ---@field dropLoot boolean If set, any items sitting on top of the reference will be "dropped" to the ground + +---@class Ashfall.Harvest.CurrentHarvestData +---@field reference tes3reference The current harvestable reference +---@field activator Ashfall.Activator.Config The current activator +---@field harvestConfig Ashfall.Harvest.Config The current harvest config +---@field weapon tes3equipmentStack The currently equipped weapon +---@field weaponData Ashfall.Harvest.WeaponData The weapon data for the currently equipped weapon \ No newline at end of file