Module:Monsters: Difference between revisions

_getMonsterMaxHit: Further fixes for various scenarios which weren't previously accounted for
(_getMonsterMaxHit: Consider curses which may increase damage taken by the player)
(_getMonsterMaxHit: Further fixes for various scenarios which weren't previously accounted for)
Line 389: Line 389:


function p.canSpecAttackApplyEffect(specAttack, effectType)
function p.canSpecAttackApplyEffect(specAttack, effectType)
for i, effect in pairs(specAttack.prehitEffects) do
local effectKeys = { 'prehitEffects', 'onhitEffects' }
if effect.type == effectType then
for i, effectKey in ipairs(effectKeys) do
            return true
if type(specAttack[effectKey]) == 'table' then
for j, effect in pairs(specAttack[effectKey]) do
if effect.type == effectType or p.canModifiersApplyEffect(effect.modifiers, effectType) then
return true
end
end
end
end
end
end
return false
end
function p.canModifiersApplyEffect(modifiers, effectType)
-- List of modifiers which can result in the application of status effects
local statusModsAll = {
["Stun"] = { 'increasedGlobalStunChance', 'increasedMeleeStunChance' },
["Sleep"] = { 'increasedGlobalSleepChance' },
["Poison"] = { 'increasedChanceToApplyPoison' },
["Slow"] = { 'increased15SlowStunChance2Turns', 'increased30Slow5TurnsChance' }
}


for i, effect in pairs(specAttack.onhitEffects) do
local statusMods = statusModsAll[effectType]
if effect.type == effectType then
if statusMods ~= nil and type(modifiers) == 'table' then
return true
for modName, modMagnitude in pairs(modifiers) do
if Shared.contains(statusMods, modName) then
return true
end
end
end
end
end
Line 412: Line 431:
end
end


-- Damage adjustments are defined as follows:
-- multiplier - Damage from modifier 'increasedDamageTaken' & additional damage while
-- stunned, asleep, or poisoned. Defined by in-game function
-- getDamageModifiers(). Applies after other percentage of flat adjustments.
-- percent - Percentage adjustments to the max hit. Applies before flat & multiplier
-- adjustments.
-- flat - Flat adjustments to the max hit. Applies after percent adjustments, and
-- after multiplier adjustments.
local dmgAdjust = { ["percent"] = 100, ["flat"] = 0, ["multiplier"] = 100 }
local dmgAdjust = { ["percent"] = 100, ["flat"] = 0, ["multiplier"] = 100 }
-- Check passives & effects that apply pre or on hit for damage modifiers
-- Check passives & effects that apply pre or on hit for damage modifiers
Line 421: Line 448:
["increasedRangedMaxHit"] = { type = 'percent', mult = 1 },
["increasedRangedMaxHit"] = { type = 'percent', mult = 1 },
["increasedMagicMaxHit"] = { type = 'percent', mult = 1 },
["increasedMagicMaxHit"] = { type = 'percent', mult = 1 },
["increasedMeleeMaxHitFlat"] = { type = 'flat', mult = 1 },
["increasedMaxHitFlat"] = { type = 'flat', mult = 10 },
["increasedRangedMaxHitFlat"] = { type = 'flat', mult = 1 },
["increasedMeleeMaxHitFlat"] = { type = 'flat', mult = 10 },
["increasedMagicMaxHitFlat"] = { type = 'flat', mult = 1 },
["increasedRangedMaxHitFlat"] = { type = 'flat', mult = 10 },
["increasedRage"] = { type = 'percent', mult = 2, maxStacks = 10 }
["increasedMagicMaxHitFlat"] = { type = 'flat', mult = 10 },
-- Rage: +2% max hit per stack, maximum of 10 stacks
["increasedRage"] = { type = 'percent', mult = 1, magnitude = 2, maxStacks = 10 },
-- Dark Blade: +1% max hit per successful hit, maximum of 30 stacks
["increasedChanceDarkBlade"] = { type = 'percent', mult = 1, magnitude = 1, maxStacks = 30 },
-- Growing Madness/Moment in Time/Reign Over Time: +2% max hit per stack, maximum of 25 stacks
["growingMadnessPassive"] = { type = 'percent', mult = 1, magnitude = 2, maxStacks = 25 },
["momentInTimePassive"] = { type = 'percent', mult = 1, magnitude = 2, maxStacks = 25 },
["reignOverTimePassive"] = { type = 'percent', mult = 1, magnitude = 2, maxStacks = 25 }
}
}
local effectKeys = { 'prehitEffects', 'onhitEffects' }
local effectKeys = { 'prehitEffects', 'onhitEffects' }
-- Check monster passives for modifiers which affect damage dealt
local dmgStatuses = {
-- List of status effects which can affect damage dealt
["Stun"] = { type = 'multiplier', magnitude = 30 },
["Sleep"] = { type = 'multiplier', magnitude = 20 }
}
local canApplyStatus = {}
-- Initialize table
for statusName, def in pairs(dmgStatuses) do
canApplyStatus[statusName] = false
end
 
local adjustForMod = function(mod, modMagnitude, effect)
local magnitude = mod.magnitude or modMagnitude
local maxStacks = mod.maxStacks or (effect ~= nil and effect.maxStacks) or 1
dmgAdjust[mod.type] = dmgAdjust[mod.type] + magnitude * mod.mult * maxStacks
end
 
