diff --git a/data/images/game/imbuing/basic-button.png b/data/images/game/imbuing/basic-button.png new file mode 100644 index 0000000000..807bea6302 Binary files /dev/null and b/data/images/game/imbuing/basic-button.png differ diff --git a/data/images/game/imbuing/blocked-panel.png b/data/images/game/imbuing/blocked-panel.png new file mode 100644 index 0000000000..7528cb5b8b Binary files /dev/null and b/data/images/game/imbuing/blocked-panel.png differ diff --git a/data/images/game/imbuing/blocked-panels.png b/data/images/game/imbuing/blocked-panels.png new file mode 100644 index 0000000000..f636cae55a Binary files /dev/null and b/data/images/game/imbuing/blocked-panels.png differ diff --git a/data/images/game/imbuing/imbue-scroll-button.png b/data/images/game/imbuing/imbue-scroll-button.png new file mode 100644 index 0000000000..b59b2bece6 Binary files /dev/null and b/data/images/game/imbuing/imbue-scroll-button.png differ diff --git a/data/images/game/imbuing/imbuement-icons-64.png b/data/images/game/imbuing/imbuement-icons-64.png new file mode 100644 index 0000000000..9583c89bb5 Binary files /dev/null and b/data/images/game/imbuing/imbuement-icons-64.png differ diff --git a/data/images/game/imbuing/imbui-progress.png b/data/images/game/imbuing/imbui-progress.png new file mode 100644 index 0000000000..59b60e5bfa Binary files /dev/null and b/data/images/game/imbuing/imbui-progress.png differ diff --git a/data/images/game/imbuing/imbui-timer-bg.png b/data/images/game/imbuing/imbui-timer-bg.png new file mode 100644 index 0000000000..e77aa82db4 Binary files /dev/null and b/data/images/game/imbuing/imbui-timer-bg.png differ diff --git a/data/images/game/imbuing/intricate-button.png b/data/images/game/imbuing/intricate-button.png new file mode 100644 index 0000000000..f2bd402205 Binary files /dev/null and b/data/images/game/imbuing/intricate-button.png differ diff --git a/data/images/game/imbuing/pick-item-button.png b/data/images/game/imbuing/pick-item-button.png new file mode 100644 index 0000000000..3c8f6fffce Binary files /dev/null and b/data/images/game/imbuing/pick-item-button.png differ diff --git a/data/images/game/imbuing/powerfull-button.png b/data/images/game/imbuing/powerfull-button.png new file mode 100644 index 0000000000..da5b3ec7c9 Binary files /dev/null and b/data/images/game/imbuing/powerfull-button.png differ diff --git a/data/images/game/imbuing/slot.png b/data/images/game/imbuing/slot.png index d2b1e051ea..d3e92c1841 100644 Binary files a/data/images/game/imbuing/slot.png and b/data/images/game/imbuing/slot.png differ diff --git a/data/images/game/imbuing/useprotection-disabled.png b/data/images/game/imbuing/useprotection-disabled.png new file mode 100644 index 0000000000..db485daffd Binary files /dev/null and b/data/images/game/imbuing/useprotection-disabled.png differ diff --git a/data/images/game/prey/balanceBg.png b/data/images/game/prey/balanceBg.png new file mode 100644 index 0000000000..2028d1e3bb Binary files /dev/null and b/data/images/game/prey/balanceBg.png differ diff --git a/data/images/ui/infoPanel.png b/data/images/ui/infoPanel.png new file mode 100644 index 0000000000..71345a4f16 Binary files /dev/null and b/data/images/ui/infoPanel.png differ diff --git a/data/images/ui/item66.png b/data/images/ui/item66.png new file mode 100644 index 0000000000..dd2a6954e5 Binary files /dev/null and b/data/images/ui/item66.png differ diff --git a/data/images/ui/right-arrow.png b/data/images/ui/right-arrow.png new file mode 100644 index 0000000000..aafb7a7719 Binary files /dev/null and b/data/images/ui/right-arrow.png differ diff --git a/data/images/ui/t2pixel-up-frame-borderimage_2.png b/data/images/ui/t2pixel-up-frame-borderimage_2.png new file mode 100644 index 0000000000..9d12827bd0 Binary files /dev/null and b/data/images/ui/t2pixel-up-frame-borderimage_2.png differ diff --git a/data/styles/10-buttons.otui b/data/styles/10-buttons.otui index 462311669c..9734efaafa 100644 --- a/data/styles/10-buttons.otui +++ b/data/styles/10-buttons.otui @@ -1,5 +1,5 @@ Button < UIButton - font: cipsoftFont + font: verdana-11px-antialised color: #dfdfdfff size: 43 20 text-offset: 0 2 @@ -211,7 +211,7 @@ NextQtButton < UIButton image-clip: 48 16 16 16 QtButton < UIButton - font: cipsoftFont + font: verdana-11px-antialised size: 106 23 text-offset: 0 0 image-source: /images/ui/button diff --git a/data/styles/10-progressbars.otui b/data/styles/10-progressbars.otui index 7025bae628..0e79ffa726 100644 --- a/data/styles/10-progressbars.otui +++ b/data/styles/10-progressbars.otui @@ -57,4 +57,4 @@ InvVerticalBar < UIVerticalProgressBarSDInverted VerticalBar < UIVerticalProgressBarSD size: 10 30 - image-border: 1 \ No newline at end of file + image-border: 1 diff --git a/modules/corelib/ui/uiprogressbarsd.lua b/modules/corelib/ui/uiprogressbarsd.lua index bae376a1a6..b4bbf8c1aa 100644 --- a/modules/corelib/ui/uiprogressbarsd.lua +++ b/modules/corelib/ui/uiprogressbarsd.lua @@ -5,8 +5,8 @@ function UIProgressBarSD.create() local progressbar = UIProgressBarSD.internalCreate() progressbar:setFocusable(false) progressbar:setOn(true) - progressbar.min = 0 - progressbar.max = 100 + progressbar.minimum = 0 + progressbar.maximum = 100 progressbar.value = 0 progressbar.bgBorderLeft = 0 progressbar.bgBorderRight = 0 diff --git a/modules/corelib/ui/uiprogressbarsdinverted.lua b/modules/corelib/ui/uiprogressbarsdinverted.lua index df78ad0c43..d095f90934 100644 --- a/modules/corelib/ui/uiprogressbarsdinverted.lua +++ b/modules/corelib/ui/uiprogressbarsdinverted.lua @@ -5,8 +5,8 @@ function UIProgressBarSDInverted.create() local progressbar = UIProgressBarSDInverted.internalCreate() progressbar:setFocusable(false) progressbar:setOn(true) - progressbar.min = 0 - progressbar.max = 100 + progressbar.minimum = 0 + progressbar.maximum = 100 progressbar.value = 0 progressbar.bgBorderLeft = 0 progressbar.bgBorderRight = 0 diff --git a/modules/corelib/uiprogressbarsd.lua b/modules/corelib/uiprogressbarsd.lua new file mode 100644 index 0000000000..c582c48f66 --- /dev/null +++ b/modules/corelib/uiprogressbarsd.lua @@ -0,0 +1,111 @@ +-- @docclass +UIProgressBarSD = extends(UIWidget, "UIProgressBarSD") + +function UIProgressBarSD.create() + local progressbar = UIProgressBarSD.internalCreate() + progressbar:setFocusable(false) + progressbar:setOn(true) + progressbar.minimum = 0 + progressbar.maximum = 100 + progressbar.value = 0 + progressbar.bgBorderLeft = 0 + progressbar.bgBorderRight = 0 + progressbar.bgBorderTop = 0 + progressbar.bgBorderBottom = 0 + progressbar:insertLuaCall("onSetup") + progressbar:insertLuaCall("onGeometryChange") + return progressbar +end + +function UIProgressBarSD:setMinimum(minimum) + self.minimum = minimum + if self.value < minimum then + self:setValue(minimum) + end +end + +function UIProgressBarSD:setMaximum(maximum) + self.maximum = maximum + if self.value > maximum then + self:setValue(maximum) + end +end + +function UIProgressBarSD:setValue(value, minimum, maximum) + if minimum then + self:setMinimum(minimum) + end + + if maximum then + self:setMaximum(maximum) + end + + self.value = math.max(math.min(value, self.maximum), self.minimum) + self:updateBackground() +end + +function UIProgressBarSD:setPercent(percent) + self:setValue(percent, 0, 100) +end + +function UIProgressBarSD:getPercent() + return self.value +end + +function UIProgressBarSD:getPercentPixels() + return (self.maximum - self.minimum) / self:getWidth() +end + +function UIProgressBarSD:getProgress() + if self.minimum == self.maximum then return 1 end + return (self.value - self.minimum) / (self.maximum - self.minimum) +end + +function UIProgressBarSD:updateBackground() + if self:isOn() then + local width = math.round(math.max((self:getProgress() * (self:getWidth() - self.bgBorderLeft - self.bgBorderRight)), 1)) + local height = self:getHeight() - self.bgBorderTop - self.bgBorderBottom + local rect = { x = self.bgBorderLeft, y = self.bgBorderTop, width = width, height = height } + self:setImageRect(rect) + + if width == 1 then + self:setImageVisible(false) + else + self:setImageVisible(true) + end + end +end + +function UIProgressBarSD:onSetup() + self:updateBackground() +end + +function UIProgressBarSD:onStyleApply(name, node) + for name,value in pairs(node) do + if name == 'background-border-left' then + self.bgBorderLeft = tonumber(value) + elseif name == 'background-border-right' then + self.bgBorderRight = tonumber(value) + elseif name == 'background-border-top' then + self.bgBorderTop = tonumber(value) + elseif name == 'background-border-bottom' then + self.bgBorderBottom = tonumber(value) + elseif name == 'background-border' then + self.bgBorderLeft = tonumber(value) + self.bgBorderRight = tonumber(value) + self.bgBorderTop = tonumber(value) + self.bgBorderBottom = tonumber(value) + elseif name == 'percent' then + self.percent = self:setPercent(tonumber(value)) + elseif name == 'tooltip-delayed' then + self.tooltipDelayed = value + end + end +end + +function UIProgressBarSD:onGeometryChange(oldRect, newRect) + if not self:isOn() then + self:setHeight(0) + end + self:updateBackground() +end diff --git a/modules/corelib/uiprogressbarsdinverted.lua b/modules/corelib/uiprogressbarsdinverted.lua new file mode 100644 index 0000000000..654f431b30 --- /dev/null +++ b/modules/corelib/uiprogressbarsdinverted.lua @@ -0,0 +1,110 @@ +-- @docclass +UIProgressBarSDInverted = extends(UIWidget, "UIProgressBarSDInverted") + +function UIProgressBarSDInverted.create() + local progressbar = UIProgressBarSDInverted.internalCreate() + progressbar:setFocusable(false) + progressbar:setOn(true) + progressbar.minimum = 0 + progressbar.maximum = 100 + progressbar.value = 0 + progressbar.bgBorderLeft = 0 + progressbar.bgBorderRight = 0 + progressbar.bgBorderTop = 0 + progressbar.bgBorderBottom = 0 + progressbar:insertLuaCall("onSetup") + progressbar:insertLuaCall("onGeometryChange") + return progressbar +end + +function UIProgressBarSDInverted:setMinimum(minimum) + self.minimum = minimum + if self.value < minimum then + self:setValue(minimum) + end +end + +function UIProgressBarSDInverted:setMaximum(maximum) + self.maximum = maximum + if self.value > maximum then + self:setValue(maximum) + end +end + +function UIProgressBarSDInverted:setValue(value, minimum, maximum) + if minimum then + self:setMinimum(minimum) + end + + if maximum then + self:setMaximum(maximum) + end + + self.value = math.max(math.min(value, self.maximum), self.minimum) + self:updateBackground() +end + +function UIProgressBarSDInverted:setPercent(percent) + self:setValue(percent, 0, 100) +end + +function UIProgressBarSDInverted:getPercent() + return self.value +end + +function UIProgressBarSDInverted:getPercentPixels() + return (self.maximum - self.minimum) / self:getWidth() +end + +function UIProgressBarSDInverted:getProgress() + if self.minimum == self.maximum then return 1 end + return (self.value - self.minimum) / (self.maximum - self.minimum) +end + +function UIProgressBarSDInverted:updateBackground() + if self:isOn() then + local width = math.round(math.max((self:getProgress() * (self:getWidth() - self.bgBorderLeft - self.bgBorderRight)), 1)) + local height = self:getHeight() - self.bgBorderTop - self.bgBorderBottom + local rect = { x = self:getWidth() - (self:getProgress() * self:getWidth()), y = self.bgBorderTop, width = width, height = height } + if width == 1 then + rect.x = rect.x - 1 + end + self:setImageRect(rect) + + if width == 1 then + self:setImageVisible(false) + else + self:setImageVisible(true) + end + end +end + +function UIProgressBarSDInverted:onSetup() + self:updateBackground() +end + +function UIProgressBarSDInverted:onStyleApply(name, node) + for name,value in pairs(node) do + if name == 'background-border-left' then + self.bgBorderLeft = tonumber(value) + elseif name == 'background-border-right' then + self.bgBorderRight = tonumber(value) + elseif name == 'background-border-top' then + self.bgBorderTop = tonumber(value) + elseif name == 'background-border-bottom' then + self.bgBorderBottom = tonumber(value) + elseif name == 'background-border' then + self.bgBorderLeft = tonumber(value) + self.bgBorderRight = tonumber(value) + self.bgBorderTop = tonumber(value) + self.bgBorderBottom = tonumber(value) + end + end +end + +function UIProgressBarSDInverted:onGeometryChange(oldRect, newRect) + if not self:isOn() then + self:setHeight(0) + end + self:updateBackground() +end \ No newline at end of file diff --git a/modules/game_imbui/classes/imbuementitem.lua b/modules/game_imbui/classes/imbuementitem.lua new file mode 100644 index 0000000000..22c6051931 --- /dev/null +++ b/modules/game_imbui/classes/imbuementitem.lua @@ -0,0 +1,440 @@ +if not ImbuementItem then + ImbuementItem = { + window = nil, + confirmWindow = nil, + lastselectedwidget = nil, + selectedSlot = 0, + itemId = 0, + tier = 0, + slots = 0, + activeSlots = {}, + availableImbuements = {}, + needItems = {}, + } +end + +ImbuementItem.__index = ImbuementItem + +local self = ImbuementItem +function ImbuementItem.setup(itemId, tier, slots, activeSlots, availableImbuements, needItems) + self.itemId = itemId + self.tier = tier + self.slots = slots + + self.activeSlots = {} + for i = 0, slots - 1 do + self.activeSlots["slot"..i] = activeSlots[i] or {} + end + self.availableImbuements = availableImbuements or {} + self.needItems = needItems or {} + + for i = 0, 2 do + Imbuement.clearImbue:recursiveGetChildById("slot"..i):setBorderWidth(0) + Imbuement.selectImbue:recursiveGetChildById("slot"..i):setBorderWidth(0) + end + + self.selectedSlot = 0 + self.onSelectImbuementSlot(self.selectedSlot) + + -- Verificar se o slot 0 tem um imbuement ativo e passar para updateWindowState + local imbuement = self.activeSlots["slot0"] + self.updateWindowState(imbuement) + + self.configureWindow(Imbuement.selectImbue) + self.configureWindow(Imbuement.clearImbue) +end + +function ImbuementItem.configureWindow(window) + local slots = window:recursiveGetChildById("slots") + for i = 1, 3 do + local slotWidget = slots:getChildById("slot"..i - 1) + if slotWidget then + slotWidget.resource:setImageSource("/images/game/imbuing/icons/0") + if i <= self.slots then + slotWidget:setVisible(true) + local imbuement = self.activeSlots["slot"..i - 1] + if imbuement and imbuement[1] then + if imbuement[1].id and imbuement[1].id ~= 0 then + slotWidget.resource:setImageSource("/images/game/imbuing/icons/" .. imbuement[1]["imageId"]) + end + end + else + slotWidget:setVisible(false) + end + end + end + + local itemName = getItemNameById(self.itemId) + local itemWidget = window:recursiveGetChildById("item") + if itemWidget then + itemWidget:setItemId(self.itemId) + itemWidget:setImageSmooth(true) + itemWidget:setItemCount(1) + end + + local itemInformation = window:recursiveGetChildById("titleInformation") + if itemInformation then + itemInformation:setText(string.capitalize(itemName)) + end +end + +function ImbuementItem.onSelectSlot(widget) + local slot = widget:getId() + ImbuementItem.onSelectImbuementSlot(widget.slot) + local imbuement = self.activeSlots[slot] + self.updateWindowState(imbuement) +end + +function ImbuementItem.updateWindowState(imbuement) + if imbuement and imbuement[1] and imbuement[1].id ~= 0 then + Imbuement:toggleMenu("clearImbue") + self.window = Imbuement.clearImbue + self.onSelectSlotClear(imbuement) + else + Imbuement:toggleMenu("selectImbue") + self.window = Imbuement.selectImbue + self.onSelectSlotImbue() + end +end + +function ImbuementItem.onSelectImbuementSlot(slot) + Imbuement.clearImbue:recursiveGetChildById("slot"..self.selectedSlot):setBorderWidth(0) + Imbuement.selectImbue:recursiveGetChildById("slot"..self.selectedSlot):setBorderWidth(0) + + self.selectedSlot = slot + Imbuement.clearImbue:recursiveGetChildById("slot"..slot):setBorderWidth(1) + Imbuement.clearImbue:recursiveGetChildById("slot"..slot):setBorderColor("white") + Imbuement.selectImbue:recursiveGetChildById("slot"..slot):setBorderWidth(1) + Imbuement.selectImbue:recursiveGetChildById("slot"..slot):setBorderColor("white") +end + +function ImbuementItem:shutdown() + self.window = nil + self.itemId = 0 + self.tier = 0 + self.slots = 0 + self.activeSlots = {} + self.availableImbuements = {} + self.needItems = {} + if self.confirmWindow then + self.confirmWindow:destroy() + end + + if self.lastselectedwidget then + self.lastselectedwidget:destroy() + self.lastselectedwidget = nil + end + self.confirmWindow = nil +end + +function ImbuementItem.onSelectSlotClear(imbuement) + local title = self.window.cleanImbuePanel:getChildById("title") + if title then + title:setText(string.format('Clear Imbuement "%s"', imbuement[1].name)) + end + + local cleanImbuementsDetails = self.window:recursiveGetChildById("cleanImbuementsDetails") + if cleanImbuementsDetails then + cleanImbuementsDetails:setText('') + end + + local timeRemaining = self.window:recursiveGetChildById("timeRemaining") + if timeRemaining then + local time = imbuement[1].duration or 0 + timeRemaining:setMinimum(0) + timeRemaining:setMaximum(time) + timeRemaining:setValue(imbuement[2], 0, time) + end + + local imbuementReqContent = self.window:recursiveGetChildById("imbuementReqContent") + if imbuementReqContent then + local hours = string.format("%02.f", math.floor(imbuement[2]/3600)) + local mins = string.format("%02.f", math.floor(imbuement[2]/60 - (hours*60))) + + imbuementReqContent.time.textLabel:setText(string.format("%dh %dmin", hours, mins)) + imbuementReqContent.time.onHoverChange = function(widget, hovered, itemName, hasItem) + if hovered then + cleanImbuementsDetails:setText(tr("Show the time the imbuement is still active for.")) + else + cleanImbuementsDetails:setText("") + end + end + end + + local clearImbuementsList = self.window:recursiveGetChildById("clearImbuementsList") + clearImbuementsList:destroyChildren() + + local widget = g_ui.createWidget("SlotImbuing", clearImbuementsList) + widget.resource:setImageSource("/images/game/imbuing/icons/" .. imbuement[1]["imageId"]) + widget:setBorderWidth(1) + widget:setBorderColor("white") + + local selectedImbuementContent = self.window:recursiveGetChildById("selectedImbuementContent") + if selectedImbuementContent then + local imbuementsDetails = selectedImbuementContent:recursiveGetChildById("imbuementsDetails") + if imbuementsDetails then + imbuementsDetails:setText(imbuement[1].description or "") + end + end + + local balance = getPlayerBalance() + local clearButton = self.window:recursiveGetChildById("clear") + if clearButton then + clearButton:setEnabled(balance >= imbuement[3]) + clearButton.onClick = function() + if self.confirmWindow then + self.confirmWindow:destroy() + self.confirmWindow = nil + end + + Imbuement.hide() + + local function confirm() + g_game.clearImbuement(self.selectedSlot) + self.confirmWindow:destroy() + self.confirmWindow = nil + + Imbuement.show() + end + + local function cancelFunc() + if self.confirmWindow then + self.confirmWindow:destroy() + self.confirmWindow = nil + end + + Imbuement.show() + end + + self.confirmWindow = displayGeneralBox(tr('Confirm Clearing'), tr("Do you wish to spend %s gold coins to clear the imbuement \"%s\" from your item?", comma_value(imbuement[3]), string.capitalize(imbuement[1].name)), + { { text=tr('Yes'), callback=confirm }, + { text=tr('No'), callback=cancelFunc }, + }, confirm, cancelFunc) + end + + if balance >= imbuement[3] then + clearButton:setImageSource("/images/game/imbuing/clear") + clearButton:setImageClip("0 0 128 66") + else + clearButton:setImageSource("/images/game/imbuing/imbue_empty") + end + + clearButton.onHoverChange = function(widget, hovered, itemName, hasItem) + if hovered then + cleanImbuementsDetails:setText(tr("Your needs have changed? Click here to clear the imbuement from your item for a fee.")) + else + cleanImbuementsDetails:setText("") + end + end + end + + local costPanel = self.window:recursiveGetChildById("costPanel") + if costPanel then + costPanel.cost:setText(comma_value(imbuement[3])) + costPanel.cost:setColor(balance < imbuement[3] and "#C04040" or "#C0C0C0") + end +end + +function ImbuementItem.onSelectSlotImbue() + self.selectBaseType('basicButton') + + self.window:recursiveGetChildById('imbuementsDetails'):setVisible(false) +end + +function ImbuementItem.selectBaseType(selectedButtonId) + self.window:recursiveGetChildById('blockedPanels'):setVisible(true) + local qualityAndImbuementContent = self.window:recursiveGetChildById("qualityAndImbuementContent") + if not qualityAndImbuementContent then + return + end + + local basicButton = qualityAndImbuementContent.basicButton + local intricateButton = qualityAndImbuementContent.intricateButton + local powerfullButton = qualityAndImbuementContent.powerfullButton + + local baseImbuement = 0 + for _, button in pairs({basicButton, intricateButton, powerfullButton}) do + button:setOn(button:getId() == selectedButtonId) + if button:getId() == selectedButtonId then + baseImbuement = button.baseImbuement or 0 + end + end + + local imbuementsList = self.window:recursiveGetChildById("imbuementsList") + imbuementsList:setWidth(70) + imbuementsList:destroyChildren() + + local imbuementsDetails = self.window:recursiveGetChildById("imbuementsDetails") + imbuementsDetails:setVisible(false) + + local maxWidth = 0 + for id, imbuement in pairs(self.availableImbuements) do + local imbuementType = imbuement.type + if imbuementType == nil and imbuement.group then + if imbuement.group == 'Basic' then imbuementType = 0 + elseif imbuement.group == 'Intricate' then imbuementType = 1 + elseif imbuement.group == 'Powerful' then imbuementType = 2 + end + end + if imbuementType == baseImbuement then + local widget = g_ui.createWidget("SlotImbuing", imbuementsList) + widget:setId(tostring(id)) + widget.resource:setImageSource("/images/game/imbuing/icons/" .. imbuement.imageId) + + widget.onClick = function() + ImbuementItem.selectImbuementWidget(widget, imbuement) + end + + maxWidth = math.min(imbuementsList.maxWidth, maxWidth + imbuementsList.incrementwidth) + end + end + + imbuementsList:setWidth(maxWidth) +end + +function ImbuementItem.onSelectImbuement(widget) + local imbuementId = tonumber(widget:getId()) + local imbuement = self.availableImbuements[imbuementId] + if not imbuement then + return + end + + self.window:recursiveGetChildById('blockedPanels'):setVisible(false) + + local imbuementReqPanel = self.window:recursiveGetChildById("imbuementReqPanel") + if imbuementReqPanel then + imbuementReqPanel.title:setText(string.format('Imbue Empty Slot with "%s"', imbuement.name)) + end + local itensDetails = self.window:recursiveGetChildById("itensDetails") + if itensDetails then + itensDetails:setText("") + end +end + +function ImbuementItem.selectImbuementWidget(widget, imbuement) + if self.lastselectedwidget then + self.lastselectedwidget:setBorderWidth(1) + self.lastselectedwidget:setBorderColorTop("#797979") + self.lastselectedwidget:setBorderColorLeft("#797979") + self.lastselectedwidget:setBorderColorRight("#2e2e2e") + self.lastselectedwidget:setBorderColorBottom("#2e2e2e") + end + self.lastselectedwidget = widget + widget:setBorderWidth(1) + widget:setBorderColor("white") + + self.onSelectImbuement(widget) + + local imbuementsDetails = self.window:recursiveGetChildById("imbuementsDetails") + if imbuementsDetails then + imbuementsDetails:setVisible(true) + imbuementsDetails:setText(imbuement.description or "") + end + + local requiredItems = self.window:recursiveGetChildById("requiredItems") + local hasRequiredItems = true + if requiredItems then + for i = 1, 3 do + local itemWidget = requiredItems:getChildById("item"..i) + if itemWidget then + local source = imbuement.sources[i] + if source then + itemWidget.item:setItemId(source.item:getId()) + itemWidget:setVisible(true) + local itemCount = self.needItems[source.item:getId()] or 0 + itemWidget.count:setText(itemCount .."/" .. source.item:getCount()) + if itemCount >= source.item:getCount() then + itemWidget.count:setColor("#C0C0C0") + else + hasRequiredItems = false + itemWidget.count:setColor("#C04040") + end + + itemWidget.onHoverChange = function(widget, hovered) + local itensDetails = self.window:recursiveGetChildById("itensDetails") + if hovered then + local itemCount = self.needItems[source.item:getId()] or 0 + if itemCount >= source.item:getCount() then + itensDetails:setText(string.format("The imbuement you have selected requires %s.", source.description)) + else + itensDetails:setText(string.format("The imbuement requires %s. Unfortunately you do not own the needed amount.", source.description)) + end + else + if itensDetails then + itensDetails:setText("") + end + end + end + else + itemWidget:setVisible(false) + end + end + end + end + + local costPanel = self.window:recursiveGetChildById("costPanel") + if costPanel then + local cost = imbuement.cost or 0 + costPanel.cost:setText(comma_value(cost)) + local balance = getPlayerBalance() + + if balance < cost then + hasRequiredItems = false + end + + costPanel.cost:setColor(balance < cost and "#C04040" or "#C0C0C0") + end + + local imbueApply = self.window:recursiveGetChildById("imbueApply") + if imbueApply then + imbueApply:setEnabled(hasRequiredItems) + if not hasRequiredItems then + imbueApply:setImageSource("/images/game/imbuing/imbue_empty") + imbueApply:setImageClip("0 0 128 66") + else + imbueApply:setImageSource("/images/game/imbuing/imbue_green") + end + + imbueApply.onHoverChange = function(widget, hovered, itemName, hasItem) + local itensDetails = self.window:recursiveGetChildById("itensDetails") + if hovered then + itensDetails:setText(tr("Apply the selected imbuement. This will consume the required astral sources and gold.")) + else + if itensDetails then + itensDetails:setText("") + end + end + end + + imbueApply.onClick = function() + if self.confirmWindow then + self.confirmWindow:destroy() + self.confirmWindow = nil + end + + Imbuement.hide() + + local function confirm() + g_game.applyImbuement(self.selectedSlot, imbuement.id) + self.confirmWindow:destroy() + self.confirmWindow = nil + + Imbuement.show() + end + + local function cancelFunc() + if self.confirmWindow then + self.confirmWindow:destroy() + self.confirmWindow = nil + end + + Imbuement.show() + end + + self.confirmWindow = displayGeneralBox(tr('Confirm Imbuing'), tr("You are about to imbue your item with \"%s\". This will consume the required astral sources and %s\ngold coins. Do you wish to proceed?", string.capitalize(imbuement.name), comma_value(imbuement.cost)), + { { text=tr('Yes'), callback=confirm }, + { text=tr('No'), callback=cancelFunc }, + }, confirm, cancelFunc) + end + end +end \ No newline at end of file diff --git a/modules/game_imbui/classes/imbuementscroll.lua b/modules/game_imbui/classes/imbuementscroll.lua new file mode 100644 index 0000000000..f2c6bf92de --- /dev/null +++ b/modules/game_imbui/classes/imbuementscroll.lua @@ -0,0 +1,240 @@ +if not ImbuementScroll then + ImbuementScroll = { + window = nil, + itemId = 51442, + confirmWindow = nil, + availableImbuements = {}, + needItems = {} + } +end + +ImbuementScroll.__index = ImbuementScroll + +local self = ImbuementScroll +function ImbuementScroll.setup(availableImbuements, needItems) + self.availableImbuements = availableImbuements or {} + self.needItems = needItems or {} + self.window = Imbuement.scrollImbue + + local itemWidget = self.window:recursiveGetChildById("itemScroll") + if itemWidget then + itemWidget:setItemId(self.itemId) + itemWidget:setImageSmooth(true) + itemWidget:setItemCount(1) + end + + self.onSelectSlotImbue() +end + +function ImbuementScroll:shutdown() + self.window = nil + self.confirmWindow = nil + self.availableImbuements = {} + self.needItems = {} +end + +function ImbuementScroll.onSelectSlotImbue() + self.selectBaseType('powerfullButton') + self.window:recursiveGetChildById('imbuementsDetails'):setVisible(false) +end + +function ImbuementScroll.selectBaseType(selectedButtonId) + local qualityAndImbuementContent = self.window:recursiveGetChildById("qualityAndImbuementContent") + if not qualityAndImbuementContent then + return + end + + local intricateButton = qualityAndImbuementContent.intricateButton + local powerfullButton = qualityAndImbuementContent.powerfullButton + + local baseImbuement = 1 + for _, button in pairs({intricateButton, powerfullButton}) do + button:setOn(button:getId() == selectedButtonId) + if button:getId() == selectedButtonId then + baseImbuement = button.baseImbuement or 1 + end + end + + local imbuementsList = self.window:recursiveGetChildById("imbuementsList") + imbuementsList:destroyChildren() + + local imbuementsDetails = self.window:recursiveGetChildById("imbuementsDetails") + imbuementsDetails:setVisible(false) + + local selected = false + local matchedCount = 0 + + for id, imbuement in ipairs(self.availableImbuements) do + local imbuementType = imbuement.type + if imbuementType == nil and imbuement.group then + if imbuement.group == 'Basic' then imbuementType = 0 + elseif imbuement.group == 'Intricate' then imbuementType = 1 + elseif imbuement.group == 'Powerful' then imbuementType = 2 + end + end + + if imbuementType == baseImbuement then + matchedCount = matchedCount + 1 + local widget = g_ui.createWidget("SlotImbuing", imbuementsList) + widget:setId(tostring(id)) + widget.resource:setImageSource("/images/game/imbuing/icons/" .. imbuement.imageId) + + if not selected then + ImbuementScroll.selectImbuementWidget(widget, imbuement) + selected = true + end + + widget.onClick = function() + ImbuementScroll.selectImbuementWidget(widget, imbuement) + end + + end + end + +end + +function ImbuementScroll.selectImbuementWidget(widget, imbuement) + if self.lastselectedwidget then + self.lastselectedwidget:setBorderWidth(1) + self.lastselectedwidget:setBorderColorTop("#797979") + self.lastselectedwidget:setBorderColorLeft("#797979") + self.lastselectedwidget:setBorderColorRight("#2e2e2e") + self.lastselectedwidget:setBorderColorBottom("#2e2e2e") + end + self.lastselectedwidget = widget + widget:setBorderWidth(1) + widget:setBorderColor("white") + + self.onSelectImbuement(widget) + + local imbuementsDetails = self.window:recursiveGetChildById("imbuementsDetails") + if imbuementsDetails then + imbuementsDetails:setVisible(true) + imbuementsDetails:setText(imbuement.description or "") + end + + local requiredItems = self.window:recursiveGetChildById("requiredItems") + local hasRequiredItems = true + if requiredItems then + for i = 1, 4 do + local itemWidget = requiredItems:getChildById("item"..i) + if itemWidget then + local source = imbuement.sources[i] + if source then + itemWidget.item:setItemId(source.item:getId()) + itemWidget:setVisible(true) + local itemCount = self.needItems[source.item:getId()] or 0 + itemWidget.count:setText(itemCount .."/" .. source.item:getCount()) + if itemCount >= source.item:getCount() then + itemWidget.count:setColor("#C0C0C0") + else + hasRequiredItems = false + itemWidget.count:setColor("#C04040") + end + + itemWidget.onHoverChange = function(widget, hovered) + local itensDetails = self.window:recursiveGetChildById("itensDetails") + if hovered then + local itemCount = self.needItems[source.item:getId()] or 0 + if itemCount >= source.item:getCount() then + itensDetails:setText(string.format("The imbuement you have selected requires %s.", source.description)) + else + itensDetails:setText(string.format("The imbuement requires %s. Unfortunately you do not own the needed amount.", source.description)) + end + else + if itensDetails then + itensDetails:setText("") + end + end + end + else + itemWidget:setVisible(false) + end + end + end + end + + local costPanel = self.window:recursiveGetChildById("costPanel") + if costPanel then + local cost = imbuement.cost or 0 + costPanel.cost:setText(comma_value(cost)) + local balance = getPlayerBalance() + + if balance < cost then + hasRequiredItems = false + end + + costPanel.cost:setColor(balance < cost and "#C04040" or "#C0C0C0") + end + + local imbuescrollApply = self.window:recursiveGetChildById("imbuescrollApply") + if imbuescrollApply then + imbuescrollApply:setEnabled(hasRequiredItems) + if not hasRequiredItems then + imbuescrollApply:setImageSource("/images/game/imbuing/imbue_empty") + imbuescrollApply:setImageClip("0 0 128 66") + else + imbuescrollApply:setImageSource("/images/game/imbuing/imbue_green") + end + + imbuescrollApply.onHoverChange = function(widget, hovered, itemName, hasItem) + local itensDetails = self.window:recursiveGetChildById("itensDetails") + if hovered then + itensDetails:setText(tr("Apply the selected imbuement. This will consume the required astral sources and gold.")) + else + if itensDetails then + itensDetails:setText("") + end + end + end + + imbuescrollApply.onClick = function() + if self.confirmWindow then + self.confirmWindow:destroy() + self.confirmWindow = nil + end + + Imbuement.hide() + + local function confirm() + g_game.applyImbuement(0, imbuement.id) + self.confirmWindow:destroy() + self.confirmWindow = nil + + Imbuement.show() + end + + local function cancelFunc() + if self.confirmWindow then + self.confirmWindow:destroy() + self.confirmWindow = nil + end + + Imbuement.show() + end + + self.confirmWindow = displayGeneralBox(tr('Confirm Imbuing'), tr("You are about to imbue your item with \"%s\". This will consume the required astral sources and %s\ngold coins. Do you wish to proceed?", string.capitalize(imbuement.name), comma_value(imbuement.cost)), + { { text=tr('Yes'), callback=confirm }, + { text=tr('No'), callback=cancelFunc }, + }, confirm, cancelFunc) + end + end +end + +function ImbuementScroll.onSelectImbuement(widget) + local imbuementId = tonumber(widget:getId()) + local imbuement = self.availableImbuements[imbuementId] + if not imbuement then + return + end + + + local imbuementReqPanel = self.window:recursiveGetChildById("imbuementReqPanel") + if imbuementReqPanel then + imbuementReqPanel.title:setText(string.format('Imbue Blank Scroll with "%s"', imbuement.name)) + end + local itensDetails = self.window:recursiveGetChildById("itensDetails") + if itensDetails then + itensDetails:setText("") + end +end diff --git a/modules/game_imbui/classes/imbuementselection.lua b/modules/game_imbui/classes/imbuementselection.lua new file mode 100644 index 0000000000..c7d9d980ec --- /dev/null +++ b/modules/game_imbui/classes/imbuementselection.lua @@ -0,0 +1,93 @@ +if not ImbuementSelection then + ImbuementSelection = { + pickItem = nil, + } +end + +ImbuementSelection.__index = ImbuementSelection + +local self = ImbuementSelection +function ImbuementSelection.startUp() + self.pickItem = g_ui.createWidget('UIWidget') + self.pickItem:setVisible(false) + self.pickItem:setFocusable(false) + self.pickItem.onMouseRelease = self.onChooseItemMouseRelease +end + +function ImbuementSelection:shutdown() + if self.pickItem then + self.pickItem:destroy() + self.pickItem = nil + end +end + +function ImbuementSelection:selectItem() + if not self.pickItem then + self:startUp() + end + + if g_mouse.isPressed() then + return + end + + self.isSelectingScroll = false + self.pickItem:grabMouse() + g_mouse.pushCursor('target') +end + +function ImbuementSelection:selectScroll() + if not self.pickItem then + self:startUp() + end + + if g_mouse.isPressed() then + return + end + + self.isSelectingScroll = true + self.pickItem:grabMouse() + g_mouse.pushCursor('target') +end + +function ImbuementSelection.onChooseItemMouseRelease(widget, mousePosition, mouseButton) + local item = nil + if mouseButton == MouseLeftButton then + local clickedWidget = modules.game_interface.getRootPanel():recursiveGetChildByPos(mousePosition, false) + if clickedWidget then + if clickedWidget:getClassName() == 'UIGameMap' then + local tile = clickedWidget:getTile(mousePosition) + if tile then + local thing = tile:getTopMoveThing() + if thing and thing:isItem() then + item = thing + end + end + elseif clickedWidget:getClassName() == 'UIItem' and not clickedWidget:isVirtual() then + item = clickedWidget:getItem() + end + end + end + + if item and item:isPickupable() then + local pos = item:getPosition() + local itemId = item:getId() + local stackPos = item:getStackPos() + + if self.isSelectingScroll then + g_game.selectImbuementScroll() + else + g_game.selectImbuementItem(itemId, pos, stackPos) + end + + self.pickItem:ungrabMouse() + g_mouse.popCursor('target') + + return true + else + modules.game_textmessage.displayFailureMessage(tr('Sorry, not possible.')) + end + + self.pickItem:ungrabMouse() + g_mouse.popCursor('target') + return true +end diff --git a/modules/game_imbui/t_imbui.lua b/modules/game_imbui/t_imbui.lua new file mode 100644 index 0000000000..03e4996d63 --- /dev/null +++ b/modules/game_imbui/t_imbui.lua @@ -0,0 +1,307 @@ +if not Imbuement then + Imbuement = { + window = nil, + selectItemOrScroll = nil, + scrollImbue = nil, + selectImbue = nil, + clearImbue = nil, + + messageWindow = nil, + + bankGold = 0, + inventoryGold = 0, + } + Imbuement.__index = Imbuement +end + +-- Funcao auxiliar para calcular a posicao de um sprite em um spritesheet +function getFramePosition(frameIndex, frameWidth, frameHeight, columns) + local row = math.floor(frameIndex / columns) + local col = frameIndex % columns + local x = col * frameWidth + local y = row * frameHeight + return string.format("%d %d", x, y) +end + +-- Funcao auxiliar para obter o saldo total do player (banco + inventario) +function getPlayerBalance() + local player = g_game.getLocalPlayer() + if not player then return 0 end + + local bankGold = player:getResourceBalance(1) or 0 -- BANK_BALANCE + local inventoryGold = player:getResourceBalance(0) or 0 -- GOLD_EQUIPPED + return bankGold + inventoryGold +end + +-- Funcao auxiliar para formatar numeros com virgulas (separador de milhares) +function comma_value(amount) + if not amount then return "0" end + local formatted = tostring(amount) + -- Usar virgula como separador de milhares (formato: 5,561,475) + while true do + formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2') + if k == 0 then break end + end + return formatted +end + +-- Adicionar capitalize a string se nao existir +if not string.capitalize then + function string.capitalize(str) + if not str or str == "" then return str end + return str:sub(1, 1):upper() .. str:sub(2) + end +end + +-- Funcao auxiliar para obter o nome de um item por ID +function getItemNameById(itemId) + local itemType = g_things.getThingType(itemId, ThingCategoryItem) + if itemType and itemType.getName and type(itemType.getName) == "function" then + return itemType:getName() or "Unknown Item" + end + return "Unknown Item" +end + +Imbuement.MessageDialog = { + ImbuementSuccess = 0, + ImbuementError = 1, + ImbuementRollFailed = 2, + ImbuingStationNotFound = 3, + ClearingCharmSuccess = 10, + ClearingCharmError = 11, + PreyMessage = 20, + PreyError = 21, +} + +local self = Imbuement +function Imbuement.init() + self.window = g_ui.displayUI('t_imbui') + self:hide() + + ImbuementSelection:startUp() + + self.selectItemOrScroll = self.window:recursiveGetChildById('selectItemOrScroll') + self.scrollImbue = self.window:recursiveGetChildById('scrollImbue') + self.selectImbue = self.window:recursiveGetChildById('selectImbue') + self.clearImbue = self.window:recursiveGetChildById('clearImbue') + + connect(g_game, { + onGameStart = self.offline, + onGameEnd = self.offline, + onOpenImbuementWindow = self.onOpenImbuementWindow, + onImbuementItem = self.onImbuementItem, + onImbuementScroll = self.onImbuementScroll, + onCloseImbuementWindow = self.offline, + onMessageDialog = self.onMessageDialog, + }) +end + +function Imbuement.terminate() + disconnect(g_game, { + onGameStart = self.offline, + onGameEnd = self.offline, + onOpenImbuementWindow = self.onOpenImbuementWindow, + onImbuementItem = self.onImbuementItem, + onImbuementScroll = self.onImbuementScroll, + onResourceBalance = self.onResourceBalance, + onCloseImbuementWindow = self.offline, + onMessageDialog = self.onMessageDialog, + }) + + + if self.messageWindow then + self.messageWindow:destroy() + self.messageWindow = nil + end + + ImbuementItem:shutdown() + ImbuementSelection:shutdown() + ImbuementScroll:shutdown() + if self.selectItemOrScroll then + self.selectItemOrScroll:destroy() + self.selectItemOrScroll = nil + end + + if self.scrollImbue then + self.scrollImbue:destroy() + self.scrollImbue = nil + end + + if self.selectImbue then + self.selectImbue:destroy() + self.selectImbue = nil + end + + if self.clearImbue then + self.clearImbue:destroy() + self.clearImbue = nil + end + + if self.window then + self.window:destroy() + self.window = nil + end +end + +function Imbuement.online() + self:hide() + if self.messageWindow then + self.messageWindow:destroy() + self.messageWindow = nil + end +end + +function Imbuement.offline() + self:hide() + ImbuementItem:shutdown() + ImbuementScroll:shutdown() + if self.messageWindow then + self.messageWindow:destroy() + self.messageWindow = nil + end +end + +function Imbuement.show() + self.window:show(true) + self.window:raise() + self.window:focus() + if self.messageWindow then + self.messageWindow:destroy() + self.messageWindow = nil + end +end + +function Imbuement.hide() + self.window:hide() +end + +function Imbuement.close() + if g_game.isOnline() then + g_game.closeImbuingWindow() + end + self.window:hide() +end + +function Imbuement:toggleMenu(menu) + for key, value in pairs(self) do + if type(value) == 'userdata' and key ~= 'window' then + if key == menu then + value:show() + -- Ajustar tamanho da janela baseado no menu + if menu == 'selectItemOrScroll' then + self.window:setHeight(388) + elseif menu == 'scrollImbue' then + self.window:setHeight(655) + elseif menu == 'selectImbue' then + self.window:setHeight(528) + elseif menu == 'clearImbue' then + self.window:setHeight(502) + end + else + value:hide() + end + end + end +end + +function Imbuement.onOpenImbuementWindow() + self:show() + -- Atualizar recursos do player + local player = g_game.getLocalPlayer() + if player then + local bankGold = player:getResourceBalance(ResourceTypes.BANK_BALANCE) or 0 + local inventoryGold = player:getResourceBalance(ResourceTypes.GOLD_EQUIPPED) or 0 + local totalGold = bankGold + inventoryGold + self.window.contentPanel.gold.gold:setText(comma_value(totalGold)) + end + self:toggleMenu("selectItemOrScroll") +end + +-- Funcao para contar itens no inventario do jogador +function getPlayerItemCount(itemId) + local player = g_game.getLocalPlayer() + if not player then return 0 end + + local totalCount = 0 + + -- Contar nos slots do inventario + for slot = InventorySlotFirst, InventorySlotLast do + local item = player:getInventoryItem(slot) + if item then + if item:getId() == itemId then + totalCount = totalCount + item:getCount() + end + end + end + + return totalCount +end + +function Imbuement.onImbuementItem(itemId, tier, slots, activeSlots, availableImbuements, needItems) + local needItemsTable = {} + + for i, item in ipairs(needItems) do + if item and item.getId then + local itemId = item:getId() + local count = item:getCount() or 0 + needItemsTable[itemId] = count + end + end + + self:show() + self:toggleMenu("selectImbue") + ImbuementItem.setup(itemId, tier, slots, activeSlots, availableImbuements, needItemsTable) +end + +function Imbuement.onImbuementScroll(availableImbuements, needItems) + -- Converter needItems de array de Items para tabela {itemId -> count} + -- USAR O COUNT QUE VEM DO SERVIDOR (ja esta no Item) + local needItemsTable = {} + + for i, item in ipairs(needItems) do + if item and item.getId then + local itemId = item:getId() + local count = item:getCount() or 0 -- Usar o count que o servidor enviou + needItemsTable[itemId] = count + end + end + + self:toggleMenu("scrollImbue") + ImbuementScroll.setup(availableImbuements, needItemsTable) +end + +function Imbuement.onSelectItem() + self:hide() + ImbuementSelection:selectItem() +end + +function Imbuement.onSelectScroll() + g_game.selectImbuementScroll() +end + +function Imbuement.onMessageDialog(type, content) + if type > Imbuement.MessageDialog.ImbuingStationNotFound or not self.window:isVisible() then + return + end + + self:hide() + local message = content or "" + if self.messageWindow then + self.messageWindow:destroy() + self.messageWindow = nil + end + + local function confirm() + self.messageWindow:destroy() + self.messageWindow = nil + + Imbuement.show() + end + + self.messageWindow = displayGeneralBox(tr('Message Dialog'), content, + { { text=tr('Ok'), callback=confirm }, + }, confirm, confirm) + + + -- g_client.setInputLockWidget(self.messageWindow) -- deprecated +end \ No newline at end of file diff --git a/modules/game_imbui/t_imbui.otmod b/modules/game_imbui/t_imbui.otmod new file mode 100644 index 0000000000..ce5f3f4d90 --- /dev/null +++ b/modules/game_imbui/t_imbui.otmod @@ -0,0 +1,6 @@ +Module + name: game_imbui + sandboxed: true + scripts: [ t_imbui, classes/imbuementselection, classes/imbuementitem, classes/imbuementscroll ] + @onLoad: Imbuement.init() + @onUnload: Imbuement.terminate() diff --git a/modules/game_imbui/t_imbui.otui b/modules/game_imbui/t_imbui.otui new file mode 100644 index 0000000000..b5a909c0c3 --- /dev/null +++ b/modules/game_imbui/t_imbui.otui @@ -0,0 +1,902 @@ +PickItemButton < UIButton + id: itemButton + size: 96 64 + anchors.top: parent.top + anchors.left: parent.left + image-source: /images/game/imbuing/pick-item-button + image-clip: 0 0 96 64 + @onClick: modules.game_imbui.Imbuement.onSelectItem() + $hover !pressed: + image-clip: 0 0 96 64 + $pressed: + image-clip: 0 64 96 64 + +ScrollItemButton < UIButton + id: scrollButton + size: 96 64 + anchors.top: parent.top + anchors.left: itemButton.right + margin-left: 6 + image-source: /images/game/imbuing/imbue-scroll-button + image-clip: 0 0 96 64 + @onClick: modules.game_imbui.Imbuement.onSelectScroll() + $hover !pressed: + image-clip: 0 0 96 64 + $pressed: + image-clip: 0 64 96 64 + +SlotImbuing < UIButton + size: 66 66 + border-width: 1 + border-color-top: #797979 + border-color-left: #797979 + border-color-bottom: #2e2e2e + border-color-right: #2e2e2e + $checked: + border-color-top: #181818 + border-color-left: #181818 + border-color-bottom: #727272 + border-color-right: #727272 + UIWidget + id: resource + size: 64 64 + anchors.left: parent.left + margin-left: 1 + anchors.verticalCenter: parent.verticalCenter + image-source: /images/game/imbuing/imbuement-icons-64 + image-clip: 0 0 64 64 + phantom: true + +RequiredItem < UIWidget + size: 66 90 + UIItem + id: item + size: 66 66 + anchors.left: parent.left + anchors.top: parent.top + priceable: false + virtual: true + phantom: true + image-source: /images/ui/item66 + draggable: false + FlatLabel + id: count + margin-top: 3 + text-align: center + anchors.left: prev.left + anchors.right: prev.right + anchors.top: prev.bottom + image-source: /images/ui/infoPanel + font: verdana-11px-rounded + color: white + text-offset: -2 1 + +SelectItemOrScroll < UIWidget + anchors.fill: parent + Panel + id: itemOrScrollPanel + height: 93 + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + image-source: /images/ui/t2pixel-up-frame-borderimage + image-border: 5 + Label + id: title + anchors.top: parent.top + anchors.horizontalCenter: parent.horizontalCenter + margin-top: 2 + !text: tr('Pick Item or Imbue a Blank Scroll') + font: verdana-11px-rounded + color: #AAAAAA + text-auto-resize: true + Panel + id: itemOrScrollContent + anchors.fill: parent + margin: 22 12 10 12 + PickItemButton + ScrollItemButton + + UIWidget + id: blockedPanels + size: 708 198 + anchors.top: prev.bottom + anchors.left: parent.left + anchors.right: parent.right + image-source: /images/game/imbuing/blocked-panels + margin-top: 11 + +SelectImbue < UIWidget + anchors.fill: parent + Panel + id: itemOrScrollPanel + height: 103 + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + image-source: /images/ui/t2pixel-up-frame-borderimage + image-border: 5 + Label + id: title + anchors.top: parent.top + anchors.horizontalCenter: parent.horizontalCenter + margin-top: 2 + !text: tr('Pick Item or Imbue a Blank Scroll') + font: verdana-11px-rounded + color: #AAAAAA + text-auto-resize: true + Panel + id: itemOrScrollContent + anchors.fill: parent + margin: 27 12 10 12 + PickItemButton + ScrollItemButton + + + Panel + id: slots + size: 208 66 + anchors.top: prev.top + anchors.right: parent.right + margin-top: -1 + margin-right: 18 + SlotImbuing + id: slot0 + margin-right: 6 + &slot: 0 + anchors.right: next.left + @onClick: modules.game_imbui.ImbuementItem.onSelectSlot(self) + SlotImbuing + id: slot1 + margin-right: 6 + anchors.right: next.left + &slot: 1 + phantom: false + @onClick: modules.game_imbui.ImbuementItem.onSelectSlot(self) + SlotImbuing + id: slot2 + &slot: 2 + anchors.right: parent.right + @onClick: modules.game_imbui.ImbuementItem.onSelectSlot(self) + + UIItem + id: item + size: 64 64 + anchors.top: prev.top + anchors.right: prev.left + priceable: false + virtual: true + margin-top: 1 + margin-right: 22 + phantom: true + draggable: false + Label + id: titleInformation + anchors.top: prev.top + anchors.right: prev.left + !text: tr('Item Information') + font: verdana-11px-rounded + color: white + text-auto-resize: true + margin-top: 24 + margin-right: 22 + + Panel + id: qualityAndImbuementPanel + height: 177 + anchors.top: prev.bottom + anchors.left: parent.left + anchors.right: parent.right + image-source: /images/ui/t2pixel-up-frame-borderimage + image-border: 17 + margin-top: 11 + Label + id: title + anchors.top: parent.top + anchors.horizontalCenter: parent.horizontalCenter + margin-top: 2 + !text: tr('Select Quality and Imbuement') + font: verdana-11px-rounded + color: #AAAAAA + text-auto-resize: true + Panel + id: qualityAndImbuementContent + anchors.fill: parent + margin: 27 12 9 12 + UIButton + id: basicButton + size: 144 37 + anchors.top: parent.top + anchors.left: parent.left + &baseImbuement: 0 + margin-left: 116 + image-source: /images/game/imbuing/basic-button + image-clip: 0 0 144 37 + $hover !pressed: + image-clip: 0 0 144 37 + $pressed: + image-clip: 0 37 144 37 + $on: + image-clip: 0 37 144 37 + @onClick: modules.game_imbui.ImbuementItem.selectBaseType(self:getId()) + + UIButton + id: intricateButton + size: 144 37 + anchors.top: parent.top + anchors.left: prev.right + &baseImbuement: 1 + margin-left: 11 + image-source: /images/game/imbuing/intricate-button + image-clip: 0 0 144 37 + $hover !pressed: + image-clip: 0 0 144 37 + $pressed: + image-clip: 0 37 144 37 + $on: + image-clip: 0 37 144 37 + @onClick: modules.game_imbui.ImbuementItem.selectBaseType(self:getId()) + + UIButton + id: powerfullButton + size: 144 37 + anchors.top: parent.top + anchors.left: prev.right + &baseImbuement: 2 + margin-left: 11 + image-source: /images/game/imbuing/powerfull-button + image-clip: 0 0 144 37 + $hover !pressed: + image-clip: 0 0 144 37 + $pressed: + image-clip: 0 37 144 37 + $on: + image-clip: 0 37 144 37 + @onClick: modules.game_imbui.ImbuementItem.selectBaseType(self:getId()) + + ScrollablePanel + id: imbuementsList + height: 70 + width: 70 + &originalwidth: 680 + &maxWidth: 675 + &incrementwidth: 73 + anchors.top: prev.bottom + anchors.horizontalCenter: parent.horizontalCenter + margin-top: 11 + layout: + type: grid + cell-spacing: 5 + cell-size: 66 66 + num-columns: 9 + num-rows: 3 + + Label + id: imbuementsDetails + anchors.bottom: parent.bottom + anchors.left: parent.left + !text: tr('Raises your walking speed by 30. Lasts for 20h 0min while equipped.') + font: verdana-11px-rounded + color: white + text-auto-resize: true + + Panel + id: imbuementReqPanel + height: 141 + anchors.top: prev.bottom + anchors.left: parent.left + anchors.right: parent.right + image-source: /images/ui/t2pixel-up-frame-borderimage + image-border: 17 + margin-top: 11 + Label + id: title + anchors.top: parent.top + anchors.horizontalCenter: parent.horizontalCenter + margin-top: 2 + !text: tr('Imbue Empty Slot with "Imbuement Name"') + font: verdana-11px-rounded + color: #AAAAAA + text-auto-resize: true + Panel + id: imbuementReqContent + anchors.fill: parent + margin: 27 12 10 12 + Panel + id: requiredItems + width: 210 + height: 90 + anchors.top: parent.top + anchors.left: parent.left + RequiredItem + id: item1 + anchors.top: parent.top + anchors.left: parent.left + RequiredItem + id: item2 + anchors.top: parent.top + anchors.left: prev.right + margin-left: 6 + RequiredItem + id: item3 + anchors.top: parent.top + anchors.left: prev.right + margin-left: 6 + + UIWidget + id: horizontalArrow + size: 14 25 + anchors.left: prev.right + anchors.verticalCenter: requiredItems.verticalCenter + margin-left: 171 + image-source: /images/ui/right-arrow + phantom: false + + Label + id: itensDetails + anchors.bottom: parent.bottom + anchors.left: parent.left + !text: tr('') + font: verdana-11px-rounded + color: white + text-auto-resize: true + visible: true + + UIButton + id: imbueApply + size: 128 66 + anchors.top: requiredItems.top + anchors.right: parent.right + image-source: /images/game/imbuing/imbue_empty + image-clip: 0 0 128 66 + $pressed: + image-clip: 0 66 128 66 + + FlatLabel + id: costPanel + margin-top: 3 + anchors.left: prev.left + anchors.right: prev.right + anchors.top: prev.bottom + image-source: /images/ui/infoPanel + Label + id: cost + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + margin-right: 5 + margin-top: 3 + text: 200 + text-align: center + font: verdana-11px-rounded + color: white + text-auto-resize: true + UIWidget + size: 9 9 + anchors.top: cost.top + anchors.left: cost.right + margin-top: 3 + margin-left: 2 + image-source: /images/game/cyclopedia/icons/icon-goldcoin + + UIWidget + id: blockedPanels + size: 708 141 + anchors.top: prev.top + anchors.left: prev.left + image-source: /images/game/imbuing/blocked-panel + visible: false + +ClearImbue < UIWidget + anchors.fill: parent + Panel + id: itemOrScrollPanel + height: 103 + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + image-source: /images/ui/t2pixel-up-frame-borderimage + image-border: 5 + Label + id: title + anchors.top: parent.top + anchors.horizontalCenter: parent.horizontalCenter + margin-top: 2 + !text: tr('Pick Item or Imbue a Blank Scroll') + font: verdana-11px-rounded + color: #AAAAAA + text-auto-resize: true + Panel + id: itemOrScrollContent + anchors.fill: parent + margin: 27 12 10 12 + PickItemButton + ScrollItemButton + + Panel + id: slots + size: 208 66 + anchors.top: prev.top + anchors.right: parent.right + margin-top: -1 + margin-right: 18 + SlotImbuing + id: slot0 + margin-right: 6 + &slot: 0 + anchors.right: next.left + @onClick: modules.game_imbui.ImbuementItem.onSelectSlot(self) + SlotImbuing + id: slot1 + margin-right: 6 + anchors.right: next.left + &slot: 1 + phantom: false + @onClick: modules.game_imbui.ImbuementItem.onSelectSlot(self) + SlotImbuing + id: slot2 + &slot: 2 + anchors.right: parent.right + @onClick: modules.game_imbui.ImbuementItem.onSelectSlot(self) + + UIItem + id: item + size: 64 64 + anchors.top: prev.top + anchors.right: prev.left + priceable: false + virtual: true + phantom: true + draggable: false + margin-top: 1 + margin-right: 22 + Label + id: titleInformation + anchors.top: prev.top + anchors.right: prev.left + !text: tr('Item Information') + font: verdana-11px-rounded + color: white + text-auto-resize: true + margin-top: 24 + margin-right: 22 + + Panel + id: selectedImbuementPanel + height: 152 + anchors.top: prev.bottom + anchors.left: parent.left + anchors.right: parent.right + image-source: /images/ui/t2pixel-up-frame-borderimage + image-border: 17 + margin-top: 11 + Label + id: title + anchors.top: parent.top + anchors.horizontalCenter: parent.horizontalCenter + margin-top: 2 + !text: tr('Selected Imbuement') + font: verdana-11px-rounded + color: #AAAAAA + text-auto-resize: true + Panel + id: selectedImbuementContent + anchors.fill: parent + margin: 27 12 9 12 + ScrollablePanel + id: clearImbuementsList + height: 70 + width: 70 + &originalwidth: 680 + anchors.top: parent.top + anchors.horizontalCenter: parent.horizontalCenter + margin-top: 20 + layout: + type: grid + cell-spacing: 6 + cell-size: 66 66 + num-columns: 9 + num-rows: 3 + + Label + id: imbuementsDetails + anchors.bottom: parent.bottom + anchors.left: parent.left + !text: tr('Raises your walking speed by 30. Lasts for 20h 0min while equipped.') + font: verdana-11px-rounded + color: white + text-auto-resize: true + + Panel + id: cleanImbuePanel + height: 141 + anchors.top: prev.bottom + anchors.left: parent.left + anchors.right: parent.right + image-source: /images/ui/t2pixel-up-frame-borderimage + image-border: 17 + margin-top: 11 + Label + id: title + anchors.top: parent.top + anchors.horizontalCenter: parent.horizontalCenter + margin-top: 2 + !text: tr('Clear Imbuement "Imbuement Name"') + font: verdana-11px-rounded + color: #AAAAAA + text-auto-resize: true + Panel + id: imbuementReqContent + anchors.fill: parent + margin: 27 12 10 12 + Panel + id: time + size: 279 20 + image-source: /images/game/imbuing/imbui-timer-bg + anchors.top: parent.top + anchors.left: parent.left + margin-top: 34 + margin-left: 91 + phantom: false + ProgressBarSD + id: timeRemaining + size: 277 18 + anchors.fill: parent + image-source: /images/game/imbuing/imbui-progress + margin: 1 1 1 1 + phantom: true + percent: 1 + Label + id: textLabel + anchors.top: parent.top + anchors.horizontalCenter: parent.horizontalCenter + text-align: center + font: verdana-11px-rounded + color: white + text-auto-resize: true + margin-top: 2 + phantom: true + + Label + id: cleanImbuementsDetails + anchors.bottom: parent.bottom + anchors.left: parent.left + font: verdana-11px-rounded + color: white + text-auto-resize: true + + UIButton + id: clear + size: 128 66 + anchors.top: parent.top + anchors.right: parent.right + image-source: /images/game/imbuing/clear + image-clip: 0 0 128 66 + $pressed: + image-clip: 0 66 128 66 + FlatLabel + id: costPanel + margin-top: 3 + anchors.left: prev.left + anchors.right: prev.right + anchors.top: prev.bottom + image-source: /images/ui/infoPanel + Label + id: cost + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + margin-right: 5 + margin-top: 3 + text: 200 + text-align: center + font: verdana-11px-rounded + color: white + text-auto-resize: true + UIWidget + size: 9 9 + anchors.top: cost.top + anchors.left: cost.right + margin-top: 3 + margin-left: 2 + image-source: /images/game/cyclopedia/icons/icon-goldcoin + +ScrollImbue < UIWidget + anchors.fill: parent + Panel + id: itemOrScrollPanel + height: 93 + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + image-source: /images/ui/t2pixel-up-frame-borderimage + image-border: 5 + Label + id: title + anchors.top: parent.top + anchors.horizontalCenter: parent.horizontalCenter + margin-top: 2 + !text: tr('Pick Item or Imbue a Blank Scroll') + font: verdana-11px-rounded + color: #AAAAAA + text-auto-resize: true + Panel + id: itemOrScrollContent + anchors.fill: parent + margin: 22 12 10 12 + PickItemButton + ScrollItemButton + + UIItem + id: itemScroll + size: 64 64 + anchors.top: prev.top + anchors.right: parent.right + priceable: false + virtual: true + draggable: false + phantom: true + margin-top: 1 + margin-right: 77 + + Label + id: titleScroll + anchors.top: prev.top + anchors.right: prev.left + !text: tr('Blank Imbuement Scroll') + font: verdana-11px-rounded + color: white + text-auto-resize: true + margin-top: 24 + margin-right: 22 + + Panel + id: selectImbuementPanel + height: 314 + anchors.top: prev.bottom + anchors.left: parent.left + anchors.right: parent.right + image-source: /images/ui/t2pixel-up-frame-borderimage + image-border: 17 + margin-top: 11 + Label + id: title + anchors.top: parent.top + anchors.horizontalCenter: parent.horizontalCenter + margin-top: 2 + !text: tr('Select an Imbuement') + font: verdana-11px-rounded + color: #AAAAAA + text-auto-resize: true + Panel + id: qualityAndImbuementContent + anchors.fill: parent + margin: 22 12 9 12 + UIButton + id: intricateButton + size: 144 37 + anchors.top: parent.top + anchors.left: parent.left + margin-left: 193 + &baseImbuement: 1 + image-source: /images/game/imbuing/intricate-button + image-clip: 0 0 144 37 + $hover !pressed: + image-clip: 0 0 144 37 + $pressed: + image-clip: 0 37 144 37 + $on: + image-clip: 0 37 144 37 + @onClick: modules.game_imbui.ImbuementScroll.selectBaseType(self:getId()) + + UIButton + id: powerfullButton + size: 144 37 + anchors.top: parent.top + anchors.left: prev.right + margin-left: 11 + &baseImbuement: 2 + image-source: /images/game/imbuing/powerfull-button + image-clip: 0 0 144 37 + $hover !pressed: + image-clip: 0 0 144 37 + $pressed: + image-clip: 0 37 144 37 + $on: + image-clip: 0 37 144 37 + @onClick: modules.game_imbui.ImbuementScroll.selectBaseType(self:getId()) + + ScrollablePanel + id: imbuementsList + height: 206 + width: 680 + anchors.top: prev.bottom + anchors.horizontalCenter: parent.horizontalCenter + margin-top: 6 + padding-left: 25 + layout: + type: grid + cell-spacing: 4 + cell-size: 66 66 + num-columns: 9 + num-rows: 3 + Label + id: imbuementsDetails + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + !text: tr('Raises your walking speed by 30. Lasts for 20h 0min while equipped.') + font: verdana-11px-rounded + color: white + text-auto-resize: true + text-wrap: true + + Panel + id: imbuementReqPanel + height: 141 + anchors.top: prev.bottom + anchors.left: parent.left + anchors.right: parent.right + image-source: /images/ui/t2pixel-up-frame-borderimage + image-border: 17 + margin-top: 11 + Label + id: title + anchors.top: parent.top + anchors.horizontalCenter: parent.horizontalCenter + margin-top: 2 + !text: tr('Imbue Blank Scroll with "Imbuement Name"') + font: verdana-11px-rounded + color: #AAAAAA + text-auto-resize: true + Panel + id: imbuementReqContent + anchors.fill: parent + margin: 27 12 10 12 + Panel + id: requiredItems + width: 280 + height: 90 + anchors.top: parent.top + anchors.left: parent.left + RequiredItem + id: item1 + anchors.top: parent.top + anchors.left: parent.left + RequiredItem + id: item2 + anchors.top: parent.top + anchors.left: prev.right + margin-left: 6 + RequiredItem + id: item3 + anchors.top: parent.top + anchors.left: prev.right + margin-left: 6 + RequiredItem + id: item4 + anchors.top: parent.top + anchors.left: prev.right + margin-left: 6 + + UIWidget + id: horizontalArrow + size: 14 25 + anchors.left: prev.right + anchors.verticalCenter: requiredItems.verticalCenter + margin-left: 171 + image-source: /images/ui/right-arrow + phantom: false + + Label + id: itensDetails + anchors.bottom: parent.bottom + anchors.left: parent.left + !text: tr('') + font: verdana-11px-rounded + color: white + text-auto-resize: true + visible: true + + UIButton + id: imbuescrollApply + size: 128 66 + anchors.top: requiredItems.top + anchors.right: parent.right + image-source: /images/game/imbuing/imbue_empty + image-clip: 0 0 128 66 + $pressed: + image-clip: 0 0 128 66 + + FlatLabel + id: costPanel + margin-top: 3 + anchors.left: prev.left + anchors.right: prev.right + anchors.top: prev.bottom + image-source: /images/ui/infoPanel + Label + id: cost + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + margin-right: 5 + margin-top: 3 + text: 200 + text-align: center + font: verdana-11px-rounded + color: white + text-auto-resize: true + UIWidget + size: 9 9 + anchors.top: cost.top + anchors.left: cost.right + margin-top: 3 + margin-left: 2 + image-source: /images/game/cyclopedia/icons/icon-goldcoin + +NewMainWindow + id: imbuingWindow + !text: tr('Imbuement Shrine') + size: 740 640 + @onEscape: modules.game_imbui.Imbuement.close() + Panel + id: contentPanel + anchors.fill: parent + margin: 12 12 12 12 + + SelectItemOrScroll + id: selectItemOrScroll + main-window-size: 740 388 + visible: true + + ScrollImbue + id: scrollImbue + main-window-size: 740 655 + visible: false + + SelectImbue + id: selectImbue + main-window-size: 740 528 + visible: false + + ClearImbue + id: clearImbue + main-window-size: 740 502 + visible: false + + HorizontalSeparator + id: bottomSep + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: close.top + margin-bottom: 11 + Button + id: close + !text: tr('Close') + size: 43 20 + anchors.right: parent.right + anchors.bottom: parent.bottom + @onClick: modules.game_imbui.Imbuement.close() + Panel + id: gold + size: 160 20 + anchors.left: parent.left + anchors.bottom: parent.bottom + image-source: /images/game/prey/balanceBg + UIWidget + id: goldIcon + anchors.top: parent.top + anchors.right: parent.right + margin-top: 6 + margin-right: 3 + image-source: /images/game/prey/prey_gold + UIWidget + id: gold + anchors.top: parent.top + anchors.right: prev.left + anchors.left: parent.left + margin-top: 3 + margin-right: 2 + text-align: right + text: 0 + font: verdana-11px-rounded + color: white + text-auto-resize: true \ No newline at end of file diff --git a/modules/game_imbuing/imbuing.lua b/modules/game_imbuing/imbuing.lua index 36e95446dd..ca97e9570c 100644 --- a/modules/game_imbuing/imbuing.lua +++ b/modules/game_imbuing/imbuing.lua @@ -13,6 +13,17 @@ local imbueItems = {} local protection = false local clearConfirmWindow local imbueConfirmWindow +local infoPanel + +local function getCorrectIconId(id) + local iconId = id + if iconId >= 16 then iconId = iconId + 3 end -- pula 16, 17, 18 + if iconId >= 22 then iconId = iconId + 3 end -- pula 22, 23, 24 + if iconId >= 43 then iconId = iconId + 3 end -- pula 43, 44, 45 + if iconId >= 61 then iconId = iconId + 3 end -- pula 61, 62, 63 + if iconId >= 79 then iconId = iconId + 3 end -- pula 79, 80, 81 + return iconId +end function init() connect(g_game, { @@ -28,12 +39,13 @@ function init() imbueLevelsCombo = emptyImbue.imbuement protectionBtn = emptyImbue.protection clearImbue = imbuingWindow.clearImbue + infoPanel = imbuingWindow.infoPanel imbuingWindow:hide() local player = g_game.getLocalPlayer() if player then bankGold = player:getResourceBalance(ResourceTypes.BANK_BALANCE) inventoryGold = player:getResourceBalance(ResourceTypes.GOLD_EQUIPPED) - imbuingWindow.balance:setText(tr('Balance') .. ':\n' .. (player:getTotalMoney())) + imbuingWindow.balance:setText(tr(comma_value (player:getTotalMoney()))) end groupsCombo.onOptionChange = function(widget) @@ -56,49 +68,75 @@ function init() if imbuement['group'] == selectedGroup then if #imbuement['sources'] == widget.currentIndex then selectedImbue = imbuement + local hasAllItems = true + for i, source in ipairs(imbuement['sources']) do + local itemFound = false for _, item in ipairs(imbueItems) do if item:getId() == source['item']:getId() then + itemFound = true if item:getCount() >= source['item']:getCount() then - emptyImbue.imbue:setImageSource('/images/game/imbuing/imbue_green') - emptyImbue.imbue:setEnabled(true) emptyImbue.requiredItems:getChildByIndex(i).count:setColor('white') - end - if item:getCount() < source['item']:getCount() then - emptyImbue.imbue:setEnabled(false) - emptyImbue.imbue:setImageSource('/images/game/imbuing/imbue_empty') + else + hasAllItems = false emptyImbue.requiredItems:getChildByIndex(i).count:setColor('red') end - emptyImbue.requiredItems:getChildByIndex(i).count:setText(item:getCount() .. '/' .. - source['item']:getCount()) + emptyImbue.requiredItems:getChildByIndex(i).count:setText(item:getCount() .. '/' .. source['item']:getCount()) end end + if not itemFound then + hasAllItems = false + emptyImbue.requiredItems:getChildByIndex(i).count:setText('0/' .. source['item']:getCount()) + emptyImbue.requiredItems:getChildByIndex(i).count:setColor('red') + end emptyImbue.requiredItems:getChildByIndex(i).item:setItemId(source['item']:getId()) - emptyImbue.requiredItems:getChildByIndex(i).item:setTooltip('The imbuement requires ' .. - source['description'] .. '.') + emptyImbue.requiredItems:getChildByIndex(i).item:setTooltip('The imbuement requires ' .. source['description'] .. '.') end + for i = 3, widget.currentIndex + 1, -1 do emptyImbue.requiredItems:getChildByIndex(i).count:setText('') emptyImbue.requiredItems:getChildByIndex(i).item:setItemId(0) emptyImbue.requiredItems:getChildByIndex(i).item:setTooltip('') end - emptyImbue.protectionCost:setText(imbuement['protectionCost']) - emptyImbue.cost:setText(imbuement['cost']) - if not protection and (bankGold + inventoryGold) < imbuement['cost'] then - emptyImbue.imbue:setEnabled(false) - emptyImbue.imbue:setImageSource('/images/game/imbuing/imbue_empty') - emptyImbue.cost:setColor('red') - end - if not protection and (bankGold + inventoryGold) >= imbuement['cost'] then - emptyImbue.cost:setColor('white') + emptyImbue.protectionCost:setText((comma_value(imbuement['protectionCost']))) + emptyImbue.cost:setText(comma_value(imbuement['cost'])) + + -- Verificar se tem todos os itens e gold suficiente + local hasEnoughGold = false + if not protection then + hasEnoughGold = (bankGold + inventoryGold) >= imbuement['cost'] + if not hasEnoughGold then + emptyImbue.cost:setColor('red') + else + emptyImbue.cost:setColor('white') + end + else + hasEnoughGold = (bankGold + inventoryGold) >= (imbuement['cost'] + imbuement['protectionCost']) + if not hasEnoughGold then + emptyImbue.cost:setColor('red') + else + emptyImbue.cost:setColor('white') + end end - if protection and (bankGold + inventoryGold) < (imbuement['cost'] + imbuement['protectionCost']) then + + -- Habilitar/desabilitar botão de imbue baseado em itens E gold + if hasAllItems and hasEnoughGold then + emptyImbue.imbue:setEnabled(true) + emptyImbue.imbue:setImageSource('/images/game/imbuing/imbue_green') + else emptyImbue.imbue:setEnabled(false) emptyImbue.imbue:setImageSource('/images/game/imbuing/imbue_empty') - emptyImbue.cost:setColor('red') end - if protection and (bankGold + inventoryGold) >= (imbuement['cost'] + imbuement['protectionCost']) then - emptyImbue.cost:setColor('white') + + -- Verificar se o botão de proteção deve ser desabilitado + if (bankGold + inventoryGold) < imbuement['protectionCost'] then + protectionBtn:setEnabled(false) + emptyImbue.protection:setImageSource('/images/game/imbuing/useprotection-disabled') + emptyImbue.protectionCost:setColor('red') + else + protectionBtn:setEnabled(true) + emptyImbue.protection:setImageSource('/images/game/imbuing/100percent') + emptyImbue.protectionCost:setColor('white') end emptyImbue.successRate:setText(imbuement['successRate'] .. '%') if selectedImbue['successRate'] > 50 then @@ -115,26 +153,84 @@ function init() protectionBtn.onClick = function() setProtection(not protection) end + + -- Configurar hover events para exibir tooltips no painel + setupTooltipEvents() end function setProtection(value) protection = value if protection then - emptyImbue.cost:setText(selectedImbue['cost'] + selectedImbue['protectionCost']) + if not selectedImbue then + protection = false + return + end + emptyImbue.cost:setText(comma_value(selectedImbue['cost'] + selectedImbue['protectionCost'])) emptyImbue.successRate:setText('100%') emptyImbue.successRate:setColor('green') protectionBtn:setImageClip(torect('66 0 66 66')) + + -- Verificar se há gold suficiente para o custo total com proteção + if (bankGold + inventoryGold) < (selectedImbue['cost'] + selectedImbue['protectionCost']) then + emptyImbue.cost:setColor('red') + else + emptyImbue.cost:setColor('white') + end else if selectedImbue then - emptyImbue.cost:setText(selectedImbue['cost']) + emptyImbue.cost:setText(comma_value(selectedImbue['cost'])) emptyImbue.successRate:setText(selectedImbue['successRate'] .. '%') if selectedImbue['successRate'] > 50 then emptyImbue.successRate:setColor('white') else emptyImbue.successRate:setColor('red') end + + -- Verificar se há gold suficiente para o custo sem proteção + if (bankGold + inventoryGold) < selectedImbue['cost'] then + emptyImbue.cost:setColor('red') + else + emptyImbue.cost:setColor('white') + end + protectionBtn:setImageClip(torect('0 0 66 66')) + end + end + + -- Re-verificar o estado do botão de imbue quando a proteção mudar + if selectedImbue then + local hasAllItems = true + for i, source in ipairs(selectedImbue['sources']) do + local itemFound = false + for _, item in ipairs(imbueItems) do + if item:getId() == source['item']:getId() then + itemFound = true + if item:getCount() < source['item']:getCount() then + hasAllItems = false + break + end + end + end + if not itemFound then + hasAllItems = false + break + end + end + + local hasEnoughGold = false + if protection then + hasEnoughGold = (bankGold + inventoryGold) >= (selectedImbue['cost'] + selectedImbue['protectionCost']) + else + hasEnoughGold = (bankGold + inventoryGold) >= selectedImbue['cost'] + end + + -- Habilitar/desabilitar botão de imbue baseado em itens E gold + if hasAllItems and hasEnoughGold then + emptyImbue.imbue:setEnabled(true) + emptyImbue.imbue:setImageSource('/images/game/imbuing/imbue_green') + else + emptyImbue.imbue:setEnabled(false) + emptyImbue.imbue:setImageSource('/images/game/imbuing/imbue_empty') end - protectionBtn:setImageClip(torect('0 0 66 66')) end end @@ -152,19 +248,31 @@ end function resetSlots() emptyImbue:setVisible(false) clearImbue:setVisible(false) + if infoPanel then + local tooltipContent = infoPanel:recursiveGetChildById('tooltipContent') + if tooltipContent then tooltipContent:setText('') end + end for i = 1, 3 do - imbuingWindow.itemInfo.slots:getChildByIndex(i):setText('Slot ' .. i) - imbuingWindow.itemInfo.slots:getChildByIndex(i):setEnabled(false) - imbuingWindow.itemInfo.slots:getChildByIndex(i):setTooltip( + local slot = imbuingWindow.itemInfo.slots:getChildByIndex(i) + slot:setText('Slot ' .. i) + slot:getChildById('icon'):setVisible(false) + slot:setEnabled(false) + slot:setTooltip( 'Items can have up to three imbuements slots. This slot is not available for this item.') - imbuingWindow.itemInfo.slots:getChildByIndex(i).onClick = nil + slot.onClick = nil end end function selectSlot(widget, slotId, activeSlot) + local slotIcon = widget:getChildById('icon') + if activeSlot then emptyImbue:setVisible(false) - widget:setText(activeSlot[1]['name']) + widget:setText('') + slotIcon:setVisible(true) + local id = activeSlot[1]['id'] or 1 + slotIcon:setImageSource('/images/game/imbuing/icons/' .. getCorrectIconId(id)) + slotIcon:setImageClip(torect('0 0 64 64')) clearImbue.title:setText('Clear Imbuement "' .. activeSlot[1]['name'] .. '"') clearImbue.groups:clearOptions() clearImbue.groups:addOption(activeSlot[1]['group']) @@ -172,20 +280,30 @@ function selectSlot(widget, slotId, activeSlot) clearImbue.imbuement:addOption(activeSlot[1]['name']) clearImbue.description:setText(activeSlot[1]['description']) - hours = string.format('%02.f', math.floor(activeSlot[2] / 3600)) - mins = string.format('%02.f', math.floor(activeSlot[2] / 60 - (hours * 60))) - clearImbue.time.timeRemaining:setText(hours .. ':' .. mins .. 'h') + local hours = string.format('%02.f', math.floor(activeSlot[2] / 3600)) + local mins = string.format('%02.f', math.floor(activeSlot[2] / 60 - (hours * 60))) + + local totalTime = activeSlot[1].duration or 72000 + local timeRemaining = clearImbue.time.timerContainer.timeRemaining + + if timeRemaining then + timeRemaining:setMinimum(0) + timeRemaining:setMaximum(totalTime) + timeRemaining:setValue(activeSlot[2], 0, totalTime) + end - clearImbue.cost:setText(activeSlot[3]) + clearImbue.time.timerContainer.timeRemaining.text:setText(hours .. ':' .. mins .. 'h') + clearImbue.cost:setText(comma_value(activeSlot[3])) if (bankGold + inventoryGold) < activeSlot[3] then - emptyImbue.clear:setEnabled(false) - emptyImbue.clear:setImageSource('/images/game/imbuing/imbue_empty') - emptyImbue.cost:setColor('red') + clearImbue.clear:setEnabled(false) + clearImbue.clear:setImageSource('/images/game/imbuing/imbue_empty') + clearImbue.cost:setColor('red') end local yesCallback = function() g_game.clearImbuement(slotId) widget:setText('Slot ' .. (slotId + 1)) + slotIcon:setVisible(true) if clearConfirmWindow then clearConfirmWindow:destroy() clearConfirmWindow = nil @@ -229,7 +347,9 @@ function selectSlot(widget, slotId, activeSlot) clearConfirmWindow:destroy() clearConfirmWindow = nil end - widget:setText(selectedImbue['name']) + slotIcon:setVisible(true) + local id = selectedImbue['id'] or 1 + slotIcon:setImageSource('/images/game/imbuing/icons/' .. getCorrectIconId(id)) imbuingWindow:show() end local noCallback = function() @@ -294,6 +414,14 @@ function Imbuing.onImbuementWindow(itemId, slots, activeSlots, imbuements, needI activeSlotBtn.onClick = function(widget) selectSlot(widget, i, slot) end + + -- Atualiza o ícone de todos os slots ativos imediatamente + local slotIcon = activeSlotBtn:getChildById('icon') + activeSlotBtn:setText('') + slotIcon:setVisible(true) + local id = slot[1]['id'] or 1 + slotIcon:setImageSource('/images/game/imbuing/icons/' .. getCorrectIconId(id)) + if activeSlotBtn:getId() == 'slot0' then selectSlot(activeSlotBtn, i, slot) end @@ -309,6 +437,9 @@ function Imbuing.onImbuementWindow(itemId, slots, activeSlots, imbuements, needI end end end + + -- Reconfigurar eventos de hover após carregar os slots + setupTooltipEvents() show() end @@ -321,7 +452,67 @@ function Imbuing.onResourcesBalanceChange(balance, oldBalance, type) local player = g_game.getLocalPlayer() if player then if type == ResourceTypes.BANK_BALANCE or type == ResourceTypes.GOLD_EQUIPPED then - imbuingWindow.balance:setText(tr('Balance') .. ':\n' .. (player:getTotalMoney())) + imbuingWindow.balance:setText(tr(comma_value (player:getTotalMoney()))) + + -- Re-verificar o estado dos botões quando o saldo mudar + if selectedImbue and emptyImbue:isVisible() then + -- Verificar botão de proteção + if (bankGold + inventoryGold) < selectedImbue['protectionCost'] then + protectionBtn:setEnabled(false) + emptyImbue.protection:setImageSource('/images/game/imbuing/useprotection-disabled') + emptyImbue.protectionCost:setColor('red') + else + protectionBtn:setEnabled(true) + emptyImbue.protection:setImageSource('/images/game/imbuing/100percent') + emptyImbue.protectionCost:setColor('white') + end + + -- Verificar se tem todos os itens + local hasAllItems = true + for i, source in ipairs(selectedImbue['sources']) do + local itemFound = false + for _, item in ipairs(imbueItems) do + if item:getId() == source['item']:getId() then + itemFound = true + if item:getCount() < source['item']:getCount() then + hasAllItems = false + break + end + end + end + if not itemFound then + hasAllItems = false + break + end + end + + -- Verificar gold suficiente + local hasEnoughGold = false + if protection then + hasEnoughGold = (bankGold + inventoryGold) >= (selectedImbue['cost'] + selectedImbue['protectionCost']) + if not hasEnoughGold then + emptyImbue.cost:setColor('red') + else + emptyImbue.cost:setColor('white') + end + else + hasEnoughGold = (bankGold + inventoryGold) >= selectedImbue['cost'] + if not hasEnoughGold then + emptyImbue.cost:setColor('red') + else + emptyImbue.cost:setColor('white') + end + end + + -- Habilitar/desabilitar botão de imbue baseado em itens E gold + if hasAllItems and hasEnoughGold then + emptyImbue.imbue:setEnabled(true) + emptyImbue.imbue:setImageSource('/images/game/imbuing/imbue_green') + else + emptyImbue.imbue:setEnabled(false) + emptyImbue.imbue:setImageSource('/images/game/imbuing/imbue_empty') + end + end end end end @@ -347,3 +538,85 @@ function toggle() end show() end + +-- Função para formatar texto com quebra de linha automática +function formatTooltipText(text) + if not text then return '' end + + -- Definir largura máxima aproximada (em caracteres) + local maxWidth = 240 + + local formattedText = '' + local currentLine = '' + + -- Dividir o texto em palavras + for word in text:gmatch("%S+") do + -- Se adicionar a palavra exceder a largura máxima + if #currentLine + #word + 1 > maxWidth and #currentLine > 0 then + formattedText = formattedText .. currentLine .. '\n' + currentLine = word + else + if #currentLine > 0 then + currentLine = currentLine .. ' ' .. word + else + currentLine = word + end + end + end + + -- Adicionar a última linha + if #currentLine > 0 then + formattedText = formattedText .. currentLine + end + + return formattedText +end + +-- Função para configurar eventos de hover em elementos com tooltip +function setupTooltipEvents() + if not infoPanel then return end + + local tooltipContent = infoPanel:recursiveGetChildById('tooltipContent') + if not tooltipContent then return end + + -- Função para adicionar hover a um widget + local function addHoverToWidget(widget) + if not widget or not widget.getTooltip then return end + + widget.onHoverChange = function(self, hovered) + if hovered then + local tooltip = self:getTooltip() + if tooltip and tooltip ~= '' then + -- Adicionar quebras de linha para textos longos + local formattedText = formatTooltipText(tooltip) + tooltipContent:setText(formattedText) + else + tooltipContent:setText('') + end + else + tooltipContent:setText('') + end + end + end + + -- Adicionar hover aos botões principais + if emptyImbue.imbue then addHoverToWidget(emptyImbue.imbue) end + if emptyImbue.protection then addHoverToWidget(emptyImbue.protection) end + if clearImbue.clear then addHoverToWidget(clearImbue.clear) end + + -- Adicionar hover aos itens requeridos + if emptyImbue.requiredItems then + for i = 1, 3 do + local item = emptyImbue.requiredItems:getChildByIndex(i).item + if item then addHoverToWidget(item) end + end + end + + -- Adicionar hover aos slots + if imbuingWindow.itemInfo and imbuingWindow.itemInfo.slots then + for i = 1, 3 do + local slot = imbuingWindow.itemInfo.slots:getChildByIndex(i) + if slot then addHoverToWidget(slot) end + end + end +end diff --git a/modules/game_imbuing/imbuing.otui b/modules/game_imbuing/imbuing.otui index 424e8d2e35..2ebb620acc 100644 --- a/modules/game_imbuing/imbuing.otui +++ b/modules/game_imbuing/imbuing.otui @@ -1,11 +1,18 @@ Slot < Button - width: 70 + width: 66 height: 66 anchors.verticalCenter: parent.verticalCenter enabled: false text-wrap: true !tooltip: tr('Items can have up to three imbuements slots. This slot is not available for this item.') + UIWidget + id: icon + size: 64 64 + anchors.centerIn: parent + phantom: true + visible: false + RequiredItem < Panel width: 66 height: 90 @@ -16,6 +23,7 @@ RequiredItem < Panel width: 66 anchors.left: parent.left anchors.top: parent.top + image-source: /images/ui/item66 FlatLabel id: count @@ -24,11 +32,14 @@ RequiredItem < Panel anchors.left: prev.left anchors.right: prev.right anchors.top: prev.bottom + text-offset: 0 0 + image-source: /images/ui/infoPanel ItemInformation < Panel height: 100 - border: 1 black - padding: 5 + padding: 3 + image-source: /images/ui/t2pixel-up-frame-borderimage + image-border: 5 Label id: title @@ -62,13 +73,26 @@ ItemInformation < Panel !text: tr("Slot 1") text-align: center anchors.left: parent.left + image-source: /images/game/imbuing/slot_inactive + image-clip: 0 0 66 66 + $hover !pressed: + image-clip: 0 0 66 66 + $pressed: + image-source: /images/game/imbuing/slot + image-clip: 0 0 66 66 Slot id: slot1 !text: tr("Slot 2") - text-align: center - margin-left: 10 anchors.left: prev.right + margin-left: 10 + image-source: /images/game/imbuing/slot_inactive + image-clip: 0 0 66 66 + $hover !pressed: + image-clip: 0 0 66 66 + $pressed: + image-source: /images/game/imbuing/slot + image-clip: 0 0 66 66 Slot id: slot2 @@ -76,6 +100,13 @@ ItemInformation < Panel text-align: center margin-left: 10 anchors.left: prev.right + image-source: /images/game/imbuing/slot_inactive + image-clip: 0 0 66 66 + $hover !pressed: + image-clip: 0 0 66 66 + $pressed: + image-source: /images/game/imbuing/slot + image-clip: 0 0 66 66 Label id: selectSlot @@ -86,8 +117,9 @@ ItemInformation < Panel EmptyImbue < Panel height: 240 - border: 1 black - padding: 5 + padding: 3 15 15 15 + image-source: /images/ui/t2pixel-up-frame-borderimage_2 + image-border: 5 Label id: title @@ -126,6 +158,7 @@ EmptyImbue < Panel anchors.bottom: prev.bottom anchors.left: parent.left !text: tr('Requires the following astral sources:') + margin-bottom: 10 Label id: successRate @@ -180,6 +213,7 @@ EmptyImbue < Panel anchors.left: prev.left anchors.right: prev.right anchors.top: prev.bottom + image-source: /images/ui/infoPanel UIWidget id: horizontalArrow @@ -214,11 +248,13 @@ EmptyImbue < Panel anchors.left: prev.left anchors.right: prev.right anchors.top: prev.bottom + image-source: /images/game/prey/balanceBg ClearImbue < Panel height: 240 - border: 1 black - padding: 5 + padding: 3 15 15 15 + image-source: /images/ui/t2pixel-up-frame-borderimage_2 + image-border: 5 Label id: title @@ -267,19 +303,35 @@ ClearImbue < Panel Panel id: time - width: 210 + width: 277 height: 90 anchors.left: parent.left anchors.bottom: parent.bottom - - FlatLabel - id: timeRemaining - size: 86 25 - margin-bottom: 20 - text-align: center - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter + + Panel + id: timerContainer + size: 277 18 + anchors.centerIn: parent + image-source: /images/game/imbuing/imbui-timer-bg + + ProgressBarSD + id: timeRemaining + size: 277 18 + anchors.fill: parent + image-source: /images/game/imbuing/imbui-progress + margin: 1 + phantom: true + percent: 1 + + Label + id: text + anchors.centerIn: parent + text-align: center + text-auto-resize: true + color: white + font: verdana-11px-rounded + margin-top: 1 + phantom: true UIButton id: clear @@ -302,12 +354,29 @@ ClearImbue < Panel anchors.left: prev.left anchors.right: prev.right anchors.top: prev.bottom + image-source: /images/game/prey/balanceBg +infoPanelTolltip < Panel + height: 75 + padding: 10 + image-source: /images/ui/2pixel_up_frame_borderimage + image-border: 5 + + Label + id: tooltipContent + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + text-align: left + color: white + font: verdana-11px-rounded + text-wrap: true MainWindow id: imbuingWindow !text: tr('Imbue Item') - size: 550 430 + size: 572 520 background-color: #AAAAAA @onEscape: modules.game_imbuing.hide() @@ -330,6 +399,13 @@ MainWindow anchors.top: emptyImbue.top anchors.right: parent.right + infoPanelTolltip + id: infoPanel + anchors.left: parent.left + anchors.top: emptyImbue.bottom + anchors.right: parent.right + margin-top: 5 + Button id: close !text: tr('Close') @@ -341,6 +417,14 @@ MainWindow Label id: balance height: 25 - anchors.right: prev.left anchors.left: parent.left anchors.bottom: parent.bottom + text-align: right + text-offset: -20 0 + image-source: /images/game/prey/balanceBg + UIWidget + id: Icon + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + margin-right: 4 + image-source: /game_cyclopedia/images/icon-goldcoin diff --git a/modules/game_interface/interface.otmod b/modules/game_interface/interface.otmod index 467a1fe222..e5dbe1b849 100644 --- a/modules/game_interface/interface.otmod +++ b/modules/game_interface/interface.otmod @@ -16,6 +16,7 @@ Module - game_prey - game_imbuing - game_imbuementtracker + - game_imbui - game_hotkeys - game_questlog - game_textmessage diff --git a/src/client/game.cpp b/src/client/game.cpp index cdd38479d0..5d8350fd03 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -1860,6 +1860,22 @@ void Game::closeImbuingWindow() m_protocolGame->sendCloseImbuingWindow(); } +void Game::selectImbuementItem(const uint16_t itemId, const Position& pos, const uint8_t stackpos) +{ + if (!canPerformGameAction()) + return; + + m_protocolGame->sendImbuementWindowAction(1, itemId, pos, stackpos); +} + +void Game::selectImbuementScroll() +{ + if (!canPerformGameAction()) + return; + + m_protocolGame->sendImbuementWindowAction(2); +} + void Game::imbuementDurations(const bool isOpen) { if (!canPerformGameAction()) diff --git a/src/client/game.h b/src/client/game.h index fa09a98ca0..cef492f9c1 100644 --- a/src/client/game.h +++ b/src/client/game.h @@ -390,9 +390,9 @@ class Game void clearImbuement(uint8_t slot); void closeImbuingWindow(); void imbuementDurations(bool isOpen = false); - void openWheelOfDestiny(uint32_t playerId); - void applyWheelOfDestiny(const std::vector& wheelPointsVec, const std::vector& activeGemsVec); - + void selectImbuementItem(uint16_t itemId, const Position& pos, uint8_t stackpos); + void selectImbuementScroll(); + void enableTileThingLuaCallback(const bool value) { m_tileThingsLuaCallback = value; } bool isTileThingLuaCallbackEnabled() { return m_tileThingsLuaCallback; } @@ -442,6 +442,8 @@ class Game void openWheel(uint32_t playerId); void sendApplyWheelPoints(const std::vector& slotPoints,uint16_t greenGem,uint16_t redGem,uint16_t acquaGem,uint16_t purpleGem); void gemAction(uint8_t actionType, uint8_t param, uint8_t pos); + void openWheelOfDestiny(uint32_t playerId); + void applyWheelOfDestiny(const std::vector& wheelPointsVec, const std::vector& activeGemsVec); void updateMapLatency() { if (!m_mapUpdateTimer.first) { diff --git a/src/client/luafunctions.cpp b/src/client/luafunctions.cpp index 9409fd7092..7273b41d7b 100644 --- a/src/client/luafunctions.cpp +++ b/src/client/luafunctions.cpp @@ -378,6 +378,8 @@ void Client::registerLuaFunctions() g_lua.bindSingletonFunction("g_game", "applyImbuement", &Game::applyImbuement, &g_game); g_lua.bindSingletonFunction("g_game", "clearImbuement", &Game::clearImbuement, &g_game); g_lua.bindSingletonFunction("g_game", "closeImbuingWindow", &Game::closeImbuingWindow, &g_game); + g_lua.bindSingletonFunction("g_game", "selectImbuementItem", &Game::selectImbuementItem, &g_game); + g_lua.bindSingletonFunction("g_game", "selectImbuementScroll", &Game::selectImbuementScroll, &g_game); g_lua.bindSingletonFunction("g_game", "isUsingProtobuf", &Game::isUsingProtobuf, &g_game); g_lua.bindSingletonFunction("g_game", "enableTileThingLuaCallback", &Game::enableTileThingLuaCallback, &g_game); g_lua.bindSingletonFunction("g_game", "isTileThingLuaCallbackEnabled", &Game::isTileThingLuaCallbackEnabled, &g_game); diff --git a/src/client/protocolcodes.h b/src/client/protocolcodes.h index 4ce7ab78a0..c5df86dc88 100644 --- a/src/client/protocolcodes.h +++ b/src/client/protocolcodes.h @@ -222,6 +222,7 @@ namespace Proto GameServerChannelEvent = 243, GameServerItemInfo = 244, GameServerPlayerInventory = 245, + GameServerInventoryImbuements = 96, // 0x60 - Inventory Imbuements opcode from server GameServerMarketEnter = 246, GameServerMarketLeave = 247, GameServerMarketDetail = 248, diff --git a/src/client/protocolgame.h b/src/client/protocolgame.h index b0487ec21f..c0a03385ff 100644 --- a/src/client/protocolgame.h +++ b/src/client/protocolgame.h @@ -142,6 +142,7 @@ class ProtocolGame final : public Protocol void sendApplyImbuement(uint8_t slot, uint32_t imbuementId, bool protectionCharm); void sendClearImbuement(uint8_t slot); void sendCloseImbuingWindow(); + void sendImbuementWindowAction(uint8_t type, uint16_t itemId = 0, const Position& pos = Position(), uint8_t stackpos = 0); void sendOpenRewardWall(); void sendOpenRewardHistory(); void sendGetRewardDaily(const uint8_t bonusShrine, const std::map& items); diff --git a/src/client/protocolgameparse.cpp b/src/client/protocolgameparse.cpp index 2dddd2fe61..62ad8447f7 100644 --- a/src/client/protocolgameparse.cpp +++ b/src/client/protocolgameparse.cpp @@ -821,6 +821,7 @@ void ProtocolGame::parseResourceBalance(const InputMessagePtr& msg) const break; } m_localPlayer->setResourceBalance(type, value); + g_lua.callGlobalField("g_game", "onResourceBalance", type, value); } void ProtocolGame::parseWorldTime(const InputMessagePtr& msg) @@ -5756,6 +5757,98 @@ Imbuement ProtocolGame::getImbuementInfo(const InputMessagePtr& msg) void ProtocolGame::parseImbuementWindow(const InputMessagePtr& msg) { + if (g_game.getClientVersion() >= 1510) { + const uint8_t windowType = msg->getU8(); + + if (windowType > 2) { + g_logger.warning(fmt::format("ProtocolGame::parseImbuementWindow: unexpected windowType {}", windowType)); + return; + } + + // 0 = CHOICE (select item or scroll) + if (windowType == 0) { + msg->getU8(); // unknown + msg->getU16(); // padding + msg->getU32(); // padding + g_lua.callGlobalField("g_game", "onOpenImbuementWindow"); + return; + } + + // 1 = SELECT_ITEM + if (windowType == 1) { + msg->getU8(); // unknown + + const uint16_t itemId = msg->getU16(); + const auto& item = Item::create(itemId); + if (!item || item->getId() == 0) { + throw Exception("ProtocolGame::parseImbuementWindow: unable to create item with invalid id {}", itemId); + } + + uint8_t tier = 0; + if (item->getClassification() > 0) { + tier = msg->getU8(); + } + + const uint8_t slots = msg->getU8(); + std::unordered_map> activeSlots; + for (auto i = 0; i < slots; i++) { + const uint8_t firstByte = msg->getU8(); + if (firstByte == 0x01) { + Imbuement imbuement = getImbuementInfo(msg); + const uint32_t duration = msg->getU32(); + const uint32_t removalCost = msg->getU32(); + activeSlots[i] = std::make_tuple(imbuement, duration, removalCost); + } + } + + const uint16_t imbuementsSize = msg->getU16(); + std::vector imbuements; + imbuements.reserve(imbuementsSize); + for (auto i = 0; i < imbuementsSize; ++i) { + imbuements.push_back(getImbuementInfo(msg)); + } + + const uint32_t neededItemsListCount = msg->getU32(); + std::vector neededItemsList; + neededItemsList.reserve(neededItemsListCount); + for (uint32_t i = 0; i < neededItemsListCount; ++i) { + const uint16_t needItemId = msg->getU16(); + const uint16_t count = msg->getU16(); + const auto& needItem = Item::create(needItemId); + needItem->setCount(count); + neededItemsList.push_back(needItem); + } + g_lua.callGlobalField("g_game", "onImbuementItem", itemId, tier, slots, activeSlots, imbuements, neededItemsList); + return; + } + + // 2 = SCROLL + if (windowType == 2) { + msg->getU8(); // unknown + msg->getU8(); // unknown + msg->getU8(); // unknown + + const uint16_t imbuementsSize = msg->getU16(); + std::vector imbuements; + imbuements.reserve(imbuementsSize); + for (auto i = 0; i < imbuementsSize; ++i) { + imbuements.push_back(getImbuementInfo(msg)); + } + + const uint32_t neededItemsListCount = msg->getU32(); + std::vector neededItemsList; + neededItemsList.reserve(neededItemsListCount); + for (uint32_t i = 0; i < neededItemsListCount; ++i) { + const uint16_t needItemId = msg->getU16(); + const uint16_t count = msg->getU16(); + const auto& needItem = Item::create(needItemId); + needItem->setCount(count); + neededItemsList.push_back(needItem); + } + g_lua.callGlobalField("g_game", "onImbuementScroll", imbuements, neededItemsList); + return; + } + } uint8_t windowType = Otc::IMBUEMENT_WINDOW_SELECT_ITEM; if (g_game.getClientVersion() >= 1510) { windowType = static_cast(msg->getU8()); // window type diff --git a/src/client/protocolgamesend.cpp b/src/client/protocolgamesend.cpp index be6b884c0d..615774e4b6 100644 --- a/src/client/protocolgamesend.cpp +++ b/src/client/protocolgamesend.cpp @@ -1488,6 +1488,21 @@ void ProtocolGame::sendCloseImbuingWindow() send(msg); } +void ProtocolGame::sendImbuementWindowAction(const uint8_t type, const uint16_t itemId, const Position& pos, const uint8_t stackpos) +{ + const auto& msg = std::make_shared(); + msg->addU8(0xB2); // same opcode as parseImbuementWindow on server + msg->addU8(type); // 1 = SELECT_ITEM, 2 = SCROLL + + if (type == 1) { // SELECT_ITEM + addPosition(msg, pos); + msg->addU16(itemId); + msg->addU8(stackpos); + } + + send(msg); +} + void ProtocolGame::sendOpenRewardWall() { const auto& msg = std::make_shared();