-- AltSystem Roll Logic -- Handles /roll 20, captures the result, and applies modifiers. AltSystem = AltSystem or {} local pendingRollType = nil -- "attack" or "defense" local pendingPetRoll = nil -- { rollType, mainRollValue } when awaiting pet/summon roll -- Get the TRP3 character first name, falling back to the WoW unit name local function GetCharacterName() if TRP3_API and TRP3_API.profile and TRP3_API.profile.getData then local ok, characteristics = pcall(TRP3_API.profile.getData, "player/characteristics") if ok and characteristics and characteristics.FN and characteristics.FN ~= "" then return characteristics.FN end end return UnitName("player") end -- Build the modifier description string for the announce message local function BuildModifierString(modifiers) local parts = {} for _, mod in ipairs(modifiers) do local sign = mod.value >= 0 and "+" or "" table.insert(parts, sign .. mod.value .. " (" .. mod.name .. ")") end return table.concat(parts, " ") end -- Build the log message string (always uses the non-emote format with character name) local function BuildLogMessage(rollValue, modifiers, total) local name = GetCharacterName() local modStr = BuildModifierString(modifiers) if modStr ~= "" then return name .. " rolled " .. rollValue .. " " .. modStr .. " = " .. total else return name .. " rolled " .. rollValue .. " = " .. total end end -- Build the critical roll log message local function BuildCriticalLogMessage(isCriticalSuccess) local name = GetCharacterName() if isCriticalSuccess then return name .. " rolled a Critical Success!" else return name .. " rolled a Critical Failure!" end end -- Add an entry to the roll log and refresh the UI local function AddLogEntry(text) local rollLog = AltSystem.State.rollLog table.insert(rollLog, { text = text, timestamp = time() }) -- Cap at 100 entries if #rollLog > 100 then table.remove(rollLog, 1) end -- Refresh the log panel if it exists if AltSystem.RefreshLogPanel then AltSystem:RefreshLogPanel() end end -- Send a message to the given channel. local function SendToChannel(msg, channel) SendChatMessage(msg, channel) end -- Announce the roll result to the selected chat channel local function AnnounceRoll(rollValue, modifiers, total) if not AltSystem.State.announceEnabled then return end local channelDef = AltSystem.AnnounceChannels[AltSystem.State.announceChannelIndex] if not channelDef then return end local isEmote = channelDef.channel == "EMOTE" local modStr = BuildModifierString(modifiers) local msg if isEmote then if modStr ~= "" then msg = "rolled " .. rollValue .. " " .. modStr .. " = " .. total else msg = "rolled " .. rollValue .. " = " .. total end else local name = GetCharacterName() if modStr ~= "" then msg = name .. " rolled " .. rollValue .. " " .. modStr .. " = " .. total else msg = name .. " rolled " .. rollValue .. " = " .. total end end SendToChannel(msg, channelDef.channel) end -- Announce a critical roll result local function AnnounceCritical(isCriticalSuccess) if not AltSystem.State.announceEnabled then return end local channelDef = AltSystem.AnnounceChannels[AltSystem.State.announceChannelIndex] if not channelDef then return end local critText = isCriticalSuccess and "rolled a Critical Success!" or "rolled a Critical Failure!" local msg if channelDef.channel == "EMOTE" then msg = critText else msg = GetCharacterName() .. " " .. critText end SendToChannel(msg, channelDef.channel) end -- Perform a roll: triggers the WoW native /roll 20 command function AltSystem:PerformRoll(rollType) pendingPetRoll = nil pendingRollType = rollType RandomRoll(1, 20) end -- Listen for the system message that contains the roll result local rollListener = CreateFrame("Frame") rollListener:RegisterEvent("CHAT_MSG_SYSTEM") rollListener:SetScript("OnEvent", function(self, event, message) -- Phase 2: waiting for pet/summon roll result (1-5) if pendingPetRoll then local petRoller, petRoll = message:match("(.+) rolls (%d+) %(1%-5%)") if not petRoll then return end if petRoller ~= UnitName("player") then return end local petValue = tonumber(petRoll) local info = pendingPetRoll pendingPetRoll = nil AltSystem:CalculateAndDisplayResult(info.rollType, info.mainRollValue, petValue) return end -- Phase 1: waiting for main roll result (1-20) if not pendingRollType then return end -- Match the roll result pattern: "PlayerName rolls X (1-20)" local roller, roll = message:match("(.+) rolls (%d+) %(1%-20%)") if not roll then return end if roller ~= UnitName("player") then return end local rollValue = tonumber(roll) local rollType = pendingRollType pendingRollType = nil -- If pet/summon is enabled, trigger a second roll (1-5) if AltSystem.State.petSummonEnabled then pendingPetRoll = { rollType = rollType, mainRollValue = rollValue } RandomRoll(1, 5) return end AltSystem:CalculateAndDisplayResult(rollType, rollValue) end) -- Calculate the final result based on the roll type and selected modifiers function AltSystem:CalculateAndDisplayResult(rollType, rollValue, petRollValue) -- Critical rolls bypass normal calculation if rollValue == 1 then AddLogEntry(BuildCriticalLogMessage(false)) AnnounceCritical(false) return elseif rollValue == 20 then AddLogEntry(BuildCriticalLogMessage(true)) AnnounceCritical(true) return end local state = AltSystem.State local skill = AltSystem.Data.Skills[state.selectedSkillIndex] local item = AltSystem.Data.Items[state.selectedItemIndex] local skillMod = skill and skill.modifier or 0 local itemMod = item and item.modifier or 0 local total = rollValue local modifiers = {} local isBaseRoll = skill and skill.name == "Base roll" local petMod = petRollValue or 0 if rollType == "attack" then -- Attack Roll = roll + skill modifier + item modifier + pet/summon modifier total = rollValue + skillMod + itemMod + petMod if not isBaseRoll then table.insert(modifiers, { name = skill and skill.name or "Skill", value = skillMod }) end if itemMod ~= 0 then table.insert(modifiers, { name = item and (item.label or item.name) or "Item", value = itemMod }) end if petMod ~= 0 then table.insert(modifiers, { name = "Pet", value = petMod }) end elseif rollType == "defense" then -- Defense Roll = roll + skill modifier + item modifier + defense modifier + shield modifier local defense = AltSystem.Data.Defenses[state.selectedDefenseIndex] local defenseMod = defense and defense.modifier or 0 local shieldMod = state.shieldEnabled and AltSystem.Data.ShieldModifier or 0 total = rollValue + skillMod + itemMod + defenseMod + shieldMod + petMod if not isBaseRoll then table.insert(modifiers, { name = skill and skill.name or "Skill", value = skillMod }) end if itemMod ~= 0 then table.insert(modifiers, { name = item and (item.label or item.name) or "Item", value = itemMod }) end if defenseMod ~= 0 then table.insert(modifiers, { name = defense and (defense.label or defense.name) or "Armor", value = defenseMod }) end if shieldMod ~= 0 then table.insert(modifiers, { name = "Shield", value = shieldMod }) end if petMod ~= 0 then table.insert(modifiers, { name = "Pet", value = petMod }) end end -- Add to log (always, regardless of announce setting) AddLogEntry(BuildLogMessage(rollValue, modifiers, total)) -- Announce to chat (if enabled) AnnounceRoll(rollValue, modifiers, total) end -- Format a modifier value with sign function FormatModifier(value) if value >= 0 then return "+" .. value else return tostring(value) end end