local adjustForCurse = function(curseID, effect)
local curse = Magic.getSpellByID(curseID, 'curse')
if type(curse) == 'table' and type(curse.targetModifiers) == 'table' then
for modName, modMagnitude in pairs(curse.targetModifiers) do
local mod = dmgMods[modName]
if mod ~= nil then
-- The modifier is one which affects damage dealt
adjustForMod(mod, modMagnitude, effect)
end
end
end
end
 
-- Check monster passives for modifiers which affect damage dealt, and alo if any modifiers
-- present can apply stun or sleep
if monster ~= nil and type(monster.passives) ~= nil then
if monster ~= nil and type(monster.passives) ~= nil then
for i, passiveID in ipairs(monster.passives) do
for i, passiveID in ipairs(monster.passives) do
Line 434: Line 500:
for modName, modMagnitude in pairs(passive.modifiers) do
for modName, modMagnitude in pairs(passive.modifiers) do
local mod = dmgMods[modName]
local mod = dmgMods[modName]
if mod ~= nil then
if modName == 'applyRandomCurseOnSpawn' then
-- Special case in which the enemy can apply a random curse. Currently
-- Anguish III is the curse with the highest +% damage taken, so use this.
adjustForCurse('melvorF:AnguishIII')
elseif mod ~= nil then
-- The modifier is one which affects damage dealt
-- The modifier is one which affects damage dealt
local maxStacks = mod.maxStacks or 1
adjustForMod(mod, modMagnitude)
dmgAdjust[mod.type] = dmgAdjust[mod.type] + modMagnitude * mod.mult * maxStacks
end
end
-- Check for application of relevant status effects
if doStuns then
for statusName, statusDef in pairs(dmgStatuses) do
if not canApplyStatus[statusName] and p.canModifiersApplyEffect(passive.modifiers, statusName) then
canApplyStatus[statusName] = true
end
end
end
end
end
Line 449: Line 526:
local hasActiveBuffSpec = false
local hasActiveBuffSpec = false
if monster.specialAttacks ~= nil then
if monster.specialAttacks ~= nil then
local canStun, canSleep = false, false
for i, specAttackID in pairs(monster.specialAttacks) do
for i, specAttackID in pairs(monster.specialAttacks) do
             local specAttack = GameData.getEntityByID('attacks', specAttackID)
             local specAttack = GameData.getEntityByID('attacks', specAttackID)
Line 463: Line 539:
if mod ~= nil then
if mod ~= nil then
-- The modifier is one which affects damage dealt
-- The modifier is one which affects damage dealt
local maxStacks = mod.maxStacks or effect.maxStacks or 1
adjustForMod(mod, modMagnitude, effect)
dmgAdjust[mod.type] = dmgAdjust[mod.type] + modMagnitude * mod.mult * maxStacks
end
end
end
end
end
end
-- Check for curses which may cause the player to incur additional damage
-- Check for curses which may cause the player to incur additional damage
if effect.effectType == 'Curse' and effect.curse ~= nil then
if effect.effectType == 'Curse' then
local curse = Magic.getSpellByID(effect.curse, 'curse')
-- If isRandom is true then a random curse is selected. Currently
if type(curse) == 'table' and type(curse.targetModifiers) == 'table' then
-- Anguish III is the curse with the highest +% damage taken, so
for modName, modMagnitude in pairs(curse.targetModifiers) do
-- use this.
local mod = dmgMods[modName]
local curseID = (effect.isRandom and 'melvorF:AnguishIII') or effect.curse
if mod ~= nil then
if curseID ~= nil then
-- The modifier is one which affects damage dealt
adjustForCurse(curseID, effect)
local maxStacks = mod.maxStacks or effect.maxStacks or 1
dmgAdjust[mod.type] = dmgAdjust[mod.type] + modMagnitude * mod.mult * maxStacks
end
end
end
end
end
end
Line 492: Line 563:
normalChance = normalChance - specAttack.defaultChance
normalChance = normalChance - specAttack.defaultChance
end
end
-- Check for application of relevant status effects
if doStuns then
for statusName, statusDef in pairs(dmgStatuses) do
if not canApplyStatus[statusName] and p.canSpecAttackApplyEffect(specAttack, statusName) then
canApplyStatus[statusName] = true
end
end
end
local thisMax = p.getSpecAttackMaxHit(specAttack, normalMaxHit, monster)
local thisMax = p.getSpecAttackMaxHit(specAttack, normalMaxHit, monster)
if not canStun and p.canSpecAttackApplyEffect(specAttack, 'Stun') then canStun = true end
if not canSleep and p.canSpecAttackApplyEffect(specAttack, 'Sleep') then canSleep = true end
if thisMax > specialMaxHit then specialMaxHit = thisMax end
if thisMax > specialMaxHit then specialMaxHit = thisMax end
if Shared.contains(string.upper(specAttack.description), 'NORMAL ATTACK INSTEAD') then  
if Shared.contains(string.upper(specAttack.description), 'NORMAL ATTACK INSTEAD') then  
Line 502: Line 580:
end
end


if canSleep and doStuns then dmgAdjust.multiplier = dmgAdjust.multiplier + 20 end
if doStuns then
if canStun and doStuns then dmgAdjust.multiplier = dmgAdjust.multiplier + 30 end
for statusName, statusDef in pairs(dmgStatuses) do
if canApplyStatus[statusName] then
local adjType = statusDef.type
dmgAdjust[adjType] = dmgAdjust[adjType] + statusDef.magnitude
end
end
end
end
end
--Ensure that if the monster never does a normal attack, the normal max hit is irrelevant
--Ensure that if the monster never does a normal attack, the normal max hit is irrelevant