feat(potions-relics): 유물 15종 효과 훅 (생성기)
- HasRelic·PickNewRelic(미보유 추첨, 소진 시 골드+25) - ApplyRelics 확장: strength/draw/heal/healOnWin/healIfLow + combatEnd 훅 - AddRelic passive: 물약 슬롯 5칸(장인의 벨트)·최대 HP+7(건강의 반지) - CalcPlayerAttack: 아카베코(첫 공격+8)·펜닙(10번째 2배)·부츠(5 미만→5) - DealDamageToPlayer: 브론즈 체인메일 반사·점토 갑옷·백년의 부적 - 챔피언 벨트(취약 부여 시 약화+1), 정예·보스 유물 보상 개선 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -32,10 +32,20 @@ for (const id of RELICS.relicPool) {
|
|||||||
}
|
}
|
||||||
function luaRelicsTable(relics) {
|
function luaRelicsTable(relics) {
|
||||||
const lines = Object.entries(relics).map(([id, r]) =>
|
const lines = Object.entries(relics).map(([id, r]) =>
|
||||||
`\t${id} = { name = ${luaStr(r.name)}, desc = ${luaStr(r.desc)}, hook = ${luaStr(r.hook)}, effect = ${luaStr(r.effect)}, value = ${r.value} },`);
|
`\t${id} = { name = ${luaStr(r.name)}, desc = ${luaStr(r.desc)}, hook = ${luaStr(r.hook)}, effect = ${luaStr(r.effect)}, value = ${r.value}, icon = ${luaStr(r.icon || '')} },`);
|
||||||
return `self.Relics = {\n${lines.join('\n')}\n}`;
|
return `self.Relics = {\n${lines.join('\n')}\n}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const POTIONS = JSON.parse(readFileSync('data/potions.json', 'utf8'));
|
||||||
|
for (const [pid, p] of Object.entries(POTIONS.potions)) {
|
||||||
|
if (!p.name || !p.effect || p.value == null) throw new Error(`[gen-slaydeck] potion 필드 누락: ${pid}`);
|
||||||
|
}
|
||||||
|
function luaPotionsTable(potions) {
|
||||||
|
const lines = Object.entries(potions).map(([id, p]) =>
|
||||||
|
`\t${id} = { name = ${luaStr(p.name)}, desc = ${luaStr(p.desc)}, effect = ${luaStr(p.effect)}, value = ${p.value}, icon = ${luaStr(p.icon || '')} },`);
|
||||||
|
return `self.Potions = {\n${lines.join('\n')}\n}`;
|
||||||
|
}
|
||||||
|
|
||||||
function luaIntentsArray(intents) {
|
function luaIntentsArray(intents) {
|
||||||
return '{ ' + intents.map((it) => {
|
return '{ ' + intents.map((it) => {
|
||||||
const fields = [`kind = ${luaStr(it.kind)}`, `value = ${it.value}`];
|
const fields = [`kind = ${luaStr(it.kind)}`, `value = ${it.value}`];
|
||||||
@@ -1942,6 +1952,15 @@ function writeCodeblocks() {
|
|||||||
prop('number', 'PlayerWeak', '0'),
|
prop('number', 'PlayerWeak', '0'),
|
||||||
prop('number', 'PlayerVuln', '0'),
|
prop('number', 'PlayerVuln', '0'),
|
||||||
prop('any', 'PlayerPowers'),
|
prop('any', 'PlayerPowers'),
|
||||||
|
prop('any', 'Potions'),
|
||||||
|
prop('any', 'RunPotions'),
|
||||||
|
prop('number', 'PotionSlots', String(POTIONS.baseSlots)),
|
||||||
|
prop('string', 'ShopPotion', '""'),
|
||||||
|
prop('boolean', 'ShopPotionBought', 'false'),
|
||||||
|
prop('number', 'FightAttackCount', '0'),
|
||||||
|
prop('boolean', 'FirstHpLossDone', 'false'),
|
||||||
|
prop('number', 'ClayBlockNext', '0'),
|
||||||
|
prop('number', 'PotionMenuSlot', '0'),
|
||||||
], [
|
], [
|
||||||
method('OnBeginPlay', `self:ShowMainMenu()`),
|
method('OnBeginPlay', `self:ShowMainMenu()`),
|
||||||
method('HideGameHud', `self:SetEntityEnabled("/ui/DefaultGroup/Button_Attack", false)
|
method('HideGameHud', `self:SetEntityEnabled("/ui/DefaultGroup/Button_Attack", false)
|
||||||
@@ -2040,6 +2059,9 @@ self.RunLength = ${ACT_COUNT}
|
|||||||
self.RunDeck = { ${CARDS.starterDeck.map(luaStr).join(', ')} }
|
self.RunDeck = { ${CARDS.starterDeck.map(luaStr).join(', ')} }
|
||||||
self.RunActive = true
|
self.RunActive = true
|
||||||
self.RunRelics = {}
|
self.RunRelics = {}
|
||||||
|
self.RunPotions = {}
|
||||||
|
self.PotionSlots = ${POTIONS.baseSlots}
|
||||||
|
${luaPotionsTable(POTIONS.potions)}
|
||||||
${luaRelicsTable(RELICS.relics)}
|
${luaRelicsTable(RELICS.relics)}
|
||||||
self.RelicPool = { ${RELICS.relicPool.map(luaStr).join(', ')} }
|
self.RelicPool = { ${RELICS.relicPool.map(luaStr).join(', ')} }
|
||||||
${luaEnemiesTable(ENEMIES.enemies)}
|
${luaEnemiesTable(ENEMIES.enemies)}
|
||||||
@@ -2049,6 +2071,7 @@ self.CurrentNodeId = ""
|
|||||||
self.CurrentEnemyId = ""
|
self.CurrentEnemyId = ""
|
||||||
self:BindButtons()
|
self:BindButtons()
|
||||||
self:AddRelic("${RELICS.startingRelic}")
|
self:AddRelic("${RELICS.startingRelic}")
|
||||||
|
self:RenderPotions()
|
||||||
self:ShowMap()`),
|
self:ShowMap()`),
|
||||||
method('StartCombat', `self:ShowState("combat")
|
method('StartCombat', `self:ShowState("combat")
|
||||||
self:SetEntityEnabled("/ui/DefaultGroup/CombatHud/Result", false)
|
self:SetEntityEnabled("/ui/DefaultGroup/CombatHud/Result", false)
|
||||||
@@ -2059,6 +2082,9 @@ self.PlayerStr = 0
|
|||||||
self.PlayerWeak = 0
|
self.PlayerWeak = 0
|
||||||
self.PlayerVuln = 0
|
self.PlayerVuln = 0
|
||||||
self.PlayerPowers = {}
|
self.PlayerPowers = {}
|
||||||
|
self.FightAttackCount = 0
|
||||||
|
self.FirstHpLossDone = false
|
||||||
|
self.ClayBlockNext = 0
|
||||||
self.CombatOver = false
|
self.CombatOver = false
|
||||||
self.DiscardPile = {}
|
self.DiscardPile = {}
|
||||||
self.Hand = {}
|
self.Hand = {}
|
||||||
@@ -2256,6 +2282,10 @@ if self.PlayerPowers ~= nil then
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
self.PlayerBlock = 0
|
self.PlayerBlock = 0
|
||||||
|
if self.ClayBlockNext > 0 then
|
||||||
|
self.PlayerBlock = self.PlayerBlock + self.ClayBlockNext
|
||||||
|
self.ClayBlockNext = 0
|
||||||
|
end
|
||||||
self:DrawCards(5)
|
self:DrawCards(5)
|
||||||
self:RenderHand(true)
|
self:RenderHand(true)
|
||||||
self:RenderCombat()`),
|
self:RenderCombat()`),
|
||||||
@@ -2480,10 +2510,21 @@ end, 1 / 60)`, [
|
|||||||
{ Type: 'any', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'toPos' },
|
{ Type: 'any', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'toPos' },
|
||||||
{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'duration' },
|
{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'duration' },
|
||||||
]),
|
]),
|
||||||
method('CalcPlayerAttack', `local dmg = base + self.PlayerStr
|
method('CalcPlayerAttack', `local base2 = base
|
||||||
|
self.FightAttackCount = self.FightAttackCount + 1
|
||||||
|
if self.FightAttackCount == 1 and self:HasRelic("akabeko") then
|
||||||
|
base2 = base2 + 8
|
||||||
|
end
|
||||||
|
local dmg = base2 + self.PlayerStr
|
||||||
|
if self:HasRelic("penNib") and self.FightAttackCount % 10 == 0 then
|
||||||
|
dmg = dmg * 2
|
||||||
|
end
|
||||||
if self.PlayerWeak > 0 then
|
if self.PlayerWeak > 0 then
|
||||||
dmg = math.floor(dmg * 0.75)
|
dmg = math.floor(dmg * 0.75)
|
||||||
end
|
end
|
||||||
|
if dmg > 0 and dmg < 5 and self:HasRelic("boot") then
|
||||||
|
dmg = 5
|
||||||
|
end
|
||||||
if dmg < 0 then
|
if dmg < 0 then
|
||||||
dmg = 0
|
dmg = 0
|
||||||
end
|
end
|
||||||
@@ -2531,7 +2572,12 @@ if c.weak ~= nil or c.vuln ~= nil then
|
|||||||
local tm = self.Monsters[self.TargetIndex]
|
local tm = self.Monsters[self.TargetIndex]
|
||||||
if tm ~= nil and tm.alive == true then
|
if tm ~= nil and tm.alive == true then
|
||||||
if c.weak ~= nil then tm.weak = tm.weak + c.weak end
|
if c.weak ~= nil then tm.weak = tm.weak + c.weak end
|
||||||
if c.vuln ~= nil then tm.vuln = tm.vuln + c.vuln end
|
if c.vuln ~= nil then
|
||||||
|
tm.vuln = tm.vuln + c.vuln
|
||||||
|
if self:HasRelic("championBelt") then
|
||||||
|
tm.weak = tm.weak + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
table.remove(self.Hand, slot)
|
table.remove(self.Hand, slot)
|
||||||
@@ -2695,10 +2741,33 @@ if self.PlayerBlock > 0 then
|
|||||||
self.PlayerBlock = self.PlayerBlock - absorbed
|
self.PlayerBlock = self.PlayerBlock - absorbed
|
||||||
dmg = dmg - absorbed
|
dmg = dmg - absorbed
|
||||||
end
|
end
|
||||||
self.PlayerHp = self.PlayerHp - dmg
|
if dmg > 0 then
|
||||||
|
self.PlayerHp = self.PlayerHp - dmg
|
||||||
|
if self:HasRelic("bronzeScales") and attackerSlot ~= nil and attackerSlot > 0 then
|
||||||
|
local am = self.Monsters[attackerSlot]
|
||||||
|
if am ~= nil and am.alive == true then
|
||||||
|
am.hp = am.hp - 3
|
||||||
|
if am.hp <= 0 then
|
||||||
|
am.hp = 0
|
||||||
|
self:KillMonster(am.slot)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if self:HasRelic("selfFormingClay") then
|
||||||
|
self.ClayBlockNext = self.ClayBlockNext + 3
|
||||||
|
end
|
||||||
|
if self:HasRelic("centennialPuzzle") and self.FirstHpLossDone == false then
|
||||||
|
self.FirstHpLossDone = true
|
||||||
|
self:DrawCards(3)
|
||||||
|
self:RenderHand(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
if self.PlayerHp < 0 then
|
if self.PlayerHp < 0 then
|
||||||
self.PlayerHp = 0
|
self.PlayerHp = 0
|
||||||
end`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'amount' }]),
|
end`, [
|
||||||
|
{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'amount' },
|
||||||
|
{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'attackerSlot' },
|
||||||
|
]),
|
||||||
method('EnemyTurn', `self.TurnBusy = true
|
method('EnemyTurn', `self.TurnBusy = true
|
||||||
self:EnemyActStep(1)`),
|
self:EnemyActStep(1)`),
|
||||||
method('EnemyActStep', `local idx = 0
|
method('EnemyActStep', `local idx = 0
|
||||||
@@ -2725,7 +2794,7 @@ _TimerService:SetTimerOnce(function()
|
|||||||
atk = math.floor(atk * 1.5)
|
atk = math.floor(atk * 1.5)
|
||||||
end
|
end
|
||||||
local before = self.PlayerHp
|
local before = self.PlayerHp
|
||||||
self:DealDamageToPlayer(atk)
|
self:DealDamageToPlayer(atk, idx)
|
||||||
self:ShowPlayerDmgPop(before - self.PlayerHp)
|
self:ShowPlayerDmgPop(before - self.PlayerHp)
|
||||||
elseif intent.kind == "Defend" then
|
elseif intent.kind == "Defend" then
|
||||||
m.block = m.block + intent.value
|
m.block = m.block + intent.value
|
||||||
@@ -2760,12 +2829,31 @@ end
|
|||||||
if anyAlive == false then
|
if anyAlive == false then
|
||||||
self.CombatOver = true
|
self.CombatOver = true
|
||||||
self.Gold = self.Gold + ${GOLD_PER_WIN}
|
self.Gold = self.Gold + ${GOLD_PER_WIN}
|
||||||
|
self:ApplyRelics("combatEnd")
|
||||||
self:ApplyRelics("combatReward")
|
self:ApplyRelics("combatReward")
|
||||||
|
self:MaybeDropPotion()
|
||||||
self:RenderRun()
|
self:RenderRun()
|
||||||
local node = self.MapNodes[self.CurrentNodeId]
|
local node = self.MapNodes[self.CurrentNodeId]
|
||||||
if node ~= nil and node.type == "elite" then
|
if node ~= nil and node.type == "elite" then
|
||||||
self.Gold = self.Gold + 15
|
self.Gold = self.Gold + 15
|
||||||
self:AddRelic(self.RelicPool[math.random(1, #self.RelicPool)])
|
local nid = self:PickNewRelic()
|
||||||
|
if nid ~= "" then
|
||||||
|
self:AddRelic(nid)
|
||||||
|
local nr = self.Relics[nid]
|
||||||
|
if nr ~= nil then
|
||||||
|
self:Toast("유물 획득: " .. nr.name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if node ~= nil and node.type == "boss" then
|
||||||
|
local bid = self:PickNewRelic()
|
||||||
|
if bid ~= "" then
|
||||||
|
self:AddRelic(bid)
|
||||||
|
local br = self.Relics[bid]
|
||||||
|
if br ~= nil then
|
||||||
|
self:Toast("유물 획득: " .. br.name)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
if node ~= nil and node.type == "boss" then
|
if node ~= nil and node.type == "boss" then
|
||||||
if self.Floor < self.RunLength then
|
if self.Floor < self.RunLength then
|
||||||
@@ -2955,6 +3043,15 @@ if hud ~= nil then
|
|||||||
hud.Enable = false
|
hud.Enable = false
|
||||||
end
|
end
|
||||||
self:ShowMap()`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }]),
|
self:ShowMap()`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }]),
|
||||||
|
method('HasRelic', `if self.RunRelics == nil then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
for i = 1, #self.RunRelics do
|
||||||
|
if self.RunRelics[i] == id then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false`, [{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'id' }], 0, 'boolean'),
|
||||||
method('ApplyRelics', `if self.RunRelics == nil then
|
method('ApplyRelics', `if self.RunRelics == nil then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
@@ -2965,11 +3062,23 @@ for i = 1, #self.RunRelics do
|
|||||||
self.PlayerBlock = self.PlayerBlock + r.value
|
self.PlayerBlock = self.PlayerBlock + r.value
|
||||||
elseif r.effect == "energy" then
|
elseif r.effect == "energy" then
|
||||||
self.Energy = self.Energy + r.value
|
self.Energy = self.Energy + r.value
|
||||||
elseif r.effect == "healOnAttack" then
|
elseif r.effect == "strength" then
|
||||||
|
self.PlayerStr = self.PlayerStr + r.value
|
||||||
|
elseif r.effect == "draw" then
|
||||||
|
self:DrawCards(r.value)
|
||||||
|
self:RenderHand(false)
|
||||||
|
elseif r.effect == "heal" or r.effect == "healOnAttack" or r.effect == "healOnWin" then
|
||||||
self.PlayerHp = self.PlayerHp + r.value
|
self.PlayerHp = self.PlayerHp + r.value
|
||||||
if self.PlayerHp > self.PlayerMaxHp then
|
if self.PlayerHp > self.PlayerMaxHp then
|
||||||
self.PlayerHp = self.PlayerMaxHp
|
self.PlayerHp = self.PlayerMaxHp
|
||||||
end
|
end
|
||||||
|
elseif r.effect == "healIfLow" then
|
||||||
|
if self.PlayerHp * 2 <= self.PlayerMaxHp then
|
||||||
|
self.PlayerHp = self.PlayerHp + r.value
|
||||||
|
if self.PlayerHp > self.PlayerMaxHp then
|
||||||
|
self.PlayerHp = self.PlayerMaxHp
|
||||||
|
end
|
||||||
|
end
|
||||||
elseif r.effect == "gold" then
|
elseif r.effect == "gold" then
|
||||||
self.Gold = self.Gold + r.value
|
self.Gold = self.Gold + r.value
|
||||||
end
|
end
|
||||||
@@ -2979,7 +3088,29 @@ end`, [{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], N
|
|||||||
self.RunRelics = {}
|
self.RunRelics = {}
|
||||||
end
|
end
|
||||||
table.insert(self.RunRelics, id)
|
table.insert(self.RunRelics, id)
|
||||||
|
local r = self.Relics[id]
|
||||||
|
if r ~= nil and r.hook == "passive" then
|
||||||
|
if r.effect == "potionSlots" then
|
||||||
|
self.PotionSlots = r.value
|
||||||
|
self:RenderPotions()
|
||||||
|
elseif r.effect == "maxHp" then
|
||||||
|
self.PlayerMaxHp = self.PlayerMaxHp + r.value
|
||||||
|
self.PlayerHp = self.PlayerHp + r.value
|
||||||
|
end
|
||||||
|
end
|
||||||
self:RenderRelics()`, [{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'id' }]),
|
self:RenderRelics()`, [{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'id' }]),
|
||||||
|
method('PickNewRelic', `local pool = {}
|
||||||
|
for i = 1, #self.RelicPool do
|
||||||
|
if self:HasRelic(self.RelicPool[i]) == false then
|
||||||
|
table.insert(pool, self.RelicPool[i])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if #pool == 0 then
|
||||||
|
self.Gold = self.Gold + 25
|
||||||
|
self:Toast("유물을 모두 모았습니다! 골드 +25")
|
||||||
|
return ""
|
||||||
|
end
|
||||||
|
return pool[math.random(1, #pool)]`, [], 0, 'string'),
|
||||||
method('RenderRelics', `local names = ""
|
method('RenderRelics', `local names = ""
|
||||||
if self.RunRelics ~= nil then
|
if self.RunRelics ~= nil then
|
||||||
for i = 1, #self.RunRelics do
|
for i = 1, #self.RunRelics do
|
||||||
|
|||||||
Reference in New Issue
Block a user