balance: 전사 계열 5섹션 성장 곡선 조정

This commit is contained in:
2026-07-04 02:22:17 +09:00
parent 90494232bc
commit 758ebd19f2
6 changed files with 153 additions and 91 deletions

View File

@@ -6,7 +6,7 @@ const cardsData = JSON.parse(readFileSync('data/cards.json', 'utf8'));
const enemiesData = JSON.parse(readFileSync('data/enemies.json', 'utf8'));
const relicsData = JSON.parse(readFileSync('data/relics.json', 'utf8'));
const PLAYER_MAX_HP = 70;
const PLAYER_MAX_HP = { rogue: 70, warrior: 80 };
const REST_HEAL = 30;
const SECTION_COUNT = 5;
const NORMAL_FIGHTS = 4;
@@ -18,6 +18,8 @@ const BOSS_POOL = ['king_slime', 'slime_boss'];
const JOBS = {
thief: { tier2: 'thief', tier3: 'thiefmaster', tier2Starter: 'DaggerAcceleration', tier3Starter: 'Venom' },
assassin: { tier2: 'assassin', tier3: 'hermit', tier2Starter: 'JavelinAcceleration', tier3Starter: 'SpiritJavelin' },
fighter: { root: 'warrior', tier2: 'fighter', tier3: 'crusader', tier2Starter: 'ComboAttack', tier3Starter: 'ComboSynergy' },
page: { root: 'warrior', tier2: 'page', tier3: 'knight', tier2Starter: 'HolyCharge', tier3Starter: 'DivineCharge' },
};
const LINEAGES = {
@@ -26,12 +28,17 @@ const LINEAGES = {
thiefmaster: ['rogue', 'thief', 'thiefmaster'],
assassin: ['rogue', 'assassin'],
hermit: ['rogue', 'assassin', 'hermit'],
warrior: ['warrior'],
fighter: ['warrior', 'fighter'],
crusader: ['warrior', 'fighter', 'crusader'],
page: ['warrior', 'page'],
knight: ['warrior', 'page', 'knight'],
};
const pick = (rng, values) => values[Math.floor(rng() * values.length)];
export function campaignJobAtSection(branch, section) {
if (section <= 1) return 'rogue';
if (section <= 1) return JOBS[branch].root || 'rogue';
if (section === 2) return JOBS[branch].tier2;
return JOBS[branch].tier3;
}
@@ -102,11 +109,22 @@ function branchCardValue(card, branch, deck, id) {
value += card.sly ? 5 : 0;
value += (card.discard || 0) * 2 + (card.drawPerDiscarded || 0) * 4;
value += (card.poisonApplicationBurstDamage || 0) * 1.5;
} else {
} else if (branch === 'assassin') {
value += (card.addShiv || 0) * 3 + (card.turnStartShiv || 0) * 8;
value += (card.shivDamageBonus || 0) * 6 + (card.firstShivDamageBonus || 0) * 3;
value += card.shivAoe ? 12 : 0;
value += card.shivRetain ? 5 : 0;
} else if (branch === 'fighter') {
value += (card.hits || 1) * 1.5;
value += (card.comboGain || 0) * 5 + (card.comboOnAttack || 0) * 10;
value += (card.damagePerCombo || 0) * 8 + (card.attackDamagePerCombo || 0) * 12;
value += (card.attackPlayedDamage || 0) * 5;
} else if (branch === 'page') {
value += (card.block || 0) * 0.5 + (card.cardPlayedBlock || 0) * 7;
value += (card.holyChargeOnHolyForce || 0) * 12 + (card.damagePerHolyCharge || 0) * 7;
value += (card.blockPerHolyCharge || 0) * 6 + (card.healPerHolyCharge || 0) * 3;
value += (card.damageTakenReduction || 0) * 40;
value += (card.blockOnDamaged || 0) * 3 + (card.strengthOnDamagedOnce || 0) * 5;
}
const copies = deck.filter((cardId) => cardId === id).length;
value -= copies * (card.kind === 'Power' ? 10 : 3);
@@ -203,11 +221,13 @@ export function simulateCampaign(branch, rng, {
minimumRewardValue = 10,
} = {}) {
if (!JOBS[branch]) throw new Error(`지원하지 않는 도적 분기: ${branch}`);
const root = JOBS[branch].root || 'rogue';
const maxHp = PLAYER_MAX_HP[root];
const state = {
hp: PLAYER_MAX_HP,
maxHp: PLAYER_MAX_HP,
deck: cardsData.starterDecks.rogue.slice(),
job: 'rogue',
hp: maxHp,
maxHp,
deck: cardsData.starterDecks[root].slice(),
job: root,
turns: 0,
sectionCleared: 0,
diedAt: '',
@@ -306,7 +326,7 @@ function main() {
else if (args[i] === '--scale-step') scaleStep = Number.parseFloat(args[++i]);
else if (args[i] === '--reward-min') minimumRewardValue = Number.parseFloat(args[++i]);
}
for (const branch of ['thief', 'assassin']) {
for (const branch of ['thief', 'assassin', 'fighter', 'page']) {
console.log(formatCampaignReport(runCampaignBatch(branch, runs, seed, { restHeal, sectionHeal, scaleStep, minimumRewardValue })));
}
}