/* ============================================================ * Diablo2D — skills.js : class skill trees (data-driven) * Engine implements each `castType`; params scale with rank. * ============================================================ */ 'use strict'; window.D2 = window.D2 || {}; (function (D2) { const MAX_RANK = D2.BAL.skillMaxRank; function L(o) { return o[(D2.i18n && D2.i18n.getLang()) || 'en'] || o.en; } /* ---------------- CRUSADER ---------------- */ const crusader = [ /* Zeal */ { id: 'cleave', branch: 'zeal', tier: 0, type: 'active', maxRank: MAX_RANK, icon: 'cleave', name: { en: 'Cleave', vi: 'Chém Rộng' }, desc: r => ({ en: `Sweeping strike hits all enemies in front for ${100 + 20 * r}% weapon damage.`, vi: `Vung chém trúng mọi kẻ địch phía trước với ${100 + 20 * r}% sát thương vũ khí.` }), manaCost: () => 0, cooldown: 0.42, castType: 'meleeArc', params: r => ({ arcDeg: 160, range: 1.65, dmgPct: 100 + 20 * r }) }, { id: 'whirlwind', branch: 'zeal', tier: 1, type: 'active', maxRank: MAX_RANK, icon: 'whirlwind', name: { en: 'Whirlwind', vi: 'Vòng Xoáy' }, desc: r => ({ en: `Spin for 1s, shredding nearby enemies for ${55 + 11 * r}% weapon damage per tick. You may move while spinning.`, vi: `Xoay trong 1s, xé nát kẻ địch gần với ${55 + 11 * r}% ST vũ khí mỗi nhịp. Di chuyển được khi xoay.` }), manaCost: r => 8 + r, cooldown: 5.5, castType: 'whirlwind', params: r => ({ range: 1.8, dmgPct: 55 + 11 * r, duration: 1.0 }) }, { id: 'consecrate', branch: 'zeal', tier: 2, type: 'active', maxRank: MAX_RANK, icon: 'consecrate', name: { en: 'Consecration', vi: 'Thánh Hóa' }, desc: r => ({ en: `Sanctify the ground at your cursor. Deals ${140 + 34 * r}% weapon damage over 3s to enemies within it.`, vi: `Thánh hóa mặt đất tại con trỏ. Gây ${140 + 34 * r}% ST vũ khí trong 3s lên kẻ địch trong vùng.` }), manaCost: r => 14 + 2 * r, cooldown: 8, castType: 'groundAoE', params: r => ({ radius: 2.6, delay: 0.25, duration: 3, dmgPct: 140 + 34 * r, elem: 'holy' }) }, /* Wrath */ { id: 'charge', branch: 'wrath', tier: 0, type: 'active', maxRank: MAX_RANK, icon: 'charge', name: { en: 'Charge', vi: 'Xung Phong' }, desc: r => ({ en: `Dash forward, damaging enemies in your path for ${130 + 26 * r}% weapon damage and stunning them ${0.5 + 0.14 * r}s.`, vi: `Lao về phía trước, gây ${130 + 26 * r}% ST vũ khí và làm choáng ${0.5 + 0.14 * r}s.` }), manaCost: () => 6, cooldown: 6, castType: 'dash', params: r => ({ distance: 5.2, speed: 16, dmgPct: 130 + 26 * r, stun: 0.5 + 0.14 * r }) }, { id: 'warcry', branch: 'wrath', tier: 1, type: 'active', maxRank: MAX_RANK, icon: 'warcry', name: { en: 'War Cry', vi: 'Chiến Khúc' }, desc: r => ({ en: `Roar: +${15 + 5 * r}% damage and +${12 + 6 * r}% armor for 8s.`, vi: `Gầm thét: +${15 + 5 * r}% sát thương và +${12 + 6 * r}% giáp trong 8s.` }), manaCost: r => 10 + r, cooldown: 18, castType: 'buffSelf', params: r => ({ dur: 8, mods: { dmgPct: 15 + 5 * r, armorPct: 12 + 6 * r }, icon: 'warcry', name: { en: 'War Cry', vi: 'Chiến Khúc' } }) }, { id: 'seismic', branch: 'wrath', tier: 2, type: 'active', maxRank: MAX_RANK, icon: 'seismic', name: { en: 'Seismic Slam', vi: 'Trấn Địa Kích' }, desc: r => ({ en: `Slam the earth: cone shockwave deals ${170 + 38 * r}% weapon damage, knocks back and stuns ${0.8 + 0.2 * r}s.`, vi: `Đập đất: sóng xung kích hình nón gây ${170 + 38 * r}% ST vũ khí, hất văng và choáng ${0.8 + 0.2 * r}s.` }), manaCost: r => 16 + 3 * r, cooldown: 9, castType: 'cone', params: r => ({ arcDeg: 70, range: 4.6, dmgPct: 170 + 38 * r, knockback: 2.4, stun: 0.8 + 0.2 * r }) }, /* Bulwark (passives) */ { id: 'ironskin', branch: 'bulwark', tier: 0, type: 'passive', maxRank: MAX_RANK, icon: 'ironskin', name: { en: 'Iron Skin', vi: 'Da Thép' }, desc: r => ({ en: `+${6 + 4 * r}% total armor.`, vi: `+${6 + 4 * r}% tổng giáp.` }), mods: r => ({ armorPct: 6 + 4 * r }) }, { id: 'renewal', branch: 'bulwark', tier: 1, type: 'passive', maxRank: MAX_RANK, icon: 'renewal', name: { en: 'Renewal', vi: 'Tái Sinh' }, desc: r => ({ en: `Regenerate ${(0.8 + 0.7 * r).toFixed(1)}% max life per second.`, vi: `Hồi ${(0.8 + 0.7 * r).toFixed(1)}% máu tối đa mỗi giây.` }), mods: r => ({ regenHpPct: 0.8 + 0.7 * r }) }, { id: 'laststand', branch: 'bulwark', tier: 2, type: 'passive', maxRank: MAX_RANK, icon: 'laststand', name: { en: 'Last Stand', vi: 'Đường Lui Cuối' }, desc: r => ({ en: `Below 35% life: take ${8 + 4 * r}% less damage and deal +${10 + 5 * r}% more.`, vi: `Dưới 35% máu: nhận ít hơn ${8 + 4 * r}% sát thương và gây thêm +${10 + 5 * r}%.` }), mods: r => ({ lowLifeDR: 8 + 4 * r, lowLifeDmg: 10 + 5 * r }) }, ]; /* ---------------- RANGER ---------------- */ const ranger = [ /* Marksmanship */ { id: 'multishot', branch: 'mark', tier: 0, type: 'active', maxRank: MAX_RANK, icon: 'multishot', name: { en: 'Multishot', vi: 'Liên Thanh' }, desc: r => ({ en: `Fire ${4 + Math.floor((r - 1) / 2)} arrows in a wide arc for ${85 + 17 * r}% weapon damage each.`, vi: `Bắn ${4 + Math.floor((r - 1) / 2)} mũi tên hình quạt, mỗi mũi ${85 + 17 * r}% ST vũ khí.` }), manaCost: r => 8 + r, cooldown: 0.9, castType: 'projSpread', params: r => ({ count: 4 + Math.floor((r - 1) / 2), spreadDeg: 42, dmgPct: 85 + 17 * r, speed: 11 }) }, { id: 'piercingbolt', branch: 'mark', tier: 1, type: 'active', maxRank: MAX_RANK, icon: 'piercingbolt', name: { en: 'Piercing Bolt', vi: 'Mũi Tên Xuyên Phá' }, desc: r => ({ en: `A heavy bolt pierces all enemies in a line for ${190 + 42 * r}% weapon damage.`, vi: `Một mũi tên nặng xuyên qua mọi kẻ địch trên đường đi, gây ${190 + 42 * r}% ST vũ khí.` }), manaCost: r => 12 + 2 * r, cooldown: 4.5, castType: 'projSpread', params: r => ({ count: 1, spreadDeg: 0, dmgPct: 190 + 42 * r, speed: 15, pierce: true, size: 1.4 }) }, { id: 'arrowstorm', branch: 'mark', tier: 2, type: 'active', maxRank: MAX_RANK, icon: 'arrowstorm', name: { en: 'Arrow Storm', vi: 'Mưa Mũi Tên' }, desc: r => ({ en: `Rain ${3 + Math.floor(r / 2)} waves of arrows on an area, each dealing ${70 + 15 * r}% weapon damage.`, vi: `Gây ${3 + Math.floor(r / 2)} đợt mưa tên lên một vùng, mỗi đợt ${70 + 15 * r}% ST vũ khí.` }), manaCost: r => 18 + 3 * r, cooldown: 11, castType: 'volleyArea', params: r => ({ waves: 3 + Math.floor(r / 2), waveDelay: 0.4, radius: 2.4, dmgPct: 70 + 15 * r }) }, /* Shadowcraft */ { id: 'shadowstep', branch: 'shadow', tier: 0, type: 'active', maxRank: MAX_RANK, icon: 'shadowstep', name: { en: 'Shadow Step', vi: 'Bóng Đêm Bước' }, desc: r => ({ en: `Blink toward the cursor. Your next attack within 3s is a guaranteed critical (+${50 + 25 * r}% crit damage).`, vi: `Dịch chuyển tới con trỏ. Đòn đánh tiếp theo trong 3s chắc chắn chí mạng (+${50 + 25 * r}% ST chí mạng).` }), manaCost: () => 10, cooldown: 7, castType: 'blink', params: r => ({ range: 5.5, guaranteedCritDur: 3, bonusCritDmg: 50 + 25 * r }) }, { id: 'fanofknives', branch: 'shadow', tier: 1, type: 'active', maxRank: MAX_RANK, icon: 'fanofknives', name: { en: 'Fan of Knives', vi: 'Quạt Dao Găm' }, desc: r => ({ en: `Hurl ${8 + 2 * r} knives in all directions for ${95 + 19 * r}% weapon damage, causing bleeding.`, vi: `Phóng ${8 + 2 * r} dao găm ra mọi hướng, gây ${95 + 19 * r}% ST vũ khí và làm chảy máu.` }), manaCost: r => 14 + 2 * r, cooldown: 8, castType: 'novaProj', params: r => ({ count: 8 + 2 * r, dmgPct: 95 + 19 * r, speed: 9, bleed: true }) }, { id: 'smokeveil', branch: 'shadow', tier: 2, type: 'active', maxRank: MAX_RANK, icon: 'smokeveil', name: { en: 'Smoke Veil', vi: 'Màn Khói' }, desc: r => ({ en: `Vanish into smoke: +${25 + 7 * r}% dodge for 5s. Enemies lose track of you.`, vi: `Tan vào khói: +${25 + 7 * r}% né tránh trong 5s. Kẻ địch mất dấu bạn.` }), manaCost: r => 12 + 2 * r, cooldown: 16, castType: 'buffSelf', params: r => ({ dur: 5, mods: { dodgePct: 25 + 7 * r }, icon: 'smokeveil', name: { en: 'Smoke Veil', vi: 'Màn Khói' } }) }, /* Poacher (passives) */ { id: 'predator', branch: 'poach', tier: 0, type: 'passive', maxRank: MAX_RANK, icon: 'predator', name: { en: 'Predator Instinct', vi: 'Bản Năng Thợ Săn' }, desc: r => ({ en: `+${8 + 6 * r}% critical damage.`, vi: `+${8 + 6 * r}% sát thương chí mạng.` }), mods: r => ({ critDmgPct: 8 + 6 * r }) }, { id: 'bleedingedge', branch: 'poach', tier: 1, type: 'passive', maxRank: MAX_RANK, icon: 'bleedingedge', name: { en: 'Bleeding Edge', vi: 'Lưỡi Chảy Máu' }, desc: r => ({ en: `Attacks cause bleeding: ${10 + 10 * r}% weapon damage over 3s.`, vi: `Đòn đánh gây chảy máu: ${10 + 10 * r}% ST vũ khí trong 3s.` }), mods: r => ({ bleedOnHitPct: 10 + 10 * r }) }, { id: 'swiftvantage', branch: 'poach', tier: 2, type: 'passive', maxRank: MAX_RANK, icon: 'swiftvantage', name: { en: 'Swift Vantage', vi: 'Lợi Thế Thần Tốc' }, desc: r => ({ en: `+${4 + 2 * r}% move speed and +${3 + 2 * r}% attack speed.`, vi: `+${4 + 2 * r}% tốc chạy và +${3 + 2 * r}% tốc đánh.` }), mods: r => ({ moveSpeedPct: 4 + 2 * r, attackSpeedPct: 3 + 2 * r }) }, ]; /* ---------------- SORCERESS ---------------- */ const sorceress = [ /* Pyromancy */ { id: 'firebolt', branch: 'pyro', tier: 0, type: 'active', maxRank: MAX_RANK, icon: 'firebolt', name: { en: 'Fire Bolt', vi: 'Tiểu Hỏa' }, desc: r => ({ en: `Hurl a bolt of fire for ${135 + 32 * r}% weapon damage. Cheap and reliable.`, vi: `Phóng một mũi lửa gây ${135 + 32 * r}% ST vũ khí. Rẻ và đáng tin.` }), manaCost: () => 4, cooldown: 0.55, castType: 'projSpread', params: r => ({ count: 1, spreadDeg: 0, dmgPct: 135 + 32 * r, speed: 10, elem: 'fire', size: 1.15 }) }, { id: 'meteor', branch: 'pyro', tier: 1, type: 'active', maxRank: MAX_RANK, icon: 'meteor', name: { en: 'Meteor', vi: 'Thiên Thạch' }, desc: r => ({ en: `Call a meteor to crash after 0.8s: ${260 + 58 * r}% weapon damage in a large area, leaving burning ground.`, vi: `Triệu hồi thiên thạch rơi sau 0.8s: ${260 + 58 * r}% ST vũ khí vùng lớn, để lại mặt đất cháy.` }), manaCost: r => 22 + 4 * r, cooldown: 9, castType: 'meteor', params: r => ({ radius: 2.6, delay: 0.8, dmgPct: 260 + 58 * r, burnGround: 2.5 }) }, { id: 'flamewave', branch: 'pyro', tier: 2, type: 'active', maxRank: MAX_RANK, icon: 'flamewave', name: { en: 'Flame Wave', vi: 'Sóng Lửa' }, desc: r => ({ en: `Erupt in a ring of fire: ${200 + 44 * r}% weapon damage around you and ignite enemies.`, vi: `Bùng nổ vòng lửa quanh bạn: ${200 + 44 * r}% ST vũ khí và thiêu rụi kẻ địch.` }), manaCost: r => 20 + 4 * r, cooldown: 7, castType: 'nova', params: r => ({ radius: 3.4, dmgPct: 200 + 44 * r, elem: 'fire', ignite: true }) }, /* Cryomancy */ { id: 'iceshards', branch: 'cryo', tier: 0, type: 'active', maxRank: MAX_RANK, icon: 'iceshards', name: { en: 'Ice Shards', vi: 'Mảnh Băng' }, desc: r => ({ en: `Launch ${5} piercing shards for ${90 + 20 * r}% weapon damage each, chilling enemies.`, vi: `Phóng 5 mảnh băng xuyên phá, mỗi mảnh ${90 + 20 * r}% ST vũ khí, làm lạnh kẻ địch.` }), manaCost: r => 9 + 2 * r, cooldown: 1.4, castType: 'projSpread', params: r => ({ count: 5, spreadDeg: 36, dmgPct: 90 + 20 * r, speed: 9.5, elem: 'cold', pierce: true, slow: 0.35, slowDur: 1.8 }) }, { id: 'frostnova', branch: 'cryo', tier: 1, type: 'active', maxRank: MAX_RANK, icon: 'frostnova', name: { en: 'Frost Nova', vi: 'Tinh Băng' }, desc: r => ({ en: `Freeze everything near you for ${1.0 + 0.22 * r}s and deal ${110 + 24 * r}% weapon damage.`, vi: `Đóng băng mọi thứ gần bạn ${1.0 + 0.22 * r}s và gây ${110 + 24 * r}% ST vũ khí.` }), manaCost: r => 18 + 3 * r, cooldown: 8, castType: 'nova', params: r => ({ radius: 3.0, dmgPct: 110 + 24 * r, elem: 'cold', freeze: 1.0 + 0.22 * r }) }, { id: 'blizzard', branch: 'cryo', tier: 2, type: 'active', maxRank: MAX_RANK, icon: 'blizzard', name: { en: 'Blizzard', vi: 'Bão Tuyết' }, desc: r => ({ en: `A storm batters an area for 3s: ${90 + 20 * r}% weapon damage per second, heavily chilled.`, vi: `Bão tuyết cuồn cuộn 3s: ${90 + 20 * r}% ST vũ khí mỗi giây, làm lạnh mạnh.` }), manaCost: r => 24 + 5 * r, cooldown: 12, castType: 'groundAoE', params: r => ({ radius: 2.8, delay: 0.3, duration: 3, dmgPct: 90 + 20 * r, elem: 'cold', slow: 0.5, slowDur: 1.2 }) }, /* Stormweaving */ { id: 'chainlightning', branch: 'storm', tier: 0, type: 'active', maxRank: MAX_RANK, icon: 'chainlightning', name: { en: 'Chain Lightning', vi: 'Lôi Điện Xích' }, desc: r => ({ en: `Lightning arcs between ${2 + r} enemies for ${150 + 33 * r}% weapon damage.`, vi: `Sét lan truyền giữa ${2 + r} kẻ địch, gây ${150 + 33 * r}% ST vũ khí.` }), manaCost: r => 14 + 2 * r, cooldown: 2.6, castType: 'chainLightning', params: r => ({ jumps: 2 + r, range: 5.5, dmgPct: 150 + 33 * r }) }, { id: 'teleport', branch: 'storm', tier: 1, type: 'active', maxRank: MAX_RANK, icon: 'teleport', name: { en: 'Teleport', vi: 'Di Chuyển' }, desc: r => ({ en: `Instantly teleport to the cursor location (${4.5 + 0.5 * r} tile range).`, vi: `Dịch chuyển tức thời đến vị trí con trỏ (${(4.5 + 0.5 * r).toFixed(1)} ô).` }), manaCost: r => 12 + r, cooldown: 4.5, castType: 'blink', params: r => ({ range: 4.5 + 0.5 * r }) }, { id: 'arcaneecho', branch: 'storm', tier: 2, type: 'passive', maxRank: MAX_RANK, icon: 'arcaneecho', name: { en: 'Arcane Echo', vi: 'Vọng Âm Huyền Thuật' }, desc: r => ({ en: `-(${3 + 3 * r})% cooldowns and +${0.3 + 0.25 * r} mana per second.`, vi: `-(giảm) ${3 + 3 * r}% thời gian hồi kỹ năng và +${(0.3 + 0.25 * r).toFixed(2)} mana/giây.` }), mods: r => ({ cdrPct: 3 + 3 * r, manaRegenFlat: 0.3 + 0.25 * r }) }, ]; const CLASSES = { crusader: { id: 'crusader', name: { en: 'Crusader', vi: 'Thập Tự Quân' }, branches: [ { id: 'zeal', name: { en: 'Zeal', vi: 'Cuồng Tín' } }, { id: 'wrath', name: { en: 'Wrath', vi: 'Thịnh Nộ' } }, { id: 'bulwark', name: { en: 'Bulwark', vi: 'Bức Tường Thép' } }, ], skills: crusader, baseMods: { str: 8, dex: 4, vit: 8, ene: 3 }, weaponKind: 'melee', palette: { body: '#8a8f9c', accent: '#c8a35a', cloth: '#5a2a2a' }, basic: { castType: 'meleeArc', params: { arcDeg: 120, range: 1.45, dmgPct: 100 } }, descKey: 'class.crusader.desc', }, ranger: { id: 'ranger', name: { en: 'Shadow Huntress', vi: 'Nữ Thợ Săn Bóng Đêm' }, branches: [ { id: 'mark', name: { en: 'Marksmanship', vi: 'Bách Bộ Xuyên Dương' } }, { id: 'shadow', name: { en: 'Shadowcraft', vi: 'Nghệ Thuật Bóng Tối' } }, { id: 'poach', name: { en: 'Poacher', vi: 'Thợ Săn Mồi' } }, ], skills: ranger, baseMods: { str: 4, dex: 9, vit: 5, ene: 5 }, weaponKind: 'ranged', palette: { body: '#3e5a46', accent: '#8adf5a', cloth: '#2a3a30' }, basic: { castType: 'projSpread', params: { count: 1, spreadDeg: 0, dmgPct: 100, speed: 12 } }, descKey: 'class.crusader.desc'.replace('crusader', 'ranger'), }, sorceress: { id: 'sorceress', name: { en: 'Sorceress', vi: 'Pháp Sư' }, branches: [ { id: 'pyro', name: { en: 'Pyromancy', vi: 'Hỏa Thuật' } }, { id: 'cryo', name: { en: 'Cryomancy', vi: 'Băng Thuật' } }, { id: 'storm', name: { en: 'Stormweaving', vi: 'Lôi Pháp' } }, ], skills: sorceress, baseMods: { str: 3, dex: 4, vit: 5, ene: 10 }, weaponKind: 'cast', palette: { body: '#5e3a72', accent: '#c86aff', cloth: '#3a2450' }, basic: { castType: 'projSpread', params: { count: 1, spreadDeg: 0, dmgPct: 100, speed: 9, size: 1.2 } }, descKey: 'class.sorceress.desc', }, }; /** all skills of a class flattened, with tier unlock level attached */ function skillsFor(clsId) { const c = CLASSES[clsId]; return c.skills.map(s => Object.assign({}, s, { unlockLevel: D2.BAL.tierUnlockLevels[s.tier] || 1 })); } function findSkill(clsId, skillId) { return CLASSES[clsId].skills.find(s => s.id === skillId); } D2.Skills = { CLASSES, skillsFor, findSkill, MAX_RANK }; })(window.D2);