From 7c776864e2f8480f33125ffba81ed1938444dca5 Mon Sep 17 00:00:00 2001 From: gahusb Date: Mon, 15 Jun 2026 23:15:04 +0900 Subject: [PATCH] Add card keyword hover tooltips --- RootDesk/MyDesk/SlayDeckController.codeblock | 71 ++++++++++++++++- tools/deck/gen-slaydeck.mjs | 81 ++++++++++++++++++-- ui/DefaultGroup.ui | 44 +++++------ 3 files changed, 165 insertions(+), 31 deletions(-) diff --git a/RootDesk/MyDesk/SlayDeckController.codeblock b/RootDesk/MyDesk/SlayDeckController.codeblock index d16f284..7372dd1 100644 --- a/RootDesk/MyDesk/SlayDeckController.codeblock +++ b/RootDesk/MyDesk/SlayDeckController.codeblock @@ -3588,6 +3588,29 @@ "Attributes": [], "Name": "RenderRelics" }, + { + "Return": { + "Type": "string", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": null + }, + "Arguments": [ + { + "Type": "any", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": "c" + } + ], + "Code": "if c == nil then\n\treturn \"\"\nend\nlocal lines = {}\nlocal function add(name, desc)\n\tfor i = 1, #lines do\n\t\tif string.find(lines[i], name .. \":\", 1, true) == 1 then\n\t\t\treturn\n\t\tend\n\tend\n\ttable.insert(lines, name .. \": \" .. desc)\nend\nlocal cardDesc = c.desc or \"\"\nif c.sly == true or string.find(cardDesc, \"교활\", 1, true) ~= nil then\n\tadd(\"교활\", \"버려지면 비용 없이 사용됩니다.\")\nend\nif c.retain == true or string.find(cardDesc, \"보존\", 1, true) ~= nil then\n\tadd(\"보존\", \"턴 종료 시 버려지지 않고 손에 남습니다.\")\nend\nif string.find(cardDesc, \"소멸\", 1, true) ~= nil then\n\tadd(\"소멸\", \"사용 후 이번 전투 동안 제거됩니다.\")\nend\nif string.find(cardDesc, \"선천성\", 1, true) ~= nil then\n\tadd(\"선천성\", \"전투 시작 시 손패에 들어옵니다.\")\nend\nif c.vuln ~= nil and c.vuln > 0 then\n\tadd(\"취약\", \"받는 공격 피해가 50% 증가합니다.\")\nend\nif c.weak ~= nil and c.weak > 0 then\n\tadd(\"약화\", \"주는 공격 피해가 25% 감소합니다.\")\nend\nif c.poison ~= nil and c.poison > 0 then\n\tadd(\"중독\", \"턴 시작 시 체력을 잃고 수치가 1 감소합니다.\")\nend\nif c.pierce == true then\n\tadd(\"관통\", \"방어도를 무시하고 피해를 줍니다.\")\nend\nif c.aoe == true then\n\tadd(\"전체\", \"모든 적에게 적용됩니다.\")\nend\nif c.kind == \"Power\" then\n\tadd(\"파워\", \"사용하면 전투 동안 지속 효과로 남습니다.\")\nend\nif c.unplayable == true then\n\tadd(\"저주\", \"사용할 수 없고 손패를 방해합니다.\")\nend\nlocal out = \"\"\nfor i = 1, #lines do\n\tif i > 1 then out = out .. \"\\n\" end\n\tout = out .. lines[i]\nend\nreturn out", + "Scope": 2, + "ExecSpace": 6, + "Attributes": [], + "Name": "BuildCardKeywordTooltip" + }, { "Return": { "Type": "void", @@ -3605,7 +3628,7 @@ "Name": "slot" } ], - "Code": "if self.DragSlot ~= nil and self.DragSlot > 0 then\n\treturn\nend\nlocal cardId = self.Hand[slot]\nif cardId == nil then\n\treturn\nend\nlocal e = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/CardHand/Card\" .. tostring(slot))\nlocal tx = 0\nif e ~= nil and e.UITransformComponent ~= nil then\n\ttx = e.UITransformComponent.anchoredPosition.x\n\te.UITransformComponent.UIScale = Vector3(1.3, 1.3, 1)\nend\nlocal c = self.Cards[cardId]\nif c ~= nil then\n\tself:ShowTooltip(c.name, c.desc, tx)\nend", + "Code": "if self.DragSlot ~= nil and self.DragSlot > 0 then\n\treturn\nend\nlocal cardId = self.Hand[slot]\nif cardId == nil then\n\treturn\nend\nlocal e = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/CardHand/Card\" .. tostring(slot))\nlocal tx = 0\nif e ~= nil and e.UITransformComponent ~= nil then\n\ttx = e.UITransformComponent.anchoredPosition.x\n\te.UITransformComponent.UIScale = Vector3(1.3, 1.3, 1)\nend\nlocal c = self.Cards[cardId]\nif c ~= nil then\n\tlocal tip = self:BuildCardKeywordTooltip(c)\n\tif tip ~= \"\" then\n\t\tlocal tipX = tx + 270\n\t\tif tx > 180 then tipX = tx - 270 end\n\t\tif tipX > 760 then tipX = tx - 270 end\n\t\tif tipX < -760 then tipX = tx + 270 end\n\t\tself:ShowTooltipAt(\"키워드\", tip, tipX, 90)\n\telse\n\t\tself:HideTooltip()\n\tend\nend", "Scope": 2, "ExecSpace": 6, "Attributes": [], @@ -3665,12 +3688,56 @@ "Name": "x" } ], - "Code": "self:SetText(\"/ui/DefaultGroup/CombatHud/TooltipBox/Name\", name)\nself:SetText(\"/ui/DefaultGroup/CombatHud/TooltipBox/Desc\", desc)\nlocal e = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/CombatHud/TooltipBox\")\nif e ~= nil then\n\tif e.UITransformComponent ~= nil then\n\t\te.UITransformComponent.anchoredPosition = Vector2(x, 400)\n\tend\n\te.Enable = true\nend", + "Code": "self:ShowTooltipAt(name, desc, x, 400)", "Scope": 2, "ExecSpace": 6, "Attributes": [], "Name": "ShowTooltip" }, + { + "Return": { + "Type": "void", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": null + }, + "Arguments": [ + { + "Type": "string", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": "name" + }, + { + "Type": "string", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": "desc" + }, + { + "Type": "number", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": "x" + }, + { + "Type": "number", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": "y" + } + ], + "Code": "self:SetText(\"/ui/DefaultGroup/CombatHud/TooltipBox/Name\", name)\nself:SetText(\"/ui/DefaultGroup/CombatHud/TooltipBox/Desc\", desc)\nlocal e = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/CombatHud/TooltipBox\")\nif e ~= nil then\n\tif e.UITransformComponent ~= nil then\n\t\te.UITransformComponent.anchoredPosition = Vector2(x, y)\n\tend\n\te.Enable = true\nend", + "Scope": 2, + "ExecSpace": 6, + "Attributes": [], + "Name": "ShowTooltipAt" + }, { "Return": { "Type": "void", diff --git a/tools/deck/gen-slaydeck.mjs b/tools/deck/gen-slaydeck.mjs index f8c3306..272e5ea 100644 --- a/tools/deck/gen-slaydeck.mjs +++ b/tools/deck/gen-slaydeck.mjs @@ -1445,7 +1445,7 @@ function upsertUi() { componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent', displayOrder: 20, components: [ - transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 300, y: 80 }, pos: { x: 0, y: 400 }, align: ALIGN_CENTER }), + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 360, y: 150 }, pos: { x: 0, y: 400 }, align: ALIGN_CENTER }), sprite({ color: { r: 0.04, g: 0.05, b: 0.08, a: 0.96 }, type: 1 }), ], }); @@ -1458,7 +1458,7 @@ function upsertUi() { componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent', displayOrder: 0, components: [ - transform({ parentW: 300, parentH: 80, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 280, y: 28 }, pos: { x: 0, y: 18 } }), + transform({ parentW: 360, parentH: 150, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 332, y: 28 }, pos: { x: 0, y: 52 } }), sprite({ color: TRANSPARENT }), text({ value: '', fontSize: 19, bold: true, color: GOLD, alignment: 4 }), ], @@ -1470,9 +1470,9 @@ function upsertUi() { componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent', displayOrder: 1, components: [ - transform({ parentW: 300, parentH: 80, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 284, y: 30 }, pos: { x: 0, y: -14 } }), + transform({ parentW: 360, parentH: 150, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 332, y: 102 }, pos: { x: 0, y: -18 } }), sprite({ color: TRANSPARENT }), - text({ value: '', fontSize: 15, bold: false, color: { r: 0.92, g: 0.92, b: 0.95, a: 1 }, alignment: 4 }), + text({ value: '', fontSize: 15, bold: false, color: { r: 0.92, g: 0.92, b: 0.95, a: 1 }, alignment: 0 }), ], })); const discardPrompt = entity({ @@ -5572,6 +5572,58 @@ if count > 10 then of = "+" .. tostring(count - 9) end self:SetText("/ui/DefaultGroup/CombatHud/TopBar/RelicOverflow", of)`), + method('BuildCardKeywordTooltip', `if c == nil then + return "" +end +local lines = {} +local function add(name, desc) + for i = 1, #lines do + if string.find(lines[i], name .. ":", 1, true) == 1 then + return + end + end + table.insert(lines, name .. ": " .. desc) +end +local cardDesc = c.desc or "" +if c.sly == true or string.find(cardDesc, "교활", 1, true) ~= nil then + add("교활", "버려지면 비용 없이 사용됩니다.") +end +if c.retain == true or string.find(cardDesc, "보존", 1, true) ~= nil then + add("보존", "턴 종료 시 버려지지 않고 손에 남습니다.") +end +if string.find(cardDesc, "소멸", 1, true) ~= nil then + add("소멸", "사용 후 이번 전투 동안 제거됩니다.") +end +if string.find(cardDesc, "선천성", 1, true) ~= nil then + add("선천성", "전투 시작 시 손패에 들어옵니다.") +end +if c.vuln ~= nil and c.vuln > 0 then + add("취약", "받는 공격 피해가 50% 증가합니다.") +end +if c.weak ~= nil and c.weak > 0 then + add("약화", "주는 공격 피해가 25% 감소합니다.") +end +if c.poison ~= nil and c.poison > 0 then + add("중독", "턴 시작 시 체력을 잃고 수치가 1 감소합니다.") +end +if c.pierce == true then + add("관통", "방어도를 무시하고 피해를 줍니다.") +end +if c.aoe == true then + add("전체", "모든 적에게 적용됩니다.") +end +if c.kind == "Power" then + add("파워", "사용하면 전투 동안 지속 효과로 남습니다.") +end +if c.unplayable == true then + add("저주", "사용할 수 없고 손패를 방해합니다.") +end +local out = "" +for i = 1, #lines do + if i > 1 then out = out .. "\\n" end + out = out .. lines[i] +end +return out`, [{ Type: 'any', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'c' }], 0, 'string'), method('HoverCard', `if self.DragSlot ~= nil and self.DragSlot > 0 then return end @@ -5587,25 +5639,40 @@ if e ~= nil and e.UITransformComponent ~= nil then end local c = self.Cards[cardId] if c ~= nil then - self:ShowTooltip(c.name, c.desc, tx) + local tip = self:BuildCardKeywordTooltip(c) + if tip ~= "" then + local tipX = tx + 270 + if tx > 180 then tipX = tx - 270 end + if tipX > 760 then tipX = tx - 270 end + if tipX < -760 then tipX = tx + 270 end + self:ShowTooltipAt("키워드", tip, tipX, 90) + else + self:HideTooltip() + end end`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }]), method('UnhoverCard', `local e = _EntityService:GetEntityByPath("/ui/DefaultGroup/CardHand/Card" .. tostring(slot)) if e ~= nil and e.UITransformComponent ~= nil then e.UITransformComponent.UIScale = Vector3(1, 1, 1) end self:HideTooltip()`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }]), - method('ShowTooltip', `self:SetText("/ui/DefaultGroup/CombatHud/TooltipBox/Name", name) + method('ShowTooltip', `self:ShowTooltipAt(name, desc, x, 400)`, [ + { Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'name' }, + { Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'desc' }, + { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'x' }, + ]), + method('ShowTooltipAt', `self:SetText("/ui/DefaultGroup/CombatHud/TooltipBox/Name", name) self:SetText("/ui/DefaultGroup/CombatHud/TooltipBox/Desc", desc) local e = _EntityService:GetEntityByPath("/ui/DefaultGroup/CombatHud/TooltipBox") if e ~= nil then if e.UITransformComponent ~= nil then - e.UITransformComponent.anchoredPosition = Vector2(x, 400) + e.UITransformComponent.anchoredPosition = Vector2(x, y) end e.Enable = true end`, [ { Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'name' }, { Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'desc' }, { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'x' }, + { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'y' }, ]), method('HideTooltip', `self:SetEntityEnabled("/ui/DefaultGroup/CombatHud/TooltipBox", false)`), method('ShowMap', `self:ShowState("map") diff --git a/ui/DefaultGroup.ui b/ui/DefaultGroup.ui index 52f0c76..ecd6708 100644 --- a/ui/DefaultGroup.ui +++ b/ui/DefaultGroup.ui @@ -29120,20 +29120,20 @@ }, "MobileOnly": false, "OffsetMax": { - "x": 150, - "y": 440 + "x": 180, + "y": 475 }, "OffsetMin": { - "x": -150, - "y": 360 + "x": -180, + "y": 325 }, "Pivot": { "x": 0.5, "y": 0.5 }, "RectSize": { - "x": 300, - "y": 80 + "x": 360, + "y": 150 }, "UIMode": 1, "UIScale": { @@ -29261,19 +29261,19 @@ }, "MobileOnly": false, "OffsetMax": { - "x": 140, - "y": 32 + "x": 166, + "y": 66 }, "OffsetMin": { - "x": -140, - "y": 4 + "x": -166, + "y": 38 }, "Pivot": { "x": 0.5, "y": 0.5 }, "RectSize": { - "x": 280, + "x": 332, "y": 28 }, "UIMode": 1, @@ -29285,11 +29285,11 @@ "UIVersion": 2, "anchoredPosition": { "x": 0, - "y": 18 + "y": 52 }, "Position": { "x": 0, - "y": 18, + "y": 52, "z": 0 }, "QuaternionRotation": { @@ -29449,20 +29449,20 @@ }, "MobileOnly": false, "OffsetMax": { - "x": 142, - "y": 1 + "x": 166, + "y": 33 }, "OffsetMin": { - "x": -142, - "y": -29 + "x": -166, + "y": -69 }, "Pivot": { "x": 0.5, "y": 0.5 }, "RectSize": { - "x": 284, - "y": 30 + "x": 332, + "y": 102 }, "UIMode": 1, "UIScale": { @@ -29473,11 +29473,11 @@ "UIVersion": 2, "anchoredPosition": { "x": 0, - "y": -14 + "y": -18 }, "Position": { "x": 0, - "y": -14, + "y": -18, "z": 0 }, "QuaternionRotation": { @@ -29551,7 +29551,7 @@ }, { "@type": "MOD.Core.TextComponent", - "Alignment": 4, + "Alignment": 0, "Bold": false, "DropShadow": false, "DropShadowAngle": 30,