diff --git a/RootDesk/MyDesk/SlayDeckController.codeblock b/RootDesk/MyDesk/SlayDeckController.codeblock index 70c853b..c49f59b 100644 --- a/RootDesk/MyDesk/SlayDeckController.codeblock +++ b/RootDesk/MyDesk/SlayDeckController.codeblock @@ -99,6 +99,27 @@ "Attributes": [], "Name": "WarriorSelectHandler" }, + { + "Type": "any", + "DefaultValue": "nil", + "SyncDirection": 0, + "Attributes": [], + "Name": "MageSelectHandler" + }, + { + "Type": "any", + "DefaultValue": "nil", + "SyncDirection": 0, + "Attributes": [], + "Name": "JobOpts" + }, + { + "Type": "any", + "DefaultValue": "nil", + "SyncDirection": 0, + "Attributes": [], + "Name": "Jobs" + }, { "Type": "any", "DefaultValue": "nil", @@ -563,7 +584,7 @@ "Name": null }, "Arguments": [], - "Code": "local buttonEntity = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/MainMenu/NewGameButton\")\nif buttonEntity ~= nil and buttonEntity.ButtonComponent ~= nil then\n\tif self.NewGameHandler ~= nil then\n\t\tbuttonEntity:DisconnectEvent(ButtonClickEvent, self.NewGameHandler)\n\t\tself.NewGameHandler = nil\n\tend\n\tself.NewGameHandler = buttonEntity:ConnectEvent(ButtonClickEvent, function() self:ShowCharacterSelect() end)\nend\nlocal warrior = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/CharacterSelectHud/WarriorButton\")\nif warrior ~= nil and warrior.ButtonComponent ~= nil then\n\tif self.WarriorSelectHandler ~= nil then\n\t\twarrior:DisconnectEvent(ButtonClickEvent, self.WarriorSelectHandler)\n\t\tself.WarriorSelectHandler = nil\n\tend\n\tself.WarriorSelectHandler = warrior:ConnectEvent(ButtonClickEvent, function() self:SelectClass(\"warrior\") end)\nend\nlocal start = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/CharacterSelectHud/StartButton\")\nif start ~= nil and start.ButtonComponent ~= nil then\n\tif self.StartGameHandler ~= nil then\n\t\tstart:DisconnectEvent(ButtonClickEvent, self.StartGameHandler)\n\t\tself.StartGameHandler = nil\n\tend\n\tself.StartGameHandler = start:ConnectEvent(ButtonClickEvent, function() self:StartNewGame() end)\nend", + "Code": "local buttonEntity = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/MainMenu/NewGameButton\")\nif buttonEntity ~= nil and buttonEntity.ButtonComponent ~= nil then\n\tif self.NewGameHandler ~= nil then\n\t\tbuttonEntity:DisconnectEvent(ButtonClickEvent, self.NewGameHandler)\n\t\tself.NewGameHandler = nil\n\tend\n\tself.NewGameHandler = buttonEntity:ConnectEvent(ButtonClickEvent, function() self:ShowCharacterSelect() end)\nend\nlocal warrior = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/CharacterSelectHud/WarriorButton\")\nif warrior ~= nil and warrior.ButtonComponent ~= nil then\n\tif self.WarriorSelectHandler ~= nil then\n\t\twarrior:DisconnectEvent(ButtonClickEvent, self.WarriorSelectHandler)\n\t\tself.WarriorSelectHandler = nil\n\tend\n\tself.WarriorSelectHandler = warrior:ConnectEvent(ButtonClickEvent, function() self:SelectClass(\"warrior\") end)\nend\nlocal mage = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/CharacterSelectHud/MageButton\")\nif mage ~= nil and mage.ButtonComponent ~= nil then\n\tif self.MageSelectHandler ~= nil then\n\t\tmage:DisconnectEvent(ButtonClickEvent, self.MageSelectHandler)\n\t\tself.MageSelectHandler = nil\n\tend\n\tself.MageSelectHandler = mage:ConnectEvent(ButtonClickEvent, function() self:SelectClass(\"magician\") end)\nend\nlocal start = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/CharacterSelectHud/StartButton\")\nif start ~= nil and start.ButtonComponent ~= nil then\n\tif self.StartGameHandler ~= nil then\n\t\tstart:DisconnectEvent(ButtonClickEvent, self.StartGameHandler)\n\t\tself.StartGameHandler = nil\n\tend\n\tself.StartGameHandler = start:ConnectEvent(ButtonClickEvent, function() self:StartNewGame() end)\nend", "Scope": 2, "ExecSpace": 6, "Attributes": [], @@ -616,7 +637,7 @@ "Name": null }, "Arguments": [], - "Code": "local warrior = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/CharacterSelectHud/WarriorButton\")\nif warrior ~= nil and warrior.SpriteGUIRendererComponent ~= nil then\n\tif self.SelectedClass == \"warrior\" then\n\t\twarrior.SpriteGUIRendererComponent.Color = Color(0.28, 0.36, 0.46, 1)\n\telse\n\t\twarrior.SpriteGUIRendererComponent.Color = Color(0.16, 0.2, 0.26, 1)\n\tend\nend\nif self.SelectedClass == \"warrior\" then\n\tself:SetText(\"/ui/DefaultGroup/CharacterSelectHud/Status\", \"전사 선택됨\")\nelse\n\tself:SetText(\"/ui/DefaultGroup/CharacterSelectHud/Status\", \"전사를 선택하고 시작하세요\")\nend", + "Code": "local warrior = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/CharacterSelectHud/WarriorButton\")\nif warrior ~= nil and warrior.SpriteGUIRendererComponent ~= nil then\n\tif self.SelectedClass == \"warrior\" then\n\t\twarrior.SpriteGUIRendererComponent.Color = Color(0.28, 0.36, 0.46, 1)\n\telse\n\t\twarrior.SpriteGUIRendererComponent.Color = Color(0.16, 0.2, 0.26, 1)\n\tend\nend\nlocal mage = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/CharacterSelectHud/MageButton\")\nif mage ~= nil and mage.SpriteGUIRendererComponent ~= nil then\n\tif self.SelectedClass == \"magician\" then\n\t\tmage.SpriteGUIRendererComponent.Color = Color(0.28, 0.36, 0.46, 1)\n\telse\n\t\tmage.SpriteGUIRendererComponent.Color = Color(0.16, 0.2, 0.26, 1)\n\tend\nend\nif self.SelectedClass == \"warrior\" then\n\tself:SetText(\"/ui/DefaultGroup/CharacterSelectHud/Status\", \"전사 선택됨\")\nelseif self.SelectedClass == \"magician\" then\n\tself:SetText(\"/ui/DefaultGroup/CharacterSelectHud/Status\", \"마법사 선택됨\")\nelse\n\tself:SetText(\"/ui/DefaultGroup/CharacterSelectHud/Status\", \"직업을 선택하고 시작하세요\")\nend", "Scope": 2, "ExecSpace": 6, "Attributes": [], @@ -631,7 +652,7 @@ "Name": null }, "Arguments": [], - "Code": "if self.SelectedClass ~= \"warrior\" then\n\tself:SetText(\"/ui/DefaultGroup/CharacterSelectHud/Status\", \"현재는 전사만 선택할 수 있습니다\")\n\treturn\nend\nself:StartRun()", + "Code": "if self.SelectedClass ~= \"warrior\" and self.SelectedClass ~= \"magician\" then\n\tself:SetText(\"/ui/DefaultGroup/CharacterSelectHud/Status\", \"직업을 먼저 선택하세요\")\n\treturn\nend\nself:StartRun()", "Scope": 2, "ExecSpace": 6, "Attributes": [], @@ -676,7 +697,7 @@ "Name": null }, "Arguments": [], - "Code": "self.PlayerMaxHp = 80\nself.PlayerHp = self.PlayerMaxHp\nself.Gold = 0\nself.Floor = 1\nself.RunLength = 3\nself.RunDeck = { \"Strike\", \"Strike\", \"Strike\", \"Strike\", \"Strike\", \"Defend\", \"Defend\", \"Defend\", \"Defend\", \"Bash\" }\nself.RunActive = true\nself.RunRelics = {}\nself.RunPotions = {}\nself.PotionSlots = 3\nself.Potions = {\n\tredPotion = { name = \"빨간 포션\", desc = \"HP 20 회복\", effect = \"heal\", value = 20, icon = \"393e2a0d8da544899eaa8b22c97f832b\" },\n\tfirebomb = { name = \"화염병\", desc = \"적에게 피해 20\", effect = \"damage\", value = 20, icon = \"7ddb464c2574456289a4eb72ce86f193\" },\n\twarriorElixir = { name = \"전사의 물약\", desc = \"힘 +2\", effect = \"strength\", value = 2, icon = \"7cfbd410581e4073815daaf5f3e6c72f\" },\n\tguardPotion = { name = \"수호의 물약\", desc = \"방어도 +12\", effect = \"block\", value = 12, icon = \"8f8402dfa0f746e18bf606ed74302c0a\" },\n\tmanaElixir = { name = \"마나 엘릭서\", desc = \"에너지 +2\", effect = \"energy\", value = 2, icon = \"ec2778c366f6477ab0f8e7f06bcd73f4\" },\n\tcursedVial = { name = \"저주의 병\", desc = \"적에게 약화 3\", effect = \"weak\", value = 3, icon = \"a9a2763fdb6849dcba3028c737487680\" },\n}\nself.Relics = {\n\tironHeart = { name = \"강철 심장\", desc = \"전투 시작 시 방어도 +6\", hook = \"combatStart\", effect = \"block\", value = 6, icon = \"e555b3a62f3c49dbb2c53784e6bd481f\" },\n\tenergyCore = { name = \"에너지 코어\", desc = \"턴 시작 시 에너지 +1\", hook = \"turnStart\", effect = \"energy\", value = 1, icon = \"a41014f28b47434ab9f49ef104523862\" },\n\tvampire = { name = \"흡혈 송곳니\", desc = \"공격 카드 사용 시 HP +1\", hook = \"cardPlayed\", effect = \"healOnAttack\", value = 1, icon = \"ed64cde7e6c44b9e99502847e54f04e9\" },\n\tgoldIdol = { name = \"황금 우상\", desc = \"전투 승리 시 골드 +10\", hook = \"combatReward\", effect = \"gold\", value = 10, icon = \"03bb05c92b8f45edb0f3dad2e118fd5a\" },\n\tpotionBelt = { name = \"장인의 벨트\", desc = \"물약 슬롯이 5칸으로 늘어난다\", hook = \"passive\", effect = \"potionSlots\", value = 5, icon = \"36725b4566ac40d4902e2ab2113c2096\" },\n\tburningBlood = { name = \"자쿰의 투구\", desc = \"전투 승리 시 HP 6 회복\", hook = \"combatEnd\", effect = \"healOnWin\", value = 6, icon = \"07f994825ce34131b419d43e890c878d\" },\n\tvajra = { name = \"미스릴 해머\", desc = \"전투 시작 시 힘 +1\", hook = \"combatStart\", effect = \"strength\", value = 1, icon = \"59d2579d46dc41d590a9e6b141ad458b\" },\n\tanchor = { name = \"메이플 실드\", desc = \"첫 턴 방어도 +10\", hook = \"combatStart\", effect = \"block\", value = 10, icon = \"6349413e08cc49848862591863d056a0\" },\n\tbagOfPrep = { name = \"모험가의 배낭\", desc = \"첫 턴 드로우 +2\", hook = \"combatStart\", effect = \"draw\", value = 2, icon = \"77b240cb8af245b4801a714380267ae9\" },\n\tbloodVial = { name = \"피의 목걸이\", desc = \"전투 시작 시 HP 2 회복\", hook = \"combatStart\", effect = \"heal\", value = 2, icon = \"c782e949506a42c49eb139c7e65527d7\" },\n\tbronzeScales = { name = \"브론즈 체인메일\", desc = \"피격 시 공격자에게 3 반사\", hook = \"onPlayerDamaged\", effect = \"thorns\", value = 3, icon = \"87272346b145412391622cf803f888d1\" },\n\tstrawberry = { name = \"건강의 반지\", desc = \"획득 시 최대 HP +7\", hook = \"passive\", effect = \"maxHp\", value = 7, icon = \"58f643e29c354c2783a5ce9a72ec155c\" },\n\tpenNib = { name = \"황금 깃펜\", desc = \"10번째 공격마다 피해 2배\", hook = \"attackCalc\", effect = \"penNib\", value = 10, icon = \"4d38d721cc064d14b31b9e9a92754139\" },\n\tboot = { name = \"브론즈 부츠\", desc = \"5 미만 공격 피해가 5로\", hook = \"attackCalc\", effect = \"boot\", value = 5, icon = \"d572b3aa4dac4162aa0d9e551b055dce\" },\n\takabeko = { name = \"황소 투구\", desc = \"전투 첫 공격 피해 +8\", hook = \"attackCalc\", effect = \"akabeko\", value = 8, icon = \"eb3330a6e2274eff958639f8792119d3\" },\n\tcentennialPuzzle = { name = \"백년의 부적\", desc = \"전투 첫 피격 시 드로우 3\", hook = \"onPlayerDamaged\", effect = \"firstLossDraw\", value = 3, icon = \"cfe5ed6556b944fc83ab58b774bb2b73\" },\n\tmeatOnBone = { name = \"고기 망치\", desc = \"승리 시 HP 50% 이하면 12 회복\", hook = \"combatEnd\", effect = \"healIfLow\", value = 12, icon = \"a93e8e87f184411c98c96b877d9f8b10\" },\n\tselfFormingClay = { name = \"점토 갑옷\", desc = \"피해를 받으면 다음 턴 방어 +3\", hook = \"onPlayerDamaged\", effect = \"clayBlock\", value = 3, icon = \"bb446793c5204d5db7d33563fe79f648\" },\n\tchampionBelt = { name = \"챔피언 벨트\", desc = \"취약 부여 시 약화 1 추가\", hook = \"cardDebuff\", effect = \"vulnAddsWeak\", value = 1, icon = \"7ca8c63026034113a561d6adf679fed2\" },\n}\nself.RelicPool = { \"energyCore\", \"vampire\", \"goldIdol\", \"potionBelt\", \"burningBlood\", \"vajra\", \"anchor\", \"bagOfPrep\", \"bloodVial\", \"bronzeScales\", \"strawberry\", \"penNib\", \"boot\", \"akabeko\", \"centennialPuzzle\", \"meatOnBone\", \"selfFormingClay\", \"championBelt\" }\nself.Enemies = {\n\tslime = { name = \"슬라임\", maxHp = 45, intents = { { kind = \"Attack\", value = 10 }, { kind = \"Attack\", value = 6 }, { kind = \"Defend\", value = 8 } } },\n\tslime_elite = { name = \"정예 슬라임\", maxHp = 70, intents = { { kind = \"Attack\", value = 14 }, { kind = \"Attack\", value = 8 }, { kind = \"Defend\", value = 10 }, { kind = \"Debuff\", value = 1, effect = \"weak\" } } },\n\tslime_boss = { name = \"슬라임 킹\", maxHp = 120, intents = { { kind = \"Attack\", value = 18 }, { kind = \"Defend\", value = 12 }, { kind = \"Debuff\", value = 2, effect = \"vuln\" }, { kind = \"Attack\", value = 10 }, { kind = \"Attack\", value = 22 } } },\n\torange_mushroom = { name = \"주황버섯\", maxHp = 16, intents = { { kind = \"Attack\", value = 5 }, { kind = \"Attack\", value = 5 }, { kind = \"Defend\", value = 4 }, { kind = \"Attack\", value = 8 } } },\n\tblue_mushroom = { name = \"파란버섯\", maxHp = 22, intents = { { kind = \"Attack\", value = 4 }, { kind = \"Attack\", value = 4 }, { kind = \"Attack\", value = 10 } } },\n\tpig = { name = \"돼지\", maxHp = 18, intents = { { kind = \"Attack\", value = 6 }, { kind = \"Attack\", value = 6 }, { kind = \"Defend\", value = 5 } } },\n\tgreen_mushroom = { name = \"초록버섯\", maxHp = 20, intents = { { kind = \"Attack\", value = 7 }, { kind = \"Defend\", value = 3 }, { kind = \"Attack\", value = 9 } } },\n\tmushmom = { name = \"머쉬맘\", maxHp = 75, intents = { { kind = \"Defend\", value = 10 }, { kind = \"Debuff\", value = 2, effect = \"weak\" }, { kind = \"Attack\", value = 16 }, { kind = \"Attack\", value = 9 }, { kind = \"Defend\", value = 6 } } },\n\tmodified_snail = { name = \"변형된 달팽이\", maxHp = 60, intents = { { kind = \"Attack\", value = 12 }, { kind = \"Defend\", value = 8 }, { kind = \"Attack\", value = 7 }, { kind = \"Attack\", value = 14 }, { kind = \"Debuff\", value = 1, effect = \"weak\" } } },\n\tking_slime = { name = \"킹 슬라임\", maxHp = 130, intents = { { kind = \"Attack\", value = 18 }, { kind = \"Defend\", value = 14 }, { kind = \"Debuff\", value = 2, effect = \"vuln\" }, { kind = \"Attack\", value = 12 }, { kind = \"Attack\", value = 24 } } },\n}\nself.CurrentNodeId = \"\"\nself.CurrentEnemyId = \"\"\nself.PlayerJob = \"\"\nself:GenerateMap()\nself:BindButtons()\nself:AddRelic(\"ironHeart\")\nself:RenderPotions()\nself:ShowMap()", + "Code": "if self.SelectedClass == \"magician\" then\n\tself.PlayerMaxHp = 70\n\tself.RunDeck = { \"EnergyBolt\", \"EnergyBolt\", \"EnergyBolt\", \"EnergyBolt\", \"EnergyBolt\", \"MagicGuard\", \"MagicGuard\", \"MagicGuard\", \"MagicGuard\", \"MagicClaw\" }\nelse\n\tself.PlayerMaxHp = 80\n\tself.RunDeck = { \"Strike\", \"Strike\", \"Strike\", \"Strike\", \"Strike\", \"Defend\", \"Defend\", \"Defend\", \"Defend\", \"Bash\" }\nend\nself.PlayerHp = self.PlayerMaxHp\nself.Gold = 0\nself.Floor = 1\nself.RunLength = 3\nself.RunActive = true\nself.RunRelics = {}\nself.RunPotions = {}\nself.PotionSlots = 3\nself.Potions = {\n\tredPotion = { name = \"빨간 포션\", desc = \"HP 20 회복\", effect = \"heal\", value = 20, icon = \"393e2a0d8da544899eaa8b22c97f832b\" },\n\tfirebomb = { name = \"화염병\", desc = \"적에게 피해 20\", effect = \"damage\", value = 20, icon = \"7ddb464c2574456289a4eb72ce86f193\" },\n\twarriorElixir = { name = \"전사의 물약\", desc = \"힘 +2\", effect = \"strength\", value = 2, icon = \"7cfbd410581e4073815daaf5f3e6c72f\" },\n\tguardPotion = { name = \"수호의 물약\", desc = \"방어도 +12\", effect = \"block\", value = 12, icon = \"8f8402dfa0f746e18bf606ed74302c0a\" },\n\tmanaElixir = { name = \"마나 엘릭서\", desc = \"에너지 +2\", effect = \"energy\", value = 2, icon = \"ec2778c366f6477ab0f8e7f06bcd73f4\" },\n\tcursedVial = { name = \"저주의 병\", desc = \"적에게 약화 3\", effect = \"weak\", value = 3, icon = \"a9a2763fdb6849dcba3028c737487680\" },\n}\nself.Relics = {\n\tironHeart = { name = \"강철 심장\", desc = \"전투 시작 시 방어도 +6\", hook = \"combatStart\", effect = \"block\", value = 6, icon = \"e555b3a62f3c49dbb2c53784e6bd481f\" },\n\tenergyCore = { name = \"에너지 코어\", desc = \"턴 시작 시 에너지 +1\", hook = \"turnStart\", effect = \"energy\", value = 1, icon = \"a41014f28b47434ab9f49ef104523862\" },\n\tvampire = { name = \"흡혈 송곳니\", desc = \"공격 카드 사용 시 HP +1\", hook = \"cardPlayed\", effect = \"healOnAttack\", value = 1, icon = \"ed64cde7e6c44b9e99502847e54f04e9\" },\n\tgoldIdol = { name = \"황금 우상\", desc = \"전투 승리 시 골드 +10\", hook = \"combatReward\", effect = \"gold\", value = 10, icon = \"03bb05c92b8f45edb0f3dad2e118fd5a\" },\n\tpotionBelt = { name = \"장인의 벨트\", desc = \"물약 슬롯이 5칸으로 늘어난다\", hook = \"passive\", effect = \"potionSlots\", value = 5, icon = \"36725b4566ac40d4902e2ab2113c2096\" },\n\tburningBlood = { name = \"자쿰의 투구\", desc = \"전투 승리 시 HP 6 회복\", hook = \"combatEnd\", effect = \"healOnWin\", value = 6, icon = \"07f994825ce34131b419d43e890c878d\" },\n\tvajra = { name = \"미스릴 해머\", desc = \"전투 시작 시 힘 +1\", hook = \"combatStart\", effect = \"strength\", value = 1, icon = \"59d2579d46dc41d590a9e6b141ad458b\" },\n\tanchor = { name = \"메이플 실드\", desc = \"첫 턴 방어도 +10\", hook = \"combatStart\", effect = \"block\", value = 10, icon = \"6349413e08cc49848862591863d056a0\" },\n\tbagOfPrep = { name = \"모험가의 배낭\", desc = \"첫 턴 드로우 +2\", hook = \"combatStart\", effect = \"draw\", value = 2, icon = \"77b240cb8af245b4801a714380267ae9\" },\n\tbloodVial = { name = \"피의 목걸이\", desc = \"전투 시작 시 HP 2 회복\", hook = \"combatStart\", effect = \"heal\", value = 2, icon = \"c782e949506a42c49eb139c7e65527d7\" },\n\tbronzeScales = { name = \"브론즈 체인메일\", desc = \"피격 시 공격자에게 3 반사\", hook = \"onPlayerDamaged\", effect = \"thorns\", value = 3, icon = \"87272346b145412391622cf803f888d1\" },\n\tstrawberry = { name = \"건강의 반지\", desc = \"획득 시 최대 HP +7\", hook = \"passive\", effect = \"maxHp\", value = 7, icon = \"58f643e29c354c2783a5ce9a72ec155c\" },\n\tpenNib = { name = \"황금 깃펜\", desc = \"10번째 공격마다 피해 2배\", hook = \"attackCalc\", effect = \"penNib\", value = 10, icon = \"4d38d721cc064d14b31b9e9a92754139\" },\n\tboot = { name = \"브론즈 부츠\", desc = \"5 미만 공격 피해가 5로\", hook = \"attackCalc\", effect = \"boot\", value = 5, icon = \"d572b3aa4dac4162aa0d9e551b055dce\" },\n\takabeko = { name = \"황소 투구\", desc = \"전투 첫 공격 피해 +8\", hook = \"attackCalc\", effect = \"akabeko\", value = 8, icon = \"eb3330a6e2274eff958639f8792119d3\" },\n\tcentennialPuzzle = { name = \"백년의 부적\", desc = \"전투 첫 피격 시 드로우 3\", hook = \"onPlayerDamaged\", effect = \"firstLossDraw\", value = 3, icon = \"cfe5ed6556b944fc83ab58b774bb2b73\" },\n\tmeatOnBone = { name = \"고기 망치\", desc = \"승리 시 HP 50% 이하면 12 회복\", hook = \"combatEnd\", effect = \"healIfLow\", value = 12, icon = \"a93e8e87f184411c98c96b877d9f8b10\" },\n\tselfFormingClay = { name = \"점토 갑옷\", desc = \"피해를 받으면 다음 턴 방어 +3\", hook = \"onPlayerDamaged\", effect = \"clayBlock\", value = 3, icon = \"bb446793c5204d5db7d33563fe79f648\" },\n\tchampionBelt = { name = \"챔피언 벨트\", desc = \"취약 부여 시 약화 1 추가\", hook = \"cardDebuff\", effect = \"vulnAddsWeak\", value = 1, icon = \"7ca8c63026034113a561d6adf679fed2\" },\n}\nself.RelicPool = { \"energyCore\", \"vampire\", \"goldIdol\", \"potionBelt\", \"burningBlood\", \"vajra\", \"anchor\", \"bagOfPrep\", \"bloodVial\", \"bronzeScales\", \"strawberry\", \"penNib\", \"boot\", \"akabeko\", \"centennialPuzzle\", \"meatOnBone\", \"selfFormingClay\", \"championBelt\" }\nself.Enemies = {\n\tslime = { name = \"슬라임\", maxHp = 45, intents = { { kind = \"Attack\", value = 10 }, { kind = \"Attack\", value = 6 }, { kind = \"Defend\", value = 8 } } },\n\tslime_elite = { name = \"정예 슬라임\", maxHp = 70, intents = { { kind = \"Attack\", value = 14 }, { kind = \"Attack\", value = 8 }, { kind = \"Defend\", value = 10 }, { kind = \"Debuff\", value = 1, effect = \"weak\" } } },\n\tslime_boss = { name = \"슬라임 킹\", maxHp = 120, intents = { { kind = \"Attack\", value = 18 }, { kind = \"Defend\", value = 12 }, { kind = \"Debuff\", value = 2, effect = \"vuln\" }, { kind = \"Attack\", value = 10 }, { kind = \"Attack\", value = 22 } } },\n\torange_mushroom = { name = \"주황버섯\", maxHp = 16, intents = { { kind = \"Attack\", value = 5 }, { kind = \"Attack\", value = 5 }, { kind = \"Defend\", value = 4 }, { kind = \"Attack\", value = 8 } } },\n\tblue_mushroom = { name = \"파란버섯\", maxHp = 22, intents = { { kind = \"Attack\", value = 4 }, { kind = \"Attack\", value = 4 }, { kind = \"Attack\", value = 10 } } },\n\tpig = { name = \"돼지\", maxHp = 18, intents = { { kind = \"Attack\", value = 6 }, { kind = \"Attack\", value = 6 }, { kind = \"Defend\", value = 5 } } },\n\tgreen_mushroom = { name = \"초록버섯\", maxHp = 20, intents = { { kind = \"Attack\", value = 7 }, { kind = \"Defend\", value = 3 }, { kind = \"Attack\", value = 9 } } },\n\tmushmom = { name = \"머쉬맘\", maxHp = 75, intents = { { kind = \"Defend\", value = 10 }, { kind = \"Debuff\", value = 2, effect = \"weak\" }, { kind = \"Attack\", value = 16 }, { kind = \"Attack\", value = 9 }, { kind = \"Defend\", value = 6 } } },\n\tmodified_snail = { name = \"변형된 달팽이\", maxHp = 60, intents = { { kind = \"Attack\", value = 12 }, { kind = \"Defend\", value = 8 }, { kind = \"Attack\", value = 7 }, { kind = \"Attack\", value = 14 }, { kind = \"Debuff\", value = 1, effect = \"weak\" } } },\n\tking_slime = { name = \"킹 슬라임\", maxHp = 130, intents = { { kind = \"Attack\", value = 18 }, { kind = \"Defend\", value = 14 }, { kind = \"Debuff\", value = 2, effect = \"vuln\" }, { kind = \"Attack\", value = 12 }, { kind = \"Attack\", value = 24 } } },\n}\nself.CurrentNodeId = \"\"\nself.CurrentEnemyId = \"\"\nself.PlayerJob = \"\"\nself.Jobs = {\n\twarrior = {\n\t\t{ id = \"fighter\", name = \"파이터\", desc = \"공격 특화\\n콤보 어택 · 버서크\\n라이징 어택\", starter = \"ComboAttack\" },\n\t\t{ id = \"page\", name = \"페이지\", desc = \"속성 차지 특화\\n썬더/블리자드 차지\\n파워 가드\", starter = \"ThunderCharge\" },\n\t\t{ id = \"spearman\", name = \"스피어맨\", desc = \"방어·관통 특화\\n피어스 · 아이언 월\\n하이퍼 바디\", starter = \"Pierce\" },\n\t},\n\tmagician = {\n\t\t{ id = \"firepoison\", name = \"위자드(불·독)\", desc = \"화염·독 특화\\n파이어 애로우\\n포이즌 브레스 · 앰플\", starter = \"FireArrow\" },\n\t\t{ id = \"icelightning\", name = \"위자드(썬·콜)\", desc = \"광역·빙결 특화\\n썬더 볼트(전체)\\n콜드 빔 · 칠링 스텝\", starter = \"ThunderBolt\" },\n\t\t{ id = \"cleric\", name = \"클레릭\", desc = \"회복·축복 특화\\n힐 · 블레스\\n홀리 애로우\", starter = \"Heal\" },\n\t},\n}\nself:GenerateMap()\nself:BindButtons()\nself:AddRelic(\"ironHeart\")\nself:RenderPotions()\nself:ShowMap()", "Scope": 2, "ExecSpace": 6, "Attributes": [], @@ -691,7 +712,7 @@ "Name": null }, "Arguments": [], - "Code": "self:ShowState(\"combat\")\nself:SetEntityEnabled(\"/ui/DefaultGroup/CombatHud/Result\", false)\nself:SetEntityEnabled(\"/ui/DefaultGroup/CombatHud/PotionMenu\", false)\nself:SetEntityEnabled(\"/ui/DefaultGroup/CombatHud/TooltipBox\", false)\nself:SetText(\"/ui/DefaultGroup/CombatHud/PlayerPanel/Name\", self:JobLabel())\nself.MaxEnergy = 3\nself.Turn = 0\nself.PlayerBlock = 0\nself.PlayerStr = 0\nself.PlayerWeak = 0\nself.PlayerVuln = 0\nself.PlayerPowers = {}\nself.FightAttackCount = 0\nself.FirstHpLossDone = false\nself.ClayBlockNext = 0\nself.CombatOver = false\nself.DiscardPile = {}\nself.Hand = {}\nself.Cards = {\n\tStrike = { name = \"파워 스트라이크\", cost = 1, desc = \"피해 6\", kind = \"Attack\", damage = 6, class = \"warrior\", image = \"a71b116807904ef2b38e1dc013e2f9a2\" },\n\tDefend = { name = \"아이언 바디\", cost = 1, desc = \"방어도 5\", kind = \"Skill\", block = 5, class = \"warrior\", image = \"1ae9b6741c5947a8b528a0f515b50e3e\" },\n\tBash = { name = \"슬래시 블러스트\", cost = 2, desc = \"피해 10\", kind = \"Attack\", damage = 10, class = \"warrior\", image = \"d5bc2953fcab4cfe9062af81c35aff86\" },\n\tWarLeap = { name = \"워 리프\", cost = 1, desc = \"피해 4, 방어도 3\", kind = \"Attack\", damage = 4, block = 3, class = \"warrior\", image = \"992dabf6aff2400e92b2f4f705d8ebe7\" },\n\tBrandish = { name = \"브랜디시\", cost = 2, desc = \"피해 13\", kind = \"Attack\", damage = 13, class = \"warrior\", image = \"21af4bccc5054a5dbc8245dfa7f08681\" },\n\tChargedBlow = { name = \"차지 블로우\", cost = 2, desc = \"피해 8, 취약 2\", kind = \"Attack\", damage = 8, vuln = 2, class = \"warrior\", image = \"fe83c7635b0e49ed83d75a2833adb53e\" },\n\tThreaten = { name = \"위협\", cost = 0, desc = \"약화 2 부여\", kind = \"Skill\", weak = 2, class = \"warrior\", image = \"64daadf1a98e490d9c14ef52ec776e63\" },\n\tEnrage = { name = \"인레이지\", cost = 1, desc = \"힘 +2\", kind = \"Skill\", strength = 2, class = \"warrior\", image = \"09370fc7551e47238fd103a80fba558e\" },\n\tRage = { name = \"분노\", cost = 1, desc = \"매 턴 시작 시 힘 +1\", kind = \"Power\", powerEffect = \"strengthPerTurn\", value = 1, class = \"warrior\", image = \"379d86e3de064959aa4612f71e84ccfb\" },\n\tComboAttack = { name = \"콤보 어택\", cost = 1, desc = \"피해 5 × 2회\", kind = \"Attack\", damage = 5, class = \"fighter\", hits = 2, image = \"1bc3e52b330648faae9eafd5a205e37b\" },\n\tBerserk = { name = \"버서크\", cost = 2, desc = \"매턴 에너지 +1, 취약 1 자가\", kind = \"Power\", powerEffect = \"energyPerTurn\", value = 1, class = \"fighter\", selfVuln = 1, image = \"cef30ea340c74e768bcee4e2cbe0577a\" },\n\tRisingAttack = { name = \"라이징 어택\", cost = 2, desc = \"피해 12\", kind = \"Attack\", damage = 12, class = \"fighter\", image = \"3a3d4b8bb5bd4137847caf883e4bf38e\" },\n\tThunderCharge = { name = \"썬더 차지\", cost = 1, desc = \"피해 7, 약화 1\", kind = \"Attack\", damage = 7, weak = 1, class = \"page\", image = \"f1b7e3041909411eb67af884b446e1e1\" },\n\tBlizzardCharge = { name = \"블리자드 차지\", cost = 1, desc = \"피해 7, 취약 1\", kind = \"Attack\", damage = 7, vuln = 1, class = \"page\", image = \"7915c70952ad432f99519ad79bf929a4\" },\n\tPowerGuard = { name = \"파워 가드\", cost = 1, desc = \"방어도 10\", kind = \"Skill\", block = 10, class = \"page\", image = \"90a9bf8eeb844b578b4e2d93ac43fedf\" },\n\tPierce = { name = \"피어스\", cost = 1, desc = \"피해 9, 방어 무시\", kind = \"Attack\", damage = 9, class = \"spearman\", pierce = true, image = \"e312e535a2bc4fed82d36f9c6027c9db\" },\n\tIronWall = { name = \"아이언 월\", cost = 2, desc = \"방어도 12\", kind = \"Skill\", block = 12, class = \"spearman\", image = \"92021d62341a4bce9cfd09d1b4b865db\" },\n\tHyperBody = { name = \"하이퍼 바디\", cost = 1, desc = \"매턴 방어도 +3\", kind = \"Power\", powerEffect = \"blockPerTurn\", value = 3, class = \"spearman\", image = \"b4020dbadee6401f9893a020fe4154b1\" },\n}\nself.DrawPile = {}\nfor i = 1, #self.RunDeck do\n\tself.DrawPile[i] = self.RunDeck[i]\nend\nself:Shuffle(self.DrawPile)\nself:BuildMonsters()\nself:RenderCombat()\nself:StartPlayerTurn()\nself:ApplyRelics(\"combatStart\")\nself:RenderCombat()", + "Code": "self:ShowState(\"combat\")\nself:SetEntityEnabled(\"/ui/DefaultGroup/CombatHud/Result\", false)\nself:SetEntityEnabled(\"/ui/DefaultGroup/CombatHud/PotionMenu\", false)\nself:SetEntityEnabled(\"/ui/DefaultGroup/CombatHud/TooltipBox\", false)\nself:SetText(\"/ui/DefaultGroup/CombatHud/PlayerPanel/Name\", self:JobLabel())\nself.MaxEnergy = 3\nself.Turn = 0\nself.PlayerBlock = 0\nself.PlayerStr = 0\nself.PlayerWeak = 0\nself.PlayerVuln = 0\nself.PlayerPowers = {}\nself.FightAttackCount = 0\nself.FirstHpLossDone = false\nself.ClayBlockNext = 0\nself.CombatOver = false\nself.DiscardPile = {}\nself.Hand = {}\nself.Cards = {\n\tStrike = { name = \"파워 스트라이크\", cost = 1, desc = \"피해 6\", kind = \"Attack\", damage = 6, class = \"warrior\", image = \"a71b116807904ef2b38e1dc013e2f9a2\" },\n\tDefend = { name = \"아이언 바디\", cost = 1, desc = \"방어도 5\", kind = \"Skill\", block = 5, class = \"warrior\", image = \"1ae9b6741c5947a8b528a0f515b50e3e\" },\n\tBash = { name = \"슬래시 블러스트\", cost = 2, desc = \"피해 10\", kind = \"Attack\", damage = 10, class = \"warrior\", image = \"d5bc2953fcab4cfe9062af81c35aff86\" },\n\tWarLeap = { name = \"워 리프\", cost = 1, desc = \"피해 4, 방어도 3\", kind = \"Attack\", damage = 4, block = 3, class = \"warrior\", image = \"992dabf6aff2400e92b2f4f705d8ebe7\" },\n\tBrandish = { name = \"브랜디시\", cost = 2, desc = \"피해 13\", kind = \"Attack\", damage = 13, class = \"warrior\", image = \"21af4bccc5054a5dbc8245dfa7f08681\" },\n\tChargedBlow = { name = \"차지 블로우\", cost = 2, desc = \"피해 8, 취약 2\", kind = \"Attack\", damage = 8, vuln = 2, class = \"warrior\", image = \"fe83c7635b0e49ed83d75a2833adb53e\" },\n\tThreaten = { name = \"위협\", cost = 0, desc = \"약화 2 부여\", kind = \"Skill\", weak = 2, class = \"warrior\", image = \"64daadf1a98e490d9c14ef52ec776e63\" },\n\tEnrage = { name = \"인레이지\", cost = 1, desc = \"힘 +2\", kind = \"Skill\", strength = 2, class = \"warrior\", image = \"09370fc7551e47238fd103a80fba558e\" },\n\tRage = { name = \"분노\", cost = 1, desc = \"매 턴 시작 시 힘 +1\", kind = \"Power\", powerEffect = \"strengthPerTurn\", value = 1, class = \"warrior\", image = \"379d86e3de064959aa4612f71e84ccfb\" },\n\tComboAttack = { name = \"콤보 어택\", cost = 1, desc = \"피해 5 × 2회\", kind = \"Attack\", damage = 5, class = \"fighter\", hits = 2, image = \"1bc3e52b330648faae9eafd5a205e37b\" },\n\tBerserk = { name = \"버서크\", cost = 2, desc = \"매턴 에너지 +1, 취약 1 자가\", kind = \"Power\", powerEffect = \"energyPerTurn\", value = 1, class = \"fighter\", selfVuln = 1, image = \"cef30ea340c74e768bcee4e2cbe0577a\" },\n\tRisingAttack = { name = \"라이징 어택\", cost = 2, desc = \"피해 12\", kind = \"Attack\", damage = 12, class = \"fighter\", image = \"3a3d4b8bb5bd4137847caf883e4bf38e\" },\n\tThunderCharge = { name = \"썬더 차지\", cost = 1, desc = \"피해 7, 약화 1\", kind = \"Attack\", damage = 7, weak = 1, class = \"page\", image = \"f1b7e3041909411eb67af884b446e1e1\" },\n\tBlizzardCharge = { name = \"블리자드 차지\", cost = 1, desc = \"피해 7, 취약 1\", kind = \"Attack\", damage = 7, vuln = 1, class = \"page\", image = \"7915c70952ad432f99519ad79bf929a4\" },\n\tPowerGuard = { name = \"파워 가드\", cost = 1, desc = \"방어도 10\", kind = \"Skill\", block = 10, class = \"page\", image = \"90a9bf8eeb844b578b4e2d93ac43fedf\" },\n\tPierce = { name = \"피어스\", cost = 1, desc = \"피해 9, 방어 무시\", kind = \"Attack\", damage = 9, class = \"spearman\", pierce = true, image = \"e312e535a2bc4fed82d36f9c6027c9db\" },\n\tIronWall = { name = \"아이언 월\", cost = 2, desc = \"방어도 12\", kind = \"Skill\", block = 12, class = \"spearman\", image = \"92021d62341a4bce9cfd09d1b4b865db\" },\n\tHyperBody = { name = \"하이퍼 바디\", cost = 1, desc = \"매턴 방어도 +3\", kind = \"Power\", powerEffect = \"blockPerTurn\", value = 3, class = \"spearman\", image = \"b4020dbadee6401f9893a020fe4154b1\" },\n\tEnergyBolt = { name = \"에너지 볼트\", cost = 1, desc = \"피해 6\", kind = \"Attack\", damage = 6, class = \"magician\", image = \"a1ee3069fce14498b92998542679ae40\" },\n\tMagicGuard = { name = \"매직 가드\", cost = 1, desc = \"방어도 5\", kind = \"Skill\", block = 5, class = \"magician\", image = \"01b249c26eb34b8aaab774bf221907a1\" },\n\tMagicClaw = { name = \"매직 클로\", cost = 1, desc = \"피해 3 × 2회\", kind = \"Attack\", damage = 3, class = \"magician\", hits = 2, image = \"d6e7c04c436f42f19e9806ac5b4401ae\" },\n\tTeleport = { name = \"텔레포트\", cost = 1, desc = \"방어도 3, 드로 1\", kind = \"Skill\", block = 3, class = \"magician\", draw = 1, image = \"80c98c8e032b4f6c8371a24b4e1d8f14\" },\n\tSlow = { name = \"슬로우\", cost = 1, desc = \"약화 2 부여\", kind = \"Skill\", weak = 2, class = \"magician\", image = \"16f79f571a964430bf1953edc9a14c73\" },\n\tFireArrow = { name = \"파이어 애로우\", cost = 1, desc = \"피해 8\", kind = \"Attack\", damage = 8, class = \"firepoison\", image = \"78b9be4e711c440f84fc21e51e812bae\" },\n\tPoisonBreath = { name = \"포이즌 브레스\", cost = 1, desc = \"독 4 부여\", kind = \"Skill\", class = \"firepoison\", poison = 4, image = \"b4e8bd7508b54d208e4f2ad7414f8c0a\" },\n\tElementAmp = { name = \"엘레멘트 앰플\", cost = 1, desc = \"매 턴 힘 +1\", kind = \"Power\", powerEffect = \"strengthPerTurn\", value = 1, class = \"firepoison\", image = \"9859f3ab41b945f797d56cd83f95b25f\" },\n\tThunderBolt = { name = \"썬더 볼트\", cost = 2, desc = \"모든 적에게 피해 6\", kind = \"Attack\", damage = 6, class = \"icelightning\", aoe = true, image = \"c6685d33cb2641f09d11cfa2d5cc820c\" },\n\tColdBeam = { name = \"콜드 빔\", cost = 2, desc = \"피해 7, 약화 2\", kind = \"Attack\", damage = 7, weak = 2, class = \"icelightning\", image = \"e8f7c148c79f497d83014e3361f59f5c\" },\n\tChillingStep = { name = \"칠링 스텝\", cost = 1, desc = \"방어도 8\", kind = \"Skill\", block = 8, class = \"icelightning\", image = \"b2a7274d868241c78aa5780f2beecddf\" },\n\tHeal = { name = \"힐\", cost = 1, desc = \"HP 10 회복\", kind = \"Skill\", class = \"cleric\", heal = 10, image = \"b4127c181e2942e38821d4a9a1f14596\" },\n\tBless = { name = \"블레스\", cost = 1, desc = \"힘 +1, 방어도 5\", kind = \"Skill\", block = 5, strength = 1, class = \"cleric\", image = \"d45553db4a414011b67486dfa8a12fe5\" },\n\tHolyArrow = { name = \"홀리 애로우\", cost = 1, desc = \"피해 8\", kind = \"Attack\", damage = 8, class = \"cleric\", image = \"0265e103b4904f178b1c2bdcd54d5975\" },\n}\nself.DrawPile = {}\nfor i = 1, #self.RunDeck do\n\tself.DrawPile[i] = self.RunDeck[i]\nend\nself:Shuffle(self.DrawPile)\nself:BuildMonsters()\nself:RenderCombat()\nself:StartPlayerTurn()\nself:ApplyRelics(\"combatStart\")\nself:RenderCombat()", "Scope": 2, "ExecSpace": 6, "Attributes": [], @@ -750,7 +771,7 @@ "Name": null }, "Arguments": [], - "Code": "self.Monsters = {}\nlocal g = \"combat\"\nlocal node = self.MapNodes[self.CurrentNodeId]\nif node ~= nil and node.type ~= nil then g = node.type end\nlocal pmap = \"\"\nlocal lp = _UserService.LocalPlayer\nif lp ~= nil and lp.CurrentMapName ~= nil then pmap = lp.CurrentMapName end\nlocal reg = self.Registered or {}\nfor i = 1, #reg do\n\tif reg[i].entity ~= nil and isvalid(reg[i].entity) then\n\t\treg[i].entity:SetVisible(false)\n\tend\nend\nlocal list = {}\nfor i = 1, #reg do\n\tlocal r = reg[i]\n\tif r.entity ~= nil and isvalid(r.entity) and r.group == g and (r.map == nil or r.map == \"\" or pmap == \"\" or r.map == pmap) then\n\t\tlocal x = 0\n\t\tif r.entity.TransformComponent ~= nil then\n\t\t\tx = r.entity.TransformComponent.WorldPosition.x\n\t\tend\n\t\ttable.insert(list, { entity = r.entity, enemyId = r.enemyId, x = x })\n\tend\nend\ntable.sort(list, function(a, b) return a.x < b.x end)\nlocal mult = 1 + (self.Floor - 1) * 0.6\nlocal n = #list\nif n > 4 then n = 4 end\nfor i = 1, n do\n\tlocal item = list[i]\n\tlocal e = self.Enemies[item.enemyId]\n\tif e == nil then e = { name = item.enemyId, maxHp = 10, intents = { { kind = \"Attack\", value = 5 } } } end\n\tlocal intents = {}\n\tfor k = 1, #e.intents do\n\t\tlocal v = e.intents[k].value\n\t\tif e.intents[k].kind ~= \"Debuff\" then\n\t\t\tv = math.floor(v * mult)\n\t\tend\n\t\tintents[k] = { kind = e.intents[k].kind, value = v, effect = e.intents[k].effect }\n\tend\n\tlocal maxHp = math.floor(e.maxHp * mult)\n\tself.Monsters[i] = { entity = item.entity, enemyId = item.enemyId, name = e.name,\n\t\thp = maxHp, maxHp = maxHp, block = 0, str = 0, weak = 0, vuln = 0,\n\t\tintents = intents, intentIdx = 1, alive = true, slot = i }\n\tself:ReviveMonsterEntity(item.entity)\n\tself:PositionMonsterSlot(i)\nend\nself.TargetIndex = 1", + "Code": "self.Monsters = {}\nlocal g = \"combat\"\nlocal node = self.MapNodes[self.CurrentNodeId]\nif node ~= nil and node.type ~= nil then g = node.type end\nlocal pmap = \"\"\nlocal lp = _UserService.LocalPlayer\nif lp ~= nil and lp.CurrentMapName ~= nil then pmap = lp.CurrentMapName end\nlocal reg = self.Registered or {}\nfor i = 1, #reg do\n\tif reg[i].entity ~= nil and isvalid(reg[i].entity) then\n\t\treg[i].entity:SetVisible(false)\n\tend\nend\nlocal list = {}\nfor i = 1, #reg do\n\tlocal r = reg[i]\n\tif r.entity ~= nil and isvalid(r.entity) and r.group == g and (r.map == nil or r.map == \"\" or pmap == \"\" or r.map == pmap) then\n\t\tlocal x = 0\n\t\tif r.entity.TransformComponent ~= nil then\n\t\t\tx = r.entity.TransformComponent.WorldPosition.x\n\t\tend\n\t\ttable.insert(list, { entity = r.entity, enemyId = r.enemyId, x = x })\n\tend\nend\ntable.sort(list, function(a, b) return a.x < b.x end)\nlocal mult = 1 + (self.Floor - 1) * 0.6\nlocal n = #list\nif n > 4 then n = 4 end\nfor i = 1, n do\n\tlocal item = list[i]\n\tlocal e = self.Enemies[item.enemyId]\n\tif e == nil then e = { name = item.enemyId, maxHp = 10, intents = { { kind = \"Attack\", value = 5 } } } end\n\tlocal intents = {}\n\tfor k = 1, #e.intents do\n\t\tlocal v = e.intents[k].value\n\t\tif e.intents[k].kind ~= \"Debuff\" then\n\t\t\tv = math.floor(v * mult)\n\t\tend\n\t\tintents[k] = { kind = e.intents[k].kind, value = v, effect = e.intents[k].effect }\n\tend\n\tlocal maxHp = math.floor(e.maxHp * mult)\n\tself.Monsters[i] = { entity = item.entity, enemyId = item.enemyId, name = e.name,\n\t\thp = maxHp, maxHp = maxHp, block = 0, str = 0, weak = 0, vuln = 0, poison = 0,\n\t\tintents = intents, intentIdx = 1, alive = true, slot = i }\n\tself:ReviveMonsterEntity(item.entity)\n\tself:PositionMonsterSlot(i)\nend\nself.TargetIndex = 1", "Scope": 2, "ExecSpace": 6, "Attributes": [], @@ -811,7 +832,7 @@ "Name": null }, "Arguments": [], - "Code": "local endTurn = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/DeckHud/EndTurnButton\")\nif endTurn ~= nil and endTurn.ButtonComponent ~= nil then\n\tif self.EndTurnHandler ~= nil then\n\t\tendTurn:DisconnectEvent(ButtonClickEvent, self.EndTurnHandler)\n\t\tself.EndTurnHandler = nil\n\tend\n\tself.EndTurnHandler = endTurn:ConnectEvent(ButtonClickEvent, function() self:EndPlayerTurn() end)\nend\nlocal drawPile = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/DeckHud/DrawPile\")\nif drawPile ~= nil and drawPile.ButtonComponent ~= nil then\n\tif self.DrawPileHandler ~= nil then\n\t\tdrawPile:DisconnectEvent(ButtonClickEvent, self.DrawPileHandler)\n\t\tself.DrawPileHandler = nil\n\tend\n\tself.DrawPileHandler = drawPile:ConnectEvent(ButtonClickEvent, function() self:OpenDeckInspect(\"draw\") end)\nend\nlocal discardPile = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/DeckHud/DiscardPile\")\nif discardPile ~= nil and discardPile.ButtonComponent ~= nil then\n\tif self.DiscardPileHandler ~= nil then\n\t\tdiscardPile:DisconnectEvent(ButtonClickEvent, self.DiscardPileHandler)\n\t\tself.DiscardPileHandler = nil\n\tend\n\tself.DiscardPileHandler = discardPile:ConnectEvent(ButtonClickEvent, function() self:OpenDeckInspect(\"discard\") end)\nend\nlocal inspectClose = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/DeckInspectHud/Close\")\nif inspectClose ~= nil and inspectClose.ButtonComponent ~= nil then\n\tif self.DeckInspectCloseHandler ~= nil then\n\t\tinspectClose:DisconnectEvent(ButtonClickEvent, self.DeckInspectCloseHandler)\n\t\tself.DeckInspectCloseHandler = nil\n\tend\n\tself.DeckInspectCloseHandler = inspectClose:ConnectEvent(ButtonClickEvent, function() self:CloseDeckInspect() end)\nend\nlocal allDeckButton = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/CombatHud/TopBar/AllDeckButton\")\nif allDeckButton ~= nil and allDeckButton.ButtonComponent ~= nil then\n\tif self.AllDeckHandler ~= nil then\n\t\tallDeckButton:DisconnectEvent(ButtonClickEvent, self.AllDeckHandler)\n\t\tself.AllDeckHandler = nil\n\tend\n\tself.AllDeckHandler = allDeckButton:ConnectEvent(ButtonClickEvent, function() self:OpenAllDeck() end)\nend\nlocal allDeckClose = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/DeckAllHud/Close\")\nif allDeckClose ~= nil and allDeckClose.ButtonComponent ~= nil then\n\tif self.AllDeckCloseHandler ~= nil then\n\t\tallDeckClose:DisconnectEvent(ButtonClickEvent, self.AllDeckCloseHandler)\n\t\tself.AllDeckCloseHandler = nil\n\tend\n\tself.AllDeckCloseHandler = allDeckClose:ConnectEvent(ButtonClickEvent, function() self:CloseAllDeck() end)\nend\nfor i = 1, 5 do\n\tlocal cardEntity = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/CardHand/Card\" .. tostring(i))\n\tif cardEntity ~= nil and cardEntity.UITouchReceiveComponent ~= nil then\n\t\tcardEntity:ConnectEvent(UITouchBeginDragEvent, function(ev) self:OnCardDragBegin(i) end)\n\t\tcardEntity:ConnectEvent(UITouchDragEvent, function(ev) self:OnCardDrag(i, ev.TouchPoint) end)\n\t\tcardEntity:ConnectEvent(UITouchEndDragEvent, function(ev) self:OnCardDragEnd(i, ev.TouchPoint) end)\n\tend\nend\nfor i = 1, 3 do\n\tlocal rc = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/RewardHud/Reward\" .. tostring(i))\n\tif rc ~= nil and rc.ButtonComponent ~= nil then\n\t\trc:ConnectEvent(ButtonClickEvent, function() self:PickReward(i) end)\n\tend\nend\nlocal skip = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/RewardHud/Skip\")\nif skip ~= nil and skip.ButtonComponent ~= nil then\n\tskip:ConnectEvent(ButtonClickEvent, function() self:PickReward(0) end)\nend\nlocal mapNodeIds = {}\nfor r = 1, 7 do\n\tfor c = 1, 4 do\n\t\ttable.insert(mapNodeIds, \"r\" .. tostring(r) .. \"c\" .. tostring(c))\n\tend\nend\ntable.insert(mapNodeIds, \"boss\")\nfor i = 1, #mapNodeIds do\n\tlocal nid = mapNodeIds[i]\n\tlocal mn = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/MapHud/Node_\" .. nid)\n\tif mn ~= nil and mn.ButtonComponent ~= nil then\n\t\tmn:ConnectEvent(ButtonClickEvent, function() self:PickNode(nid) end)\n\tend\nend\nfor i = 1, 3 do\n\tlocal sc = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/ShopHud/Card\" .. tostring(i))\n\tif sc ~= nil and sc.ButtonComponent ~= nil then\n\t\tsc:ConnectEvent(ButtonClickEvent, function() self:BuyCard(i) end)\n\tend\nend\nlocal shopLeave = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/ShopHud/Leave\")\nif shopLeave ~= nil and shopLeave.ButtonComponent ~= nil then\n\tshopLeave:ConnectEvent(ButtonClickEvent, function() self:LeaveNode() end)\nend\nlocal shopRelic = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/ShopHud/Relic\")\nif shopRelic ~= nil and shopRelic.ButtonComponent ~= nil then\n\tshopRelic:ConnectEvent(ButtonClickEvent, function() self:BuyRelic() end)\nend\nlocal restLeave = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/RestHud/Leave\")\nif restLeave ~= nil and restLeave.ButtonComponent ~= nil then\n\trestLeave:ConnectEvent(ButtonClickEvent, function() self:LeaveNode() end)\nend\nfor i = 1, 4 do\n\tlocal ms = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/CombatHud/MonsterSlot\" .. tostring(i))\n\tif ms ~= nil and ms.ButtonComponent ~= nil then\n\t\tms:ConnectEvent(ButtonClickEvent, function() self:SetTarget(i) end)\n\tend\nend\nfor i = 1, 10 do\n\tlocal rs = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/CombatHud/TopBar/RelicSlot\" .. tostring(i))\n\tif rs ~= nil and rs.UITouchReceiveComponent ~= nil then\n\t\tlocal idx = i\n\t\trs:ConnectEvent(UITouchEnterEvent, function()\n\t\t\tlocal rid = nil\n\t\t\tif self.RunRelics ~= nil then rid = self.RunRelics[idx] end\n\t\t\tif rid ~= nil and self.Relics[rid] ~= nil then\n\t\t\t\tself:ShowTooltip(self.Relics[rid].name, self.Relics[rid].desc, -240 + (idx - 1) * 48)\n\t\t\tend\n\t\tend)\n\t\trs:ConnectEvent(UITouchExitEvent, function() self:HideTooltip() end)\n\tend\nend\nfor i = 1, 5 do\n\tlocal ps = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/CombatHud/TopBar/PotionSlot\" .. tostring(i))\n\tif ps ~= nil and ps.UITouchReceiveComponent ~= nil then\n\t\tlocal idx = i\n\t\tps:ConnectEvent(UITouchEnterEvent, function()\n\t\t\tlocal pid = nil\n\t\t\tif self.RunPotions ~= nil then pid = self.RunPotions[idx] end\n\t\t\tif pid ~= nil and self.Potions[pid] ~= nil then\n\t\t\t\tself:ShowTooltip(self.Potions[pid].name, self.Potions[pid].desc, 240 + (idx - 1) * 44)\n\t\t\tend\n\t\tend)\n\t\tps:ConnectEvent(UITouchExitEvent, function() self:HideTooltip() end)\n\t\tps:ConnectEvent(UITouchDownEvent, function() self:OpenPotionMenu(idx) end)\n\tend\nend\nlocal pmUse = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/CombatHud/PotionMenu/Use\")\nif pmUse ~= nil and pmUse.ButtonComponent ~= nil then\n\tpmUse:ConnectEvent(ButtonClickEvent, function() self:UsePotion() end)\nend\nlocal pmToss = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/CombatHud/PotionMenu/Toss\")\nif pmToss ~= nil and pmToss.ButtonComponent ~= nil then\n\tpmToss:ConnectEvent(ButtonClickEvent, function() self:TossPotion() end)\nend\nlocal pmClose = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/CombatHud/PotionMenu/Close\")\nif pmClose ~= nil and pmClose.ButtonComponent ~= nil then\n\tpmClose:ConnectEvent(ButtonClickEvent, function() self:ClosePotionMenu() end)\nend\nlocal shopPotion = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/ShopHud/Potion\")\nif shopPotion ~= nil and shopPotion.ButtonComponent ~= nil then\n\tshopPotion:ConnectEvent(ButtonClickEvent, function() self:BuyPotion() end)\nend\nlocal chest = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/TreasureHud/Chest\")\nif chest ~= nil and chest.ButtonComponent ~= nil then\n\tchest:ConnectEvent(ButtonClickEvent, function() self:OpenChest() end)\nend\nlocal treasureLeave = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/TreasureHud/Leave\")\nif treasureLeave ~= nil and treasureLeave.ButtonComponent ~= nil then\n\ttreasureLeave:ConnectEvent(ButtonClickEvent, function() self:LeaveNode() end)\nend\nlocal jcRelic = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/JobChoiceHud/RelicButton\")\nif jcRelic ~= nil and jcRelic.ButtonComponent ~= nil then\n\tjcRelic:ConnectEvent(ButtonClickEvent, function() self:PickJobReward(\"relic\") end)\nend\nlocal jcJob = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/JobChoiceHud/JobButton\")\nif jcJob ~= nil and jcJob.ButtonComponent ~= nil then\n\tjcJob:ConnectEvent(ButtonClickEvent, function() self:PickJobReward(\"job\") end)\nend\nlocal jobIds = { \"fighter\", \"page\", \"spearman\" }\nfor i = 1, #jobIds do\n\tlocal jid = jobIds[i]\n\tlocal jb = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/JobSelectHud/Job_\" .. jid)\n\tif jb ~= nil and jb.ButtonComponent ~= nil then\n\t\tjb:ConnectEvent(ButtonClickEvent, function() self:SetJob(jid) end)\n\tend\nend", + "Code": "local endTurn = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/DeckHud/EndTurnButton\")\nif endTurn ~= nil and endTurn.ButtonComponent ~= nil then\n\tif self.EndTurnHandler ~= nil then\n\t\tendTurn:DisconnectEvent(ButtonClickEvent, self.EndTurnHandler)\n\t\tself.EndTurnHandler = nil\n\tend\n\tself.EndTurnHandler = endTurn:ConnectEvent(ButtonClickEvent, function() self:EndPlayerTurn() end)\nend\nlocal drawPile = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/DeckHud/DrawPile\")\nif drawPile ~= nil and drawPile.ButtonComponent ~= nil then\n\tif self.DrawPileHandler ~= nil then\n\t\tdrawPile:DisconnectEvent(ButtonClickEvent, self.DrawPileHandler)\n\t\tself.DrawPileHandler = nil\n\tend\n\tself.DrawPileHandler = drawPile:ConnectEvent(ButtonClickEvent, function() self:OpenDeckInspect(\"draw\") end)\nend\nlocal discardPile = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/DeckHud/DiscardPile\")\nif discardPile ~= nil and discardPile.ButtonComponent ~= nil then\n\tif self.DiscardPileHandler ~= nil then\n\t\tdiscardPile:DisconnectEvent(ButtonClickEvent, self.DiscardPileHandler)\n\t\tself.DiscardPileHandler = nil\n\tend\n\tself.DiscardPileHandler = discardPile:ConnectEvent(ButtonClickEvent, function() self:OpenDeckInspect(\"discard\") end)\nend\nlocal inspectClose = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/DeckInspectHud/Close\")\nif inspectClose ~= nil and inspectClose.ButtonComponent ~= nil then\n\tif self.DeckInspectCloseHandler ~= nil then\n\t\tinspectClose:DisconnectEvent(ButtonClickEvent, self.DeckInspectCloseHandler)\n\t\tself.DeckInspectCloseHandler = nil\n\tend\n\tself.DeckInspectCloseHandler = inspectClose:ConnectEvent(ButtonClickEvent, function() self:CloseDeckInspect() end)\nend\nlocal allDeckButton = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/CombatHud/TopBar/AllDeckButton\")\nif allDeckButton ~= nil and allDeckButton.ButtonComponent ~= nil then\n\tif self.AllDeckHandler ~= nil then\n\t\tallDeckButton:DisconnectEvent(ButtonClickEvent, self.AllDeckHandler)\n\t\tself.AllDeckHandler = nil\n\tend\n\tself.AllDeckHandler = allDeckButton:ConnectEvent(ButtonClickEvent, function() self:OpenAllDeck() end)\nend\nlocal allDeckClose = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/DeckAllHud/Close\")\nif allDeckClose ~= nil and allDeckClose.ButtonComponent ~= nil then\n\tif self.AllDeckCloseHandler ~= nil then\n\t\tallDeckClose:DisconnectEvent(ButtonClickEvent, self.AllDeckCloseHandler)\n\t\tself.AllDeckCloseHandler = nil\n\tend\n\tself.AllDeckCloseHandler = allDeckClose:ConnectEvent(ButtonClickEvent, function() self:CloseAllDeck() end)\nend\nfor i = 1, 5 do\n\tlocal cardEntity = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/CardHand/Card\" .. tostring(i))\n\tif cardEntity ~= nil and cardEntity.UITouchReceiveComponent ~= nil then\n\t\tcardEntity:ConnectEvent(UITouchBeginDragEvent, function(ev) self:OnCardDragBegin(i) end)\n\t\tcardEntity:ConnectEvent(UITouchDragEvent, function(ev) self:OnCardDrag(i, ev.TouchPoint) end)\n\t\tcardEntity:ConnectEvent(UITouchEndDragEvent, function(ev) self:OnCardDragEnd(i, ev.TouchPoint) end)\n\tend\nend\nfor i = 1, 3 do\n\tlocal rc = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/RewardHud/Reward\" .. tostring(i))\n\tif rc ~= nil and rc.ButtonComponent ~= nil then\n\t\trc:ConnectEvent(ButtonClickEvent, function() self:PickReward(i) end)\n\tend\nend\nlocal skip = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/RewardHud/Skip\")\nif skip ~= nil and skip.ButtonComponent ~= nil then\n\tskip:ConnectEvent(ButtonClickEvent, function() self:PickReward(0) end)\nend\nlocal mapNodeIds = {}\nfor r = 1, 7 do\n\tfor c = 1, 4 do\n\t\ttable.insert(mapNodeIds, \"r\" .. tostring(r) .. \"c\" .. tostring(c))\n\tend\nend\ntable.insert(mapNodeIds, \"boss\")\nfor i = 1, #mapNodeIds do\n\tlocal nid = mapNodeIds[i]\n\tlocal mn = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/MapHud/Node_\" .. nid)\n\tif mn ~= nil and mn.ButtonComponent ~= nil then\n\t\tmn:ConnectEvent(ButtonClickEvent, function() self:PickNode(nid) end)\n\tend\nend\nfor i = 1, 3 do\n\tlocal sc = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/ShopHud/Card\" .. tostring(i))\n\tif sc ~= nil and sc.ButtonComponent ~= nil then\n\t\tsc:ConnectEvent(ButtonClickEvent, function() self:BuyCard(i) end)\n\tend\nend\nlocal shopLeave = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/ShopHud/Leave\")\nif shopLeave ~= nil and shopLeave.ButtonComponent ~= nil then\n\tshopLeave:ConnectEvent(ButtonClickEvent, function() self:LeaveNode() end)\nend\nlocal shopRelic = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/ShopHud/Relic\")\nif shopRelic ~= nil and shopRelic.ButtonComponent ~= nil then\n\tshopRelic:ConnectEvent(ButtonClickEvent, function() self:BuyRelic() end)\nend\nlocal restLeave = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/RestHud/Leave\")\nif restLeave ~= nil and restLeave.ButtonComponent ~= nil then\n\trestLeave:ConnectEvent(ButtonClickEvent, function() self:LeaveNode() end)\nend\nfor i = 1, 4 do\n\tlocal ms = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/CombatHud/MonsterSlot\" .. tostring(i))\n\tif ms ~= nil and ms.ButtonComponent ~= nil then\n\t\tms:ConnectEvent(ButtonClickEvent, function() self:SetTarget(i) end)\n\tend\nend\nfor i = 1, 10 do\n\tlocal rs = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/CombatHud/TopBar/RelicSlot\" .. tostring(i))\n\tif rs ~= nil and rs.UITouchReceiveComponent ~= nil then\n\t\tlocal idx = i\n\t\trs:ConnectEvent(UITouchEnterEvent, function()\n\t\t\tlocal rid = nil\n\t\t\tif self.RunRelics ~= nil then rid = self.RunRelics[idx] end\n\t\t\tif rid ~= nil and self.Relics[rid] ~= nil then\n\t\t\t\tself:ShowTooltip(self.Relics[rid].name, self.Relics[rid].desc, -240 + (idx - 1) * 48)\n\t\t\tend\n\t\tend)\n\t\trs:ConnectEvent(UITouchExitEvent, function() self:HideTooltip() end)\n\tend\nend\nfor i = 1, 5 do\n\tlocal ps = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/CombatHud/TopBar/PotionSlot\" .. tostring(i))\n\tif ps ~= nil and ps.UITouchReceiveComponent ~= nil then\n\t\tlocal idx = i\n\t\tps:ConnectEvent(UITouchEnterEvent, function()\n\t\t\tlocal pid = nil\n\t\t\tif self.RunPotions ~= nil then pid = self.RunPotions[idx] end\n\t\t\tif pid ~= nil and self.Potions[pid] ~= nil then\n\t\t\t\tself:ShowTooltip(self.Potions[pid].name, self.Potions[pid].desc, 240 + (idx - 1) * 44)\n\t\t\tend\n\t\tend)\n\t\tps:ConnectEvent(UITouchExitEvent, function() self:HideTooltip() end)\n\t\tps:ConnectEvent(UITouchDownEvent, function() self:OpenPotionMenu(idx) end)\n\tend\nend\nlocal pmUse = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/CombatHud/PotionMenu/Use\")\nif pmUse ~= nil and pmUse.ButtonComponent ~= nil then\n\tpmUse:ConnectEvent(ButtonClickEvent, function() self:UsePotion() end)\nend\nlocal pmToss = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/CombatHud/PotionMenu/Toss\")\nif pmToss ~= nil and pmToss.ButtonComponent ~= nil then\n\tpmToss:ConnectEvent(ButtonClickEvent, function() self:TossPotion() end)\nend\nlocal pmClose = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/CombatHud/PotionMenu/Close\")\nif pmClose ~= nil and pmClose.ButtonComponent ~= nil then\n\tpmClose:ConnectEvent(ButtonClickEvent, function() self:ClosePotionMenu() end)\nend\nlocal shopPotion = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/ShopHud/Potion\")\nif shopPotion ~= nil and shopPotion.ButtonComponent ~= nil then\n\tshopPotion:ConnectEvent(ButtonClickEvent, function() self:BuyPotion() end)\nend\nlocal chest = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/TreasureHud/Chest\")\nif chest ~= nil and chest.ButtonComponent ~= nil then\n\tchest:ConnectEvent(ButtonClickEvent, function() self:OpenChest() end)\nend\nlocal treasureLeave = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/TreasureHud/Leave\")\nif treasureLeave ~= nil and treasureLeave.ButtonComponent ~= nil then\n\ttreasureLeave:ConnectEvent(ButtonClickEvent, function() self:LeaveNode() end)\nend\nlocal jcRelic = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/JobChoiceHud/RelicButton\")\nif jcRelic ~= nil and jcRelic.ButtonComponent ~= nil then\n\tjcRelic:ConnectEvent(ButtonClickEvent, function() self:PickJobReward(\"relic\") end)\nend\nlocal jcJob = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/JobChoiceHud/JobButton\")\nif jcJob ~= nil and jcJob.ButtonComponent ~= nil then\n\tjcJob:ConnectEvent(ButtonClickEvent, function() self:PickJobReward(\"job\") end)\nend\nfor i = 1, 3 do\n\tlocal slotIdx = i\n\tlocal jb = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/JobSelectHud/Job_slot\" .. tostring(i))\n\tif jb ~= nil and jb.ButtonComponent ~= nil then\n\t\tjb:ConnectEvent(ButtonClickEvent, function()\n\t\t\tif self.JobOpts ~= nil and self.JobOpts[slotIdx] ~= nil then\n\t\t\t\tself:SetJob(self.JobOpts[slotIdx].id)\n\t\t\tend\n\t\tend)\n\tend\nend", "Scope": 2, "ExecSpace": 6, "Attributes": [], @@ -1270,7 +1291,7 @@ "Name": "slot" } ], - "Code": "if self.CombatOver == true or self.FxBusy == true or self.TurnBusy == true then\n\treturn\nend\nif self.Hand == nil then\n\treturn\nend\nlocal cardId = self.Hand[slot]\nif cardId == nil then\n\treturn\nend\nlocal c = self.Cards[cardId]\nif c == nil then\n\treturn\nend\nif self.Energy < c.cost then\n\tself:Toast(\"에너지가 부족합니다\")\n\treturn\nend\nself.Energy = self.Energy - c.cost\nif c.kind == \"Attack\" then\n\tif c.damage ~= nil then\n\t\tlocal total = 0\n\t\tlocal hitN = c.hits or 1\n\t\tfor h = 1, hitN do\n\t\t\ttotal = total + self:CalcPlayerAttack(c.damage)\n\t\tend\n\t\tself:PlayAttackFx(self.TargetIndex, c.image, total, c.pierce == true)\n\tend\n\tif c.block ~= nil then\n\t\tself.PlayerBlock = self.PlayerBlock + c.block\n\tend\n\tself:ApplyRelics(\"cardPlayed\")\nelseif c.kind == \"Skill\" then\n\tif c.block ~= nil then\n\t\tself.PlayerBlock = self.PlayerBlock + c.block\n\tend\nelseif c.kind == \"Power\" then\n\tif c.powerEffect ~= nil then\n\t\ttable.insert(self.PlayerPowers, cardId)\n\tend\nend\nif c.strength ~= nil then\n\tself.PlayerStr = self.PlayerStr + c.strength\nend\nif c.selfVuln ~= nil then\n\tself.PlayerVuln = self.PlayerVuln + c.selfVuln\nend\nif c.weak ~= nil or c.vuln ~= nil then\n\tlocal tm = self.Monsters[self.TargetIndex]\n\tif tm ~= nil and tm.alive == true then\n\t\tif c.weak ~= nil then tm.weak = tm.weak + c.weak end\n\t\tif c.vuln ~= nil then\n\t\t\ttm.vuln = tm.vuln + c.vuln\n\t\t\tif self:HasRelic(\"championBelt\") then\n\t\t\t\ttm.weak = tm.weak + 1\n\t\t\tend\n\t\tend\n\tend\nend\ntable.remove(self.Hand, slot)\nif c.kind ~= \"Power\" then\n\ttable.insert(self.DiscardPile, cardId)\nend\nself:RenderHand(false)\nself:RenderPiles()\nself:RenderCombat()\nself:CheckCombatEnd()", + "Code": "if self.CombatOver == true or self.FxBusy == true or self.TurnBusy == true then\n\treturn\nend\nif self.Hand == nil then\n\treturn\nend\nlocal cardId = self.Hand[slot]\nif cardId == nil then\n\treturn\nend\nlocal c = self.Cards[cardId]\nif c == nil then\n\treturn\nend\nif self.Energy < c.cost then\n\tself:Toast(\"에너지가 부족합니다\")\n\treturn\nend\nself.Energy = self.Energy - c.cost\nif c.kind == \"Attack\" then\n\tif c.damage ~= nil then\n\t\tlocal total = 0\n\t\tlocal hitN = c.hits or 1\n\t\tfor h = 1, hitN do\n\t\t\ttotal = total + self:CalcPlayerAttack(c.damage)\n\t\tend\n\t\tif c.aoe == true then\n\t\t\tself:PlayAoeFx(c.image, total)\n\t\telse\n\t\t\tself:PlayAttackFx(self.TargetIndex, c.image, total, c.pierce == true)\n\t\tend\n\tend\n\tif c.block ~= nil then\n\t\tself.PlayerBlock = self.PlayerBlock + c.block\n\tend\n\tself:ApplyRelics(\"cardPlayed\")\nelseif c.kind == \"Skill\" then\n\tif c.block ~= nil then\n\t\tself.PlayerBlock = self.PlayerBlock + c.block\n\tend\nelseif c.kind == \"Power\" then\n\tif c.powerEffect ~= nil then\n\t\ttable.insert(self.PlayerPowers, cardId)\n\tend\nend\nif c.strength ~= nil then\n\tself.PlayerStr = self.PlayerStr + c.strength\nend\nif c.selfVuln ~= nil then\n\tself.PlayerVuln = self.PlayerVuln + c.selfVuln\nend\nif c.heal ~= nil then\n\tself.PlayerHp = math.min(self.PlayerHp + c.heal, self.PlayerMaxHp)\nend\nif c.weak ~= nil or c.vuln ~= nil or c.poison ~= nil then\n\tlocal tm = self.Monsters[self.TargetIndex]\n\tif tm ~= nil and tm.alive == true then\n\t\tif c.weak ~= nil then tm.weak = tm.weak + c.weak end\n\t\tif c.poison ~= nil then tm.poison = (tm.poison or 0) + c.poison end\n\t\tif c.vuln ~= nil then\n\t\t\ttm.vuln = tm.vuln + c.vuln\n\t\t\tif self:HasRelic(\"championBelt\") then\n\t\t\t\ttm.weak = tm.weak + 1\n\t\t\tend\n\t\tend\n\tend\nend\ntable.remove(self.Hand, slot)\nif c.kind ~= \"Power\" then\n\ttable.insert(self.DiscardPile, cardId)\nend\nif c.draw ~= nil then\n\tself:DrawCards(c.draw)\nend\nself:RenderHand(false)\nself:RenderPiles()\nself:RenderCombat()\nself:CheckCombatEnd()", "Scope": 2, "ExecSpace": 6, "Attributes": [], @@ -1486,6 +1507,36 @@ "Attributes": [], "Name": "PlayAttackFx" }, + { + "Return": { + "Type": "void", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": null + }, + "Arguments": [ + { + "Type": "string", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": "image" + }, + { + "Type": "number", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": "damage" + } + ], + "Code": "self.FxBusy = true\nlocal fx = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/CombatHud/SkillFx\")\nif fx ~= nil then\n\tif fx.SpriteGUIRendererComponent ~= nil and image ~= nil and image ~= \"\" then\n\t\tfx.SpriteGUIRendererComponent.ImageRUID = image\n\tend\n\tif fx.UITransformComponent ~= nil then\n\t\tfx.UITransformComponent.anchoredPosition = Vector2(300, 60)\n\tend\n\tfx.Enable = true\nend\n_TimerService:SetTimerOnce(function()\n\tif fx ~= nil then fx.Enable = false end\n\tself.FxBusy = false\n\tfor i = 1, #self.Monsters do\n\t\tlocal m = self.Monsters[i]\n\t\tif m ~= nil and m.alive == true then\n\t\t\tlocal dmg = damage\n\t\t\tif m.vuln > 0 then\n\t\t\t\tdmg = math.floor(dmg * 1.5)\n\t\t\tend\n\t\t\tif m.block > 0 then\n\t\t\t\tlocal absorbed = math.min(m.block, dmg)\n\t\t\t\tm.block = m.block - absorbed\n\t\t\t\tdmg = dmg - absorbed\n\t\t\tend\n\t\t\tm.hp = m.hp - dmg\n\t\t\tself:ShowDmgPop(i, dmg)\n\t\t\tif m.hp <= 0 then\n\t\t\t\tm.hp = 0\n\t\t\t\tself:KillMonster(m.slot)\n\t\t\tend\n\t\tend\n\tend\n\tself:RenderCombat()\n\tself:CheckCombatEnd()\nend, 0.35)", + "Scope": 2, + "ExecSpace": 6, + "Attributes": [], + "Name": "PlayAoeFx" + }, { "Return": { "Type": "void", @@ -1571,7 +1622,7 @@ "Name": "fromIndex" } ], - "Code": "local idx = 0\nfor i = fromIndex, #self.Monsters do\n\tif self.Monsters[i].alive == true then idx = i; break end\nend\nif idx == 0 or self.PlayerHp <= 0 then\n\tself:FinishEnemyTurn()\n\treturn\nend\nlocal m = self.Monsters[idx]\nlocal base = \"/ui/DefaultGroup/CombatHud/MonsterSlot\" .. tostring(idx)\nself:SetEntityEnabled(base .. \"/ActFrame\", true)\n_TimerService:SetTimerOnce(function()\n\tm.block = 0\n\tlocal intent = m.intents[m.intentIdx]\n\tif intent ~= nil then\n\t\tif intent.kind == \"Attack\" then\n\t\t\tlocal atk = intent.value + m.str\n\t\t\tif m.weak > 0 then\n\t\t\t\tatk = math.floor(atk * 0.75)\n\t\t\tend\n\t\t\tif self.PlayerVuln > 0 then\n\t\t\t\tatk = math.floor(atk * 1.5)\n\t\t\tend\n\t\t\tlocal before = self.PlayerHp\n\t\t\tself:DealDamageToPlayer(atk, idx)\n\t\t\tself:ShowPlayerDmgPop(before - self.PlayerHp)\n\t\telseif intent.kind == \"Defend\" then\n\t\t\tm.block = m.block + intent.value\n\t\telseif intent.kind == \"Debuff\" then\n\t\t\tif intent.effect == \"weak\" then\n\t\t\t\tself.PlayerWeak = self.PlayerWeak + intent.value\n\t\t\telseif intent.effect == \"vuln\" then\n\t\t\t\tself.PlayerVuln = self.PlayerVuln + intent.value\n\t\t\tend\n\t\tend\n\tend\n\tm.intentIdx = m.intentIdx + 1\n\tif m.intentIdx > #m.intents then\n\t\tm.intentIdx = 1\n\tend\n\tif m.weak > 0 then m.weak = m.weak - 1 end\n\tif m.vuln > 0 then m.vuln = m.vuln - 1 end\n\tself:RenderCombat()\n\tself:SetEntityEnabled(base .. \"/ActFrame\", false)\n\t_TimerService:SetTimerOnce(function() self:EnemyActStep(idx + 1) end, 0.15)\nend, 0.45)", + "Code": "local idx = 0\nfor i = fromIndex, #self.Monsters do\n\tif self.Monsters[i].alive == true then idx = i; break end\nend\nif idx == 0 or self.PlayerHp <= 0 then\n\tself:FinishEnemyTurn()\n\treturn\nend\nlocal m = self.Monsters[idx]\nlocal base = \"/ui/DefaultGroup/CombatHud/MonsterSlot\" .. tostring(idx)\nself:SetEntityEnabled(base .. \"/ActFrame\", true)\n_TimerService:SetTimerOnce(function()\n\tif m.poison ~= nil and m.poison > 0 then\n\t\tm.hp = m.hp - m.poison\n\t\tself:ShowDmgPop(idx, m.poison)\n\t\tm.poison = m.poison - 1\n\t\tif m.hp <= 0 then\n\t\t\tm.hp = 0\n\t\t\tself:KillMonster(m.slot)\n\t\t\tself:RenderCombat()\n\t\t\tself:SetEntityEnabled(base .. \"/ActFrame\", false)\n\t\t\t_TimerService:SetTimerOnce(function() self:EnemyActStep(idx + 1) end, 0.15)\n\t\t\treturn\n\t\tend\n\tend\n\tm.block = 0\n\tlocal intent = m.intents[m.intentIdx]\n\tif intent ~= nil then\n\t\tif intent.kind == \"Attack\" then\n\t\t\tlocal atk = intent.value + m.str\n\t\t\tif m.weak > 0 then\n\t\t\t\tatk = math.floor(atk * 0.75)\n\t\t\tend\n\t\t\tif self.PlayerVuln > 0 then\n\t\t\t\tatk = math.floor(atk * 1.5)\n\t\t\tend\n\t\t\tlocal before = self.PlayerHp\n\t\t\tself:DealDamageToPlayer(atk, idx)\n\t\t\tself:ShowPlayerDmgPop(before - self.PlayerHp)\n\t\telseif intent.kind == \"Defend\" then\n\t\t\tm.block = m.block + intent.value\n\t\telseif intent.kind == \"Debuff\" then\n\t\t\tif intent.effect == \"weak\" then\n\t\t\t\tself.PlayerWeak = self.PlayerWeak + intent.value\n\t\t\telseif intent.effect == \"vuln\" then\n\t\t\t\tself.PlayerVuln = self.PlayerVuln + intent.value\n\t\t\tend\n\t\tend\n\tend\n\tm.intentIdx = m.intentIdx + 1\n\tif m.intentIdx > #m.intents then\n\t\tm.intentIdx = 1\n\tend\n\tif m.weak > 0 then m.weak = m.weak - 1 end\n\tif m.vuln > 0 then m.vuln = m.vuln - 1 end\n\tself:RenderCombat()\n\tself:SetEntityEnabled(base .. \"/ActFrame\", false)\n\t_TimerService:SetTimerOnce(function() self:EnemyActStep(idx + 1) end, 0.15)\nend, 0.45)", "Scope": 2, "ExecSpace": 6, "Attributes": [], @@ -1654,12 +1705,27 @@ "Name": "kind" } ], - "Code": "self:SetEntityEnabled(\"/ui/DefaultGroup/JobChoiceHud\", false)\nif kind == \"relic\" then\n\tlocal bid = self:PickNewRelic()\n\tif bid ~= \"\" then\n\t\tself:AddRelic(bid)\n\t\tlocal br = self.Relics[bid]\n\t\tif br ~= nil then\n\t\t\tself:Toast(\"유물 획득: \" .. br.name)\n\t\tend\n\tend\n\tself:ContinueAfterBoss()\nelse\n\tself:SetEntityEnabled(\"/ui/DefaultGroup/JobSelectHud\", true)\nend", + "Code": "self:SetEntityEnabled(\"/ui/DefaultGroup/JobChoiceHud\", false)\nif kind == \"relic\" then\n\tlocal bid = self:PickNewRelic()\n\tif bid ~= \"\" then\n\t\tself:AddRelic(bid)\n\t\tlocal br = self.Relics[bid]\n\t\tif br ~= nil then\n\t\t\tself:Toast(\"유물 획득: \" .. br.name)\n\t\tend\n\tend\n\tself:ContinueAfterBoss()\nelse\n\tself:ShowJobSelect()\nend", "Scope": 2, "ExecSpace": 6, "Attributes": [], "Name": "PickJobReward" }, + { + "Return": { + "Type": "void", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": null + }, + "Arguments": [], + "Code": "local opts = self.Jobs[self.SelectedClass]\nif opts == nil then\n\topts = self.Jobs[\"warrior\"]\nend\nself.JobOpts = opts\nfor i = 1, 3 do\n\tlocal base = \"/ui/DefaultGroup/JobSelectHud/Job_slot\" .. tostring(i)\n\tlocal o = opts[i]\n\tif o ~= nil then\n\t\tself:SetEntityEnabled(base, true)\n\t\tself:SetText(base .. \"/Name\", o.name)\n\t\tself:SetText(base .. \"/Desc\", o.desc)\n\t\tlocal sc = self.Cards[o.starter]\n\t\tif sc ~= nil then\n\t\t\tself:SetText(base .. \"/Starter\", \"대표 카드: \" .. sc.name)\n\t\tend\n\telse\n\t\tself:SetEntityEnabled(base, false)\n\tend\nend\nself:SetEntityEnabled(\"/ui/DefaultGroup/JobSelectHud\", true)", + "Scope": 2, + "ExecSpace": 6, + "Attributes": [], + "Name": "ShowJobSelect" + }, { "Return": { "Type": "string", @@ -1669,7 +1735,7 @@ "Name": null }, "Arguments": [], - "Code": "if self.PlayerJob == \"fighter\" then\n\treturn \"파이터\"\nelseif self.PlayerJob == \"page\" then\n\treturn \"페이지\"\nelseif self.PlayerJob == \"spearman\" then\n\treturn \"스피어맨\"\nend\nif self.SelectedClass == \"warrior\" then\n\treturn \"전사\"\nend\nreturn \"플레이어\"", + "Code": "if self.PlayerJob ~= \"\" and self.Jobs ~= nil then\n\tfor cls, list in pairs(self.Jobs) do\n\t\tfor i = 1, #list do\n\t\t\tif list[i].id == self.PlayerJob then\n\t\t\t\treturn list[i].name\n\t\t\tend\n\t\tend\n\tend\nend\nif self.SelectedClass == \"warrior\" then\n\treturn \"전사\"\nelseif self.SelectedClass == \"magician\" then\n\treturn \"마법사\"\nend\nreturn \"플레이어\"", "Scope": 2, "ExecSpace": 6, "Attributes": [], @@ -1692,7 +1758,7 @@ "Name": "jobId" } ], - "Code": "self.PlayerJob = jobId\nlocal starter = \"\"\nif jobId == \"fighter\" then\n\tstarter = \"ComboAttack\"\nelseif jobId == \"page\" then\n\tstarter = \"ThunderCharge\"\nelseif jobId == \"spearman\" then\n\tstarter = \"Pierce\"\nend\nif starter ~= \"\" then\n\ttable.insert(self.RunDeck, starter)\n\tlocal sc = self.Cards[starter]\n\tif sc ~= nil then\n\t\tself:Toast(\"2차 전직: \" .. self:JobLabel() .. \"! 신규 카드 — \" .. sc.name)\n\tend\nend\nself:SetText(\"/ui/DefaultGroup/CombatHud/PlayerPanel/Name\", self:JobLabel())\nself:SetEntityEnabled(\"/ui/DefaultGroup/JobSelectHud\", false)\nself:ContinueAfterBoss()", + "Code": "self.PlayerJob = jobId\nlocal starter = \"\"\nlocal opts = self.Jobs[self.SelectedClass] or {}\nfor i = 1, #opts do\n\tif opts[i].id == jobId then\n\t\tstarter = opts[i].starter\n\tend\nend\nif starter ~= \"\" then\n\ttable.insert(self.RunDeck, starter)\n\tlocal sc = self.Cards[starter]\n\tif sc ~= nil then\n\t\tself:Toast(\"2차 전직: \" .. self:JobLabel() .. \"! 신규 카드 — \" .. sc.name)\n\tend\nend\nself:SetText(\"/ui/DefaultGroup/CombatHud/PlayerPanel/Name\", self:JobLabel())\nself:SetEntityEnabled(\"/ui/DefaultGroup/JobSelectHud\", false)\nself:ContinueAfterBoss()", "Scope": 2, "ExecSpace": 6, "Attributes": [], @@ -1788,9 +1854,16 @@ "SyncDirection": 0, "Attributes": [], "Name": "vuln" + }, + { + "Type": "number", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": "poison" } ], - "Code": "local parts = {}\nif str ~= nil and str > 0 then table.insert(parts, \"힘+\" .. tostring(str)) end\nif weak ~= nil and weak > 0 then table.insert(parts, \"약화\" .. tostring(weak)) end\nif vuln ~= nil and vuln > 0 then table.insert(parts, \"취약\" .. tostring(vuln)) end\nreturn table.concat(parts, \" \")", + "Code": "local parts = {}\nif str ~= nil and str > 0 then table.insert(parts, \"힘+\" .. tostring(str)) end\nif weak ~= nil and weak > 0 then table.insert(parts, \"약화\" .. tostring(weak)) end\nif vuln ~= nil and vuln > 0 then table.insert(parts, \"취약\" .. tostring(vuln)) end\nif poison ~= nil and poison > 0 then table.insert(parts, \"독\" .. tostring(poison)) end\nreturn table.concat(parts, \" \")", "Scope": 2, "ExecSpace": 6, "Attributes": [], @@ -1805,7 +1878,7 @@ "Name": null }, "Arguments": [], - "Code": "for i = 1, 4 do\n\tlocal base = \"/ui/DefaultGroup/CombatHud/MonsterSlot\" .. tostring(i)\n\tlocal m = self.Monsters[i]\n\tif m ~= nil and m.alive == true then\n\t\tself:SetEntityEnabled(base, true)\n\t\tself:SetText(base .. \"/Name\", m.name)\n\t\tself:SetText(base .. \"/Hp\", string.format(\"%d\", m.hp) .. \"/\" .. string.format(\"%d\", m.maxHp))\n\t\tlocal intent = m.intents[m.intentIdx]\n\t\tlocal t = \"\"\n\t\tif intent ~= nil then\n\t\t\tif intent.kind == \"Attack\" then\n\t\t\t\tlocal atk = intent.value + m.str\n\t\t\t\tif m.weak > 0 then atk = math.floor(atk * 0.75) end\n\t\t\t\tif self.PlayerVuln > 0 then atk = math.floor(atk * 1.5) end\n\t\t\t\tt = \"공격 \" .. tostring(atk)\n\t\t\telseif intent.kind == \"Defend\" then t = \"방어 \" .. tostring(intent.value)\n\t\t\telseif intent.kind == \"Debuff\" then\n\t\t\t\tif intent.effect == \"weak\" then t = \"약화 \" .. tostring(intent.value) .. \" 부여\"\n\t\t\t\telse t = \"취약 \" .. tostring(intent.value) .. \" 부여\" end\n\t\t\tend\n\t\tend\n\t\tself:SetText(base .. \"/Intent\", t)\n\t\tself:SetEntityEnabled(base .. \"/TargetFrame\", i == self.TargetIndex)\n\t\tlocal intentEntity = _EntityService:GetEntityByPath(base .. \"/Intent\")\n\t\tif intentEntity ~= nil and intentEntity.TextComponent ~= nil and intent ~= nil then\n\t\t\tif intent.kind == \"Attack\" then\n\t\t\t\tintentEntity.TextComponent.FontColor = Color(1, 0.45, 0.35, 1)\n\t\t\telseif intent.kind == \"Debuff\" then\n\t\t\t\tintentEntity.TextComponent.FontColor = Color(0.8, 0.5, 1, 1)\n\t\t\telse\n\t\t\t\tintentEntity.TextComponent.FontColor = Color(0.5, 0.75, 1, 1)\n\t\t\tend\n\t\tend\n\t\tself:SetHpBar(base .. \"/HpBarFill\", m.hp, m.maxHp, 140)\n\t\tself:SetEntityEnabled(base .. \"/BlockBadge\", m.block > 0)\n\t\tself:SetText(base .. \"/BlockBadge/Value\", string.format(\"%d\", m.block))\n\t\tself:SetText(base .. \"/Buffs\", self:BuffsLabel(m.str, m.weak, m.vuln))\n\telse\n\t\tself:SetEntityEnabled(base, false)\n\tend\nend\nself:SetText(\"/ui/DefaultGroup/CombatHud/PlayerPanel/HpText\", string.format(\"%d\", self.PlayerHp) .. \"/\" .. string.format(\"%d\", self.PlayerMaxHp))\nself:SetHpBar(\"/ui/DefaultGroup/CombatHud/PlayerPanel/HpBarFill\", self.PlayerHp, self.PlayerMaxHp, 220)\nself:SetEntityEnabled(\"/ui/DefaultGroup/CombatHud/PlayerPanel/BlockBadge\", self.PlayerBlock > 0)\nself:SetText(\"/ui/DefaultGroup/CombatHud/PlayerPanel/BlockBadge/Value\", string.format(\"%d\", self.PlayerBlock))\nlocal pb = self:BuffsLabel(self.PlayerStr, self.PlayerWeak, self.PlayerVuln)\nif self.PlayerPowers ~= nil and #self.PlayerPowers > 0 then\n\tlocal names = {}\n\tfor i = 1, #self.PlayerPowers do\n\t\tlocal pc = self.Cards[self.PlayerPowers[i]]\n\t\tif pc ~= nil then table.insert(names, pc.name) end\n\tend\n\tif pb ~= \"\" then pb = pb .. \" · \" end\n\tpb = pb .. table.concat(names, \" \")\nend\nself:SetText(\"/ui/DefaultGroup/CombatHud/PlayerPanel/Buffs\", pb)\nself:RenderRun()", + "Code": "for i = 1, 4 do\n\tlocal base = \"/ui/DefaultGroup/CombatHud/MonsterSlot\" .. tostring(i)\n\tlocal m = self.Monsters[i]\n\tif m ~= nil and m.alive == true then\n\t\tself:SetEntityEnabled(base, true)\n\t\tself:SetText(base .. \"/Name\", m.name)\n\t\tself:SetText(base .. \"/Hp\", string.format(\"%d\", m.hp) .. \"/\" .. string.format(\"%d\", m.maxHp))\n\t\tlocal intent = m.intents[m.intentIdx]\n\t\tlocal t = \"\"\n\t\tif intent ~= nil then\n\t\t\tif intent.kind == \"Attack\" then\n\t\t\t\tlocal atk = intent.value + m.str\n\t\t\t\tif m.weak > 0 then atk = math.floor(atk * 0.75) end\n\t\t\t\tif self.PlayerVuln > 0 then atk = math.floor(atk * 1.5) end\n\t\t\t\tt = \"공격 \" .. tostring(atk)\n\t\t\telseif intent.kind == \"Defend\" then t = \"방어 \" .. tostring(intent.value)\n\t\t\telseif intent.kind == \"Debuff\" then\n\t\t\t\tif intent.effect == \"weak\" then t = \"약화 \" .. tostring(intent.value) .. \" 부여\"\n\t\t\t\telse t = \"취약 \" .. tostring(intent.value) .. \" 부여\" end\n\t\t\tend\n\t\tend\n\t\tself:SetText(base .. \"/Intent\", t)\n\t\tself:SetEntityEnabled(base .. \"/TargetFrame\", i == self.TargetIndex)\n\t\tlocal intentEntity = _EntityService:GetEntityByPath(base .. \"/Intent\")\n\t\tif intentEntity ~= nil and intentEntity.TextComponent ~= nil and intent ~= nil then\n\t\t\tif intent.kind == \"Attack\" then\n\t\t\t\tintentEntity.TextComponent.FontColor = Color(1, 0.45, 0.35, 1)\n\t\t\telseif intent.kind == \"Debuff\" then\n\t\t\t\tintentEntity.TextComponent.FontColor = Color(0.8, 0.5, 1, 1)\n\t\t\telse\n\t\t\t\tintentEntity.TextComponent.FontColor = Color(0.5, 0.75, 1, 1)\n\t\t\tend\n\t\tend\n\t\tself:SetHpBar(base .. \"/HpBarFill\", m.hp, m.maxHp, 140)\n\t\tself:SetEntityEnabled(base .. \"/BlockBadge\", m.block > 0)\n\t\tself:SetText(base .. \"/BlockBadge/Value\", string.format(\"%d\", m.block))\n\t\tself:SetText(base .. \"/Buffs\", self:BuffsLabel(m.str, m.weak, m.vuln, m.poison or 0))\n\telse\n\t\tself:SetEntityEnabled(base, false)\n\tend\nend\nself:SetText(\"/ui/DefaultGroup/CombatHud/PlayerPanel/HpText\", string.format(\"%d\", self.PlayerHp) .. \"/\" .. string.format(\"%d\", self.PlayerMaxHp))\nself:SetHpBar(\"/ui/DefaultGroup/CombatHud/PlayerPanel/HpBarFill\", self.PlayerHp, self.PlayerMaxHp, 220)\nself:SetEntityEnabled(\"/ui/DefaultGroup/CombatHud/PlayerPanel/BlockBadge\", self.PlayerBlock > 0)\nself:SetText(\"/ui/DefaultGroup/CombatHud/PlayerPanel/BlockBadge/Value\", string.format(\"%d\", self.PlayerBlock))\nlocal pb = self:BuffsLabel(self.PlayerStr, self.PlayerWeak, self.PlayerVuln, 0)\nif self.PlayerPowers ~= nil and #self.PlayerPowers > 0 then\n\tlocal names = {}\n\tfor i = 1, #self.PlayerPowers do\n\t\tlocal pc = self.Cards[self.PlayerPowers[i]]\n\t\tif pc ~= nil then table.insert(names, pc.name) end\n\tend\n\tif pb ~= \"\" then pb = pb .. \" · \" end\n\tpb = pb .. table.concat(names, \" \")\nend\nself:SetText(\"/ui/DefaultGroup/CombatHud/PlayerPanel/Buffs\", pb)\nself:RenderRun()", "Scope": 2, "ExecSpace": 6, "Attributes": [], diff --git a/data/cards.json b/data/cards.json index a8c8b7a..548ce2b 100644 --- a/data/cards.json +++ b/data/cards.json @@ -171,18 +171,164 @@ "value": 3, "desc": "매턴 방어도 +3", "image": "b4020dbadee6401f9893a020fe4154b1" + }, + "EnergyBolt": { + "name": "에너지 볼트", + "cost": 1, + "kind": "Attack", + "class": "magician", + "damage": 6, + "desc": "피해 6", + "image": "a1ee3069fce14498b92998542679ae40" + }, + "MagicGuard": { + "name": "매직 가드", + "cost": 1, + "kind": "Skill", + "class": "magician", + "block": 5, + "desc": "방어도 5", + "image": "01b249c26eb34b8aaab774bf221907a1" + }, + "MagicClaw": { + "name": "매직 클로", + "cost": 1, + "kind": "Attack", + "class": "magician", + "damage": 3, + "hits": 2, + "desc": "피해 3 × 2회", + "image": "d6e7c04c436f42f19e9806ac5b4401ae" + }, + "Teleport": { + "name": "텔레포트", + "cost": 1, + "kind": "Skill", + "class": "magician", + "block": 3, + "draw": 1, + "desc": "방어도 3, 드로 1", + "image": "80c98c8e032b4f6c8371a24b4e1d8f14" + }, + "Slow": { + "name": "슬로우", + "cost": 1, + "kind": "Skill", + "class": "magician", + "weak": 2, + "desc": "약화 2 부여", + "image": "16f79f571a964430bf1953edc9a14c73" + }, + "FireArrow": { + "name": "파이어 애로우", + "cost": 1, + "kind": "Attack", + "class": "firepoison", + "damage": 8, + "desc": "피해 8", + "image": "78b9be4e711c440f84fc21e51e812bae" + }, + "PoisonBreath": { + "name": "포이즌 브레스", + "cost": 1, + "kind": "Skill", + "class": "firepoison", + "poison": 4, + "desc": "독 4 부여", + "image": "b4e8bd7508b54d208e4f2ad7414f8c0a" + }, + "ElementAmp": { + "name": "엘레멘트 앰플", + "cost": 1, + "kind": "Power", + "class": "firepoison", + "powerEffect": "strengthPerTurn", + "value": 1, + "desc": "매 턴 힘 +1", + "image": "9859f3ab41b945f797d56cd83f95b25f" + }, + "ThunderBolt": { + "name": "썬더 볼트", + "cost": 2, + "kind": "Attack", + "class": "icelightning", + "damage": 6, + "aoe": true, + "desc": "모든 적에게 피해 6", + "image": "c6685d33cb2641f09d11cfa2d5cc820c" + }, + "ColdBeam": { + "name": "콜드 빔", + "cost": 2, + "kind": "Attack", + "class": "icelightning", + "damage": 7, + "weak": 2, + "desc": "피해 7, 약화 2", + "image": "e8f7c148c79f497d83014e3361f59f5c" + }, + "ChillingStep": { + "name": "칠링 스텝", + "cost": 1, + "kind": "Skill", + "class": "icelightning", + "block": 8, + "desc": "방어도 8", + "image": "b2a7274d868241c78aa5780f2beecddf" + }, + "Heal": { + "name": "힐", + "cost": 1, + "kind": "Skill", + "class": "cleric", + "heal": 10, + "desc": "HP 10 회복", + "image": "b4127c181e2942e38821d4a9a1f14596" + }, + "Bless": { + "name": "블레스", + "cost": 1, + "kind": "Skill", + "class": "cleric", + "strength": 1, + "block": 5, + "desc": "힘 +1, 방어도 5", + "image": "d45553db4a414011b67486dfa8a12fe5" + }, + "HolyArrow": { + "name": "홀리 애로우", + "cost": 1, + "kind": "Attack", + "class": "cleric", + "damage": 8, + "desc": "피해 8", + "image": "0265e103b4904f178b1c2bdcd54d5975" } }, - "starterDeck": [ - "Strike", - "Strike", - "Strike", - "Strike", - "Strike", - "Defend", - "Defend", - "Defend", - "Defend", - "Bash" - ] + "starterDecks": { + "warrior": [ + "Strike", + "Strike", + "Strike", + "Strike", + "Strike", + "Defend", + "Defend", + "Defend", + "Defend", + "Bash" + ], + "magician": [ + "EnergyBolt", + "EnergyBolt", + "EnergyBolt", + "EnergyBolt", + "EnergyBolt", + "MagicGuard", + "MagicGuard", + "MagicGuard", + "MagicGuard", + "MagicClaw" + ] + } } diff --git a/docs/superpowers/plans/2026-06-12-magician.md b/docs/superpowers/plans/2026-06-12-magician.md new file mode 100644 index 0000000..8ff6c25 --- /dev/null +++ b/docs/superpowers/plans/2026-06-12-magician.md @@ -0,0 +1,38 @@ +# P10 — 법사 클래스 구현 계획 + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox 구문. + +**Goal:** 법사 클래스(1차 5종 + 2차 3계열 9종)·신규 메커니즘 4종(독/AoE/회복/드로)·캐릭터 선택 오픈·전직 화면 동적화. + +설계: `docs/superpowers/specs/2026-06-12-magician-design.md` + +### Task 1: 이미지 RUID 10종 선별 (4종은 기존 후보 재사용) +- [ ] 재사용 확정: FireArrow=78b9be4e(큰 불꽃)·ThunderBolt=c6685d33(낙뢰)·ColdBeam=e8f7c148(얼음)·ChillingStep=b2a7274d(빙수림) +- [ ] 검색(마법/독/회복/빛/포털/정령) → 메이커 격자 미리보기 → EnergyBolt·MagicGuard·MagicClaw·Teleport·Slow·PoisonBreath·ElementAmp·Heal·Bless·HolyArrow 확정 + +### Task 2: 데이터 — cards.json +- [ ] `starterDeck` → `starterDecks{warrior, magician}` (마법사: EnergyBolt×5·MagicGuard×4·MagicClaw×1), 생성기 검증 갱신 +- [ ] 신규 14종 추가 (설계 표 그대로: class=magician/firepoison/icelightning/cleric, draw/heal/poison/aoe 필드) → 커밋 + +### Task 3: 생성기 — 메커니즘 (Lua) +- [ ] 직렬화: draw·heal·poison·aoe + starterDecks 주입(StartRun 클래스 분기: MaxHp 80/70·RunDeck) +- [ ] PlayCard: `aoe` → `PlayAoeFx(image, total)` (단일 대상 로직과 동일 합산, 0.35s 후 전 생존 적에 각자 취약/방어 적용·슬롯별 팝업·KillMonster·CheckCombatEnd) / 공통부: heal(상한 클램프)·draw(`DrawCards`)·poison(타겟 `tm.poison += N`) +- [ ] BuildMonsters `poison = 0` 초기화, EnemyActStep 행동 타이머 시작부에 독 틱(피해 팝업·사망 시 행동 생략 후 체인 계속), BuffsLabel 4번째 인자 poison(`독N`) — RenderCombat 호출부 갱신(플레이어는 0) +- [ ] 커밋 + +### Task 4: 생성기 — 클래스 선택·전직 동적화 +- [ ] classCards Mage 활성화(enabled·tint·desc '마법 원거리 딜러'), BindMenuButtons MageButton→`SelectClass("magician")`, RenderCharacterSelect 2클래스 하이라이트·상태 텍스트, StartNewGame 가드 warrior|magician +- [ ] JobSelectHud 패널 경로 `Job_slot{1..3}` 범용화, `ShowJobSelect`(JOBS 상수→JobOpts prop, 슬롯 텍스트 채움) 신설 — PickJobReward("job")가 호출, 바인딩은 슬롯 인덱스→`SetJob(self.JobOpts[i].id)` +- [ ] SetJob 대표 카드 매핑(JOBS 테이블에 starter 포함: firepoison→FireArrow·icelightning→ThunderBolt·cleric→Heal), JobLabel 확장(마법사·위자드(불·독)·위자드(썬·콜)·클레릭) +- [ ] 커밋 + +### Task 5: 시뮬 동기화 (TDD) +- [ ] 실패 테스트: poison 틱·사망 / aoe 전체 피해 / heal 클램프 / draw / 법사 시작 덱은 시뮬 무관(주석) → 구현 → 전체 PASS → 커밋 + +### Task 6: 재생성·메이커 검증·PR +- [ ] 재생성 + grep -c 카운트 + 전체 테스트 → 커밋 +- [ ] 메이커: 법사 선택 시작(HP70·시작 덱), 전직 화면 마법사 3직업 표기, 클레릭 전직→힐 동작, 독/AoE 실측 → 스크린샷 +- [ ] push → gitea-pr.mjs PR·머지 → main pull + +## Self-Review +- 설계 전 항목 매핑 ✓ / JobSelect 동적화로 P9 고정 경로 제거 명시 ✓ / BuffsLabel 시그니처 변경 시 호출부(몬스터·플레이어) 동시 갱신 명시 ✓ diff --git a/docs/superpowers/specs/2026-06-12-magician-design.md b/docs/superpowers/specs/2026-06-12-magician-design.md new file mode 100644 index 0000000..5e92218 --- /dev/null +++ b/docs/superpowers/specs/2026-06-12-magician-design.md @@ -0,0 +1,63 @@ +# P10 — 법사 클래스 설계 + +날짜: 2026-06-12 (사용자 승인 — P9/P10/P11 중 2단계) +브랜치: `feature/p10-magician` +선행: P9 (클래스 모델·전직 흐름·CardPool 필터) + +## 범위 + +1. **캐릭터 선택 오픈** — 시작 화면 전사/법사 2택 (법사 시작 HP 70, 전용 시작 덱) +2. **법사 1차 카드 5종** + **2차 3계열 9종** (위자드(불·독)/위자드(썬·콜)/클레릭 — 실제 메이플 직업) +3. **신규 메커니즘 4종**: 독(DoT)·전체 공격(AoE)·회복 카드·드로 카드 (Lua + 시뮬 동기화) +4. 전직 선택 화면을 **클래스별 동적 구성**으로 리팩터 (P9의 고정 3패널 → 슬롯 3개 + 런타임 채움) + +## 데이터 + +- `cards.json`: `starterDeck` → **`starterDecks`** `{ warrior: [...], magician: [에너지 볼트×5, 매직 가드×4, 매직 클로×1] }` +- 신규 카드 필드: `draw`(드로 N)·`heal`(HP 회복)·`poison`(적에게 독 N)·`aoe`(true=전체 공격) +- 클래스 상수(생성기): warrior HP 80 / magician HP 70 + +법사 카드 14종 (메이플 스킬명): + +| id | 직업 | 이름 | 코 | 효과 | +|----|------|------|----|------| +| EnergyBolt | magician | 에너지 볼트 | 1 | 피해 6 | +| MagicGuard | magician | 매직 가드 | 1 | 방어 5 | +| MagicClaw | magician | 매직 클로 | 1 | 피해 3 × 2회 | +| Teleport | magician | 텔레포트 | 1 | 방어 3, 드로 1 | +| Slow | magician | 슬로우 | 1 | 약화 2 부여 | +| FireArrow | firepoison | 파이어 애로우 | 1 | 피해 8 | +| PoisonBreath | firepoison | 포이즌 브레스 | 1 | **독 4** 부여 | +| ElementAmp | firepoison | 엘레멘트 앰플 | 1 | Power: 매턴 힘 +1 | +| ThunderBolt | icelightning | 썬더 볼트 | 2 | **전체 적** 피해 6 | +| ColdBeam | icelightning | 콜드 빔 | 2 | 피해 7, 약화 2 | +| ChillingStep | icelightning | 칠링 스텝 | 1 | 방어 8 | +| Heal | cleric | 힐 | 1 | **HP 10 회복** | +| Bless | cleric | 블레스 | 1 | 힘 +1, 방어 5 | +| HolyArrow | cleric | 홀리 애로우 | 1 | 피해 8 | + +(설계 초안 대비 수치 미세 조정: 힐 12→10·블레스 방어 6→5·홀리 애로우 9→8 — 1코 효율 정렬) + +## 신규 메커니즘 규칙 + +- **독**: 적 디버프. 해당 적 행동 시작 시 `hp -= poison` 후 `poison -= 1` (StS 동일). 방어 무시. 독 사망 시 행동 생략·체인 계속. 버프 라인에 `독N` 표시. +- **AoE**(`aoe: true`): 생존 적 전원에게 각자 취약/방어 적용해 피해. 중앙 이펙트 1회(`PlayAoeFx`), 슬롯별 팝업. +- **회복**(`heal`): `PlayerHp = min(+N, Max)`. +- **드로**(`draw`): 사용 시 N장 드로 (손패 상한 5 초과분은 기존 DrawCards 동작 따름). + +## 전직 화면 동적화 + +- `JobSelectHud`의 패널을 `Job_slot1..3`(범용)으로 변경, `ShowJobSelect`가 `SelectedClass`별 옵션 테이블(JOBS 상수 주입)로 이름/설명/대표 카드 텍스트를 채움. 클릭 → `SetJob(JobOpts[i].id)`. +- JOBS: warrior=[fighter/page/spearman], magician=[firepoison(위자드 불·독)/icelightning(위자드 썬·콜)/cleric(클레릭)] +- 대표 카드: firepoison→파이어 애로우, icelightning→썬더 볼트, cleric→힐 +- `JobLabel` 확장: 마법사/위자드(불·독)/위자드(썬·콜)/클레릭 + +## 캐릭터 선택 + +- 기존 `MageButton`(잠금) → 활성: key Mage, `SelectClass("magician")`, 하이라이트·상태 텍스트 클래스 공용화, `StartNewGame` 가드 warrior|magician 허용 +- `StartRun`: 클래스별 MaxHp·RunDeck 분기 + +## 검증 + +1. 시뮬: poison/aoe/heal/draw 재현 + 테스트 4건 이상 (전체 40건+) +2. 메이커: 법사 선택→시작 덱 확인→전직(클레릭 등)→전용 카드 풀·독/AoE/힐 실동작, 빌드·런타임 0에러 diff --git a/tools/balance/sim-balance.mjs b/tools/balance/sim-balance.mjs index 5f4c910..f9ec7dd 100644 --- a/tools/balance/sim-balance.mjs +++ b/tools/balance/sim-balance.mjs @@ -59,7 +59,8 @@ export function loadData() { if (!e) throw new Error(`simEncounter 적 없음: ${id}`); return { name: e.name, maxHp: e.maxHp, intents: e.intents }; }); - return { cards: cardsData.cards, starterDeck: cardsData.starterDeck, monsters }; + // 시뮬 기본 덱은 전사 시작 덱 (클래스별 시뮬은 starterDeck 직접 주입으로 가능) + return { cards: cardsData.cards, starterDeck: cardsData.starterDecks.warrior, monsters }; } // 주의: 인게임은 플레이어가 카드를 직접 선택한다. 이 chooseAction은 밸런스 추정용 자동 플레이 휴리스틱일 뿐 @@ -105,7 +106,7 @@ export function simulateCombat(data, rng, stats) { let pStr = 0, pWeak = 0, pVuln = 0; const powers = []; const mob = monsters.map((m) => ({ - name: m.name, hp: m.maxHp, maxHp: m.maxHp, block: 0, str: 0, weak: 0, vuln: 0, + name: m.name, hp: m.maxHp, maxHp: m.maxHp, block: 0, str: 0, weak: 0, vuln: 0, poison: 0, intents: m.intents, intentIdx: 0, alive: true, })); let turns = 0; @@ -148,18 +149,30 @@ export function simulateCombat(data, rng, stats) { const hitN = c.hits || 1; let totalNv = 0; for (let h = 0; h < hitN; h++) totalNv += calcAttack(c.damage || 0, pStr, pWeak, 0); - const dmg = target.vuln > 0 ? Math.floor(totalNv * 1.5) : totalNv; - if (c.pierce === true) { - target.hp -= dmg; // 방어 무시 - if (target.hp < 0) target.hp = 0; + let dmg = totalNv; // 통계 보고용 (aoe는 1대상 기준) + if (c.aoe === true) { + // 전체 공격 — 대상마다 취약/방어 개별 적용 (Lua PlayAoeFx 동기화) + for (const m2 of aliveList()) { + const d2 = m2.vuln > 0 ? Math.floor(totalNv * 1.5) : totalNv; + const r2 = applyDamage(m2.hp, m2.block, d2); + m2.hp = r2.hp; m2.block = r2.block; + if (m2.hp <= 0) m2.alive = false; + } } else { - const r = applyDamage(target.hp, target.block, dmg); - target.hp = r.hp; target.block = r.block; + dmg = target.vuln > 0 ? Math.floor(totalNv * 1.5) : totalNv; + if (c.pierce === true) { + target.hp -= dmg; // 방어 무시 + if (target.hp < 0) target.hp = 0; + } else { + const r = applyDamage(target.hp, target.block, dmg); + target.hp = r.hp; target.block = r.block; + } + if (target.hp <= 0) target.alive = false; } - if (target.hp <= 0) target.alive = false; if (c.block) pBlock += c.block; if (c.strength) pStr += c.strength; if (c.selfVuln) pVuln += c.selfVuln; + if (c.heal) pHp = Math.min(pHp + c.heal, PLAYER_HP); if (stats) stats[id] = bump(stats[id], c.cost, dmg, c.block || 0); } else if (c.kind === 'Power') { if (c.powerEffect) powers.push(id); @@ -168,15 +181,18 @@ export function simulateCombat(data, rng, stats) { pBlock += c.block || 0; if (c.strength) pStr += c.strength; if (c.selfVuln) pVuln += c.selfVuln; - if (c.weak || c.vuln) { + if (c.heal) pHp = Math.min(pHp + c.heal, PLAYER_HP); + if (c.weak || c.vuln || c.poison) { const target = chooseTarget(alive, 0); if (c.weak) target.weak += c.weak; if (c.vuln) target.vuln += c.vuln; + if (c.poison) target.poison += c.poison; } if (stats) stats[id] = bump(stats[id], c.cost, 0, c.block || 0); } hand.splice(idx, 1); if (c.kind !== 'Power') discard.push(id); // 파워는 소멸 — Lua 동기화 + if (c.draw) draw(c.draw); if (aliveList().length === 0) return { win: true, turns, playerHpRemaining: pHp }; } discard.push(...hand); hand = []; @@ -185,6 +201,12 @@ export function simulateCombat(data, rng, stats) { if (pVuln > 0) pVuln--; for (const m of mob) { if (!m.alive) continue; + // 독 틱 — 행동 시작 시 (Lua EnemyActStep 동기화). 사망 시 행동 생략 + if (m.poison > 0) { + m.hp -= m.poison; + m.poison--; + if (m.hp <= 0) { m.hp = 0; m.alive = false; continue; } + } m.block = 0; // 매 턴 초기화 (이전 턴 블록 미이월) const it = m.intents[m.intentIdx]; if (it) { @@ -203,6 +225,8 @@ export function simulateCombat(data, rng, stats) { if (m.vuln > 0) m.vuln--; if (pHp <= 0) return { win: false, turns, playerHpRemaining: 0 }; } + // 독 사망 등 적 페이즈 중 전멸 처리 (Lua FinishEnemyTurn→CheckCombatEnd 동기화) + if (!mob.some((m) => m.alive)) return { win: true, turns, playerHpRemaining: pHp }; } return { win: false, turns, playerHpRemaining: pHp, draw: true }; } diff --git a/tools/balance/sim-balance.test.mjs b/tools/balance/sim-balance.test.mjs index d20b6b1..ce837f0 100644 --- a/tools/balance/sim-balance.test.mjs +++ b/tools/balance/sim-balance.test.mjs @@ -280,3 +280,59 @@ test('simulateCombat: blockPerTurn 파워 — 매턴 방어로 약공 무효', ( assert.equal(r.draw, true); assert.equal(r.playerHpRemaining, 77); }); + +test('simulateCombat: poison — 적 행동 시작 시 틱·1 감소·독 사망 시 승리 처리', () => { + const data = { + cards: { PB: { name: '포이즌', cost: 3, kind: 'Skill', poison: 4 } }, + starterDeck: ['PB', 'PB', 'PB', 'PB', 'PB'], + monsters: [{ name: '적', maxHp: 10, intents: [{ kind: 'Defend', value: 0 }] }], + }; + // T1: 독4 부여 → 틱 4 (hp 6, 독 3). T2: +4 → 7 틱 → hp 0 사망 → 승리 + const r = simulateCombat(data, mulberry32(1)); + assert.equal(r.win, true); + assert.equal(r.turns, 2); +}); + +test('simulateCombat: aoe — 모든 생존 적에게 피해', () => { + const data = { + cards: { TB: { name: '썬더 볼트', cost: 3, kind: 'Attack', damage: 6, aoe: true } }, + starterDeck: ['TB', 'TB', 'TB', 'TB', 'TB'], + monsters: [ + { name: 'A', maxHp: 6, intents: [{ kind: 'Attack', value: 5 }] }, + { name: 'B', maxHp: 6, intents: [{ kind: 'Attack', value: 5 }] }, + { name: 'C', maxHp: 6, intents: [{ kind: 'Attack', value: 5 }] }, + ], + }; + const r = simulateCombat(data, mulberry32(1)); + assert.equal(r.win, true); + assert.equal(r.turns, 1); +}); + +test('simulateCombat: heal — 최대 HP 클램프', () => { + const data = { + cards: { H: { name: '힐', cost: 1, kind: 'Skill', heal: 10 } }, + starterDeck: ['H', 'H', 'H', 'H', 'H'], + monsters: [{ name: '적', maxHp: 9999, intents: [{ kind: 'Attack', value: 10 }] }], + }; + // 매턴: 힐로 80까지 회복(클램프) → 적 10 → 70. MAX_TURNS 도달 시 hp 70 + const r = simulateCombat(data, mulberry32(1)); + assert.equal(r.draw, true); + assert.equal(r.playerHpRemaining, 70); +}); + +test('simulateCombat: draw — 카드 드로로 손패 보충', () => { + const data = { + cards: { + D: { name: '텔레포트류', cost: 0, kind: 'Skill', draw: 1, block: 0 }, + Hit: { name: '타격', cost: 1, kind: 'Attack', damage: 1 }, + }, + starterDeck: ['D', 'D', 'D', 'D', 'D', 'Hit', 'Hit', 'Hit'], + monsters: [{ name: '적', maxHp: 4, intents: [{ kind: 'Defend', value: 0 }] }], + }; + // 드로 덕에 첫 턴 히트 3장 전부 접근 → 늦어도 2턴 내 처치 (시드 무관) + for (let s = 1; s <= 10; s++) { + const r = simulateCombat(data, mulberry32(s)); + assert.equal(r.win, true, `seed ${s}`); + assert.ok(r.turns <= 2, `seed ${s}: ${r.turns}턴`); + } +}); diff --git a/tools/deck/gen-slaydeck.mjs b/tools/deck/gen-slaydeck.mjs index 5689279..a94c85c 100644 --- a/tools/deck/gen-slaydeck.mjs +++ b/tools/deck/gen-slaydeck.mjs @@ -4,9 +4,32 @@ const CARDS = JSON.parse(readFileSync('data/cards.json', 'utf8')); const ENEMIES = JSON.parse(readFileSync('data/enemies.json', 'utf8')); // 검증 (fail-fast): 잘못된 데이터면 생성 중단 -for (const id of CARDS.starterDeck) { - if (!CARDS.cards[id]) { - throw new Error(`[gen-slaydeck] starterDeck에 없는 카드 id 참조: ${id}`); +const CLASSES = { + warrior: { label: '전사', maxHp: 80 }, + magician: { label: '마법사', maxHp: 70 }, +}; +for (const cls of Object.keys(CLASSES)) { + if (!CARDS.starterDecks?.[cls]) throw new Error(`[gen-slaydeck] starterDecks.${cls} 없음`); + for (const id of CARDS.starterDecks[cls]) { + if (!CARDS.cards[id]) throw new Error(`[gen-slaydeck] starterDecks.${cls}에 없는 카드 id 참조: ${id}`); + } +} +// 전직 옵션 (클래스별 2차 — JobSelectHud 동적 구성·SetJob 대표 카드) +const JOBS = { + warrior: [ + { id: 'fighter', name: '파이터', desc: '공격 특화\n콤보 어택 · 버서크\n라이징 어택', starter: 'ComboAttack' }, + { id: 'page', name: '페이지', desc: '속성 차지 특화\n썬더/블리자드 차지\n파워 가드', starter: 'ThunderCharge' }, + { id: 'spearman', name: '스피어맨', desc: '방어·관통 특화\n피어스 · 아이언 월\n하이퍼 바디', starter: 'Pierce' }, + ], + magician: [ + { id: 'firepoison', name: '위자드(불·독)', desc: '화염·독 특화\n파이어 애로우\n포이즌 브레스 · 앰플', starter: 'FireArrow' }, + { id: 'icelightning', name: '위자드(썬·콜)', desc: '광역·빙결 특화\n썬더 볼트(전체)\n콜드 빔 · 칠링 스텝', starter: 'ThunderBolt' }, + { id: 'cleric', name: '클레릭', desc: '회복·축복 특화\n힐 · 블레스\n홀리 애로우', starter: 'Heal' }, + ], +}; +for (const [cls, jobs] of Object.entries(JOBS)) { + for (const j of jobs) { + if (!CARDS.cards[j.starter]) throw new Error(`[gen-slaydeck] JOBS.${cls}.${j.id} 대표 카드 없음: ${j.starter}`); } } if (!ENEMIES.enemies[ENEMIES.activeEnemy]) { @@ -56,7 +79,14 @@ function luaEnemiesTable(enemies) { } // Lua 직렬화 헬퍼 function luaStr(s) { - return '"' + String(s).replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"'; + return '"' + String(s).replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n') + '"'; +} +function luaJobsTable(jobs) { + const cls = Object.entries(jobs).map(([clsId, list]) => { + const items = list.map((j) => `\t\t{ id = ${luaStr(j.id)}, name = ${luaStr(j.name)}, desc = ${luaStr(j.desc)}, starter = ${luaStr(j.starter)} },`).join('\n'); + return `\t${clsId} = {\n${items}\n\t},`; + }).join('\n'); + return `self.Jobs = {\n${cls}\n}`; } function luaCardsTable(cards) { const lines = Object.entries(cards).map(([id, c]) => { @@ -73,6 +103,10 @@ function luaCardsTable(cards) { if (c.hits != null) fields.push(`hits = ${c.hits}`); if (c.pierce === true) fields.push('pierce = true'); if (c.selfVuln != null) fields.push(`selfVuln = ${c.selfVuln}`); + if (c.draw != null) fields.push(`draw = ${c.draw}`); + if (c.heal != null) fields.push(`heal = ${c.heal}`); + if (c.poison != null) fields.push(`poison = ${c.poison}`); + if (c.aoe === true) fields.push('aoe = true'); if (c.image != null) fields.push(`image = ${luaStr(c.image)}`); return `\t${id} = { ${fields.join(', ')} },`; }); @@ -1952,10 +1986,11 @@ function upsertUi() { text({ value: '2차 전직 — 직업을 선택하세요', fontSize: 36, bold: true, color: GOLD, alignment: 4 }), ], })); + // 범용 슬롯 3개 — ShowJobSelect(Lua)가 클래스별 JOBS로 텍스트를 채움 (P10 동적화) const jobs = [ - ['fighter', '파이터', '공격 특화\n콤보 어택 · 버서크\n라이징 어택', '대표 카드: 콤보 어택', -440, { r: 0.82, g: 0.4, b: 0.34, a: 1 }], - ['page', '페이지', '속성 차지 특화\n썬더/블리자드 차지\n파워 가드', '대표 카드: 썬더 차지', 0, { r: 0.4, g: 0.55, b: 0.85, a: 1 }], - ['spearman', '스피어맨', '방어·관통 특화\n피어스 · 아이언 월\n하이퍼 바디', '대표 카드: 피어스', 440, { r: 0.42, g: 0.72, b: 0.46, a: 1 }], + ['slot1', '', '', '', -440, { r: 0.82, g: 0.4, b: 0.34, a: 1 }], + ['slot2', '', '', '', 0, { r: 0.4, g: 0.55, b: 0.85, a: 1 }], + ['slot3', '', '', '', 440, { r: 0.42, g: 0.72, b: 0.46, a: 1 }], ]; jobs.forEach(([jobId, name, desc, starter, x, color], ji) => { const base = `/ui/DefaultGroup/JobSelectHud/Job_${jobId}`; @@ -2143,7 +2178,7 @@ function upsertUi() { const classCards = [ { key: 'Warrior', label: '\uC804\uC0AC', desc: '\uAC15\uD55C \uACF5\uACA9\uACFC \uBC29\uC5B4', x: -360, enabled: true, tint: { r: 0.74, g: 0.32, b: 0.28, a: 1 } }, { key: 'Thief', label: '\uB3C4\uC801', desc: '\uCD94\uD6C4 \uC5F4\uB9BC', x: 0, enabled: false, tint: { r: 0.18, g: 0.19, b: 0.21, a: 1 } }, - { key: 'Mage', label: '\uB9C8\uBC95\uC0AC', desc: '\uCD94\uD6C4 \uC5F4\uB9BC', x: 360, enabled: false, tint: { r: 0.18, g: 0.19, b: 0.21, a: 1 } }, + { key: 'Mage', label: '\uB9C8\uBC95\uC0AC', desc: '\uB9C8\uBC95 \uC6D0\uAC70\uB9AC \uB51C\uB7EC', x: 360, enabled: true, tint: { r: 0.3, g: 0.4, b: 0.75, a: 1 } }, ]; for (let i = 0; i < classCards.length; i++) { const cls = classCards[i]; @@ -2324,6 +2359,9 @@ function writeCodeblocks() { prop('any', 'EndTurnHandler'), prop('any', 'NewGameHandler'), prop('any', 'WarriorSelectHandler'), + prop('any', 'MageSelectHandler'), + prop('any', 'JobOpts'), + prop('any', 'Jobs'), prop('any', 'StartGameHandler'), prop('string', 'SelectedClass', '""'), prop('any', 'DrawPileHandler'), @@ -2434,6 +2472,14 @@ if warrior ~= nil and warrior.ButtonComponent ~= nil then end self.WarriorSelectHandler = warrior:ConnectEvent(ButtonClickEvent, function() self:SelectClass("warrior") end) end +local mage = _EntityService:GetEntityByPath("/ui/DefaultGroup/CharacterSelectHud/MageButton") +if mage ~= nil and mage.ButtonComponent ~= nil then + if self.MageSelectHandler ~= nil then + mage:DisconnectEvent(ButtonClickEvent, self.MageSelectHandler) + self.MageSelectHandler = nil + end + self.MageSelectHandler = mage:ConnectEvent(ButtonClickEvent, function() self:SelectClass("magician") end) +end local start = _EntityService:GetEntityByPath("/ui/DefaultGroup/CharacterSelectHud/StartButton") if start ~= nil and start.ButtonComponent ~= nil then if self.StartGameHandler ~= nil then @@ -2457,13 +2503,23 @@ if warrior ~= nil and warrior.SpriteGUIRendererComponent ~= nil then warrior.SpriteGUIRendererComponent.Color = Color(0.16, 0.2, 0.26, 1) end end +local mage = _EntityService:GetEntityByPath("/ui/DefaultGroup/CharacterSelectHud/MageButton") +if mage ~= nil and mage.SpriteGUIRendererComponent ~= nil then + if self.SelectedClass == "magician" then + mage.SpriteGUIRendererComponent.Color = Color(0.28, 0.36, 0.46, 1) + else + mage.SpriteGUIRendererComponent.Color = Color(0.16, 0.2, 0.26, 1) + end +end if self.SelectedClass == "warrior" then self:SetText("/ui/DefaultGroup/CharacterSelectHud/Status", "전사 선택됨") +elseif self.SelectedClass == "magician" then + self:SetText("/ui/DefaultGroup/CharacterSelectHud/Status", "마법사 선택됨") else - self:SetText("/ui/DefaultGroup/CharacterSelectHud/Status", "전사를 선택하고 시작하세요") + self:SetText("/ui/DefaultGroup/CharacterSelectHud/Status", "직업을 선택하고 시작하세요") end`), - method('StartNewGame', `if self.SelectedClass ~= "warrior" then - self:SetText("/ui/DefaultGroup/CharacterSelectHud/Status", "현재는 전사만 선택할 수 있습니다") + method('StartNewGame', `if self.SelectedClass ~= "warrior" and self.SelectedClass ~= "magician" then + self:SetText("/ui/DefaultGroup/CharacterSelectHud/Status", "직업을 먼저 선택하세요") return end self:StartRun()`), @@ -2474,12 +2530,17 @@ end`, [ { Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'path' }, { Type: 'boolean', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'enabled' }, ]), - method('StartRun', `self.PlayerMaxHp = 80 + method('StartRun', `if self.SelectedClass == "magician" then + self.PlayerMaxHp = ${CLASSES.magician.maxHp} + self.RunDeck = { ${CARDS.starterDecks.magician.map(luaStr).join(', ')} } +else + self.PlayerMaxHp = ${CLASSES.warrior.maxHp} + self.RunDeck = { ${CARDS.starterDecks.warrior.map(luaStr).join(', ')} } +end self.PlayerHp = self.PlayerMaxHp self.Gold = 0 self.Floor = 1 self.RunLength = ${ACT_COUNT} -self.RunDeck = { ${CARDS.starterDeck.map(luaStr).join(', ')} } self.RunActive = true self.RunRelics = {} self.RunPotions = {} @@ -2491,6 +2552,7 @@ ${luaEnemiesTable(ENEMIES.enemies)} self.CurrentNodeId = "" self.CurrentEnemyId = "" self.PlayerJob = "" +${luaJobsTable(JOBS)} self:GenerateMap() self:BindButtons() self:AddRelic("${RELICS.startingRelic}") @@ -2580,7 +2642,7 @@ for i = 1, n do end local maxHp = math.floor(e.maxHp * mult) self.Monsters[i] = { entity = item.entity, enemyId = item.enemyId, name = e.name, - hp = maxHp, maxHp = maxHp, block = 0, str = 0, weak = 0, vuln = 0, + hp = maxHp, maxHp = maxHp, block = 0, str = 0, weak = 0, vuln = 0, poison = 0, intents = intents, intentIdx = 1, alive = true, slot = i } self:ReviveMonsterEntity(item.entity) self:PositionMonsterSlot(i) @@ -2763,12 +2825,15 @@ local jcJob = _EntityService:GetEntityByPath("/ui/DefaultGroup/JobChoiceHud/JobB if jcJob ~= nil and jcJob.ButtonComponent ~= nil then jcJob:ConnectEvent(ButtonClickEvent, function() self:PickJobReward("job") end) end -local jobIds = { "fighter", "page", "spearman" } -for i = 1, #jobIds do - local jid = jobIds[i] - local jb = _EntityService:GetEntityByPath("/ui/DefaultGroup/JobSelectHud/Job_" .. jid) +for i = 1, 3 do + local slotIdx = i + local jb = _EntityService:GetEntityByPath("/ui/DefaultGroup/JobSelectHud/Job_slot" .. tostring(i)) if jb ~= nil and jb.ButtonComponent ~= nil then - jb:ConnectEvent(ButtonClickEvent, function() self:SetJob(jid) end) + jb:ConnectEvent(ButtonClickEvent, function() + if self.JobOpts ~= nil and self.JobOpts[slotIdx] ~= nil then + self:SetJob(self.JobOpts[slotIdx].id) + end + end) end end`), method('StartPlayerTurn', `self.Turn = self.Turn + 1 @@ -3062,7 +3127,11 @@ if c.kind == "Attack" then for h = 1, hitN do total = total + self:CalcPlayerAttack(c.damage) end - self:PlayAttackFx(self.TargetIndex, c.image, total, c.pierce == true) + if c.aoe == true then + self:PlayAoeFx(c.image, total) + else + self:PlayAttackFx(self.TargetIndex, c.image, total, c.pierce == true) + end end if c.block ~= nil then self.PlayerBlock = self.PlayerBlock + c.block @@ -3083,10 +3152,14 @@ end if c.selfVuln ~= nil then self.PlayerVuln = self.PlayerVuln + c.selfVuln end -if c.weak ~= nil or c.vuln ~= nil then +if c.heal ~= nil then + self.PlayerHp = math.min(self.PlayerHp + c.heal, self.PlayerMaxHp) +end +if c.weak ~= nil or c.vuln ~= nil or c.poison ~= nil then local tm = self.Monsters[self.TargetIndex] if tm ~= nil and tm.alive == true then if c.weak ~= nil then tm.weak = tm.weak + c.weak end + if c.poison ~= nil then tm.poison = (tm.poison or 0) + c.poison end if c.vuln ~= nil then tm.vuln = tm.vuln + c.vuln if self:HasRelic("championBelt") then @@ -3099,6 +3172,9 @@ table.remove(self.Hand, slot) if c.kind ~= "Power" then table.insert(self.DiscardPile, cardId) end +if c.draw ~= nil then + self:DrawCards(c.draw) +end self:RenderHand(false) self:RenderPiles() self:RenderCombat() @@ -3241,6 +3317,46 @@ end, 0.35)`, [ { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'damage' }, { Type: 'boolean', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'pierce' }, ]), + method('PlayAoeFx', `self.FxBusy = true +local fx = _EntityService:GetEntityByPath("/ui/DefaultGroup/CombatHud/SkillFx") +if fx ~= nil then + if fx.SpriteGUIRendererComponent ~= nil and image ~= nil and image ~= "" then + fx.SpriteGUIRendererComponent.ImageRUID = image + end + if fx.UITransformComponent ~= nil then + fx.UITransformComponent.anchoredPosition = Vector2(300, 60) + end + fx.Enable = true +end +_TimerService:SetTimerOnce(function() + if fx ~= nil then fx.Enable = false end + self.FxBusy = false + for i = 1, #self.Monsters do + local m = self.Monsters[i] + if m ~= nil and m.alive == true then + local dmg = damage + if m.vuln > 0 then + dmg = math.floor(dmg * 1.5) + end + if m.block > 0 then + local absorbed = math.min(m.block, dmg) + m.block = m.block - absorbed + dmg = dmg - absorbed + end + m.hp = m.hp - dmg + self:ShowDmgPop(i, dmg) + if m.hp <= 0 then + m.hp = 0 + self:KillMonster(m.slot) + end + end + end + self:RenderCombat() + self:CheckCombatEnd() +end, 0.35)`, [ + { Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'image' }, + { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'damage' }, + ]), method('KillMonster', `local m = self.Monsters[slot] if m == nil then return @@ -3301,6 +3417,19 @@ local m = self.Monsters[idx] local base = "/ui/DefaultGroup/CombatHud/MonsterSlot" .. tostring(idx) self:SetEntityEnabled(base .. "/ActFrame", true) _TimerService:SetTimerOnce(function() + if m.poison ~= nil and m.poison > 0 then + m.hp = m.hp - m.poison + self:ShowDmgPop(idx, m.poison) + m.poison = m.poison - 1 + if m.hp <= 0 then + m.hp = 0 + self:KillMonster(m.slot) + self:RenderCombat() + self:SetEntityEnabled(base .. "/ActFrame", false) + _TimerService:SetTimerOnce(function() self:EnemyActStep(idx + 1) end, 0.15) + return + end + end m.block = 0 local intent = m.intents[m.intentIdx] if intent ~= nil then @@ -3411,27 +3540,51 @@ if kind == "relic" then end self:ContinueAfterBoss() else - self:SetEntityEnabled("/ui/DefaultGroup/JobSelectHud", true) + self:ShowJobSelect() end`, [{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'kind' }]), - method('JobLabel', `if self.PlayerJob == "fighter" then - return "파이터" -elseif self.PlayerJob == "page" then - return "페이지" -elseif self.PlayerJob == "spearman" then - return "스피어맨" + method('ShowJobSelect', `local opts = self.Jobs[self.SelectedClass] +if opts == nil then + opts = self.Jobs["warrior"] +end +self.JobOpts = opts +for i = 1, 3 do + local base = "/ui/DefaultGroup/JobSelectHud/Job_slot" .. tostring(i) + local o = opts[i] + if o ~= nil then + self:SetEntityEnabled(base, true) + self:SetText(base .. "/Name", o.name) + self:SetText(base .. "/Desc", o.desc) + local sc = self.Cards[o.starter] + if sc ~= nil then + self:SetText(base .. "/Starter", "대표 카드: " .. sc.name) + end + else + self:SetEntityEnabled(base, false) + end +end +self:SetEntityEnabled("/ui/DefaultGroup/JobSelectHud", true)`), + method('JobLabel', `if self.PlayerJob ~= "" and self.Jobs ~= nil then + for cls, list in pairs(self.Jobs) do + for i = 1, #list do + if list[i].id == self.PlayerJob then + return list[i].name + end + end + end end if self.SelectedClass == "warrior" then return "전사" +elseif self.SelectedClass == "magician" then + return "마법사" end return "플레이어"`, [], 0, 'string'), method('SetJob', `self.PlayerJob = jobId local starter = "" -if jobId == "fighter" then - starter = "ComboAttack" -elseif jobId == "page" then - starter = "ThunderCharge" -elseif jobId == "spearman" then - starter = "Pierce" +local opts = self.Jobs[self.SelectedClass] or {} +for i = 1, #opts do + if opts[i].id == jobId then + starter = opts[i].starter + end end if starter ~= "" then table.insert(self.RunDeck, starter) @@ -3468,10 +3621,12 @@ _TimerService:SetTimerOnce(function() self:ShowMainMenu() end, 4)`, [{ Type: 'st if str ~= nil and str > 0 then table.insert(parts, "힘+" .. tostring(str)) end if weak ~= nil and weak > 0 then table.insert(parts, "약화" .. tostring(weak)) end if vuln ~= nil and vuln > 0 then table.insert(parts, "취약" .. tostring(vuln)) end +if poison ~= nil and poison > 0 then table.insert(parts, "독" .. tostring(poison)) end return table.concat(parts, " ")`, [ { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'str' }, { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'weak' }, { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'vuln' }, + { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'poison' }, ], 0, 'string'), method('RenderCombat', `for i = 1, ${MAX_MONSTERS} do local base = "/ui/DefaultGroup/CombatHud/MonsterSlot" .. tostring(i) @@ -3509,7 +3664,7 @@ return table.concat(parts, " ")`, [ self:SetHpBar(base .. "/HpBarFill", m.hp, m.maxHp, ${HP_BAR_W}) self:SetEntityEnabled(base .. "/BlockBadge", m.block > 0) self:SetText(base .. "/BlockBadge/Value", string.format("%d", m.block)) - self:SetText(base .. "/Buffs", self:BuffsLabel(m.str, m.weak, m.vuln)) + self:SetText(base .. "/Buffs", self:BuffsLabel(m.str, m.weak, m.vuln, m.poison or 0)) else self:SetEntityEnabled(base, false) end @@ -3518,7 +3673,7 @@ self:SetText("/ui/DefaultGroup/CombatHud/PlayerPanel/HpText", string.format("%d" self:SetHpBar("/ui/DefaultGroup/CombatHud/PlayerPanel/HpBarFill", self.PlayerHp, self.PlayerMaxHp, 220) self:SetEntityEnabled("/ui/DefaultGroup/CombatHud/PlayerPanel/BlockBadge", self.PlayerBlock > 0) self:SetText("/ui/DefaultGroup/CombatHud/PlayerPanel/BlockBadge/Value", string.format("%d", self.PlayerBlock)) -local pb = self:BuffsLabel(self.PlayerStr, self.PlayerWeak, self.PlayerVuln) +local pb = self:BuffsLabel(self.PlayerStr, self.PlayerWeak, self.PlayerVuln, 0) if self.PlayerPowers ~= nil and #self.PlayerPowers > 0 then local names = {} for i = 1, #self.PlayerPowers do diff --git a/ui/DefaultGroup.ui b/ui/DefaultGroup.ui index 83e1e01..d93ab2a 100644 --- a/ui/DefaultGroup.ui +++ b/ui/DefaultGroup.ui @@ -75219,11 +75219,11 @@ }, { "id": "0e40000c-0000-4000-8000-00000e40000c", - "path": "/ui/DefaultGroup/JobSelectHud/Job_fighter", + "path": "/ui/DefaultGroup/JobSelectHud/Job_slot1", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent", "jsonString": { - "name": "Job_fighter", - "path": "/ui/DefaultGroup/JobSelectHud/Job_fighter", + "name": "Job_slot1", + "path": "/ui/DefaultGroup/JobSelectHud/Job_slot1", "nameEditable": true, "enable": true, "visible": true, @@ -75407,11 +75407,11 @@ }, { "id": "0e40000d-0000-4000-8000-00000e40000d", - "path": "/ui/DefaultGroup/JobSelectHud/Job_fighter/Name", + "path": "/ui/DefaultGroup/JobSelectHud/Job_slot1/Name", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "jsonString": { "name": "Name", - "path": "/ui/DefaultGroup/JobSelectHud/Job_fighter/Name", + "path": "/ui/DefaultGroup/JobSelectHud/Job_slot1/Name", "nameEditable": true, "enable": true, "visible": true, @@ -75585,7 +75585,7 @@ "bottom": 0 }, "SizeFit": false, - "Text": "파이터", + "Text": "", "UseOutLine": true, "Enable": true } @@ -75595,11 +75595,11 @@ }, { "id": "0e40000e-0000-4000-8000-00000e40000e", - "path": "/ui/DefaultGroup/JobSelectHud/Job_fighter/Desc", + "path": "/ui/DefaultGroup/JobSelectHud/Job_slot1/Desc", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "jsonString": { "name": "Desc", - "path": "/ui/DefaultGroup/JobSelectHud/Job_fighter/Desc", + "path": "/ui/DefaultGroup/JobSelectHud/Job_slot1/Desc", "nameEditable": true, "enable": true, "visible": true, @@ -75773,7 +75773,7 @@ "bottom": 0 }, "SizeFit": false, - "Text": "공격 특화\n콤보 어택 · 버서크\n라이징 어택", + "Text": "", "UseOutLine": true, "Enable": true } @@ -75783,11 +75783,11 @@ }, { "id": "0e40000f-0000-4000-8000-00000e40000f", - "path": "/ui/DefaultGroup/JobSelectHud/Job_fighter/Starter", + "path": "/ui/DefaultGroup/JobSelectHud/Job_slot1/Starter", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "jsonString": { "name": "Starter", - "path": "/ui/DefaultGroup/JobSelectHud/Job_fighter/Starter", + "path": "/ui/DefaultGroup/JobSelectHud/Job_slot1/Starter", "nameEditable": true, "enable": true, "visible": true, @@ -75961,7 +75961,7 @@ "bottom": 0 }, "SizeFit": false, - "Text": "대표 카드: 콤보 어택", + "Text": "", "UseOutLine": true, "Enable": true } @@ -75971,11 +75971,11 @@ }, { "id": "0e400010-0000-4000-8000-00000e400010", - "path": "/ui/DefaultGroup/JobSelectHud/Job_page", + "path": "/ui/DefaultGroup/JobSelectHud/Job_slot2", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent", "jsonString": { - "name": "Job_page", - "path": "/ui/DefaultGroup/JobSelectHud/Job_page", + "name": "Job_slot2", + "path": "/ui/DefaultGroup/JobSelectHud/Job_slot2", "nameEditable": true, "enable": true, "visible": true, @@ -76159,11 +76159,11 @@ }, { "id": "0e400011-0000-4000-8000-00000e400011", - "path": "/ui/DefaultGroup/JobSelectHud/Job_page/Name", + "path": "/ui/DefaultGroup/JobSelectHud/Job_slot2/Name", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "jsonString": { "name": "Name", - "path": "/ui/DefaultGroup/JobSelectHud/Job_page/Name", + "path": "/ui/DefaultGroup/JobSelectHud/Job_slot2/Name", "nameEditable": true, "enable": true, "visible": true, @@ -76337,7 +76337,7 @@ "bottom": 0 }, "SizeFit": false, - "Text": "페이지", + "Text": "", "UseOutLine": true, "Enable": true } @@ -76347,11 +76347,11 @@ }, { "id": "0e400012-0000-4000-8000-00000e400012", - "path": "/ui/DefaultGroup/JobSelectHud/Job_page/Desc", + "path": "/ui/DefaultGroup/JobSelectHud/Job_slot2/Desc", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "jsonString": { "name": "Desc", - "path": "/ui/DefaultGroup/JobSelectHud/Job_page/Desc", + "path": "/ui/DefaultGroup/JobSelectHud/Job_slot2/Desc", "nameEditable": true, "enable": true, "visible": true, @@ -76525,7 +76525,7 @@ "bottom": 0 }, "SizeFit": false, - "Text": "속성 차지 특화\n썬더/블리자드 차지\n파워 가드", + "Text": "", "UseOutLine": true, "Enable": true } @@ -76535,11 +76535,11 @@ }, { "id": "0e400013-0000-4000-8000-00000e400013", - "path": "/ui/DefaultGroup/JobSelectHud/Job_page/Starter", + "path": "/ui/DefaultGroup/JobSelectHud/Job_slot2/Starter", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "jsonString": { "name": "Starter", - "path": "/ui/DefaultGroup/JobSelectHud/Job_page/Starter", + "path": "/ui/DefaultGroup/JobSelectHud/Job_slot2/Starter", "nameEditable": true, "enable": true, "visible": true, @@ -76713,7 +76713,7 @@ "bottom": 0 }, "SizeFit": false, - "Text": "대표 카드: 썬더 차지", + "Text": "", "UseOutLine": true, "Enable": true } @@ -76723,11 +76723,11 @@ }, { "id": "0e400014-0000-4000-8000-00000e400014", - "path": "/ui/DefaultGroup/JobSelectHud/Job_spearman", + "path": "/ui/DefaultGroup/JobSelectHud/Job_slot3", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent", "jsonString": { - "name": "Job_spearman", - "path": "/ui/DefaultGroup/JobSelectHud/Job_spearman", + "name": "Job_slot3", + "path": "/ui/DefaultGroup/JobSelectHud/Job_slot3", "nameEditable": true, "enable": true, "visible": true, @@ -76911,11 +76911,11 @@ }, { "id": "0e400015-0000-4000-8000-00000e400015", - "path": "/ui/DefaultGroup/JobSelectHud/Job_spearman/Name", + "path": "/ui/DefaultGroup/JobSelectHud/Job_slot3/Name", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "jsonString": { "name": "Name", - "path": "/ui/DefaultGroup/JobSelectHud/Job_spearman/Name", + "path": "/ui/DefaultGroup/JobSelectHud/Job_slot3/Name", "nameEditable": true, "enable": true, "visible": true, @@ -77089,7 +77089,7 @@ "bottom": 0 }, "SizeFit": false, - "Text": "스피어맨", + "Text": "", "UseOutLine": true, "Enable": true } @@ -77099,11 +77099,11 @@ }, { "id": "0e400016-0000-4000-8000-00000e400016", - "path": "/ui/DefaultGroup/JobSelectHud/Job_spearman/Desc", + "path": "/ui/DefaultGroup/JobSelectHud/Job_slot3/Desc", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "jsonString": { "name": "Desc", - "path": "/ui/DefaultGroup/JobSelectHud/Job_spearman/Desc", + "path": "/ui/DefaultGroup/JobSelectHud/Job_slot3/Desc", "nameEditable": true, "enable": true, "visible": true, @@ -77277,7 +77277,7 @@ "bottom": 0 }, "SizeFit": false, - "Text": "방어·관통 특화\n피어스 · 아이언 월\n하이퍼 바디", + "Text": "", "UseOutLine": true, "Enable": true } @@ -77287,11 +77287,11 @@ }, { "id": "0e400017-0000-4000-8000-00000e400017", - "path": "/ui/DefaultGroup/JobSelectHud/Job_spearman/Starter", + "path": "/ui/DefaultGroup/JobSelectHud/Job_slot3/Starter", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "jsonString": { "name": "Starter", - "path": "/ui/DefaultGroup/JobSelectHud/Job_spearman/Starter", + "path": "/ui/DefaultGroup/JobSelectHud/Job_slot3/Starter", "nameEditable": true, "enable": true, "visible": true, @@ -77465,7 +77465,7 @@ "bottom": 0 }, "SizeFit": false, - "Text": "대표 카드: 피어스", + "Text": "", "UseOutLine": true, "Enable": true } @@ -289904,9 +289904,9 @@ "PreserveSprite": 0, "StartFrameIndex": 0, "Color": { - "r": 0.11, - "g": 0.12, - "b": 0.14, + "r": 0.16, + "g": 0.2, + "b": 0.26, "a": 1 }, "DropShadow": false, @@ -289936,7 +289936,7 @@ "a": 1 }, "OutlineWidth": 3, - "RaycastTarget": false, + "RaycastTarget": true, "Type": 1, "Enable": true }, @@ -289985,7 +289985,7 @@ "KeyCode": 0, "OverrideSorting": false, "Transition": 1, - "Enable": false + "Enable": true } ], "@version": 1 @@ -290143,9 +290143,9 @@ "DropShadowDistance": 32, "Font": 0, "FontColor": { - "r": 0.55, - "g": 0.58, - "b": 0.62, + "r": 0.94, + "g": 0.74, + "b": 0.26, "a": 1 }, "FontSize": 34, @@ -290280,9 +290280,9 @@ "PreserveSprite": 0, "StartFrameIndex": 0, "Color": { - "r": 0.18, - "g": 0.19, - "b": 0.21, + "r": 0.3, + "g": 0.4, + "b": 0.75, "a": 1 }, "DropShadow": false, @@ -290472,9 +290472,9 @@ "DropShadowDistance": 32, "Font": 0, "FontColor": { - "r": 0.52, - "g": 0.55, - "b": 0.59, + "r": 0.86, + "g": 0.9, + "b": 0.94, "a": 1 }, "FontSize": 20, @@ -290500,7 +290500,7 @@ "bottom": 0 }, "SizeFit": false, - "Text": "추후 열림", + "Text": "마법 원거리 딜러", "UseOutLine": true, "Enable": true } @@ -290508,288 +290508,6 @@ "@version": 1 } }, - { - "id": "0e000098-0000-4000-8000-00000e000098", - "path": "/ui/DefaultGroup/CharacterSelectHud/MageButton/LockBody", - "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent", - "jsonString": { - "name": "LockBody", - "path": "/ui/DefaultGroup/CharacterSelectHud/MageButton/LockBody", - "nameEditable": true, - "enable": true, - "visible": true, - "localize": true, - "displayOrder": 3, - "pathConstraints": "/////", - "revision": 1, - "origin": { - "type": "Model", - "entry_id": "UISprite", - "sub_entity_id": null, - "root_entity_id": null, - "replaced_model_id": null - }, - "modelId": "uisprite", - "@components": [ - { - "@type": "MOD.Core.UITransformComponent", - "ActivePlatform": 255, - "AlignmentOption": 0, - "AnchorsMax": { - "x": 0.5, - "y": 0.5 - }, - "AnchorsMin": { - "x": 0.5, - "y": 0.5 - }, - "MobileOnly": false, - "OffsetMax": { - "x": 38, - "y": 33 - }, - "OffsetMin": { - "x": -38, - "y": -25 - }, - "Pivot": { - "x": 0.5, - "y": 0.5 - }, - "RectSize": { - "x": 76, - "y": 58 - }, - "UIMode": 1, - "UIScale": { - "x": 1, - "y": 1, - "z": 1 - }, - "UIVersion": 2, - "anchoredPosition": { - "x": 0, - "y": 4 - }, - "Position": { - "x": 0, - "y": 4, - "z": 0 - }, - "QuaternionRotation": { - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "Scale": { - "x": 1, - "y": 1, - "z": 1 - }, - "Enable": true - }, - { - "@type": "MOD.Core.SpriteGUIRendererComponent", - "AnimClipPlayType": 0, - "EndFrameIndex": 2147483647, - "ImageRUID": { - "DataId": "" - }, - "LocalPosition": { - "x": 0, - "y": 0 - }, - "LocalScale": { - "x": 1, - "y": 1 - }, - "OverrideSorting": false, - "PlayRate": 1, - "PreserveSprite": 0, - "StartFrameIndex": 0, - "Color": { - "r": 0.78, - "g": 0.69, - "b": 0.42, - "a": 1 - }, - "DropShadow": false, - "DropShadowAngle": 30, - "DropShadowColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 0.72 - }, - "DropShadowDistance": 32, - "FillAmount": 1, - "FillCenter": true, - "FillClockWise": true, - "FillMethod": 0, - "FillOrigin": 0, - "FlipX": false, - "FlipY": false, - "FrameColumn": 1, - "FrameRate": 0, - "FrameRow": 1, - "Outline": false, - "OutlineColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 1 - }, - "OutlineWidth": 3, - "RaycastTarget": false, - "Type": 1, - "Enable": true - } - ], - "@version": 1 - } - }, - { - "id": "0e0000a2-0000-4000-8000-00000e0000a2", - "path": "/ui/DefaultGroup/CharacterSelectHud/MageButton/LockShackle", - "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent", - "jsonString": { - "name": "LockShackle", - "path": "/ui/DefaultGroup/CharacterSelectHud/MageButton/LockShackle", - "nameEditable": true, - "enable": true, - "visible": true, - "localize": true, - "displayOrder": 4, - "pathConstraints": "/////", - "revision": 1, - "origin": { - "type": "Model", - "entry_id": "UISprite", - "sub_entity_id": null, - "root_entity_id": null, - "replaced_model_id": null - }, - "modelId": "uisprite", - "@components": [ - { - "@type": "MOD.Core.UITransformComponent", - "ActivePlatform": 255, - "AlignmentOption": 0, - "AnchorsMax": { - "x": 0.5, - "y": 0.5 - }, - "AnchorsMin": { - "x": 0.5, - "y": 0.5 - }, - "MobileOnly": false, - "OffsetMax": { - "x": 27, - "y": 69 - }, - "OffsetMin": { - "x": -27, - "y": 27 - }, - "Pivot": { - "x": 0.5, - "y": 0.5 - }, - "RectSize": { - "x": 54, - "y": 42 - }, - "UIMode": 1, - "UIScale": { - "x": 1, - "y": 1, - "z": 1 - }, - "UIVersion": 2, - "anchoredPosition": { - "x": 0, - "y": 48 - }, - "Position": { - "x": 0, - "y": 48, - "z": 0 - }, - "QuaternionRotation": { - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "Scale": { - "x": 1, - "y": 1, - "z": 1 - }, - "Enable": true - }, - { - "@type": "MOD.Core.SpriteGUIRendererComponent", - "AnimClipPlayType": 0, - "EndFrameIndex": 2147483647, - "ImageRUID": { - "DataId": "" - }, - "LocalPosition": { - "x": 0, - "y": 0 - }, - "LocalScale": { - "x": 1, - "y": 1 - }, - "OverrideSorting": false, - "PlayRate": 1, - "PreserveSprite": 0, - "StartFrameIndex": 0, - "Color": { - "r": 0.78, - "g": 0.69, - "b": 0.42, - "a": 1 - }, - "DropShadow": false, - "DropShadowAngle": 30, - "DropShadowColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 0.72 - }, - "DropShadowDistance": 32, - "FillAmount": 1, - "FillCenter": true, - "FillClockWise": true, - "FillMethod": 0, - "FillOrigin": 0, - "FlipX": false, - "FlipY": false, - "FrameColumn": 1, - "FrameRate": 0, - "FrameRow": 1, - "Outline": false, - "OutlineColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 1 - }, - "OutlineWidth": 3, - "RaycastTarget": false, - "Type": 1, - "Enable": true - } - ], - "@version": 1 - } - }, { "id": "0e0000b4-0000-4000-8000-00000e0000b4", "path": "/ui/DefaultGroup/CharacterSelectHud/StartButton",