Module:DiceUtils

From Baldur's Gate 3 Wiki
Revision as of 07:33, 21 July 2023 by NamelessWonder (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Documentation for this module may be created at Module:DiceUtils/doc

local p = {}

-- For internal use only
function p._parseDiceInfo(diceStr)
    local dice = string.gmatch(diceStr, "([^,]+)")
    local result = {}

    for die in dice do
        -- Search for modifier info
        local modIdx = string.find(die, "+") or string.find(die, "-")
        local dieMod = modIdx and tonumber(string.sub(die, modIdx)) or 0
        local die_ = modIdx and string.sub(die, 1, modIdx - 1) or die

        local idx = string.find(die_, "d")
		local dieCount = string.sub(die_, 1, idx - 1) -- Holds the value before 'd' (die count)
		local dieType = string.sub(die_, idx + 1) -- Holds the value after 'd' (die type)

		local info = {
		    count = dieCount,
		    type_ = dieType,
		    mod_ = dieMod
		}

		table.insert(result, info)
    end

    return result
end

-- For internal use only
function p._average(diceInfo, modifier)
    local minDamage = 0
    local maxDamage = 0

    for i, info in ipairs(diceInfo) do
        -- Minimum damage cannot be below 0
        if ((info.count + info.mod_) > 0) then
            minDamage = minDamage + info.count + info.mod_
        end
        maxDamage = maxDamage + info.count * info.type_ + info.mod_
    end

    if ((minDamage + modifier) < 0) then
        minDamage = 0
    else
        minDamage = minDamage + modifier
    end

    if ((maxDamage + modifier) < 0) then
        maxDamage = 0
    else
        maxDamage = maxDamage + modifier
    end


    return string.format("(%d~%d)", minDamage, maxDamage)
end

-- For internal use only
function p._diceInfoText(diceInfo, modifier)
    local diceText = ""
    local modText = ""

    for i, info in ipairs(diceInfo) do
        if diceText == "" then
            diceText = info.count .. "d" .. info.type_
        else
            diceText = diceText .. " + " .. info.count .. "d" .. info.type_
        end

        local modText_ = (info.mod_ == 0) and
            "" or ((info.mod_ > 0 and " + " or " - ") .. math.abs(info.mod_))

        if modText == "" then
            modText = modText_
        else
            modText = modText .. modText_
        end
    end

    if not (modText == "") then
        diceText = diceText .. modText
    end

    return diceText .. ((modifier == 0) and
            "" or ((modifier > 0 and " + " or " - ") .. math.abs(modifier)))
end

function p.average(frame)
    local args = frame.args
    local dice = args.dice
    local modifier = tonumber(args.modifier) or 0

    if dice == nil then return "" end

    local diceInfo = p._parseDiceInfo(dice)

    return p._average(diceInfo, modifier)
end

function p.diceInfoText(frame)
    local args = frame.args
    local dice = args.dice
    local modifier = tonumber(args.modifier) or 0

    if dice == nil then return "" end

    local diceInfo = p._parseDiceInfo(dice)

    return p._diceInfoText(diceInfo, modifier)
end

function p.diceInfoTextWithAvg(frame)
    local args = frame.args
    local dice = args.dice
    local modifier = tonumber(args.modifier) or 0

    if dice == nil then return "" end

    local diceInfo = p._parseDiceInfo(dice)

    return p._diceInfoText(diceInfo, modifier) .. " " .. p._average(diceInfo, modifier)
end

return p