Merge pull request '밴딧 카드 공용 효과 확장 및 문서 정리' (#82) from codex/bandit-effect-pack into main
This commit was merged in pull request #82.
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -728,6 +728,7 @@
|
|||||||
"rarity": "unique",
|
"rarity": "unique",
|
||||||
"desc": "피해를 12 줍니다. 다음에 사용하는 스킬 카드의 비용이 0 이 됩니다.",
|
"desc": "피해를 12 줍니다. 다음에 사용하는 스킬 카드의 비용이 0 이 됩니다.",
|
||||||
"damage": 12,
|
"damage": 12,
|
||||||
|
"nextSkillCostZero": true,
|
||||||
"image": "91a2d1c16cb041549adbf1a0d7b1f37f"
|
"image": "91a2d1c16cb041549adbf1a0d7b1f37f"
|
||||||
},
|
},
|
||||||
"Dash": {
|
"Dash": {
|
||||||
@@ -760,6 +761,7 @@
|
|||||||
"rarity": "unique",
|
"rarity": "unique",
|
||||||
"desc": "피해를 15 줍니다. 이번 턴에 스킬을 사용할 때마다 비용이 1 감소합니다.",
|
"desc": "피해를 15 줍니다. 이번 턴에 스킬을 사용할 때마다 비용이 1 감소합니다.",
|
||||||
"damage": 15,
|
"damage": 15,
|
||||||
|
"skillCostReductionThisTurn": 1,
|
||||||
"image": "1b0f2dc8abd0434990eee1befefcbe0d"
|
"image": "1b0f2dc8abd0434990eee1befefcbe0d"
|
||||||
},
|
},
|
||||||
"CalculatedGamble": {
|
"CalculatedGamble": {
|
||||||
@@ -1127,7 +1129,7 @@
|
|||||||
"class": "bandit",
|
"class": "bandit",
|
||||||
"rarity": "legend",
|
"rarity": "legend",
|
||||||
"desc": "이번 턴 동안 얻는 방어도가 2배가 됩니다.",
|
"desc": "이번 턴 동안 얻는 방어도가 2배가 됩니다.",
|
||||||
"draw": 1,
|
"blockGainMultiplier": 2,
|
||||||
"image": "0946f69d84464df29b24b94c744c868d"
|
"image": "0946f69d84464df29b24b94c744c868d"
|
||||||
},
|
},
|
||||||
"CorrosiveWave": {
|
"CorrosiveWave": {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
## 구현됨
|
## 구현됨
|
||||||
|
|
||||||
`Neutralize`, `SilentStrike`, `Survivor`, `SilentDefend`, `Slice`, `DaggerSpray`, `DaggerThrow`, `PoisonedStab`, `SuckerPunch`, `LeadingStrike`, `FollowThrough`, `FlickFlack`, `Prepared`, `Deflect`, `BladeDance`, `Backflip`, `DodgeAndRoll`, `CloakAndDagger`, `DeadlyPoison`, `Snakebite`, `Untouchable`, `Backstab`, `PreciseCut`, `Finisher`, `MementoMori`, `Flechettes`, `Dash`, `Predator`, `CalculatedGamble`, `HiddenDaggers`, `Acrobatics`, `Blur`, `LegSweep`, `Reflex`, `Haze`, `Tactician`, `WellLaidPlans`, `InfiniteBlades`, `Footwork`, `GrandFinale`, `Adrenaline`, `ShadowStep`, `Assassinate`, `Nightmare`, `ToolsOfTheTrade`, `Afterimage`, `StormOfSteel`, `Abrasive`, `Suppress`, `Expertise`
|
`Neutralize`, `SilentStrike`, `Survivor`, `SilentDefend`, `Slice`, `DaggerSpray`, `DaggerThrow`, `PoisonedStab`, `SuckerPunch`, `LeadingStrike`, `FollowThrough`, `FlickFlack`, `Prepared`, `Deflect`, `BladeDance`, `Backflip`, `DodgeAndRoll`, `CloakAndDagger`, `DeadlyPoison`, `Snakebite`, `Untouchable`, `Backstab`, `PreciseCut`, `Finisher`, `MementoMori`, `Flechettes`, `Dash`, `Predator`, `CalculatedGamble`, `HiddenDaggers`, `Acrobatics`, `Blur`, `LegSweep`, `Reflex`, `Haze`, `Tactician`, `WellLaidPlans`, `InfiniteBlades`, `Footwork`, `GrandFinale`, `Adrenaline`, `ShadowStep`, `Assassinate`, `Nightmare`, `ToolsOfTheTrade`, `Afterimage`, `StormOfSteel`, `Abrasive`, `Suppress`, `Expertise`, `Shadowmeld`, `Pounce`, `Pinpoint`
|
||||||
|
|
||||||
공용 메모:
|
공용 메모:
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
- `turnStartDraw`, `turnStartDiscard` 구현됨
|
- `turnStartDraw`, `turnStartDiscard` 구현됨
|
||||||
- `nextTurnBlock`, `nextTurnDraw`, `nextTurnKeepBlock`, `nextTurnAttackMultiplier`, `nextTurnCopies`, `nextTurnSelectHandCard` 구현됨
|
- `nextTurnBlock`, `nextTurnDraw`, `nextTurnKeepBlock`, `nextTurnAttackMultiplier`, `nextTurnCopies`, `nextTurnSelectHandCard` 구현됨
|
||||||
- `damagePerOtherHandCard`, `damagePerAttackPlayedThisTurn`, `damagePerDiscardedThisTurn`, `damagePerSkillInHand`, `otherHandAtLeast`, `bonusHitsWhenOtherHandAtLeast` 구현됨
|
- `damagePerOtherHandCard`, `damagePerAttackPlayedThisTurn`, `damagePerDiscardedThisTurn`, `damagePerSkillInHand`, `otherHandAtLeast`, `bonusHitsWhenOtherHandAtLeast` 구현됨
|
||||||
- `gainEnergy`, `drawUntilHandSize`, `drawPerDiscarded`, `cardPlayedBlock` 구현됨
|
- `gainEnergy`, `drawUntilHandSize`, `drawPerDiscarded`, `cardPlayedBlock`, `blockGainMultiplier`, `nextSkillCostZero`, `skillCostReductionThisTurn` 구현됨
|
||||||
|
|
||||||
## 부분구현
|
## 부분구현
|
||||||
|
|
||||||
@@ -43,10 +43,6 @@
|
|||||||
|
|
||||||
`Strangle`: 이번 턴 카드 사용마다 추가 피해
|
`Strangle`: 이번 턴 카드 사용마다 추가 피해
|
||||||
|
|
||||||
`Pounce`: 다음 스킬 카드 비용 0
|
|
||||||
|
|
||||||
`Pinpoint`: 이번 턴 스킬 사용 시 비용 감소
|
|
||||||
|
|
||||||
`EscapePlan`: 드로우한 카드가 스킬이면 방어도 3
|
`EscapePlan`: 드로우한 카드가 스킬이면 방어도 3
|
||||||
|
|
||||||
`HandTrick`: 손패의 스킬 카드 하나에 교활 부여
|
`HandTrick`: 손패의 스킬 카드 하나에 교활 부여
|
||||||
@@ -71,7 +67,7 @@
|
|||||||
|
|
||||||
`Malaise`: X코스트 약화/피해 감소
|
`Malaise`: X코스트 약화/피해 감소
|
||||||
|
|
||||||
`Shadowmeld`: 이번 턴 얻는 방어도 2배
|
`Pinpoint`: 이번 턴 스킬 비용 감소
|
||||||
|
|
||||||
`CorrosiveWave`: 드로우할 때마다 독
|
`CorrosiveWave`: 드로우할 때마다 독
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
- `block`: 방어도 획득
|
- `block`: 방어도 획득
|
||||||
- `cardPlayedBlock`: 카드를 사용할 때마다 방어도 획득
|
- `cardPlayedBlock`: 카드를 사용할 때마다 방어도 획득
|
||||||
|
- `blockGainMultiplier`: 이번 턴 동안 얻는 방어도 배수
|
||||||
- `hits`: 다단히트 횟수
|
- `hits`: 다단히트 횟수
|
||||||
- `aoe`: 모든 적 대상
|
- `aoe`: 모든 적 대상
|
||||||
- `pierce`: 방어도 무시
|
- `pierce`: 방어도 무시
|
||||||
@@ -65,6 +66,8 @@
|
|||||||
- `nextTurnCopies`: 다음 턴에 손패에서 가져올 복사본 수
|
- `nextTurnCopies`: 다음 턴에 손패에서 가져올 복사본 수
|
||||||
- `nextTurnSelectHandCard`: 현재 손패에서 카드 1장 선택
|
- `nextTurnSelectHandCard`: 현재 손패에서 카드 1장 선택
|
||||||
- `nextTurnSelectPrompt`: 선택 UI 문구
|
- `nextTurnSelectPrompt`: 선택 UI 문구
|
||||||
|
- `nextSkillCostZero`: 다음 스킬 카드 비용을 0으로 만듦
|
||||||
|
- `skillCostReductionThisTurn`: 이번 턴 스킬 카드 비용을 일정량 감소
|
||||||
|
|
||||||
## 기타
|
## 기타
|
||||||
|
|
||||||
|
|||||||
@@ -90,12 +90,29 @@ function canPlayCardNow(card, ctx = {}) {
|
|||||||
// 이며, Lua에 대응 AI가 없다(동기화 대상은 데미지/방어/의도/승패 규칙이지 플레이어 선택이 아님).
|
// 이며, Lua에 대응 AI가 없다(동기화 대상은 데미지/방어/의도/승패 규칙이지 플레이어 선택이 아님).
|
||||||
// 손패에서 낼 카드 인덱스(-1=종료). 파워 우선(지속 가치) → 공격 → 스킬.
|
// 손패에서 낼 카드 인덱스(-1=종료). 파워 우선(지속 가치) → 공격 → 스킬.
|
||||||
export function chooseAction(hand, cards, energy, ctx = {}) {
|
export function chooseAction(hand, cards, energy, ctx = {}) {
|
||||||
const entries = hand.map((id, i) => ({ id, i })).filter((x) => cards[x.id] && cards[x.id].cost <= energy && !cards[x.id].unplayable && canPlayCardNow(cards[x.id], ctx));
|
const entries = hand.map((id, i) => ({ id, i })).filter((x) => {
|
||||||
|
const card = cards[x.id];
|
||||||
|
if (!card || card.unplayable || !canPlayCardNow(card, ctx)) return false;
|
||||||
|
let effectiveCost = card.cost || 0;
|
||||||
|
if (card.kind === 'Skill') {
|
||||||
|
if (ctx.nextSkillCostZero === true) effectiveCost = 0;
|
||||||
|
else effectiveCost = Math.max(0, effectiveCost - (ctx.skillCostReductionThisTurn || 0));
|
||||||
|
}
|
||||||
|
return effectiveCost <= energy;
|
||||||
|
});
|
||||||
const powers = entries.filter((x) => cards[x.id].kind === 'Power');
|
const powers = entries.filter((x) => cards[x.id].kind === 'Power');
|
||||||
const attacks = entries.filter((x) => cards[x.id].kind === 'Attack');
|
const attacks = entries.filter((x) => cards[x.id].kind === 'Attack');
|
||||||
const skills = entries.filter((x) => cards[x.id].kind === 'Skill');
|
const skills = entries.filter((x) => cards[x.id].kind === 'Skill');
|
||||||
const dmgEff = (x) => (cards[x.id].damage || 0) / Math.max(cards[x.id].cost, 1);
|
const effectiveCost = (card) => {
|
||||||
const blkEff = (x) => (cards[x.id].block || 0) / Math.max(cards[x.id].cost, 1);
|
let cost = card.cost || 0;
|
||||||
|
if (card.kind === 'Skill') {
|
||||||
|
if (ctx.nextSkillCostZero === true) cost = 0;
|
||||||
|
else cost = Math.max(0, cost - (ctx.skillCostReductionThisTurn || 0));
|
||||||
|
}
|
||||||
|
return cost;
|
||||||
|
};
|
||||||
|
const dmgEff = (x) => (cards[x.id].damage || 0) / Math.max(effectiveCost(cards[x.id]), 1);
|
||||||
|
const blkEff = (x) => (cards[x.id].block || 0) / Math.max(effectiveCost(cards[x.id]), 1);
|
||||||
const bestBy = (list, fn) => list.slice().sort((a, b) => fn(b) - fn(a))[0];
|
const bestBy = (list, fn) => list.slice().sort((a, b) => fn(b) - fn(a))[0];
|
||||||
if (powers.length) return powers[0].i;
|
if (powers.length) return powers[0].i;
|
||||||
if (attacks.length) return bestBy(attacks, dmgEff).i;
|
if (attacks.length) return bestBy(attacks, dmgEff).i;
|
||||||
@@ -128,6 +145,9 @@ export function simulateCombat(data, rng, stats) {
|
|||||||
let hand = [];
|
let hand = [];
|
||||||
let pHp = PLAYER_HP, pBlock = 0;
|
let pHp = PLAYER_HP, pBlock = 0;
|
||||||
let pStr = 0, pDex = 0, pThorns = 0, pWeak = 0, pVuln = 0;
|
let pStr = 0, pDex = 0, pThorns = 0, pWeak = 0, pVuln = 0;
|
||||||
|
let blockGainMultiplier = 1;
|
||||||
|
let nextSkillCostZero = false;
|
||||||
|
let skillCostReductionThisTurn = 0;
|
||||||
let nextTurnBlock = 0, nextTurnDraw = 0, nextTurnKeepBlock = false;
|
let nextTurnBlock = 0, nextTurnDraw = 0, nextTurnKeepBlock = false;
|
||||||
let nextTurnAttackMultiplier = 1, turnAttackMultiplier = 1;
|
let nextTurnAttackMultiplier = 1, turnAttackMultiplier = 1;
|
||||||
let nextTurnAddCards = [];
|
let nextTurnAddCards = [];
|
||||||
@@ -161,6 +181,7 @@ export function simulateCombat(data, rng, stats) {
|
|||||||
function addBlock(base) {
|
function addBlock(base) {
|
||||||
let amount = base || 0;
|
let amount = base || 0;
|
||||||
if (amount > 0) amount += pDex;
|
if (amount > 0) amount += pDex;
|
||||||
|
if (blockGainMultiplier > 1) amount *= blockGainMultiplier;
|
||||||
if (amount < 0) amount = 0;
|
if (amount < 0) amount = 0;
|
||||||
pBlock += amount;
|
pBlock += amount;
|
||||||
return amount;
|
return amount;
|
||||||
@@ -243,6 +264,9 @@ export function simulateCombat(data, rng, stats) {
|
|||||||
const alive = aliveList();
|
const alive = aliveList();
|
||||||
let dmg = 0;
|
let dmg = 0;
|
||||||
let blockGained = 0;
|
let blockGained = 0;
|
||||||
|
if (c.blockGainMultiplier && c.blockGainMultiplier > 0) blockGainMultiplier *= c.blockGainMultiplier;
|
||||||
|
if (c.nextSkillCostZero === true) nextSkillCostZero = true;
|
||||||
|
if (c.skillCostReductionThisTurn && c.skillCostReductionThisTurn > 0) skillCostReductionThisTurn += c.skillCostReductionThisTurn;
|
||||||
if (c.kind === 'Attack') {
|
if (c.kind === 'Attack') {
|
||||||
if (alive.length && c.damage) {
|
if (alive.length && c.damage) {
|
||||||
const baseDamage = attackBaseForCard(id, c);
|
const baseDamage = attackBaseForCard(id, c);
|
||||||
@@ -331,6 +355,8 @@ export function simulateCombat(data, rng, stats) {
|
|||||||
turns++;
|
turns++;
|
||||||
turnAttackCardsPlayed = 0;
|
turnAttackCardsPlayed = 0;
|
||||||
turnDiscardedCards = 0;
|
turnDiscardedCards = 0;
|
||||||
|
blockGainMultiplier = 1;
|
||||||
|
skillCostReductionThisTurn = 0;
|
||||||
// 파워 발동 — Lua StartPlayerTurn 동기화 (블록 리셋 후 strength/energy/block 파워)
|
// 파워 발동 — Lua StartPlayerTurn 동기화 (블록 리셋 후 strength/energy/block 파워)
|
||||||
if (nextTurnKeepBlock === true) nextTurnKeepBlock = false;
|
if (nextTurnKeepBlock === true) nextTurnKeepBlock = false;
|
||||||
else pBlock = 0;
|
else pBlock = 0;
|
||||||
@@ -362,12 +388,16 @@ export function simulateCombat(data, rng, stats) {
|
|||||||
while (true) {
|
while (true) {
|
||||||
const alive = aliveList();
|
const alive = aliveList();
|
||||||
if (alive.length === 0) break;
|
if (alive.length === 0) break;
|
||||||
const idx = chooseAction(hand, cards, energy, { drawPileCount: drawPile.length });
|
const idx = chooseAction(hand, cards, energy, { drawPileCount: drawPile.length, nextSkillCostZero, skillCostReductionThisTurn });
|
||||||
if (idx < 0) break;
|
if (idx < 0) break;
|
||||||
const id = hand[idx], c = cards[id];
|
const id = hand[idx], c = cards[id];
|
||||||
energy -= c.cost;
|
const skillFree = c.kind === 'Skill' && nextSkillCostZero === true;
|
||||||
resolveCardEffects(id, c, c.cost);
|
const baseCost = c.cost || 0;
|
||||||
|
const cost = skillFree ? 0 : (c.kind === 'Skill' ? Math.max(0, baseCost - skillCostReductionThisTurn) : baseCost);
|
||||||
|
energy -= cost;
|
||||||
|
resolveCardEffects(id, c, cost);
|
||||||
if (c.kind === 'Attack') turnAttackCardsPlayed++;
|
if (c.kind === 'Attack') turnAttackCardsPlayed++;
|
||||||
|
if (skillFree === true && c.nextSkillCostZero !== true) nextSkillCostZero = false;
|
||||||
const playedBlock = powerFieldTotal('cardPlayedBlock');
|
const playedBlock = powerFieldTotal('cardPlayedBlock');
|
||||||
if (playedBlock > 0) addBlock(playedBlock);
|
if (playedBlock > 0) addBlock(playedBlock);
|
||||||
hand.splice(idx, 1);
|
hand.splice(idx, 1);
|
||||||
|
|||||||
@@ -684,3 +684,39 @@ test("simulateCombat: cardPlayedBlock grants block whenever a card is played", (
|
|||||||
assert.equal(r.draw, true);
|
assert.equal(r.draw, true);
|
||||||
assert.equal(r.playerHpRemaining, 80);
|
assert.equal(r.playerHpRemaining, 80);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("simulateCombat: blockGainMultiplier doubles block gain for the turn", () => {
|
||||||
|
const data = {
|
||||||
|
cards: {
|
||||||
|
Shadow: { name: "Shadowmeld", cost: 1, kind: "Skill", block: 5, blockGainMultiplier: 2 },
|
||||||
|
Shield: { name: "Shield", cost: 1, kind: "Skill", block: 2 },
|
||||||
|
},
|
||||||
|
starterDeck: ["Shadow", "Shield"],
|
||||||
|
monsters: [{ name: "Dummy", maxHp: 9999, intents: [{ kind: "Attack", value: 8 }] }],
|
||||||
|
};
|
||||||
|
const r = simulateCombat(data, () => 0.999999);
|
||||||
|
assert.equal(r.draw, true);
|
||||||
|
assert.equal(r.playerHpRemaining, 80);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("simulateCombat: nextSkillCostZero makes the next skill free", () => {
|
||||||
|
const data = {
|
||||||
|
cards: {
|
||||||
|
Pounce: { name: "Pounce", cost: 2, kind: "Attack", damage: 12, nextSkillCostZero: true },
|
||||||
|
Guard: { name: "Guard", cost: 2, kind: "Skill", block: 8 },
|
||||||
|
},
|
||||||
|
starterDeck: ["Pounce", "Guard"],
|
||||||
|
monsters: [{ name: "Dummy", maxHp: 9999, intents: [{ kind: "Attack", value: 8 }] }],
|
||||||
|
};
|
||||||
|
const r = simulateCombat(data, () => 0.999999);
|
||||||
|
assert.equal(r.draw, true);
|
||||||
|
assert.equal(r.playerHpRemaining, 80);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("chooseAction: skillCostReductionThisTurn allows discounted skills", () => {
|
||||||
|
const cards = {
|
||||||
|
Guard: { name: "Guard", cost: 2, kind: "Skill", block: 8 },
|
||||||
|
};
|
||||||
|
assert.equal(chooseAction(["Guard"], cards, 1, { skillCostReductionThisTurn: 1 }), 0);
|
||||||
|
assert.equal(chooseAction(["Guard"], cards, 1, {}), -1);
|
||||||
|
});
|
||||||
|
|||||||
@@ -44,15 +44,29 @@ end
|
|||||||
if self:CanPlayCardNow(c) ~= true then
|
if self:CanPlayCardNow(c) ~= true then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if self.Energy < c.cost then
|
local cost = c.cost or 0
|
||||||
|
local skillFree = false
|
||||||
|
if c.kind == "Skill" and self.NextSkillCostZero == true then
|
||||||
|
cost = 0
|
||||||
|
skillFree = true
|
||||||
|
end
|
||||||
|
if c.kind == "Skill" and self.SkillCostReductionThisTurn ~= nil and self.SkillCostReductionThisTurn > 0 then
|
||||||
|
cost = math.max(0, cost - self.SkillCostReductionThisTurn)
|
||||||
|
end
|
||||||
|
if self.Energy < cost then
|
||||||
self:Toast("에너지가 부족합니다")
|
self:Toast("에너지가 부족합니다")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
self.Energy = self.Energy - c.cost
|
self.Energy = self.Energy - cost
|
||||||
self:ResolveCardEffects(cardId, slot, c, false)
|
self:ResolveCardEffects(cardId, slot, c, false)
|
||||||
if c.kind == "Attack" then
|
if c.kind == "Attack" then
|
||||||
self.TurnAttackCardsPlayed = (self.TurnAttackCardsPlayed or 0) + 1
|
self.TurnAttackCardsPlayed = (self.TurnAttackCardsPlayed or 0) + 1
|
||||||
end
|
end
|
||||||
|
if skillFree == true then
|
||||||
|
if c.nextSkillCostZero ~= true then
|
||||||
|
self.NextSkillCostZero = false
|
||||||
|
end
|
||||||
|
end
|
||||||
if self:HasPowerField("cardPlayedBlock") == true then
|
if self:HasPowerField("cardPlayedBlock") == true then
|
||||||
self:AddCardBlock(self:AddPowerFieldTotal("cardPlayedBlock"))
|
self:AddCardBlock(self:AddPowerFieldTotal("cardPlayedBlock"))
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -230,8 +230,10 @@ self.TurnAttackCardsPlayed = 0
|
|||||||
self.TurnDiscardedCards = 0
|
self.TurnDiscardedCards = 0
|
||||||
self.NextTurnSelectCopies = 0
|
self.NextTurnSelectCopies = 0
|
||||||
self.NextTurnSelectPrompt = ""
|
self.NextTurnSelectPrompt = ""
|
||||||
|
self.SkillCostReductionThisTurn = 0
|
||||||
self:UpdateDiscardPrompt()
|
self:UpdateDiscardPrompt()
|
||||||
self.Energy = self.MaxEnergy
|
self.Energy = self.MaxEnergy
|
||||||
|
self.BlockGainMultiplier = 1
|
||||||
self:ApplyRelics("turnStart")
|
self:ApplyRelics("turnStart")
|
||||||
if self.NextTurnKeepBlock == true then
|
if self.NextTurnKeepBlock == true then
|
||||||
self.NextTurnKeepBlock = false
|
self.NextTurnKeepBlock = false
|
||||||
|
|||||||
@@ -269,6 +269,9 @@ end, 1 / 60)`, [
|
|||||||
if amount > 0 and self.PlayerDex ~= nil then
|
if amount > 0 and self.PlayerDex ~= nil then
|
||||||
amount = amount + self.PlayerDex
|
amount = amount + self.PlayerDex
|
||||||
end
|
end
|
||||||
|
if self.BlockGainMultiplier ~= nil and self.BlockGainMultiplier > 1 then
|
||||||
|
amount = amount * self.BlockGainMultiplier
|
||||||
|
end
|
||||||
if amount < 0 then
|
if amount < 0 then
|
||||||
amount = 0
|
amount = 0
|
||||||
end
|
end
|
||||||
@@ -367,9 +370,18 @@ if c.nextTurnAttackMultiplier ~= nil and c.nextTurnAttackMultiplier > 0 then
|
|||||||
local cur = self.NextTurnAttackMultiplier or 1
|
local cur = self.NextTurnAttackMultiplier or 1
|
||||||
self.NextTurnAttackMultiplier = cur * c.nextTurnAttackMultiplier
|
self.NextTurnAttackMultiplier = cur * c.nextTurnAttackMultiplier
|
||||||
end`, [{ Type: 'any', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'c' }]),
|
end`, [{ Type: 'any', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'c' }]),
|
||||||
method('ResolveCardEffects', `if c == nil then
|
method('ResolveCardEffects', `if c == nil then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
if c.blockGainMultiplier ~= nil and c.blockGainMultiplier > 0 then
|
||||||
|
self.BlockGainMultiplier = (self.BlockGainMultiplier or 1) * c.blockGainMultiplier
|
||||||
|
end
|
||||||
|
if c.nextSkillCostZero == true then
|
||||||
|
self.NextSkillCostZero = true
|
||||||
|
end
|
||||||
|
if c.skillCostReductionThisTurn ~= nil and c.skillCostReductionThisTurn > 0 then
|
||||||
|
self.SkillCostReductionThisTurn = (self.SkillCostReductionThisTurn or 0) + c.skillCostReductionThisTurn
|
||||||
|
end
|
||||||
if c.kind == "Attack" then
|
if c.kind == "Attack" then
|
||||||
if c.damage ~= nil then
|
if c.damage ~= nil then
|
||||||
self:PlayerAttackMotion()
|
self:PlayerAttackMotion()
|
||||||
|
|||||||
@@ -67,6 +67,9 @@ self:SetText("/ui/RunUIGroup/CombatHud/PlayerPanel/Name", self:JobLabel())
|
|||||||
self.MaxEnergy = 3
|
self.MaxEnergy = 3
|
||||||
self.Turn = 0
|
self.Turn = 0
|
||||||
self.PlayerBlock = 0
|
self.PlayerBlock = 0
|
||||||
|
self.BlockGainMultiplier = 1
|
||||||
|
self.NextSkillCostZero = false
|
||||||
|
self.SkillCostReductionThisTurn = 0
|
||||||
self.PlayerStr = 0
|
self.PlayerStr = 0
|
||||||
self.PlayerDex = 0
|
self.PlayerDex = 0
|
||||||
self.PlayerThorns = 0
|
self.PlayerThorns = 0
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ function writeCodeblocks() {
|
|||||||
prop('number', 'PlayerHp', '0'),
|
prop('number', 'PlayerHp', '0'),
|
||||||
prop('number', 'PlayerMaxHp', '80'),
|
prop('number', 'PlayerMaxHp', '80'),
|
||||||
prop('number', 'PlayerBlock', '0'),
|
prop('number', 'PlayerBlock', '0'),
|
||||||
|
prop('number', 'BlockGainMultiplier', '1'),
|
||||||
prop('number', 'PlayerDex', '0'),
|
prop('number', 'PlayerDex', '0'),
|
||||||
prop('number', 'PlayerThorns', '0'),
|
prop('number', 'PlayerThorns', '0'),
|
||||||
prop('boolean', 'CombatOver', 'false'),
|
prop('boolean', 'CombatOver', 'false'),
|
||||||
@@ -141,6 +142,8 @@ function writeCodeblocks() {
|
|||||||
prop('number', 'TurnAttackMultiplier', '1'),
|
prop('number', 'TurnAttackMultiplier', '1'),
|
||||||
prop('string', 'NextTurnSelectPrompt', '""'),
|
prop('string', 'NextTurnSelectPrompt', '""'),
|
||||||
prop('number', 'NextTurnSelectCopies', '0'),
|
prop('number', 'NextTurnSelectCopies', '0'),
|
||||||
|
prop('boolean', 'NextSkillCostZero', 'false'),
|
||||||
|
prop('number', 'SkillCostReductionThisTurn', '0'),
|
||||||
prop('any', 'NextTurnAddCards'),
|
prop('any', 'NextTurnAddCards'),
|
||||||
], [
|
], [
|
||||||
...bootMethods,
|
...bootMethods,
|
||||||
|
|||||||
@@ -165,6 +165,7 @@ function luaCardsTable(cards) {
|
|||||||
if (c.otherHandAtLeast != null) fields.push(`otherHandAtLeast = ${c.otherHandAtLeast}`);
|
if (c.otherHandAtLeast != null) fields.push(`otherHandAtLeast = ${c.otherHandAtLeast}`);
|
||||||
if (c.bonusHitsWhenOtherHandAtLeast != null) fields.push(`bonusHitsWhenOtherHandAtLeast = ${c.bonusHitsWhenOtherHandAtLeast}`);
|
if (c.bonusHitsWhenOtherHandAtLeast != null) fields.push(`bonusHitsWhenOtherHandAtLeast = ${c.bonusHitsWhenOtherHandAtLeast}`);
|
||||||
if (c.block != null) fields.push(`block = ${c.block}`);
|
if (c.block != null) fields.push(`block = ${c.block}`);
|
||||||
|
if (c.blockGainMultiplier != null) fields.push(`blockGainMultiplier = ${c.blockGainMultiplier}`);
|
||||||
if (c.strength != null) fields.push(`strength = ${c.strength}`);
|
if (c.strength != null) fields.push(`strength = ${c.strength}`);
|
||||||
if (c.dex != null) fields.push(`dex = ${c.dex}`);
|
if (c.dex != null) fields.push(`dex = ${c.dex}`);
|
||||||
if (c.thorns != null) fields.push(`thorns = ${c.thorns}`);
|
if (c.thorns != null) fields.push(`thorns = ${c.thorns}`);
|
||||||
@@ -199,6 +200,8 @@ function luaCardsTable(cards) {
|
|||||||
if (c.nextTurnCopies != null) fields.push(`nextTurnCopies = ${c.nextTurnCopies}`);
|
if (c.nextTurnCopies != null) fields.push(`nextTurnCopies = ${c.nextTurnCopies}`);
|
||||||
if (c.nextTurnSelectHandCard === true) fields.push('nextTurnSelectHandCard = true');
|
if (c.nextTurnSelectHandCard === true) fields.push('nextTurnSelectHandCard = true');
|
||||||
if (c.nextTurnSelectPrompt != null) fields.push(`nextTurnSelectPrompt = ${luaStr(c.nextTurnSelectPrompt)}`);
|
if (c.nextTurnSelectPrompt != null) fields.push(`nextTurnSelectPrompt = ${luaStr(c.nextTurnSelectPrompt)}`);
|
||||||
|
if (c.nextSkillCostZero === true) fields.push('nextSkillCostZero = true');
|
||||||
|
if (c.skillCostReductionThisTurn != null) fields.push(`skillCostReductionThisTurn = ${c.skillCostReductionThisTurn}`);
|
||||||
if (c.innate === true) fields.push('innate = true');
|
if (c.innate === true) fields.push('innate = true');
|
||||||
if (c.playableWhenDrawPileEmpty === true) fields.push('playableWhenDrawPileEmpty = true');
|
if (c.playableWhenDrawPileEmpty === true) fields.push('playableWhenDrawPileEmpty = true');
|
||||||
if (c.sly === true) fields.push('sly = true');
|
if (c.sly === true) fields.push('sly = true');
|
||||||
|
|||||||
Reference in New Issue
Block a user