Refactor and improve encounter data handling and formatting
All checks were successful
forgejo/Czech Quests/addon/pipeline/head This commit looks good

This commit is contained in:
Roman Jaroš 2025-04-19 00:41:40 +02:00
parent a32b698ebf
commit f37808207e
8 changed files with 226 additions and 83 deletions

View file

@ -48,7 +48,7 @@ local function HideOtherContent()
addon.EncounterFrame:ClearHeaders()
end
local function SetAbilityIcon(header, tags)
local function SetAbilityTagIcon(header, tags)
local result = {}
for part in string.gmatch(tags, "([^,]+)") do
table.insert(result, part)
@ -71,6 +71,36 @@ local function SetAbilityIcon(header, tags)
end
end
local function SetAbilityIcon(header, name)
if ORIGINALS[name] == nil then
return
end
local abilityIcon = ORIGINALS[name].abilityIcon or nil
if abilityIcon then
header.button.abilityIcon:SetTexture(abilityIcon)
header.button.abilityIcon:Show()
header.button.title:SetPoint("TOPLEFT", header, "TOPLEFT", 45, -7);
end
end
local function SetEncounterPortraitIcon(header, name)
if ORIGINALS[name] == nil then
return
end
local creatureDisplayID = ORIGINALS[name].creatureDisplayID or 0
local uiModelSceneID = ORIGINALS[name].uiModelSceneID or 0
if creatureDisplayID ~= 0 and uiModelSceneID ~= 0 then
header.button.portrait.displayInfo = creatureDisplayID;
header.button.portrait.uiModelSceneID = uiModelSceneID;
SetPortraitTextureFromCreatureDisplayID(header.button.portrait.icon, creatureDisplayID);
header.button.portrait:Show()
header.button.title:SetPoint("TOPLEFT", header, "TOPLEFT", 50, -7);
end
end
local function RenderBossAbilities(data, parent, layer)
local frame = addon.EncounterFrame
@ -92,12 +122,17 @@ local function RenderBossAbilities(data, parent, layer)
header:SetWidth(frame:GetWidth() - (layer * 20))
header.button.title:SetText(name)
SetAbilityIcon(header, name)
SetEncounterPortraitIcon(header, name)
if tag and tag ~= '???' then
SetAbilityIcon(header, tag)
SetAbilityTagIcon(header, tag)
end
header.description:SetWidth(header:GetWidth() - 20)
header.description:SetText(addon.API.SetAbilityDescription(description, ORIGINALS[name]))
header.description:SetText(
addon.API.FormatEncounterDescription(description, ORIGINALS[name].description)
)
if description == "" then header.empty = true end
table.insert(parent.children, header)
@ -112,41 +147,42 @@ local function RenderBossAbilities(data, parent, layer)
end
end
local function RenderBossEncounter(bossKey, bossName)
local function RenderBossEncounter(bossKey, bossName, encounterName)
local frame = addon.EncounterFrame
HideOtherContent()
local bossData = CzechQuestsAddon:GetData('encounter', bossKey)
frame.summary:SetText(
addon.API.SetAbilityDescription(bossData.overview, ORIGINALS['Overview'])
addon.API.FormatEncounterDescription(bossData.overview, ORIGINALS['Overview'].description)
)
local tankHeader = frame:CreateHeader()
tankHeader.button.title:SetText("Tank")
local tankDescription = addon.API.SetSummaryDescription(bossData.tank, ORIGINALS['Tanks'])
local tankDescription = addon.API.FormatEncounterDescription(bossData.tank, ORIGINALS['Tanks'].description)
tankHeader.description:SetText(tankDescription)
table.insert(frame.headers, tankHeader)
SetAbilityIcon(tankHeader, 'tank')
SetAbilityTagIcon(tankHeader, 'tank')
local healHeader = frame:CreateHeader()
healHeader.button.title:SetText("Healer")
local healerDescription = addon.API.SetSummaryDescription(bossData.healer, ORIGINALS['Healers'])
local healerDescription = addon.API.FormatEncounterDescription(bossData.healer, ORIGINALS['Healers'].description)
healHeader.description:SetText(healerDescription)
table.insert(frame.headers, healHeader)
SetAbilityIcon(healHeader, 'healer')
SetAbilityTagIcon(healHeader, 'healer')
local dpsHeader = frame:CreateHeader()
dpsHeader.button.title:SetText("Damage Dealer")
local dpsDescription = addon.API.SetSummaryDescription(bossData.dps, ORIGINALS['Damage Dealers'])
local dpsDescription = addon.API.FormatEncounterDescription(bossData.dps, ORIGINALS['Damage Dealers'].description)
dpsHeader.description:SetText(dpsDescription)
table.insert(frame.headers, dpsHeader)
SetAbilityIcon(dpsHeader, 'dps')
SetAbilityTagIcon(dpsHeader, 'dps')
local bossHeader = frame:CreateHeader()
bossHeader.button.title:SetText(bossName)
bossHeader.button.title:SetText(encounterName)
bossHeader.empty = true
table.insert(frame.headers, bossHeader)
SetEncounterPortraitIcon(bossHeader, bossName)
RenderBossAbilities(bossData.abilities, bossHeader, 1)
frame:GetParent():Show()
@ -155,16 +191,31 @@ end
local function StoreBossAbilities()
ORIGINALS = {}
local stack, _, _, _, curSectionID = {}, EJ_GetEncounterInfo(ENCOUNTER_ID)
local _, bossName, description, displayInfo, iconImage, uiModelSceneID = EJ_GetCreatureInfo(1, ENCOUNTER_ID)
ORIGINALS[bossName] = {}
ORIGINALS[bossName].description = description
ORIGINALS[bossName].creatureDisplayID = displayInfo
ORIGINALS[bossName].uiModelSceneID = uiModelSceneID
ORIGINALS[bossName].abilityIcon = iconImage
repeat
local info = C_EncounterJournal.GetSectionInfo(curSectionID)
if not info.filteredByDifficulty then
ORIGINALS[info.title] = info.description
ORIGINALS[info.title] = {}
ORIGINALS[info.title].description = info.description
ORIGINALS[info.title].creatureDisplayID = info.creatureDisplayID
ORIGINALS[info.title].uiModelSceneID = info.uiModelSceneID
ORIGINALS[info.title].abilityIcon = info.abilityIcon
end
table.insert(stack, info.siblingSectionID)
if not info.filteredByDifficulty then
table.insert(stack, info.firstChildSectionID)
end
curSectionID = table.remove(stack)
until not curSectionID
end
@ -173,6 +224,7 @@ local function DetectBossToRender()
local frame = addon.EncounterFrame
local encounterName = EJ_GetEncounterInfo(ENCOUNTER_ID)
local _, bossName = EJ_GetCreatureInfo(1, ENCOUNTER_ID)
local difficulty = 'lfg_raid' -- 17
if DIFFICULTY == 14 then
@ -203,11 +255,11 @@ local function DetectBossToRender()
if bossKey then
StoreBossAbilities()
RenderBossEncounter(bossKey, encounterName)
RenderBossEncounter(bossKey, bossName, encounterName)
end
if bossKey == nil then
frame.summary:SetText("V souboji " .. encounterName .. " není tato obtížnost přeložena.")
frame.summary:SetText("Pro souboj " .. encounterName .. " není vytvořen překlad.")
frame:GetParent():Show()
end
end

