Module:Sandbox/User:CephHunter/Ruby bolts

Documentation for this module may be created at Module:Sandbox/User:CephHunter/Ruby bolts/doc

-- <nowiki>
require( 'Module:Mw.html extension' )
local chart = require( 'Module:Chart data' )
local yesno = require( 'Module:Yesno' )
local commas = require( 'Module:Addcommas' )._add
local p = {}

local function round( num )
    return math.floor( num * 10 + 0.5 ) / 10
end

local function fnum( num )
    return commas( round( num ) )
end

local function damage( maxHp, currentHp, cap )
    local dmgPercent = math.max( 1, math.floor( 20 * currentHp / maxHp ) ) / 100
    local dmg = math.floor( dmgPercent * currentHp )
    return math.min( dmg, cap )
end

local function triggerChance( seers, rangeCape, attackSpeed, currentHp )
    local cooldown = 10 -- ticks. 6 seconds
    local pc = 0.05 -- Proc chance
    if seers then
        pc = pc + 0.02
    end
    if rangeCape then
        pc = pc * 1.2
    end
    pc = math.max( 0, pc - math.floor( currentHp / 1e6 ) / 100 )

    if attackSpeed then
        local c = math.floor( (cooldown - 1) / attackSpeed ) -- Blocked count

        -- Calculate average chain length
        -- https://www.wolframalpha.com/input/?i=1%2Fsum%28%28n%2Bc%29+*+p*%281-p%29%5E%28n-1%29%2C+n%3D1..inf%29
        return 1 / (c + 1 / pc)
    end

    return pc
end

function p.main( frame )
    return p._main( frame:getParent().args )
end

function p._main( args )
    local lookup_toggle = yesno( args.lookup_toggle or false )
    local max_hp = tonumber( args.max_hp or 100000 )
    local monster_Name = args.monster_Name or ''
    local current_hp = math.min( tonumber( args.current_hp or 50000 ), max_hp )
    local critical = yesno( args.critical or false )
    local your_hp = tonumber( args.your_hp or 10000 )
    local seers = yesno( args.seers or true )
    local ranged_Cape = yesno( args.ranged_Cape or true )
    local cap = critical and 12000 or 10000

    if lookup_toggle == true then
        local data = mw.smw.ask{
            '[[' .. monster_Name .. ']]',
            '?NPC life points'
        }

        if data and data[1] and data[1]['NPC life points'] then
            max_hp = data[1]['NPC life points']
        else
            return "Can't find any data for monster '" .. monster_Name .. "'."
        end
    end



    local plot = chart.newChart{ type='scatter' }
        :setDimensions( '60em', '45em', '300px', '300px', true )
        :setTitle( 'Average damage increase when procced VS your average damage per hit' )
        :setXLabel( 'Monster HP' )
        :setYLabel( 'Damage increase [%]' )

    for _, i in ipairs{ 1000, 2000, 3000, 4000, 6000, 8000, 10000, 12000, 15000} do
        plot:newDataSet{
            data = chart.generateXYFromFunc( function(x)
                return ((damage( max_hp, x, cap ) - i)/ i) * 100
            end, 0, max_hp, math.max( 1000, max_hp / 500 ) ),
            pointRadius = 0,
            label = i
        }
    end

    local procTable = mw.html.create( 'table' ):addClass( 'wikitable' )
    procTable:tr()
            :th( 'Attack speed' )
            :th( 'Average proc rate' )
        :tr()
            :td( '1 tick' )
            :td( fnum( triggerChance( seers, ranged_Cape, 1, current_hp ) * 100 ) .. '%' )
        :tr()
            :td( '2 ticks' )
            :td( fnum( triggerChance( seers, ranged_Cape, 2, current_hp ) * 100 ) .. '%' )
        :tr()
            :td( '3 ticks' )
            :td( fnum( triggerChance( seers, ranged_Cape, 3, current_hp ) * 100 ) .. '%' )
        :tr()
            :td( '4 ticks' )
            :td( fnum( triggerChance( seers, ranged_Cape, 4, current_hp ) * 100 ) .. '%' )
        :tr()
            :td( '5 ticks' )
            :td( fnum( triggerChance( seers, ranged_Cape, 5, current_hp ) * 100 ) .. '%' )

    local boltDamage = damage( max_hp, current_hp, cap )

    local res = ''
    if lookup_toggle then
        res = res .. string.format( '%s has %s lp.<br>', monster_Name, fnum( max_hp ) )
    end
    res = res .. string.format( 'Ruby bolts (e) will deal %s damage at %s life points.<br>', fnum( boltDamage ), fnum( current_hp ) )
    res = res .. string.format( 'You will take %s damage.', fnum( math.floor( your_hp * 0.1 ) ) )
    res = res .. tostring( procTable )
    res = res .. plot

    return res
end

return p
-- </nowiki>