diff --git a/RootDesk/MyDesk/SlayDeckController.codeblock b/RootDesk/MyDesk/SlayDeckController.codeblock index 90e12bc..016d358 100644 --- a/RootDesk/MyDesk/SlayDeckController.codeblock +++ b/RootDesk/MyDesk/SlayDeckController.codeblock @@ -935,7 +935,7 @@ "Name": null }, "Arguments": [], - "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.PlayerMaxHp = self.PlayerMaxHp - self:AscStartHpPenalty()\nself.PlayerHp = self.PlayerMaxHp\nself.Gold = 0\nself.Floor = 1\nself.RunLength = 5\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.CardFrames = {\n\twarrior = { normal = \"4bb57ef88ef449fdaf958f6cf37fe44b\", unique = \"4f71c124c8bc4e13b5e9fad392995f68\", legend = \"6d741a60c60743cb98ee740a1e2dbfed\" },\n\tmagician = { normal = \"d788d09f6f50467ebc67f01dec45f9e2\", unique = \"f5def2e8022b4e59a17d3c16414034fe\", legend = \"cff71f2e472041ce80c6fbd296f42e2d\" },\n\tbandit = { normal = \"9487b06867bc46269ed1d855420f457f\", unique = \"b3081fb2fb1445fa90b12b01481a78ef\", legend = \"c357d2daf31a489d95b8fa47e50dd879\" },\n}\nself.ClassToFrame = {\n\twarrior = \"warrior\",\n\tfighter = \"warrior\",\n\tpage = \"warrior\",\n\tspearman = \"warrior\",\n\tmagician = \"magician\",\n\tfirepoison = \"magician\",\n\ticelightning = \"magician\",\n\tcleric = \"magician\",\n}\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.PlayerMaxHp = self.PlayerMaxHp - self:AscStartHpPenalty()\nself.PlayerHp = self.PlayerMaxHp\nself.Gold = 0\nself.Floor = 1\nself.RunLength = 5\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 }, { kind = \"AddCard\", value = 0, card = \"Wound\", count = 1 } } },\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\tred_snail = { name = \"빨간 달팽이\", maxHp = 14, intents = { { kind = \"Attack\", value = 5 }, { kind = \"Defend\", value = 6 }, { kind = \"Attack\", value = 7 } } },\n\tstump = { name = \"나무토막\", maxHp = 19, intents = { { kind = \"Defend\", value = 5 }, { kind = \"Attack\", value = 8 }, { kind = \"Attack\", value = 6 } } },\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 }, { kind = \"AddCard\", value = 0, card = \"Burn\", count = 1 } } },\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.CardFrames = {\n\twarrior = { normal = \"4bb57ef88ef449fdaf958f6cf37fe44b\", unique = \"4f71c124c8bc4e13b5e9fad392995f68\", legend = \"6d741a60c60743cb98ee740a1e2dbfed\" },\n\tmagician = { normal = \"d788d09f6f50467ebc67f01dec45f9e2\", unique = \"f5def2e8022b4e59a17d3c16414034fe\", legend = \"cff71f2e472041ce80c6fbd296f42e2d\" },\n\tbandit = { normal = \"9487b06867bc46269ed1d855420f457f\", unique = \"b3081fb2fb1445fa90b12b01481a78ef\", legend = \"c357d2daf31a489d95b8fa47e50dd879\" },\n}\nself.ClassToFrame = {\n\twarrior = \"warrior\",\n\tfighter = \"warrior\",\n\tpage = \"warrior\",\n\tspearman = \"warrior\",\n\tmagician = \"magician\",\n\tfirepoison = \"magician\",\n\ticelightning = \"magician\",\n\tcleric = \"magician\",\n\tcurse = \"bandit\",\n}\nself:GenerateMap()\nself:BindButtons()\nself:AddRelic(\"ironHeart\")\nself:RenderPotions()\nself:ShowMap()", "Scope": 2, "ExecSpace": 6, "Attributes": [], @@ -950,7 +950,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\", rarity = \"normal\", image = \"a71b116807904ef2b38e1dc013e2f9a2\" },\n\tDefend = { name = \"아이언 바디\", cost = 1, desc = \"방어도 5\", kind = \"Skill\", block = 5, class = \"warrior\", rarity = \"normal\", image = \"1ae9b6741c5947a8b528a0f515b50e3e\" },\n\tBash = { name = \"슬래시 블러스트\", cost = 2, desc = \"피해 10\", kind = \"Attack\", damage = 10, class = \"warrior\", rarity = \"normal\", image = \"d5bc2953fcab4cfe9062af81c35aff86\" },\n\tWarLeap = { name = \"워 리프\", cost = 1, desc = \"피해 4, 방어도 3\", kind = \"Attack\", damage = 4, block = 3, class = \"warrior\", rarity = \"normal\", image = \"992dabf6aff2400e92b2f4f705d8ebe7\" },\n\tBrandish = { name = \"브랜디시\", cost = 2, desc = \"피해 13\", kind = \"Attack\", damage = 13, class = \"warrior\", rarity = \"unique\", image = \"21af4bccc5054a5dbc8245dfa7f08681\" },\n\tChargedBlow = { name = \"차지 블로우\", cost = 2, desc = \"피해 8, 취약 2\", kind = \"Attack\", damage = 8, vuln = 2, class = \"warrior\", rarity = \"unique\", image = \"fe83c7635b0e49ed83d75a2833adb53e\" },\n\tThreaten = { name = \"위협\", cost = 0, desc = \"약화 2 부여\", kind = \"Skill\", weak = 2, class = \"warrior\", rarity = \"normal\", image = \"64daadf1a98e490d9c14ef52ec776e63\" },\n\tEnrage = { name = \"인레이지\", cost = 1, desc = \"힘 +2\", kind = \"Skill\", strength = 2, class = \"warrior\", rarity = \"unique\", image = \"09370fc7551e47238fd103a80fba558e\" },\n\tRage = { name = \"분노\", cost = 1, desc = \"매 턴 시작 시 힘 +1\", kind = \"Power\", powerEffect = \"strengthPerTurn\", value = 1, class = \"warrior\", rarity = \"legend\", image = \"379d86e3de064959aa4612f71e84ccfb\" },\n\tComboAttack = { name = \"콤보 어택\", cost = 1, desc = \"피해 5 × 2회\", kind = \"Attack\", damage = 5, class = \"fighter\", rarity = \"unique\", hits = 2, image = \"1bc3e52b330648faae9eafd5a205e37b\" },\n\tBerserk = { name = \"버서크\", cost = 2, desc = \"매턴 에너지 +1, 취약 1 자가\", kind = \"Power\", powerEffect = \"energyPerTurn\", value = 1, class = \"fighter\", rarity = \"legend\", selfVuln = 1, image = \"cef30ea340c74e768bcee4e2cbe0577a\" },\n\tRisingAttack = { name = \"라이징 어택\", cost = 2, desc = \"피해 12\", kind = \"Attack\", damage = 12, class = \"fighter\", rarity = \"unique\", image = \"3a3d4b8bb5bd4137847caf883e4bf38e\" },\n\tThunderCharge = { name = \"썬더 차지\", cost = 1, desc = \"피해 7, 약화 1\", kind = \"Attack\", damage = 7, weak = 1, class = \"page\", rarity = \"unique\", image = \"f1b7e3041909411eb67af884b446e1e1\" },\n\tBlizzardCharge = { name = \"블리자드 차지\", cost = 1, desc = \"피해 7, 취약 1\", kind = \"Attack\", damage = 7, vuln = 1, class = \"page\", rarity = \"unique\", image = \"7915c70952ad432f99519ad79bf929a4\" },\n\tPowerGuard = { name = \"파워 가드\", cost = 1, desc = \"방어도 10\", kind = \"Skill\", block = 10, class = \"page\", rarity = \"unique\", image = \"90a9bf8eeb844b578b4e2d93ac43fedf\" },\n\tPierce = { name = \"피어스\", cost = 1, desc = \"피해 9, 방어 무시\", kind = \"Attack\", damage = 9, class = \"spearman\", rarity = \"unique\", pierce = true, image = \"e312e535a2bc4fed82d36f9c6027c9db\" },\n\tIronWall = { name = \"아이언 월\", cost = 2, desc = \"방어도 12\", kind = \"Skill\", block = 12, class = \"spearman\", rarity = \"unique\", image = \"92021d62341a4bce9cfd09d1b4b865db\" },\n\tHyperBody = { name = \"하이퍼 바디\", cost = 1, desc = \"매턴 방어도 +3\", kind = \"Power\", powerEffect = \"blockPerTurn\", value = 3, class = \"spearman\", rarity = \"legend\", image = \"b4020dbadee6401f9893a020fe4154b1\" },\n\tEnergyBolt = { name = \"에너지 볼트\", cost = 1, desc = \"피해 6\", kind = \"Attack\", damage = 6, class = \"magician\", rarity = \"normal\", image = \"a1ee3069fce14498b92998542679ae40\" },\n\tMagicGuard = { name = \"매직 가드\", cost = 1, desc = \"방어도 5\", kind = \"Skill\", block = 5, class = \"magician\", rarity = \"normal\", image = \"01b249c26eb34b8aaab774bf221907a1\" },\n\tMagicClaw = { name = \"매직 클로\", cost = 1, desc = \"피해 3 × 2회\", kind = \"Attack\", damage = 3, class = \"magician\", rarity = \"normal\", hits = 2, image = \"d6e7c04c436f42f19e9806ac5b4401ae\" },\n\tTeleport = { name = \"텔레포트\", cost = 1, desc = \"방어도 3, 드로 1\", kind = \"Skill\", block = 3, class = \"magician\", rarity = \"normal\", draw = 1, image = \"80c98c8e032b4f6c8371a24b4e1d8f14\" },\n\tSlow = { name = \"슬로우\", cost = 1, desc = \"약화 2 부여\", kind = \"Skill\", weak = 2, class = \"magician\", rarity = \"normal\", image = \"16f79f571a964430bf1953edc9a14c73\" },\n\tFireArrow = { name = \"파이어 애로우\", cost = 1, desc = \"피해 8\", kind = \"Attack\", damage = 8, class = \"firepoison\", rarity = \"unique\", image = \"78b9be4e711c440f84fc21e51e812bae\" },\n\tPoisonBreath = { name = \"포이즌 브레스\", cost = 1, desc = \"독 4 부여\", kind = \"Skill\", class = \"firepoison\", rarity = \"unique\", poison = 4, image = \"b4e8bd7508b54d208e4f2ad7414f8c0a\" },\n\tElementAmp = { name = \"엘레멘트 앰플\", cost = 1, desc = \"매 턴 힘 +1\", kind = \"Power\", powerEffect = \"strengthPerTurn\", value = 1, class = \"firepoison\", rarity = \"legend\", image = \"9859f3ab41b945f797d56cd83f95b25f\" },\n\tThunderBolt = { name = \"썬더 볼트\", cost = 2, desc = \"모든 적에게 피해 6\", kind = \"Attack\", damage = 6, class = \"icelightning\", rarity = \"legend\", aoe = true, image = \"c6685d33cb2641f09d11cfa2d5cc820c\" },\n\tColdBeam = { name = \"콜드 빔\", cost = 2, desc = \"피해 7, 약화 2\", kind = \"Attack\", damage = 7, weak = 2, class = \"icelightning\", rarity = \"unique\", image = \"e8f7c148c79f497d83014e3361f59f5c\" },\n\tChillingStep = { name = \"칠링 스텝\", cost = 1, desc = \"방어도 8\", kind = \"Skill\", block = 8, class = \"icelightning\", rarity = \"unique\", image = \"b2a7274d868241c78aa5780f2beecddf\" },\n\tHeal = { name = \"힐\", cost = 1, desc = \"HP 10 회복\", kind = \"Skill\", class = \"cleric\", rarity = \"unique\", heal = 10, image = \"b4127c181e2942e38821d4a9a1f14596\" },\n\tBless = { name = \"블레스\", cost = 1, desc = \"힘 +1, 방어도 5\", kind = \"Skill\", block = 5, strength = 1, class = \"cleric\", rarity = \"unique\", image = \"d45553db4a414011b67486dfa8a12fe5\" },\n\tHolyArrow = { name = \"홀리 애로우\", cost = 1, desc = \"피해 8\", kind = \"Attack\", damage = 8, class = \"cleric\", rarity = \"unique\", 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()", + "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\", rarity = \"normal\", image = \"a71b116807904ef2b38e1dc013e2f9a2\" },\n\tDefend = { name = \"아이언 바디\", cost = 1, desc = \"방어도 5\", kind = \"Skill\", block = 5, class = \"warrior\", rarity = \"normal\", image = \"1ae9b6741c5947a8b528a0f515b50e3e\" },\n\tBash = { name = \"슬래시 블러스트\", cost = 2, desc = \"피해 10\", kind = \"Attack\", damage = 10, class = \"warrior\", rarity = \"normal\", image = \"d5bc2953fcab4cfe9062af81c35aff86\" },\n\tWarLeap = { name = \"워 리프\", cost = 1, desc = \"피해 4, 방어도 3\", kind = \"Attack\", damage = 4, block = 3, class = \"warrior\", rarity = \"normal\", image = \"992dabf6aff2400e92b2f4f705d8ebe7\" },\n\tBrandish = { name = \"브랜디시\", cost = 2, desc = \"피해 13\", kind = \"Attack\", damage = 13, class = \"warrior\", rarity = \"unique\", image = \"21af4bccc5054a5dbc8245dfa7f08681\" },\n\tChargedBlow = { name = \"차지 블로우\", cost = 2, desc = \"피해 8, 취약 2\", kind = \"Attack\", damage = 8, vuln = 2, class = \"warrior\", rarity = \"unique\", image = \"fe83c7635b0e49ed83d75a2833adb53e\" },\n\tThreaten = { name = \"위협\", cost = 0, desc = \"약화 2 부여\", kind = \"Skill\", weak = 2, class = \"warrior\", rarity = \"normal\", image = \"64daadf1a98e490d9c14ef52ec776e63\" },\n\tEnrage = { name = \"인레이지\", cost = 1, desc = \"힘 +2\", kind = \"Skill\", strength = 2, class = \"warrior\", rarity = \"unique\", image = \"09370fc7551e47238fd103a80fba558e\" },\n\tRage = { name = \"분노\", cost = 1, desc = \"매 턴 시작 시 힘 +1\", kind = \"Power\", powerEffect = \"strengthPerTurn\", value = 1, class = \"warrior\", rarity = \"legend\", image = \"379d86e3de064959aa4612f71e84ccfb\" },\n\tComboAttack = { name = \"콤보 어택\", cost = 1, desc = \"피해 5 × 2회\", kind = \"Attack\", damage = 5, class = \"fighter\", rarity = \"unique\", hits = 2, image = \"1bc3e52b330648faae9eafd5a205e37b\" },\n\tBerserk = { name = \"버서크\", cost = 2, desc = \"매턴 에너지 +1, 취약 1 자가\", kind = \"Power\", powerEffect = \"energyPerTurn\", value = 1, class = \"fighter\", rarity = \"legend\", selfVuln = 1, image = \"cef30ea340c74e768bcee4e2cbe0577a\" },\n\tRisingAttack = { name = \"라이징 어택\", cost = 2, desc = \"피해 12\", kind = \"Attack\", damage = 12, class = \"fighter\", rarity = \"unique\", image = \"3a3d4b8bb5bd4137847caf883e4bf38e\" },\n\tThunderCharge = { name = \"썬더 차지\", cost = 1, desc = \"피해 7, 약화 1\", kind = \"Attack\", damage = 7, weak = 1, class = \"page\", rarity = \"unique\", image = \"f1b7e3041909411eb67af884b446e1e1\" },\n\tBlizzardCharge = { name = \"블리자드 차지\", cost = 1, desc = \"피해 7, 취약 1\", kind = \"Attack\", damage = 7, vuln = 1, class = \"page\", rarity = \"unique\", image = \"7915c70952ad432f99519ad79bf929a4\" },\n\tPowerGuard = { name = \"파워 가드\", cost = 1, desc = \"방어도 10\", kind = \"Skill\", block = 10, class = \"page\", rarity = \"unique\", image = \"90a9bf8eeb844b578b4e2d93ac43fedf\" },\n\tPierce = { name = \"피어스\", cost = 1, desc = \"피해 9, 방어 무시\", kind = \"Attack\", damage = 9, class = \"spearman\", rarity = \"unique\", pierce = true, image = \"e312e535a2bc4fed82d36f9c6027c9db\" },\n\tIronWall = { name = \"아이언 월\", cost = 2, desc = \"방어도 12\", kind = \"Skill\", block = 12, class = \"spearman\", rarity = \"unique\", image = \"92021d62341a4bce9cfd09d1b4b865db\" },\n\tHyperBody = { name = \"하이퍼 바디\", cost = 1, desc = \"매턴 방어도 +3\", kind = \"Power\", powerEffect = \"blockPerTurn\", value = 3, class = \"spearman\", rarity = \"legend\", image = \"b4020dbadee6401f9893a020fe4154b1\" },\n\tEnergyBolt = { name = \"에너지 볼트\", cost = 1, desc = \"피해 6\", kind = \"Attack\", damage = 6, class = \"magician\", rarity = \"normal\", image = \"a1ee3069fce14498b92998542679ae40\" },\n\tMagicGuard = { name = \"매직 가드\", cost = 1, desc = \"방어도 5\", kind = \"Skill\", block = 5, class = \"magician\", rarity = \"normal\", image = \"01b249c26eb34b8aaab774bf221907a1\" },\n\tMagicClaw = { name = \"매직 클로\", cost = 1, desc = \"피해 3 × 2회\", kind = \"Attack\", damage = 3, class = \"magician\", rarity = \"normal\", hits = 2, image = \"d6e7c04c436f42f19e9806ac5b4401ae\" },\n\tTeleport = { name = \"텔레포트\", cost = 1, desc = \"방어도 3, 드로 1\", kind = \"Skill\", block = 3, class = \"magician\", rarity = \"normal\", draw = 1, image = \"80c98c8e032b4f6c8371a24b4e1d8f14\" },\n\tSlow = { name = \"슬로우\", cost = 1, desc = \"약화 2 부여\", kind = \"Skill\", weak = 2, class = \"magician\", rarity = \"normal\", image = \"16f79f571a964430bf1953edc9a14c73\" },\n\tFireArrow = { name = \"파이어 애로우\", cost = 1, desc = \"피해 8\", kind = \"Attack\", damage = 8, class = \"firepoison\", rarity = \"unique\", image = \"78b9be4e711c440f84fc21e51e812bae\" },\n\tPoisonBreath = { name = \"포이즌 브레스\", cost = 1, desc = \"독 4 부여\", kind = \"Skill\", class = \"firepoison\", rarity = \"unique\", poison = 4, image = \"b4e8bd7508b54d208e4f2ad7414f8c0a\" },\n\tElementAmp = { name = \"엘레멘트 앰플\", cost = 1, desc = \"매 턴 힘 +1\", kind = \"Power\", powerEffect = \"strengthPerTurn\", value = 1, class = \"firepoison\", rarity = \"legend\", image = \"9859f3ab41b945f797d56cd83f95b25f\" },\n\tThunderBolt = { name = \"썬더 볼트\", cost = 2, desc = \"모든 적에게 피해 6\", kind = \"Attack\", damage = 6, class = \"icelightning\", rarity = \"legend\", aoe = true, image = \"c6685d33cb2641f09d11cfa2d5cc820c\" },\n\tColdBeam = { name = \"콜드 빔\", cost = 2, desc = \"피해 7, 약화 2\", kind = \"Attack\", damage = 7, weak = 2, class = \"icelightning\", rarity = \"unique\", image = \"e8f7c148c79f497d83014e3361f59f5c\" },\n\tChillingStep = { name = \"칠링 스텝\", cost = 1, desc = \"방어도 8\", kind = \"Skill\", block = 8, class = \"icelightning\", rarity = \"unique\", image = \"b2a7274d868241c78aa5780f2beecddf\" },\n\tHeal = { name = \"힐\", cost = 1, desc = \"HP 10 회복\", kind = \"Skill\", class = \"cleric\", rarity = \"unique\", heal = 10, image = \"b4127c181e2942e38821d4a9a1f14596\" },\n\tBless = { name = \"블레스\", cost = 1, desc = \"힘 +1, 방어도 5\", kind = \"Skill\", block = 5, strength = 1, class = \"cleric\", rarity = \"unique\", image = \"d45553db4a414011b67486dfa8a12fe5\" },\n\tHolyArrow = { name = \"홀리 애로우\", cost = 1, desc = \"피해 8\", kind = \"Attack\", damage = 8, class = \"cleric\", rarity = \"unique\", image = \"0265e103b4904f178b1c2bdcd54d5975\" },\n\tWound = { name = \"상처\", cost = 0, desc = \"사용할 수 없다. 손패를 막는 저주.\", kind = \"Status\", class = \"curse\", rarity = \"normal\", unplayable = true, curse = true },\n\tBurn = { name = \"화상\", cost = 0, desc = \"사용 불가. 손패에 있으면 턴 종료 시 피해 2.\", kind = \"Status\", class = \"curse\", rarity = \"normal\", unplayable = true, curse = true, endTurnDamage = 2 },\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": [], @@ -1009,7 +1009,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.45\nif g == \"elite\" or g == \"boss\" then\n\tmult = mult + self:AscEliteBonus()\nend\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 == \"Attack\" then\n\t\t\tv = math.floor(v * mult * self:AscAtkMult())\n\t\telseif 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 * self:AscHpMult())\n\tlocal hitClip = nil\n\tlocal standClip = nil\n\tif item.entity.StateAnimationComponent ~= nil then\n\t\tpcall(function()\n\t\t\thitClip = item.entity.StateAnimationComponent.ActionSheet[\"hit\"]\n\t\t\tstandClip = item.entity.StateAnimationComponent.ActionSheet[\"stand\"]\n\t\tend)\n\tend\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\thitClip = hitClip, standClip = standClip, motionBusy = false,\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 byGroup = {}\nfor i = 1, #reg do\n\tlocal r = reg[i]\n\tif r.entity ~= nil and isvalid(r.entity) and (r.map == nil or r.map == \"\" or pmap == \"\" or r.map == pmap) then\n\t\tlocal gg = r.group\n\t\tif gg == nil or gg == \"\" then gg = \"combat\" end\n\t\tif byGroup[gg] == nil then byGroup[gg] = {} end\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(byGroup[gg], { entity = r.entity, enemyId = r.enemyId, x = x })\n\tend\nend\n-- 노드 타입별 랜덤 구성: 일반 1~3 / 엘리트 1+일반0~2 / 보스 1\nlocal chosen = {}\nlocal function takeFrom(key, k)\n\tlocal src = byGroup[key] or {}\n\tlocal pool = {}\n\tfor i = 1, #src do pool[i] = src[i] end\n\tself:Shuffle(pool)\n\tlocal taken = 0\n\tfor i = 1, #pool do\n\t\tif taken >= k then break end\n\t\ttable.insert(chosen, pool[i])\n\t\ttaken = taken + 1\n\tend\nend\nif g == \"boss\" then\n\ttakeFrom(\"boss\", 1)\nelseif g == \"elite\" then\n\ttakeFrom(\"elite\", 1)\n\ttakeFrom(\"combat\", math.random(0, 2))\nelse\n\ttakeFrom(\"combat\", math.random(1, 3))\nend\nif #chosen == 0 then takeFrom(g, 1) end\nif #chosen == 0 then takeFrom(\"combat\", 1) end\ntable.sort(chosen, function(a, b) return a.x < b.x end)\nlocal mult = 1 + (self.Floor - 1) * 0.45\nif g == \"elite\" or g == \"boss\" then\n\tmult = mult + self:AscEliteBonus()\nend\nlocal n = #chosen\nif n > 4 then n = 4 end\nfor i = 1, n do\n\tlocal item = chosen[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 or 0\n\t\tif e.intents[k].kind == \"Attack\" then\n\t\t\tv = math.floor(v * mult * self:AscAtkMult())\n\t\telseif 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, card = e.intents[k].card, count = e.intents[k].count }\n\tend\n\tlocal maxHp = math.floor(e.maxHp * mult * self:AscHpMult())\n\tlocal hitClip = nil\n\tlocal standClip = nil\n\tif item.entity.StateAnimationComponent ~= nil then\n\t\tpcall(function()\n\t\t\thitClip = item.entity.StateAnimationComponent.ActionSheet[\"hit\"]\n\t\t\tstandClip = item.entity.StateAnimationComponent.ActionSheet[\"stand\"]\n\t\tend)\n\tend\n\tlocal startIdx = 1\n\tif #intents > 0 then startIdx = math.random(1, #intents) end\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\thitClip = hitClip, standClip = standClip, motionBusy = false,\n\t\tintents = intents, intentIdx = startIdx, alive = true, slot = i }\n\tself:ReviveMonsterEntity(item.entity)\n\tself:PositionMonsterSlot(i)\nend\nself.TargetIndex = 1", "Scope": 2, "ExecSpace": 6, "Attributes": [], @@ -1100,7 +1100,7 @@ "Name": null }, "Arguments": [], - "Code": "if self.CombatOver == true or self.FxBusy == true or self.TurnBusy == true then\n\treturn\nend\nfor i = 1, #self.Hand do\n\ttable.insert(self.DiscardPile, self.Hand[i])\nend\nself.Hand = {}\nif self.PlayerWeak > 0 then self.PlayerWeak = self.PlayerWeak - 1 end\nif self.PlayerVuln > 0 then self.PlayerVuln = self.PlayerVuln - 1 end\nself:RenderHand(false)\nself:RenderPiles()\nself:EnemyTurn()", + "Code": "if self.CombatOver == true or self.FxBusy == true or self.TurnBusy == true then\n\treturn\nend\nlocal burn = 0\nfor bi = 1, #self.Hand do\n\tlocal hc = self.Cards[self.Hand[bi]]\n\tif hc ~= nil and hc.endTurnDamage ~= nil then burn = burn + hc.endTurnDamage end\nend\nif burn > 0 then\n\tself.PlayerHp = self.PlayerHp - burn\n\tif self.PlayerHp < 0 then self.PlayerHp = 0 end\n\tself:ShowPlayerDmgPop(burn)\n\tself:RenderCombat()\nend\nfor i = 1, #self.Hand do\n\ttable.insert(self.DiscardPile, self.Hand[i])\nend\nself.Hand = {}\nif self.PlayerWeak > 0 then self.PlayerWeak = self.PlayerWeak - 1 end\nif self.PlayerVuln > 0 then self.PlayerVuln = self.PlayerVuln - 1 end\nself:RenderHand(false)\nself:RenderPiles()\nself:EnemyTurn()", "Scope": 2, "ExecSpace": 6, "Attributes": [], @@ -1529,7 +1529,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\tself:PlayerAttackMotion()\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()", + "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 c.unplayable == true then\n\tself:Toast(\"사용할 수 없는 카드입니다\")\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\tself:PlayerAttackMotion()\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": [], @@ -1860,7 +1860,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\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\tself:MonsterHitMotion(idx)\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\tself:MonsterLunge(idx)\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\t\tself:PlayerHitMotion()\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\tself:MonsterHitMotion(idx)\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\tself:MonsterLunge(idx)\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\t\tself:PlayerHitMotion()\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\telseif intent.kind == \"AddCard\" then\n\t\t\tlocal cnt = intent.count or 1\n\t\t\tfor ci = 1, cnt do\n\t\t\t\ttable.insert(self.DiscardPile, intent.card)\n\t\t\tend\n\t\t\tself:RenderPiles()\n\t\t\tlocal cn = intent.card\n\t\t\tlocal cc = self.Cards[intent.card]\n\t\t\tif cc ~= nil then cn = cc.name end\n\t\t\tself:Toast(m.name .. \": \" .. cn .. \" 추가!\")\n\t\tend\n\tend\n\tif #m.intents > 0 then\n\t\tm.intentIdx = math.random(1, #m.intents)\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": [], @@ -2116,7 +2116,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, 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()", + "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\telseif intent.kind == \"AddCard\" then\n\t\t\t\tt = \"저주 카드 추가\"\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\telseif intent.kind == \"AddCard\" then\n\t\t\t\tintentEntity.TextComponent.FontColor = Color(0.6, 0.85, 0.4, 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/cardframes.json b/data/cardframes.json index 99267c0..2c8a2f9 100644 --- a/data/cardframes.json +++ b/data/cardframes.json @@ -6,7 +6,8 @@ }, "classToFrame": { "warrior": "warrior", "fighter": "warrior", "page": "warrior", "spearman": "warrior", - "magician": "magician", "firepoison": "magician", "icelightning": "magician", "cleric": "magician" + "magician": "magician", "firepoison": "magician", "icelightning": "magician", "cleric": "magician", + "curse": "bandit" }, "rewardWeights": { "normal": 70, "unique": 25, "legend": 5 } } diff --git a/data/cards.json b/data/cards.json index d30dd02..cc9ed03 100644 --- a/data/cards.json +++ b/data/cards.json @@ -335,6 +335,27 @@ "desc": "피해 8", "image": "0265e103b4904f178b1c2bdcd54d5975", "rarity": "unique" + }, + "Wound": { + "name": "상처", + "cost": 0, + "kind": "Status", + "desc": "사용할 수 없다. 손패를 막는 저주.", + "class": "curse", + "rarity": "normal", + "unplayable": true, + "curse": true + }, + "Burn": { + "name": "화상", + "cost": 0, + "kind": "Status", + "desc": "사용 불가. 손패에 있으면 턴 종료 시 피해 2.", + "class": "curse", + "rarity": "normal", + "unplayable": true, + "curse": true, + "endTurnDamage": 2 } }, "starterDecks": { diff --git a/data/enemies.json b/data/enemies.json index 668886c..635df3b 100644 --- a/data/enemies.json +++ b/data/enemies.json @@ -46,7 +46,8 @@ "intents": [ { "kind": "Attack", "value": 4 }, { "kind": "Attack", "value": 4 }, - { "kind": "Attack", "value": 10 } + { "kind": "Attack", "value": 10 }, + { "kind": "AddCard", "card": "Wound", "count": 1 } ] }, "pig": { @@ -67,6 +68,24 @@ { "kind": "Attack", "value": 9 } ] }, + "red_snail": { + "name": "빨간 달팽이", + "maxHp": 14, + "intents": [ + { "kind": "Attack", "value": 5 }, + { "kind": "Defend", "value": 6 }, + { "kind": "Attack", "value": 7 } + ] + }, + "stump": { + "name": "나무토막", + "maxHp": 19, + "intents": [ + { "kind": "Defend", "value": 5 }, + { "kind": "Attack", "value": 8 }, + { "kind": "Attack", "value": 6 } + ] + }, "mushmom": { "name": "머쉬맘", "maxHp": 75, @@ -75,7 +94,8 @@ { "kind": "Debuff", "effect": "weak", "value": 2 }, { "kind": "Attack", "value": 16 }, { "kind": "Attack", "value": 9 }, - { "kind": "Defend", "value": 6 } + { "kind": "Defend", "value": 6 }, + { "kind": "AddCard", "card": "Burn", "count": 1 } ] }, "modified_snail": { diff --git a/map/map01.map b/map/map01.map index 2dabf27..76b4b0e 100644 --- a/map/map01.map +++ b/map/map01.map @@ -6364,12 +6364,12 @@ } }, { - "id": "595374e9-e49a-4733-b00f-77e8e61dad59", - "path": "/maps/map01/주황버섯", + "id": "000005dc-0000-4000-8000-0000000005dc", + "path": "/maps/map01/combat_1", "componentNames": "MOD.Core.TransformComponent,MOD.Core.StateAnimationComponent,MOD.Core.SpriteRendererComponent,MOD.Core.RigidbodyComponent,MOD.Core.MovementComponent,MOD.Core.StateComponent,MOD.Core.HitComponent,MOD.Core.DamageSkinSpawnerComponent,script.Monster,script.MonsterAttack,MOD.Core.KinematicbodyComponent,MOD.Core.SideviewbodyComponent,MOD.Core.DamageSkinSettingComponent,script.CombatMonster", "jsonString": { - "name": "주황버섯", - "path": "/maps/map01/주황버섯", + "name": "combat_1", + "path": "/maps/map01/combat_1", "nameEditable": true, "enable": true, "visible": true, @@ -6381,7 +6381,7 @@ "type": "Model", "entry_id": "ChaseMonster", "sub_entity_id": null, - "root_entity_id": "595374e9-e49a-4733-b00f-77e8e61dad59", + "root_entity_id": "000005dc-0000-4000-8000-0000000005dc", "replaced_model_id": null }, "modelId": "chasemonster", @@ -6389,7 +6389,7 @@ { "@type": "MOD.Core.TransformComponent", "Position": { - "x": 5.2, + "x": 2.6, "y": 0.03499998, "z": 999.999 }, @@ -6409,11 +6409,9 @@ { "@type": "MOD.Core.StateAnimationComponent", "ActionSheet": { - "move": "573fe938562a4abf91eebf951f21afd5", - "stand": "6d381bea1bcb4504b518a1fbfa0904ac", - "jump": "59823e146a034e48b8667ebb6f0724b1", - "hit": "642ece38d8d449b29ce4479100e37a54", - "die": "3c99d6b9b89b4295a9c2749eb02e28e9" + "stand": "a2204a21d88942b281d2cac6053ffbaa", + "hit": "afc08936b8a64b26bc3dd8c03ead1f26", + "die": "fc1c6d9ba9bc413ab53b6dbfae3ac45b" }, "Enable": true }, @@ -6423,7 +6421,159 @@ "EndFrameIndex": 0, "RenderSetting": 1, "SortingLayer": "MapLayer0", - "SpriteRUID": "6d381bea1bcb4504b518a1fbfa0904ac", + "SpriteRUID": "a2204a21d88942b281d2cac6053ffbaa", + "StartFrameIndex": 0, + "Enable": true + }, + { + "@type": "MOD.Core.RigidbodyComponent", + "MoveVelocity": { + "x": 0, + "y": 0 + }, + "RealMoveVelocity": { + "x": 0, + "y": 0 + }, + "Enable": true + }, + { + "@type": "MOD.Core.MovementComponent", + "InputSpeed": 0, + "JumpForce": 6, + "Enable": false + }, + { + "@type": "MOD.Core.StateComponent", + "IsLegacy": false, + "Enable": true + }, + { + "@type": "MOD.Core.HitComponent", + "BoxSize": { + "x": 0.63, + "y": 0.58 + }, + "ColliderOffset": { + "x": 0.0449999869, + "y": 0.29 + }, + "IsLegacy": false, + "Enable": true + }, + { + "@type": "MOD.Core.DamageSkinSpawnerComponent", + "Enable": true + }, + { + "@type": "script.Monster", + "Enable": true, + "IsDead": false + }, + { + "@type": "script.MonsterAttack", + "Enable": true, + "SpriteSize": { + "x": 0, + "y": 0 + }, + "PositionOffset": { + "x": 0, + "y": 0 + } + }, + { + "@type": "MOD.Core.KinematicbodyComponent", + "MoveVelocity": { + "x": 0, + "y": 0 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SideviewbodyComponent", + "MoveVelocity": { + "x": 0, + "y": 0 + }, + "Enable": true + }, + { + "@type": "MOD.Core.DamageSkinSettingComponent", + "DamageSkinId": { + "DataId": "02c22d93421b4038b3c413b3e40b57ec" + }, + "Enable": true + }, + { + "@type": "script.CombatMonster", + "Enable": true, + "EnemyId": "green_mushroom", + "Group": "combat" + } + ], + "@version": 1 + } + }, + { + "id": "000005dd-0000-4000-8000-0000000005dd", + "path": "/maps/map01/combat_2", + "componentNames": "MOD.Core.TransformComponent,MOD.Core.StateAnimationComponent,MOD.Core.SpriteRendererComponent,MOD.Core.RigidbodyComponent,MOD.Core.MovementComponent,MOD.Core.StateComponent,MOD.Core.HitComponent,MOD.Core.DamageSkinSpawnerComponent,script.Monster,script.MonsterAttack,MOD.Core.KinematicbodyComponent,MOD.Core.SideviewbodyComponent,MOD.Core.DamageSkinSettingComponent,script.CombatMonster", + "jsonString": { + "name": "combat_2", + "path": "/maps/map01/combat_2", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": false, + "displayOrder": 4, + "pathConstraints": "///", + "revision": 2, + "origin": { + "type": "Model", + "entry_id": "ChaseMonster", + "sub_entity_id": null, + "root_entity_id": "000005dd-0000-4000-8000-0000000005dd", + "replaced_model_id": null + }, + "modelId": "chasemonster", + "@components": [ + { + "@type": "MOD.Core.TransformComponent", + "Position": { + "x": 3.6, + "y": 0.03499998, + "z": 999.999 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.StateAnimationComponent", + "ActionSheet": { + "stand": "4ca39dbfa1c6492283ba8bd352d12b0a", + "hit": "7ac78511036e4ebe988b97c35fc275d1", + "die": "740f3f2b2e7a4b71bec5eac84e8539f9" + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteRendererComponent", + "ActionSheet": {}, + "EndFrameIndex": 0, + "RenderSetting": 1, + "SortingLayer": "MapLayer0", + "SpriteRUID": "4ca39dbfa1c6492283ba8bd352d12b0a", "StartFrameIndex": 0, "Enable": true }, @@ -6518,24 +6668,24 @@ } }, { - "id": "b1923aa6-7265-4f99-9ab6-1c45eae9af23", - "path": "/maps/map01/돼지", - "componentNames": "MOD.Core.TransformComponent,MOD.Core.StateAnimationComponent,MOD.Core.SpriteRendererComponent,MOD.Core.RigidbodyComponent,MOD.Core.MovementComponent,MOD.Core.StateComponent,MOD.Core.HitComponent,MOD.Core.DamageSkinSpawnerComponent,script.Monster,script.MonsterAttack,MOD.Core.KinematicbodyComponent,MOD.Core.SideviewbodyComponent,script.CombatMonster", + "id": "000005de-0000-4000-8000-0000000005de", + "path": "/maps/map01/combat_3", + "componentNames": "MOD.Core.TransformComponent,MOD.Core.StateAnimationComponent,MOD.Core.SpriteRendererComponent,MOD.Core.RigidbodyComponent,MOD.Core.MovementComponent,MOD.Core.StateComponent,MOD.Core.HitComponent,MOD.Core.DamageSkinSpawnerComponent,script.Monster,script.MonsterAttack,MOD.Core.KinematicbodyComponent,MOD.Core.SideviewbodyComponent,MOD.Core.DamageSkinSettingComponent,script.CombatMonster", "jsonString": { - "name": "돼지", - "path": "/maps/map01/돼지", + "name": "combat_3", + "path": "/maps/map01/combat_3", "nameEditable": true, "enable": true, "visible": true, "localize": false, - "displayOrder": 5, + "displayOrder": 4, "pathConstraints": "///", - "revision": 1, + "revision": 2, "origin": { "type": "Model", "entry_id": "ChaseMonster", "sub_entity_id": null, - "root_entity_id": "b1923aa6-7265-4f99-9ab6-1c45eae9af23", + "root_entity_id": "000005de-0000-4000-8000-0000000005de", "replaced_model_id": null }, "modelId": "chasemonster", @@ -6543,7 +6693,7 @@ { "@type": "MOD.Core.TransformComponent", "Position": { - "x": 3.767562, + "x": 4.6, "y": 0.03499998, "z": 999.999 }, @@ -6553,16 +6703,19 @@ "z": 0, "w": 1 }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, "Enable": true }, { "@type": "MOD.Core.StateAnimationComponent", "ActionSheet": { - "move": "8baad61512be4b33b2a0879fec7a266e", - "stand": "528a8638b12f41b8b5781a05360d2949", - "jump": "c9e27ce6f8344aefba169c5ca6571def", - "hit": "60e42a918a0342478903cc71adba1dc5", - "die": "0644beff80a44ec7acc011ea0961df57" + "stand": "17b55730c26f4fd6b8fcfa288da388de", + "hit": "eac48e84a9fc4580a4018de5cf52ddb3", + "die": "51c2f4b59a2c413db26035aa57002fc8" }, "Enable": true }, @@ -6572,7 +6725,7 @@ "EndFrameIndex": 0, "RenderSetting": 1, "SortingLayer": "MapLayer0", - "SpriteRUID": "528a8638b12f41b8b5781a05360d2949", + "SpriteRUID": "17b55730c26f4fd6b8fcfa288da388de", "StartFrameIndex": 0, "Enable": true }, @@ -6602,12 +6755,12 @@ { "@type": "MOD.Core.HitComponent", "BoxSize": { - "x": 0.68, - "y": 0.4 + "x": 0.63, + "y": 0.58 }, "ColliderOffset": { - "x": 0, - "y": 0.2 + "x": 0.0449999869, + "y": 0.29 }, "IsLegacy": false, "Enable": true @@ -6649,6 +6802,317 @@ }, "Enable": true }, + { + "@type": "MOD.Core.DamageSkinSettingComponent", + "DamageSkinId": { + "DataId": "02c22d93421b4038b3c413b3e40b57ec" + }, + "Enable": true + }, + { + "@type": "script.CombatMonster", + "Enable": true, + "EnemyId": "red_snail", + "Group": "combat" + } + ], + "@version": 1 + } + }, + { + "id": "000005df-0000-4000-8000-0000000005df", + "path": "/maps/map01/combat_4", + "componentNames": "MOD.Core.TransformComponent,MOD.Core.StateAnimationComponent,MOD.Core.SpriteRendererComponent,MOD.Core.RigidbodyComponent,MOD.Core.MovementComponent,MOD.Core.StateComponent,MOD.Core.HitComponent,MOD.Core.DamageSkinSpawnerComponent,script.Monster,script.MonsterAttack,MOD.Core.KinematicbodyComponent,MOD.Core.SideviewbodyComponent,MOD.Core.DamageSkinSettingComponent,script.CombatMonster", + "jsonString": { + "name": "combat_4", + "path": "/maps/map01/combat_4", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": false, + "displayOrder": 4, + "pathConstraints": "///", + "revision": 2, + "origin": { + "type": "Model", + "entry_id": "ChaseMonster", + "sub_entity_id": null, + "root_entity_id": "000005df-0000-4000-8000-0000000005df", + "replaced_model_id": null + }, + "modelId": "chasemonster", + "@components": [ + { + "@type": "MOD.Core.TransformComponent", + "Position": { + "x": 5.6, + "y": 0.03499998, + "z": 999.999 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.StateAnimationComponent", + "ActionSheet": { + "stand": "48c10437ae8344a9b2a1d3f36185728f", + "hit": "9044063647854f5e9128efcf80e909be", + "die": "f414577d18c94cc387c275df4abdbc3b" + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteRendererComponent", + "ActionSheet": {}, + "EndFrameIndex": 0, + "RenderSetting": 1, + "SortingLayer": "MapLayer0", + "SpriteRUID": "48c10437ae8344a9b2a1d3f36185728f", + "StartFrameIndex": 0, + "Enable": true + }, + { + "@type": "MOD.Core.RigidbodyComponent", + "MoveVelocity": { + "x": 0, + "y": 0 + }, + "RealMoveVelocity": { + "x": 0, + "y": 0 + }, + "Enable": true + }, + { + "@type": "MOD.Core.MovementComponent", + "InputSpeed": 0, + "JumpForce": 6, + "Enable": false + }, + { + "@type": "MOD.Core.StateComponent", + "IsLegacy": false, + "Enable": true + }, + { + "@type": "MOD.Core.HitComponent", + "BoxSize": { + "x": 0.63, + "y": 0.58 + }, + "ColliderOffset": { + "x": 0.0449999869, + "y": 0.29 + }, + "IsLegacy": false, + "Enable": true + }, + { + "@type": "MOD.Core.DamageSkinSpawnerComponent", + "Enable": true + }, + { + "@type": "script.Monster", + "Enable": true, + "IsDead": false + }, + { + "@type": "script.MonsterAttack", + "Enable": true, + "SpriteSize": { + "x": 0, + "y": 0 + }, + "PositionOffset": { + "x": 0, + "y": 0 + } + }, + { + "@type": "MOD.Core.KinematicbodyComponent", + "MoveVelocity": { + "x": 0, + "y": 0 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SideviewbodyComponent", + "MoveVelocity": { + "x": 0, + "y": 0 + }, + "Enable": true + }, + { + "@type": "MOD.Core.DamageSkinSettingComponent", + "DamageSkinId": { + "DataId": "02c22d93421b4038b3c413b3e40b57ec" + }, + "Enable": true + }, + { + "@type": "script.CombatMonster", + "Enable": true, + "EnemyId": "blue_mushroom", + "Group": "combat" + } + ], + "@version": 1 + } + }, + { + "id": "000005e0-0000-4000-8000-0000000005e0", + "path": "/maps/map01/combat_5", + "componentNames": "MOD.Core.TransformComponent,MOD.Core.StateAnimationComponent,MOD.Core.SpriteRendererComponent,MOD.Core.RigidbodyComponent,MOD.Core.MovementComponent,MOD.Core.StateComponent,MOD.Core.HitComponent,MOD.Core.DamageSkinSpawnerComponent,script.Monster,script.MonsterAttack,MOD.Core.KinematicbodyComponent,MOD.Core.SideviewbodyComponent,MOD.Core.DamageSkinSettingComponent,script.CombatMonster", + "jsonString": { + "name": "combat_5", + "path": "/maps/map01/combat_5", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": false, + "displayOrder": 4, + "pathConstraints": "///", + "revision": 2, + "origin": { + "type": "Model", + "entry_id": "ChaseMonster", + "sub_entity_id": null, + "root_entity_id": "000005e0-0000-4000-8000-0000000005e0", + "replaced_model_id": null + }, + "modelId": "chasemonster", + "@components": [ + { + "@type": "MOD.Core.TransformComponent", + "Position": { + "x": 6.6, + "y": 0.03499998, + "z": 999.999 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.StateAnimationComponent", + "ActionSheet": { + "stand": "d8f014043ce8418f96700c2b6c9ebf6c", + "hit": "c3cf643b618346c7bfa6574187b396f9", + "die": "a88d9b3d60f941e4890dc89a6ccaa8ee" + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteRendererComponent", + "ActionSheet": {}, + "EndFrameIndex": 0, + "RenderSetting": 1, + "SortingLayer": "MapLayer0", + "SpriteRUID": "d8f014043ce8418f96700c2b6c9ebf6c", + "StartFrameIndex": 0, + "Enable": true + }, + { + "@type": "MOD.Core.RigidbodyComponent", + "MoveVelocity": { + "x": 0, + "y": 0 + }, + "RealMoveVelocity": { + "x": 0, + "y": 0 + }, + "Enable": true + }, + { + "@type": "MOD.Core.MovementComponent", + "InputSpeed": 0, + "JumpForce": 6, + "Enable": false + }, + { + "@type": "MOD.Core.StateComponent", + "IsLegacy": false, + "Enable": true + }, + { + "@type": "MOD.Core.HitComponent", + "BoxSize": { + "x": 0.63, + "y": 0.58 + }, + "ColliderOffset": { + "x": 0.0449999869, + "y": 0.29 + }, + "IsLegacy": false, + "Enable": true + }, + { + "@type": "MOD.Core.DamageSkinSpawnerComponent", + "Enable": true + }, + { + "@type": "script.Monster", + "Enable": true, + "IsDead": false + }, + { + "@type": "script.MonsterAttack", + "Enable": true, + "SpriteSize": { + "x": 0, + "y": 0 + }, + "PositionOffset": { + "x": 0, + "y": 0 + } + }, + { + "@type": "MOD.Core.KinematicbodyComponent", + "MoveVelocity": { + "x": 0, + "y": 0 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SideviewbodyComponent", + "MoveVelocity": { + "x": 0, + "y": 0 + }, + "Enable": true + }, + { + "@type": "MOD.Core.DamageSkinSettingComponent", + "DamageSkinId": { + "DataId": "02c22d93421b4038b3c413b3e40b57ec" + }, + "Enable": true + }, { "@type": "script.CombatMonster", "Enable": true, @@ -6660,166 +7124,24 @@ } }, { - "id": "566e94be-0efe-4536-9007-882dde030a95", - "path": "/maps/map01/초록버섯", - "componentNames": "MOD.Core.TransformComponent,MOD.Core.StateAnimationComponent,MOD.Core.SpriteRendererComponent,MOD.Core.RigidbodyComponent,MOD.Core.MovementComponent,MOD.Core.StateComponent,MOD.Core.HitComponent,MOD.Core.DamageSkinSpawnerComponent,script.Monster,script.MonsterAttack,MOD.Core.KinematicbodyComponent,MOD.Core.SideviewbodyComponent,script.CombatMonster", + "id": "000005e1-0000-4000-8000-0000000005e1", + "path": "/maps/map01/elite_6", + "componentNames": "MOD.Core.TransformComponent,MOD.Core.StateAnimationComponent,MOD.Core.SpriteRendererComponent,MOD.Core.RigidbodyComponent,MOD.Core.MovementComponent,MOD.Core.StateComponent,MOD.Core.HitComponent,MOD.Core.DamageSkinSpawnerComponent,script.Monster,script.MonsterAttack,MOD.Core.KinematicbodyComponent,MOD.Core.SideviewbodyComponent,MOD.Core.DamageSkinSettingComponent,script.CombatMonster", "jsonString": { - "name": "초록버섯", - "path": "/maps/map01/초록버섯", + "name": "elite_6", + "path": "/maps/map01/elite_6", "nameEditable": true, "enable": true, "visible": true, "localize": false, - "displayOrder": 6, + "displayOrder": 4, "pathConstraints": "///", - "revision": 1, - "origin": { - "type": "Model", - "entry_id": "MoveMonster", - "sub_entity_id": null, - "root_entity_id": "566e94be-0efe-4536-9007-882dde030a95", - "replaced_model_id": null - }, - "modelId": "movemonster", - "@components": [ - { - "@type": "MOD.Core.TransformComponent", - "Position": { - "x": 2.289299, - "y": 0.03499998, - "z": 999.999 - }, - "QuaternionRotation": { - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "Enable": true - }, - { - "@type": "MOD.Core.StateAnimationComponent", - "ActionSheet": { - "move": "7dc99047c46d4b2e85ec6a1e4252d0a8", - "stand": "7863066dbf184f06886ce96b69fea4ee", - "attack": "91804a0917634cc198238d4cda1dd735", - "hit": "a16e83f471684ef4a2543263caca7799", - "die": "f45adb12126f4167bddcfece10ddf6e3" - }, - "Enable": true - }, - { - "@type": "MOD.Core.SpriteRendererComponent", - "ActionSheet": {}, - "EndFrameIndex": 0, - "RenderSetting": 1, - "SortingLayer": "MapLayer0", - "SpriteRUID": "7863066dbf184f06886ce96b69fea4ee", - "StartFrameIndex": 0, - "Enable": true - }, - { - "@type": "MOD.Core.RigidbodyComponent", - "MoveVelocity": { - "x": 0, - "y": 0 - }, - "RealMoveVelocity": { - "x": 0, - "y": 0 - }, - "Enable": true - }, - { - "@type": "MOD.Core.MovementComponent", - "InputSpeed": 0, - "JumpForce": 0, - "Enable": false - }, - { - "@type": "MOD.Core.StateComponent", - "IsLegacy": false, - "Enable": true - }, - { - "@type": "MOD.Core.HitComponent", - "BoxSize": { - "x": 0.56, - "y": 0.52 - }, - "ColliderOffset": { - "x": 0.00999999, - "y": 0.26 - }, - "IsLegacy": false, - "Enable": true - }, - { - "@type": "MOD.Core.DamageSkinSpawnerComponent", - "Enable": true - }, - { - "@type": "script.Monster", - "Enable": true, - "IsDead": false - }, - { - "@type": "script.MonsterAttack", - "Enable": true, - "SpriteSize": { - "x": 0, - "y": 0 - }, - "PositionOffset": { - "x": 0, - "y": 0 - } - }, - { - "@type": "MOD.Core.KinematicbodyComponent", - "MoveVelocity": { - "x": 0, - "y": 0 - }, - "Enable": true - }, - { - "@type": "MOD.Core.SideviewbodyComponent", - "MoveVelocity": { - "x": 0, - "y": 0 - }, - "Enable": true - }, - { - "@type": "script.CombatMonster", - "Enable": true, - "EnemyId": "green_mushroom", - "Group": "combat" - } - ], - "@version": 1 - } - }, - { - "id": "b289e720-40fa-4c28-b5bf-6cd30ddf9c6b", - "path": "/maps/map01/머쉬맘", - "componentNames": "MOD.Core.TransformComponent,MOD.Core.StateAnimationComponent,MOD.Core.SpriteRendererComponent,MOD.Core.RigidbodyComponent,MOD.Core.MovementComponent,MOD.Core.StateComponent,MOD.Core.HitComponent,MOD.Core.DamageSkinSpawnerComponent,script.Monster,script.MonsterAttack,MOD.Core.KinematicbodyComponent,MOD.Core.SideviewbodyComponent,script.CombatMonster", - "jsonString": { - "name": "머쉬맘", - "path": "/maps/map01/머쉬맘", - "nameEditable": true, - "enable": true, - "visible": true, - "localize": false, - "displayOrder": 7, - "pathConstraints": "///", - "revision": 1, + "revision": 2, "origin": { "type": "Model", "entry_id": "ChaseMonster", "sub_entity_id": null, - "root_entity_id": "b289e720-40fa-4c28-b5bf-6cd30ddf9c6b", + "root_entity_id": "000005e1-0000-4000-8000-0000000005e1", "replaced_model_id": null }, "modelId": "chasemonster", @@ -6827,7 +7149,7 @@ { "@type": "MOD.Core.TransformComponent", "Position": { - "x": 3, + "x": 4.6, "y": 0.03499998, "z": 999.999 }, @@ -6837,17 +7159,19 @@ "z": 0, "w": 1 }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, "Enable": true }, { "@type": "MOD.Core.StateAnimationComponent", "ActionSheet": { - "move": "f3bf3c053e444315b83a9663ea85a37f", - "stand": "2688d39a56874f24aa0d46efbf89ff21", - "jump": "2f45374c97224e0cb108241398710a3c", - "attack": "907f4089e0b840a993967c5211af9b89", - "hit": "b771488c84fe437e9707f233c48ec477", - "die": "c03225c35c124bcbaf9ec9299b7d8a1f" + "stand": "3109357701ae41a4bcc7543f52f1f4c3", + "hit": "ce0269079e884545b5bb6ea075e2a67f", + "die": "a5e65650e00e47878cac1be7a5b999a0" }, "Enable": true }, @@ -6857,7 +7181,7 @@ "EndFrameIndex": 0, "RenderSetting": 1, "SortingLayer": "MapLayer0", - "SpriteRUID": "2688d39a56874f24aa0d46efbf89ff21", + "SpriteRUID": "3109357701ae41a4bcc7543f52f1f4c3", "StartFrameIndex": 0, "Enable": true }, @@ -6887,12 +7211,12 @@ { "@type": "MOD.Core.HitComponent", "BoxSize": { - "x": 1.19, - "y": 1.1 + "x": 0.63, + "y": 0.58 }, "ColliderOffset": { - "x": 0.005000055, - "y": 0.55 + "x": 0.0449999869, + "y": 0.29 }, "IsLegacy": false, "Enable": true @@ -6934,6 +7258,13 @@ }, "Enable": true }, + { + "@type": "MOD.Core.DamageSkinSettingComponent", + "DamageSkinId": { + "DataId": "02c22d93421b4038b3c413b3e40b57ec" + }, + "Enable": true + }, { "@type": "script.CombatMonster", "Enable": true, @@ -6945,167 +7276,24 @@ } }, { - "id": "aa4b464c-a4ea-48e7-a475-9015771ca2ef", - "path": "/maps/map01/변형된 달팽이", - "componentNames": "MOD.Core.TransformComponent,MOD.Core.StateAnimationComponent,MOD.Core.SpriteRendererComponent,MOD.Core.RigidbodyComponent,MOD.Core.MovementComponent,MOD.Core.StateComponent,MOD.Core.HitComponent,MOD.Core.DamageSkinSpawnerComponent,script.Monster,script.MonsterAttack,MOD.Core.KinematicbodyComponent,MOD.Core.SideviewbodyComponent,script.CombatMonster", + "id": "000005e2-0000-4000-8000-0000000005e2", + "path": "/maps/map01/boss_7", + "componentNames": "MOD.Core.TransformComponent,MOD.Core.StateAnimationComponent,MOD.Core.SpriteRendererComponent,MOD.Core.RigidbodyComponent,MOD.Core.MovementComponent,MOD.Core.StateComponent,MOD.Core.HitComponent,MOD.Core.DamageSkinSpawnerComponent,script.Monster,script.MonsterAttack,MOD.Core.KinematicbodyComponent,MOD.Core.SideviewbodyComponent,MOD.Core.DamageSkinSettingComponent,script.CombatMonster", "jsonString": { - "name": "변형된 달팽이", - "path": "/maps/map01/변형된 달팽이", + "name": "boss_7", + "path": "/maps/map01/boss_7", "nameEditable": true, "enable": true, "visible": true, "localize": false, - "displayOrder": 8, + "displayOrder": 4, "pathConstraints": "///", - "revision": 1, - "origin": { - "type": "Model", - "entry_id": "MoveMonster", - "sub_entity_id": null, - "root_entity_id": "aa4b464c-a4ea-48e7-a475-9015771ca2ef", - "replaced_model_id": null - }, - "modelId": "movemonster", - "@components": [ - { - "@type": "MOD.Core.TransformComponent", - "Position": { - "x": 5, - "y": 0.03499998, - "z": 999.999 - }, - "QuaternionRotation": { - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "Enable": true - }, - { - "@type": "MOD.Core.StateAnimationComponent", - "ActionSheet": { - "stand": "be8403cdd1534522ac060ec8497371cd", - "move": "1e24c789979b4ababce55b34861ace25", - "attack": "11eb051bbc134831851e8764befc155e", - "skill": "220a397a05f4460395721591cdcc023d", - "hit": "7200391494bb4dea8109202bb3e2cf47", - "die": "c5d61976c96c4130903ab227c7bf72ce" - }, - "Enable": true - }, - { - "@type": "MOD.Core.SpriteRendererComponent", - "ActionSheet": {}, - "EndFrameIndex": 0, - "RenderSetting": 1, - "SortingLayer": "MapLayer0", - "SpriteRUID": "be8403cdd1534522ac060ec8497371cd", - "StartFrameIndex": 0, - "Enable": true - }, - { - "@type": "MOD.Core.RigidbodyComponent", - "MoveVelocity": { - "x": 0, - "y": 0 - }, - "RealMoveVelocity": { - "x": 0, - "y": 0 - }, - "Enable": true - }, - { - "@type": "MOD.Core.MovementComponent", - "InputSpeed": 0, - "JumpForce": 0, - "Enable": false - }, - { - "@type": "MOD.Core.StateComponent", - "IsLegacy": false, - "Enable": true - }, - { - "@type": "MOD.Core.HitComponent", - "BoxSize": { - "x": 1.42, - "y": 1.42 - }, - "ColliderOffset": { - "x": -0.0500000119, - "y": 0.69 - }, - "IsLegacy": false, - "Enable": true - }, - { - "@type": "MOD.Core.DamageSkinSpawnerComponent", - "Enable": true - }, - { - "@type": "script.Monster", - "Enable": true, - "IsDead": false - }, - { - "@type": "script.MonsterAttack", - "Enable": true, - "SpriteSize": { - "x": 0, - "y": 0 - }, - "PositionOffset": { - "x": 0, - "y": 0 - } - }, - { - "@type": "MOD.Core.KinematicbodyComponent", - "MoveVelocity": { - "x": 0, - "y": 0 - }, - "Enable": true - }, - { - "@type": "MOD.Core.SideviewbodyComponent", - "MoveVelocity": { - "x": 0, - "y": 0 - }, - "Enable": true - }, - { - "@type": "script.CombatMonster", - "Enable": true, - "EnemyId": "modified_snail", - "Group": "elite" - } - ], - "@version": 1 - } - }, - { - "id": "5d2a7707-d1d2-4f89-b87f-86786f4a305e", - "path": "/maps/map01/monster-2326", - "componentNames": "MOD.Core.TransformComponent,MOD.Core.StateAnimationComponent,MOD.Core.SpriteRendererComponent,MOD.Core.RigidbodyComponent,MOD.Core.MovementComponent,MOD.Core.StateComponent,MOD.Core.HitComponent,MOD.Core.DamageSkinSpawnerComponent,script.Monster,script.MonsterAttack,MOD.Core.KinematicbodyComponent,MOD.Core.SideviewbodyComponent,script.CombatMonster", - "jsonString": { - "name": "monster-2326", - "path": "/maps/map01/monster-2326", - "nameEditable": true, - "enable": true, - "visible": true, - "localize": false, - "displayOrder": 9, - "pathConstraints": "///", - "revision": 1, + "revision": 2, "origin": { "type": "Model", "entry_id": "ChaseMonster", "sub_entity_id": null, - "root_entity_id": "5d2a7707-d1d2-4f89-b87f-86786f4a305e", + "root_entity_id": "000005e2-0000-4000-8000-0000000005e2", "replaced_model_id": null }, "modelId": "chasemonster", @@ -7113,7 +7301,7 @@ { "@type": "MOD.Core.TransformComponent", "Position": { - "x": 4, + "x": 4.6, "y": 0.03499998, "z": 999.999 }, @@ -7123,18 +7311,19 @@ "z": 0, "w": 1 }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, "Enable": true }, { "@type": "MOD.Core.StateAnimationComponent", "ActionSheet": { - "move": "297ad7bed08046e8a4a0bb47fed0f7d1", - "stand": "6401ef4c5ccb4269a5c97831fa8fd329", - "jump": "e4ae46df71794213b2a5432906ed1f85", - "attack": "763ee1a40786483f9671435c1a8e8537", - "skill": "bf6150842fff4f4aa0f3e8e97b574e59", - "hit": "a660f29769d74e4b81c49d9435a7f5b2", - "die": "193c446258954f9cb7dfddab7b498bd8" + "stand": "f86992ba9c41487c8480fcb893fcbda6", + "hit": "d305b942b1704c8084548108ff3b7a6b", + "die": "5a563e5fd98c4132b61057dc6bb8aaf2" }, "Enable": true }, @@ -7144,7 +7333,7 @@ "EndFrameIndex": 0, "RenderSetting": 1, "SortingLayer": "MapLayer0", - "SpriteRUID": "6401ef4c5ccb4269a5c97831fa8fd329", + "SpriteRUID": "f86992ba9c41487c8480fcb893fcbda6", "StartFrameIndex": 0, "Enable": true }, @@ -7174,12 +7363,12 @@ { "@type": "MOD.Core.HitComponent", "BoxSize": { - "x": 2.19, - "y": 1.39 + "x": 0.63, + "y": 0.58 }, "ColliderOffset": { - "x": 0.335000038, - "y": 0.695 + "x": 0.0449999869, + "y": 0.29 }, "IsLegacy": false, "Enable": true @@ -7221,6 +7410,13 @@ }, "Enable": true }, + { + "@type": "MOD.Core.DamageSkinSettingComponent", + "DamageSkinId": { + "DataId": "02c22d93421b4038b3c413b3e40b57ec" + }, + "Enable": true + }, { "@type": "script.CombatMonster", "Enable": true, diff --git a/map/map02.map b/map/map02.map index 347f976..62211eb 100644 --- a/map/map02.map +++ b/map/map02.map @@ -6508,7 +6508,7 @@ { "@type": "script.CombatMonster", "Enable": true, - "EnemyId": "green_mushroom", + "EnemyId": "pig", "Group": "combat" } ], @@ -6660,7 +6660,7 @@ { "@type": "script.CombatMonster", "Enable": true, - "EnemyId": "pig", + "EnemyId": "green_mushroom", "Group": "combat" } ], @@ -6812,7 +6812,7 @@ { "@type": "script.CombatMonster", "Enable": true, - "EnemyId": "blue_mushroom", + "EnemyId": "stump", "Group": "combat" } ], diff --git a/map/map03.map b/map/map03.map index 5e99a4b..9ae6f0e 100644 --- a/map/map03.map +++ b/map/map03.map @@ -6508,7 +6508,7 @@ { "@type": "script.CombatMonster", "Enable": true, - "EnemyId": "green_mushroom", + "EnemyId": "pig", "Group": "combat" } ], @@ -6660,7 +6660,7 @@ { "@type": "script.CombatMonster", "Enable": true, - "EnemyId": "blue_mushroom", + "EnemyId": "red_snail", "Group": "combat" } ], diff --git a/map/map04.map b/map/map04.map index 089c75d..0025b22 100644 --- a/map/map04.map +++ b/map/map04.map @@ -6508,7 +6508,7 @@ { "@type": "script.CombatMonster", "Enable": true, - "EnemyId": "pig", + "EnemyId": "blue_mushroom", "Group": "combat" } ], @@ -6660,7 +6660,7 @@ { "@type": "script.CombatMonster", "Enable": true, - "EnemyId": "blue_mushroom", + "EnemyId": "stump", "Group": "combat" } ], @@ -6812,7 +6812,7 @@ { "@type": "script.CombatMonster", "Enable": true, - "EnemyId": "orange_mushroom", + "EnemyId": "green_mushroom", "Group": "combat" } ], diff --git a/map/map05.map b/map/map05.map index 0d98a4b..11cb1b1 100644 --- a/map/map05.map +++ b/map/map05.map @@ -6508,7 +6508,7 @@ { "@type": "script.CombatMonster", "Enable": true, - "EnemyId": "pig", + "EnemyId": "blue_mushroom", "Group": "combat" } ], @@ -6660,7 +6660,7 @@ { "@type": "script.CombatMonster", "Enable": true, - "EnemyId": "orange_mushroom", + "EnemyId": "green_mushroom", "Group": "combat" } ], @@ -6812,7 +6812,7 @@ { "@type": "script.CombatMonster", "Enable": true, - "EnemyId": "green_mushroom", + "EnemyId": "pig", "Group": "combat" } ], diff --git a/tools/balance/sim-balance.mjs b/tools/balance/sim-balance.mjs index 8ac149e..ca3ee65 100644 --- a/tools/balance/sim-balance.mjs +++ b/tools/balance/sim-balance.mjs @@ -74,7 +74,7 @@ export function loadData() { // 이며, Lua에 대응 AI가 없다(동기화 대상은 데미지/방어/의도/승패 규칙이지 플레이어 선택이 아님). // 손패에서 낼 카드 인덱스(-1=종료). 파워 우선(지속 가치) → 공격 → 스킬. export function chooseAction(hand, cards, energy) { - const entries = hand.map((id, i) => ({ id, i })).filter((x) => cards[x.id].cost <= energy); + const entries = hand.map((id, i) => ({ id, i })).filter((x) => cards[x.id] && cards[x.id].cost <= energy && !cards[x.id].unplayable); const powers = entries.filter((x) => cards[x.id].kind === 'Power'); const attacks = entries.filter((x) => cards[x.id].kind === 'Attack'); const skills = entries.filter((x) => cards[x.id].kind === 'Skill'); @@ -202,7 +202,12 @@ export function simulateCombat(data, rng, stats) { if (c.draw) draw(c.draw); if (aliveList().length === 0) return { win: true, turns, playerHpRemaining: pHp }; } + // 화상(endTurnDamage) — 손패에 있으면 턴 종료 시 피해 (Lua EndPlayerTurn 동기화) + let burn = 0; + for (const hid of hand) { const hc = cards[hid]; if (hc && hc.endTurnDamage) burn += hc.endTurnDamage; } + if (burn > 0) { pHp -= burn; if (pHp < 0) pHp = 0; } discard.push(...hand); hand = []; + if (pHp <= 0) return { win: false, turns, playerHpRemaining: 0 }; // 플레이어 디버프 감소 — Lua EndPlayerTurn 동기화 (적 행동 전) if (pWeak > 0) pWeak--; if (pVuln > 0) pVuln--; @@ -215,7 +220,8 @@ export function simulateCombat(data, rng, stats) { if (m.hp <= 0) { m.hp = 0; m.alive = false; continue; } } m.block = 0; // 매 턴 초기화 (이전 턴 블록 미이월) - const it = m.intents[m.intentIdx]; + // 정의된 intent 중 랜덤 선택 (Lua EnemyActStep 동기화 — 순차→랜덤) + const it = m.intents.length ? m.intents[Math.floor(rng() * m.intents.length)] : null; if (it) { if (it.kind === 'Attack') { const atk = calcAttack(it.value, m.str, m.weak, pVuln); @@ -224,9 +230,12 @@ export function simulateCombat(data, rng, stats) { else if (it.kind === 'Debuff') { if (it.effect === 'weak') pWeak += it.value; else if (it.effect === 'vuln') pVuln += it.value; + } else if (it.kind === 'AddCard') { + // StS2식 덱 오염 — 저주 카드를 버린 더미에 추가 (Lua 동기화) + const cnt = it.count || 1; + for (let k = 0; k < cnt; k++) discard.push(it.card); } } - m.intentIdx = (m.intentIdx + 1) % m.intents.length; // 적 디버프 감소 — Lua EnemyActStep 동기화 (자기 행동 후) if (m.weak > 0) m.weak--; if (m.vuln > 0) m.vuln--; diff --git a/tools/balance/sim-balance.test.mjs b/tools/balance/sim-balance.test.mjs index d9130a1..82d77cf 100644 --- a/tools/balance/sim-balance.test.mjs +++ b/tools/balance/sim-balance.test.mjs @@ -345,3 +345,33 @@ test('simulateCombat: draw — 카드 드로로 손패 보충', () => { assert.ok(r.turns <= 2, `seed ${s}: ${r.turns}턴`); } }); + +test('chooseAction: unplayable(저주) 카드는 건너뜀', () => { + const cards = { Strike: { cost: 1, kind: 'Attack', damage: 6 }, Wound: { cost: 0, kind: 'Status', unplayable: true } }; + assert.equal(chooseAction(['Wound', 'Strike'], cards, 3), 1); // Strike 선택 + assert.equal(chooseAction(['Wound'], cards, 3), -1); // 낼 카드 없음 +}); + +test('simulateCombat: AddCard intent가 저주를 덱에 추가(오염)', () => { + const data = { + cards: { Hit: { name: '히트', cost: 1, kind: 'Attack', damage: 1 }, Wound: { name: '상처', cost: 0, kind: 'Status', unplayable: true } }, + starterDeck: ['Hit', 'Hit', 'Hit', 'Hit', 'Hit'], + monsters: [{ name: '오염자', maxHp: 9999, intents: [{ kind: 'AddCard', card: 'Wound', count: 1 }] }], + }; + // 적은 공격 안 하고 매 턴 저주만 추가 → 플레이어 무피해(승리 불가, 9999hp) → 무승부, 사망 아님 + const r = simulateCombat(data, mulberry32(1)); + assert.equal(r.win, false); + assert.equal(r.draw, true); +}); + +test('simulateCombat: endTurnDamage(화상)이 턴 종료 시 누적 피해', () => { + const data = { + cards: { Skip: { name: '대기', cost: 3, kind: 'Skill', block: 0 }, Burn: { name: '화상', cost: 0, kind: 'Status', unplayable: true, endTurnDamage: 2 } }, + starterDeck: ['Burn', 'Skip', 'Skip', 'Skip', 'Skip'], + monsters: [{ name: '무공격', maxHp: 9999, intents: [{ kind: 'Defend', value: 0 }] }], + }; + // 적은 방어만(무피해). 손패의 Burn이 매 턴 -2 → 80hp 잠식 → MAX_TURNS 전 사망 → win false(draw 아님) + const r = simulateCombat(data, mulberry32(1)); + assert.equal(r.win, false); + assert.notEqual(r.draw, true); +}); diff --git a/tools/deck/gen-slaydeck.mjs b/tools/deck/gen-slaydeck.mjs index 421aa14..512b606 100644 --- a/tools/deck/gen-slaydeck.mjs +++ b/tools/deck/gen-slaydeck.mjs @@ -90,8 +90,10 @@ function luaPotionsTable(potions) { function luaIntentsArray(intents) { return '{ ' + intents.map((it) => { - const fields = [`kind = ${luaStr(it.kind)}`, `value = ${it.value}`]; + const fields = [`kind = ${luaStr(it.kind)}`, `value = ${it.value != null ? it.value : 0}`]; if (it.effect != null) fields.push(`effect = ${luaStr(it.effect)}`); + if (it.card != null) fields.push(`card = ${luaStr(it.card)}`); + if (it.count != null) fields.push(`count = ${it.count}`); return `{ ${fields.join(', ')} }`; }).join(', ') + ' }'; } @@ -131,6 +133,9 @@ function luaCardsTable(cards) { 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.unplayable === true) fields.push('unplayable = true'); + if (c.curse === true) fields.push('curse = true'); + if (c.endTurnDamage != null) fields.push(`endTurnDamage = ${c.endTurnDamage}`); if (c.image != null) fields.push(`image = ${luaStr(c.image)}`); return `\t${id} = { ${fields.join(', ')} },`; }); @@ -2761,37 +2766,64 @@ for i = 1, #reg do reg[i].entity:SetVisible(false) end end -local list = {} +local byGroup = {} for i = 1, #reg do local r = reg[i] - if 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 + if r.entity ~= nil and isvalid(r.entity) and (r.map == nil or r.map == "" or pmap == "" or r.map == pmap) then + local gg = r.group + if gg == nil or gg == "" then gg = "combat" end + if byGroup[gg] == nil then byGroup[gg] = {} end local x = 0 if r.entity.TransformComponent ~= nil then x = r.entity.TransformComponent.WorldPosition.x end - table.insert(list, { entity = r.entity, enemyId = r.enemyId, x = x }) + table.insert(byGroup[gg], { entity = r.entity, enemyId = r.enemyId, x = x }) end end -table.sort(list, function(a, b) return a.x < b.x end) +-- 노드 타입별 랜덤 구성: 일반 1~3 / 엘리트 1+일반0~2 / 보스 1 +local chosen = {} +local function takeFrom(key, k) + local src = byGroup[key] or {} + local pool = {} + for i = 1, #src do pool[i] = src[i] end + self:Shuffle(pool) + local taken = 0 + for i = 1, #pool do + if taken >= k then break end + table.insert(chosen, pool[i]) + taken = taken + 1 + end +end +if g == "boss" then + takeFrom("boss", 1) +elseif g == "elite" then + takeFrom("elite", 1) + takeFrom("combat", math.random(0, 2)) +else + takeFrom("combat", math.random(1, 3)) +end +if #chosen == 0 then takeFrom(g, 1) end +if #chosen == 0 then takeFrom("combat", 1) end +table.sort(chosen, function(a, b) return a.x < b.x end) local mult = 1 + (self.Floor - 1) * 0.45 if g == "elite" or g == "boss" then mult = mult + self:AscEliteBonus() end -local n = #list +local n = #chosen if n > ${MAX_MONSTERS} then n = ${MAX_MONSTERS} end for i = 1, n do - local item = list[i] + local item = chosen[i] local e = self.Enemies[item.enemyId] if e == nil then e = { name = item.enemyId, maxHp = 10, intents = { { kind = "Attack", value = 5 } } } end local intents = {} for k = 1, #e.intents do - local v = e.intents[k].value + local v = e.intents[k].value or 0 if e.intents[k].kind == "Attack" then v = math.floor(v * mult * self:AscAtkMult()) elseif e.intents[k].kind ~= "Debuff" then v = math.floor(v * mult) end - intents[k] = { kind = e.intents[k].kind, value = v, effect = e.intents[k].effect } + intents[k] = { kind = e.intents[k].kind, value = v, effect = e.intents[k].effect, card = e.intents[k].card, count = e.intents[k].count } end local maxHp = math.floor(e.maxHp * mult * self:AscHpMult()) local hitClip = nil @@ -2802,10 +2834,12 @@ for i = 1, n do standClip = item.entity.StateAnimationComponent.ActionSheet["stand"] end) end + local startIdx = 1 + if #intents > 0 then startIdx = math.random(1, #intents) end self.Monsters[i] = { entity = item.entity, enemyId = item.enemyId, name = e.name, hp = maxHp, maxHp = maxHp, block = 0, str = 0, weak = 0, vuln = 0, poison = 0, hitClip = hitClip, standClip = standClip, motionBusy = false, - intents = intents, intentIdx = 1, alive = true, slot = i } + intents = intents, intentIdx = startIdx, alive = true, slot = i } self:ReviveMonsterEntity(item.entity) self:PositionMonsterSlot(i) end @@ -3026,6 +3060,17 @@ self:RenderCombat()`), method('EndPlayerTurn', `if self.CombatOver == true or self.FxBusy == true or self.TurnBusy == true then return end +local burn = 0 +for bi = 1, #self.Hand do +\tlocal hc = self.Cards[self.Hand[bi]] +\tif hc ~= nil and hc.endTurnDamage ~= nil then burn = burn + hc.endTurnDamage end +end +if burn > 0 then +\tself.PlayerHp = self.PlayerHp - burn +\tif self.PlayerHp < 0 then self.PlayerHp = 0 end +\tself:ShowPlayerDmgPop(burn) +\tself:RenderCombat() +end for i = 1, #self.Hand do \ttable.insert(self.DiscardPile, self.Hand[i]) end @@ -3279,6 +3324,10 @@ local c = self.Cards[cardId] if c == nil then return end +if c.unplayable == true then + self:Toast("사용할 수 없는 카드입니다") + return +end if self.Energy < c.cost then self:Toast("에너지가 부족합니다") return @@ -3623,11 +3672,20 @@ _TimerService:SetTimerOnce(function() elseif intent.effect == "vuln" then self.PlayerVuln = self.PlayerVuln + intent.value end + elseif intent.kind == "AddCard" then + local cnt = intent.count or 1 + for ci = 1, cnt do + table.insert(self.DiscardPile, intent.card) + end + self:RenderPiles() + local cn = intent.card + local cc = self.Cards[intent.card] + if cc ~= nil then cn = cc.name end + self:Toast(m.name .. ": " .. cn .. " 추가!") end end - m.intentIdx = m.intentIdx + 1 - if m.intentIdx > #m.intents then - m.intentIdx = 1 + if #m.intents > 0 then + m.intentIdx = math.random(1, #m.intents) end if m.weak > 0 then m.weak = m.weak - 1 end if m.vuln > 0 then m.vuln = m.vuln - 1 end @@ -3828,6 +3886,8 @@ return table.concat(parts, " ")`, [ elseif intent.kind == "Debuff" then if intent.effect == "weak" then t = "약화 " .. tostring(intent.value) .. " 부여" else t = "취약 " .. tostring(intent.value) .. " 부여" end + elseif intent.kind == "AddCard" then + t = "저주 카드 추가" end end self:SetText(base .. "/Intent", t) @@ -3838,6 +3898,8 @@ return table.concat(parts, " ")`, [ intentEntity.TextComponent.FontColor = Color(1, 0.45, 0.35, 1) elseif intent.kind == "Debuff" then intentEntity.TextComponent.FontColor = Color(0.8, 0.5, 1, 1) + elseif intent.kind == "AddCard" then + intentEntity.TextComponent.FontColor = Color(0.6, 0.85, 0.4, 1) else intentEntity.TextComponent.FontColor = Color(0.5, 0.75, 1, 1) end diff --git a/tools/map/gen-map-encounters.mjs b/tools/map/gen-map-encounters.mjs index 103a190..ffc7694 100644 --- a/tools/map/gen-map-encounters.mjs +++ b/tools/map/gen-map-encounters.mjs @@ -2,15 +2,24 @@ import { readFileSync, writeFileSync } from 'node:fs'; // map02~11에 노드 타입별 몬스터 그룹(combat3/elite2/boss1)을 맵별 테마로 자동 구성. // 기존 몬스터 엔티티를 전부 제거하고 첫 몬스터를 템플릿으로 6마리 재생성(결정론). -const MAP_NUMBERS = [2, 3, 4, 5]; -const COMBAT_POOL = ['orange_mushroom', 'green_mushroom', 'pig', 'blue_mushroom']; +const MAP_NUMBERS = [1, 2, 3, 4, 5]; +const COMBAT_POOL = ['orange_mushroom', 'green_mushroom', 'pig', 'blue_mushroom', 'red_snail', 'stump']; const ELITE_POOL = ['mushmom', 'modified_snail']; const BOSS_POOL = ['king_slime', 'slime_boss']; -const LAYOUT = [ +// map01: StS2식 일반 5종 + 엘리트 1 + 보스 1(보스 노드용, 화면 우측 포메이션). +// 그 외 맵: 일반 3 + 엘리트 2 + 보스 1. 전투 시 BuildMonsters가 노드 타입별로 1~3마리 랜덤 추첨. +const LAYOUT_MAP01 = [ + { group: 'combat', x: 2.6 }, { group: 'combat', x: 3.6 }, { group: 'combat', x: 4.6 }, + { group: 'combat', x: 5.6 }, { group: 'combat', x: 6.6 }, + { group: 'elite', x: 4.6 }, + { group: 'boss', x: 4.6 }, +]; +const LAYOUT_DEFAULT = [ { group: 'combat', x: 2.3 }, { group: 'combat', x: 3.8 }, { group: 'combat', x: 5.2 }, { group: 'elite', x: 3.0 }, { group: 'elite', x: 5.0 }, { group: 'boss', x: 4.0 }, ]; +const layoutFor = (nn) => (nn === 1 ? LAYOUT_MAP01 : LAYOUT_DEFAULT); const MONSTER_VARIANTS = [ { sprite: '96e955c1bf27415e84f96deea200a8f1', stand: '96e955c1bf27415e84f96deea200a8f1', hit: 'aec9504d5dc24aceb5646b79d30abad4', die: '65a2bfb039614f2e9e4ccc354340153d' }, { sprite: 'f86992ba9c41487c8480fcb893fcbda6', stand: 'f86992ba9c41487c8480fcb893fcbda6', hit: 'd305b942b1704c8084548108ff3b7a6b', die: '5a563e5fd98c4132b61057dc6bb8aaf2' }, @@ -54,13 +63,17 @@ function patchMap(nn) { const template = monsters[0]; map.ContentProto.Entities = ents.filter((e) => !isMonster(e)); const rand = rng(nn * 7919 + 17); - const combatIds = pickN(rand, COMBAT_POOL, 3); - const eliteIds = pickN(rand, ELITE_POOL, 2); + const layout = layoutFor(nn); + const nCombat = layout.filter((s) => s.group === 'combat').length; + const nElite = layout.filter((s) => s.group === 'elite').length; + const combatIds = pickN(rand, COMBAT_POOL, nCombat); + const eliteIds = pickN(rand, ELITE_POOL, nElite); const bossId = pick(rand, BOSS_POOL); - const variants = pickN(rand, MONSTER_VARIANTS, 6); - LAYOUT.forEach((slot, idx) => { + const variants = pickN(rand, MONSTER_VARIANTS, layout.length); + let ci = 0, ei = 0; + layout.forEach((slot, idx) => { const m = JSON.parse(JSON.stringify(template)); - const enemyId = slot.group === 'combat' ? combatIds[idx] : slot.group === 'elite' ? eliteIds[idx - 3] : bossId; + const enemyId = slot.group === 'combat' ? combatIds[ci++] : slot.group === 'elite' ? eliteIds[ei++] : bossId; const name = `${slot.group}_${idx + 1}`; m.id = encGuid(nn, idx); m.path = `/maps/map${tag}/${name}`;