View file

@ -1,5 +1,6 @@
local _, addon = ...
-- Parse ability information from data
local function ParseAbilities(input, delimiter)
local result = {}
local pattern = "([^" .. delimiter .. "]*)"
@ -19,37 +20,58 @@ local function ParseAbilities(input, delimiter)
end
addon.API.ParseAbilities = ParseAbilities
local function SetAbilityDescription(text, original)
local withNumbers = addon.API.FillNumbers(text, original)
return addon.API.ColorSpellNames(withNumbers)
end
addon.API.SetAbilityDescription = SetAbilityDescription
-- Replace special marks with actual numbers from original description
local function FillNumberPositionPlaceholder(text, sourceText)
local numbers = {}
local source = addon.API.ClearStringFormatMarks(sourceText)
local function SetSummaryDescription(text, original)
local withNumbers = addon.API.FillNumbers(text, original)
local lines = {}
for part in string.gmatch(withNumbers, "([^\n]+)") do
table.insert(lines, part)
for num in source:gmatch("(%d[%d,%.]*)") do
table.insert(numbers, num)
end
local description = ""
for i, line in pairs(lines) do
description = description
.. "- "
.. addon.API.ColorSpellNames(line)
.. (i == #lines and "" or "\n\n")
end
local replacedText = text:gsub("<#(%d+)%?>", function(position)
position = tonumber(position)
local n = numbers[position]
return description
if n then
if source:match("%s+" .. n .. "%s+million") then
return n .. "<mil>"
elseif source:match("%s+" .. n .. "%s+millions") then
return n .. "<mil>"
else
return n
end
end
return "?"
end)
return replacedText
end
addon.API.SetSummaryDescription = SetSummaryDescription
-- Prepare description for Encounter ability
local function FormatEncounterDescription(text, original)
local formated = FillNumberPositionPlaceholder(text, original)
formated = addon.API.ParseParagraphs(original, formated)
formated = addon.API.ColorSpellNames(formated)
-- fix [mil]
formated = string.gsub(formated, "<mil>", "mil.")
-- fix 4 s
formated = string.gsub(formated, "(%d+) s([%s%.])", "%1s%2")
return formated
end
addon.API.FormatEncounterDescription = FormatEncounterDescription
-- Return specific Encounter Ability data
local function GetEncounterAbility(abilityKey)
return addon.data.encounter[abilityKey].m or nil
end
addon.API.GetEncounterAbility = GetEncounterAbility
-- Get Encounter object
local function GetEncounter(bossKey)
return {
abilities = addon.data.encounter[bossKey] or nil,

View file

@ -115,7 +115,6 @@ function EncounterFrame:CreateHeader()
HeaderFrame.button.icon3:Hide()
HeaderFrame.button.icon4:Hide()
HeaderFrame.button.expandedIcon:SetPoint("TOPLEFT", HeaderFrame.button, "TOPLEFT", 10, -6);
HeaderFrame.button.expandedIcon:SetText("+")
HeaderFrame.expanded = false
HeaderFrame.empty = false
@ -141,7 +140,6 @@ function EncounterFrame:CreateHeader()
function HeaderFrame:Open()
local header = self
header.button.expandedIcon:SetText("-")
header.button.expandedIcon:SetPoint("TOPLEFT", HeaderFrame.button, "TOPLEFT", 10, -5);
if (header.empty == false) then
header.description:Show()
header.descriptionBG:Show()
@ -152,7 +150,6 @@ function EncounterFrame:CreateHeader()
function HeaderFrame:Close()
local header = self
header.button.expandedIcon:SetText("+")
header.button.expandedIcon:SetPoint("TOPLEFT", HeaderFrame.button, "TOPLEFT", 10, -6);
header.description:Hide()
header.descriptionBG:Hide()
header.descriptionBGBottom:Hide()

View file

@ -1,5 +1,6 @@
local _, addon = ...
-- Split original text by break-lines
local function SplitParagraphs(text)
local paragraphs = {}
for paragraph in string.gmatch(text, "([^\n]+)") do
@ -8,6 +9,7 @@ local function SplitParagraphs(text)
return paragraphs
end
-- Split text into sentences
local function SplitSentences(paragraph)
local sentences = {}
for sentence in string.gmatch(paragraph, "([^%.%?!]+[%.%?!]?)[%s]*") do
@ -19,10 +21,17 @@ local function SplitSentences(paragraph)
return sentences
end
-- Put break-lines
local function ReplaceMultipleBreakLines(text)
return text:gsub("\r?\n+", "\n\n")
end
-- Because decimal numbers contain dot, remove space between those numbers
local function FixDecimalNumbers(text)
return text:gsub("(%d+)%.%s+(%d+)", "%1.%2")
end
-- Fix translated text break-line based on original
local function ParseParagraphs(original_text, translation_text)
if original_text == "" or original_text == nil then
return translation_text
@ -58,8 +67,9 @@ local function ParseParagraphs(original_text, translation_text)
table.insert(translation_paragraphs, table.concat(collected, " "))
end
return ReplaceMultipleBreakLines(
table.concat(translation_paragraphs, "\n")
)
local multilines = ReplaceMultipleBreakLines(table.concat(translation_paragraphs, "\n"))
local fixedDecimalNumbers = FixDecimalNumbers(multilines)
return fixedDecimalNumbers
end
addon.API.ParseParagraphs = ParseParagraphs

View file

@ -1,39 +1,13 @@
local _, addon = ...
local function ClearNumberStringMarks(text)
local function ClearStringFormatMarks(text)
-- Remove colors (|cffffff...)
text = text:gsub("|c%x%x%x%x%x%x%x%x", "")
-- Remove H marks
text = text:gsub("|H.-|h", "")
return text
end
local function FillNumbers(text, sourceText)
local numbers = {}
local currentIndex = 1
local source = ClearNumberStringMarks(sourceText)
for num in source:gmatch("(%d[%d,%.]*)") do
table.insert(numbers, num)
end
local replacedText = text:gsub("#%?", function()
local n = numbers[currentIndex]
currentIndex = currentIndex + 1
if n and source:match("%s+" .. n .. "%s+million") then
return n .. " mil."
elseif n and source:match("%s+" .. n .. "%s+million") then
return n .. " mil."
end
return n or "?"
end)
return replacedText
end
addon.API.FillNumbers = FillNumbers
addon.API.ClearStringFormatMarks = ClearStringFormatMarks
local function ColorSpellNames(text, color)
return text:gsub("%[(.-)%]", function(match)

View file

@ -6,6 +6,23 @@ local function BuildIndex(text)
return normalized
end
local function FillNumbers(text, sourceText)
local numbers = {}
local currentIndex = 1
for num in sourceText:gmatch("%d+%.?%d*") do
table.insert(numbers, num)
end
local replacedText = text:gsub("#%?", function()
local n = numbers[currentIndex]
currentIndex = currentIndex + 1
return n or "?"
end)
return replacedText
end
local function FillPlaceholders(text)
if text == nil then
return text
@ -24,9 +41,7 @@ local function GetSpeech(message)
local speech = addon.data.speech[index];
local text = speech and speech.m or nil
if text then
return FillPlaceholders(
addon.API.FillNumbers(text, message)
)
return FillPlaceholders(FillNumbers(text, message))
else
return nil
end

View file

@ -183,38 +183,74 @@ addon.data.encounter["The Gobfather_normal_raid_a03b221c_00"] = {
m = "???||",
}
addon.data.encounter["The Gobfather_normal_raid_0dd77eb8_0000"] = {
m = "Bombfield|Important|Gobfather rozptýlí Primed Boomcrawlery, které se potulují #? s. Šlápnutí na Primed Boomcrawlera způsobí jeho předčasnou detonaci a spustí Bomb Voyage.",
m = "Bombfield|Important|The Gobfather rozptýlí Primed Boomcrawlers, kteří se potulují <#1?> sekundu. Šlápnutí na Primed Boomcrawlera způsobí jeho předčasnou detonaci a spustí Bomb Voyage.",
}
addon.data.encounter["The Gobfather_normal_raid_3df09614_000000"] = {
m = "Primed Boomcrawler||",
}
addon.data.encounter["The Gobfather_normal_raid_2dff6e6c_00000000"] = {
m = "Reinforced Plating||Zesílené opláštění snižuje poškození, které obdrží Primed Boomcrawler, o #? %.",
m = "Reinforced Plating||Zesílené opláštění snižuje poškození, které obdrží Primed Boomcrawler, o <#1?> %.",
}
addon.data.encounter["The Gobfather_normal_raid_eeafee09_00000001"] = {
m = "Bomb Voyage||Primed Boomcrawler detonuje, způsobí #? Fire poškození a odhodí hráče v okruhu #? yardů.",
m = "Bomb Voyage||Primed Boomcrawler se odpálí, způsobí <#1?> Fire poškození a odhodí hráče v okruhu <#2?> yardů.",
}
addon.data.encounter["The Gobfather_normal_raid_a4190c2f_0001"] = {
m = "Death From Above||Gobfather vypustí ze svých raketometů příval raket, které způsobí #? Fire poškození hráčům v okruhu #? yardů od každého zásahu.",
m = "Death From Above||The Gobfather vypustí ze svých raketometů příval raket, které způsobí <#1?> Fire poškození hráčům v okruhu <#2?> yardů od každého dopadu.",
}
addon.data.encounter["The Gobfather_normal_raid_b3ae61ca_0002"] = {
m = "Giga-Rocket Slam||Gobfather udeří obrovskou silou do země, čímž všem hráčům způsobí #? Fire poškození a odhodí je zpět.",
m = "Giga-Rocket Slam||The Gobfather udeří obrovskou silou do země, způsobí všem hráčům <#1?> Fire poškození a odhodí je zpět.",
}
addon.data.encounter["The Gobfather_normal_raid_c5b3601a_0003"] = {
m = "Flaming Flames||The Gobfather vypustí plamenný proud, který způsobí #? Fire poškození hráčům v čelním kuželu.",
m = "Flaming Flames||The Gobfather vypustí proud plamenů a způsobí hráčům v čelním kuželu <#1?> Fire poškození.",
}
addon.data.encounter["The Gobfather_normal_raid_09c97f46_0004"] = {
m = "Excessive Pollutants|Healer|Gobfather vypouští do vzduchu nebezpečné chemikálie, které způsobují #? Nature poškození v intervalu #? s.",
m = "Excessive Pollutants|Healer|The Gobfather vypouští do vzduchu nebezpečné chemikálie, které způsobují <#1?> Nature poškození v intervalu <#2?> sek.",
}
addon.data.encounter["The Gobfather_normal_raid_summary_instance"] = {
m = "Gobfather se nehodlá vzdát bez závěrečného boje, rozmetá Primed Boomcrawlers pomocí [Bombfield] a srazí hráče k zemi pomocí [Giga-Rocket Slam]. Při #? energiích vytasí veškerou sílu a zasype je [Death From Above].",
m = "Gobfather se nehodlá vzdát bez závěrečného boje, rozpráší Primed Boomcrawlers pomocí [Bombfield] a srazí hráče zpět pomocí [Giga-Rocket Slam]. Při energii <#1?> se vytasí se všemi prostředky a sesílá na hráče [Death From Above].",
}
addon.data.encounter["The Gobfather_normal_raid_summary_tank"] = {
m = "Primed Boomcrawlers explodují pomocí [Bomb Voyage] po sešlápnutí. \nThe Gobfather způsobuje masivní poškození svými střelami [Death From Above]. \n[Flaming Flames] způsobují poškození hráčům před The Gobfatherem.",
m = "Boomcrawleři se základním nátěrem po sešlápnutí explodují pomocí [Bomb Voyage]. \nThe Gobfather způsobuje masivní poškození svými střelami [Death From Above]. \nPlameny [Flaming Flames] způsobují poškození hráčům před The Gobfatherem.",
}
addon.data.encounter["The Gobfather_normal_raid_summary_healer"] = {
m = "Primed Boomcrawlers po sešlápnutí explodují pomocí [Bomb Voyage]. \n[Excessive Pollutants] neustále způsobuje poškození všem hráčům. \nGobfather uděluje obrovské poškození svými střelami [Death From Above].",
m = "Boomcrawleři se základním nátěrem po sešlápnutí explodují pomocí [Bomb Voyage]. \n[Excessive Pollutants] neustále způsobuje poškození všem hráčům. \nGobfather způsobuje obrovské poškození svými střelami [Death From Above].",
}
addon.data.encounter["The Gobfather_normal_raid_summary_dps"] = {
m = "Boomcrawleři Primed Boomcrawlers po sešlápnutí explodují pomocí [Bomb Voyage]. \nThe Gobfather způsobuje masivní poškození svými střelami [Death From Above]. \n[Flaming Flames] způsobují poškození hráčům před The Gobfatherem.",
m = "Boomcrawleři se základním nátěrem po sešlápnutí explodují pomocí [Bomb Voyage]. \nThe Gobfather způsobuje masivní poškození svými střelami [Death From Above]. \nPlameny [Flaming Flames] způsobují poškození hráčům před The Gobfatherem.",
}
addon.data.encounter["Shurrai_normal_raid_a03b221c_00"] = {
m = "???||",
}
addon.data.encounter["Shurrai_normal_raid_a3276252_0000"] = {
m = "Abyssal Strike|Tank|Shurrai udeří svůj aktuální cíl nekromanticky napuštěnou kotvou, způsobí <#1?> Plague poškození a použije absorpci léčení rovnající se výši nezměrného poškození.",
}
addon.data.encounter["Shurrai_normal_raid_16361c5b_0001"] = {
m = "Briny Vomit||Shurrai chrlí solanku a způsobuje <#1?> Frost poškození hráčům v okruhu <#2?> yardů od každého dopadu. Kaluže solanky přetrvávají a způsobují hráčům <#3?> Frost poškození v intervalu <#4?> s a zpomalují rychlost pohybu o <#5?> %.",
}
addon.data.encounter["Shurrai_normal_raid_2a89e69f_0002"] = {
m = "Dark Tide||Shurrai vyvolává <#1?> vlny, které se šíří směrem ven, způsobují <#2?> Frost poškození a odstrkují hráče. Tyto vlny rozpouštějí všechny kaluže Briny Vomit, kterými projdou.",
}
addon.data.encounter["Shurrai_normal_raid_44f7529e_0003"] = {
m = "Regurgitate Souls|Important|Shurrai regurgituje duše svých minulých obětí v intervalu <#1?> s po dobu <#2?> s, čímž způsobuje <#3?> Plague poškození všem hráčům a vyvrhuje masu zajatých duší, která způsobuje <#4?> Plague poškození hráčům v okruhu <#5?> yardů od dopadu. Každý dopad vyvolá <#6?> Drowned Arathi.",
}
addon.data.encounter["Shurrai_normal_raid_69996e5a_000300"] = {
m = "Shroud of the Drowned||Shurrai se zahalí do nekromantických energií a během Regurgitate Souls absorbuje <#1?> poškození po dobu <#2?> min v intervalu <#3?> s.",
}
addon.data.encounter["Shurrai_normal_raid_ad190ebe_000301"] = {
m = "Drowned Arathi||",
}
addon.data.encounter["Shurrai_normal_raid_12617f60_00030100"] = {
m = "Ocean's Reckoning|Disease|Po smrti způsobí zaklínač hráčům v okruhu <#2?> yardů <#1?> Plague poškození a další <#3?> Plague poškození v intervalu <#4?> s po dobu <#5?> s. Tento efekt se sčítá. Vypuštěné duše se také vrhnou zpět na svého věznitele a způsobí mu <#6?> Plague poškození Shurrai.",
}
addon.data.encounter["Shurrai_normal_raid_summary_instance"] = {
m = "Shurrai zasype hráče [Briny Vomit] a poté smyje hráče i zvratky pomocí [Dark Tide]. Při <#1?> energii Shurrai vrhne [Regurgitate Souls], vyvolá Drowned Arathi a zaštítí se [Shroud of the Drowned].",
}
addon.data.encounter["Shurrai_normal_raid_summary_tank"] = {
m = "[Abyssal Strike] aplikuje léčebnou absorpci odpovídající výši způsobeného poškození. \n[Dark Tide] odplaví kaluže [Briny Vomit]. \nUtopené Arathi způsobí Shurraiovi při zabití značné poškození.",
}
addon.data.encounter["Shurrai_normal_raid_summary_healer"] = {
m = "Utopený Arathi způsobí po smrti [Ocean's Reckoning]. \n[Dark Tide] odplaví kaluže [Briny Vomit]. \nUtopený Arathi způsobí Shurraiovi po zabití značné poškození.",
}
addon.data.encounter["Shurrai_normal_raid_summary_dps"] = {
m = "[Dark Tide] odplaví kaluže [Briny Vomit]. \nUtopená Arathi způsobí Shurraiovi při zabití značné poškození.",
}

View file

@ -40,3 +40,40 @@ addon.data.encounter["The Gobfather_normal_raid"] = {
},
},
}
addon.data.encounter["Shurrai_normal_raid"] = {
{
key = "Shurrai_normal_raid_a03b221c_00",
children = {
{
key = "Shurrai_normal_raid_a3276252_0000",
children = {},
},
{
key = "Shurrai_normal_raid_16361c5b_0001",
children = {},
},
{
key = "Shurrai_normal_raid_2a89e69f_0002",
children = {},
},
{
key = "Shurrai_normal_raid_44f7529e_0003",
children = {
{
key = "Shurrai_normal_raid_69996e5a_000300",
children = {},
},
{
key = "Shurrai_normal_raid_ad190ebe_000301",
children = {
{
key = "Shurrai_normal_raid_12617f60_00030100",
children = {},
},
},
},
},
},
},
},
}