mirror of
https://github.com/seigler/bl3skills.com
synced 2025-07-27 01:36:12 +00:00
Add validation, better initials
This commit is contained in:
parent
9ec942b3fa
commit
3b3e3c9c42
11 changed files with 255 additions and 75 deletions
5
package-lock.json
generated
5
package-lock.json
generated
|
@ -2953,6 +2953,11 @@
|
||||||
"integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
|
"integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"deepmerge": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-6+LuZGU7QCNUnAJyX8cIrlzoEgggTM6B7mm+znKOX4t5ltluT9KLjN6g61ECMS0LTsLW7yDpNoxhix5FZcrIow=="
|
||||||
|
},
|
||||||
"default-gateway": {
|
"default-gateway": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz",
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
"watch": "pwa watch"
|
"watch": "pwa watch"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"deepmerge": "^3.2.0",
|
||||||
"preact": "^10.0.0-beta.1",
|
"preact": "^10.0.0-beta.1",
|
||||||
"preact-router": "^3.0.0",
|
"preact-router": "^3.0.0",
|
||||||
"sirv-cli": "^0.2.0"
|
"sirv-cli": "^0.2.0"
|
||||||
|
|
|
@ -5,7 +5,25 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
line-height: 2;
|
line-height: 2;
|
||||||
background-image: linear-gradient(to top, hsla(0,0%,0%,0.8), hsla(0,0%,0%,0));
|
background-image: linear-gradient(
|
||||||
|
to top,
|
||||||
|
hsla(0, 0%, 0%, 0.5) 0%,
|
||||||
|
hsla(0, 0%, 0%, 0.494) 8.1%,
|
||||||
|
hsla(0, 0%, 0%, 0.476) 15.5%,
|
||||||
|
hsla(0, 0%, 0%, 0.448) 22.5%,
|
||||||
|
hsla(0, 0%, 0%, 0.412) 29%,
|
||||||
|
hsla(0, 0%, 0%, 0.37) 35.3%,
|
||||||
|
hsla(0, 0%, 0%, 0.324) 41.2%,
|
||||||
|
hsla(0, 0%, 0%, 0.275) 47.1%,
|
||||||
|
hsla(0, 0%, 0%, 0.225) 52.9%,
|
||||||
|
hsla(0, 0%, 0%, 0.176) 58.8%,
|
||||||
|
hsla(0, 0%, 0%, 0.13) 64.7%,
|
||||||
|
hsla(0, 0%, 0%, 0.088) 71%,
|
||||||
|
hsla(0, 0%, 0%, 0.052) 77.5%,
|
||||||
|
hsla(0, 0%, 0%, 0.024) 84.5%,
|
||||||
|
hsla(0, 0%, 0%, 0.006) 91.9%,
|
||||||
|
hsla(0, 0%, 0%, 0) 100%
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer span a {
|
.footer span a {
|
||||||
|
|
|
@ -1,7 +1,25 @@
|
||||||
.nav {
|
.nav {
|
||||||
align-self: stretch;
|
align-self: stretch;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
background-image: linear-gradient(to bottom, hsla(0,0%,0%,0.8), hsla(0,0%,0%,0));
|
background-image: linear-gradient(
|
||||||
|
to bottom,
|
||||||
|
hsla(0, 0%, 0%, 0.5) 0%,
|
||||||
|
hsla(0, 0%, 0%, 0.494) 8.1%,
|
||||||
|
hsla(0, 0%, 0%, 0.476) 15.5%,
|
||||||
|
hsla(0, 0%, 0%, 0.448) 22.5%,
|
||||||
|
hsla(0, 0%, 0%, 0.412) 29%,
|
||||||
|
hsla(0, 0%, 0%, 0.37) 35.3%,
|
||||||
|
hsla(0, 0%, 0%, 0.324) 41.2%,
|
||||||
|
hsla(0, 0%, 0%, 0.275) 47.1%,
|
||||||
|
hsla(0, 0%, 0%, 0.225) 52.9%,
|
||||||
|
hsla(0, 0%, 0%, 0.176) 58.8%,
|
||||||
|
hsla(0, 0%, 0%, 0.13) 64.7%,
|
||||||
|
hsla(0, 0%, 0%, 0.088) 71%,
|
||||||
|
hsla(0, 0%, 0%, 0.052) 77.5%,
|
||||||
|
hsla(0, 0%, 0%, 0.024) 84.5%,
|
||||||
|
hsla(0, 0%, 0%, 0.006) 91.9%,
|
||||||
|
hsla(0, 0%, 0%, 0) 100%
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
.links {
|
.links {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
.skill {
|
.skill {
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
|
||||||
flex: 0 1 auto;
|
flex: 0 1 auto;
|
||||||
margin: 0.5rem;
|
margin: 0.5rem;
|
||||||
padding: 0rem;
|
padding: 0rem;
|
||||||
|
@ -11,6 +10,7 @@
|
||||||
text-align: center;
|
text-align: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
color: hsla(0, 0%, 100%, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.skill:before, .skill:after {
|
.skill:before, .skill:after {
|
||||||
|
@ -24,14 +24,17 @@
|
||||||
|
|
||||||
.skill:before {
|
.skill:before {
|
||||||
content: '';
|
content: '';
|
||||||
z-index: -1;
|
z-index: 2;
|
||||||
background-image: linear-gradient(to bottom, #333, #555);
|
background-image: linear-gradient(to bottom, #111, #222);
|
||||||
transform: scale(0.9);
|
transform: scale(0.9);
|
||||||
filter: brightness(50%);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.skill.enabled:before {
|
.enabled {
|
||||||
filter: brightness(100%);
|
color: var(--whiteText);
|
||||||
|
}
|
||||||
|
|
||||||
|
.enabled:before {
|
||||||
|
background-image: linear-gradient(to bottom, #333, #555);
|
||||||
}
|
}
|
||||||
|
|
||||||
.usable:before {
|
.usable:before {
|
||||||
|
@ -39,14 +42,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.usable:after {
|
.usable:after {
|
||||||
z-index: -2;
|
z-index: 1;
|
||||||
content: '';
|
content: '';
|
||||||
background-color: hsl(var(--themeHue),91%,70%);
|
background-color: hsl(var(--themeHue),91%,70%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.skill:hover:before {
|
.skill:hover:before {
|
||||||
background-image: linear-gradient(to bottom, hsl(51, 100%, 40%), hsl(51, 100%, 50%));
|
background-image: linear-gradient(to bottom, hsl(51, 100%, 40%), hsl(51, 100%, 50%));
|
||||||
filter: brightness(100%);
|
|
||||||
}
|
}
|
||||||
.skill:hover:after {
|
.skill:hover:after {
|
||||||
background-color: black;
|
background-color: black;
|
||||||
|
@ -56,6 +58,13 @@
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.augment:before {
|
||||||
|
transform: scale(0.7);
|
||||||
|
}
|
||||||
|
.augment:after {
|
||||||
|
transform: scale(0.79);
|
||||||
|
}
|
||||||
|
|
||||||
.actionSkill:before, .actionSkill:after {
|
.actionSkill:before, .actionSkill:after {
|
||||||
clip-path: polygon(50% 0%, 93.3% 25%, 93.3% 75%, 50% 100%, 6.7% 75%, 6.7% 25%);
|
clip-path: polygon(50% 0%, 93.3% 25%, 93.3% 75%, 50% 100%, 6.7% 75%, 6.7% 25%);
|
||||||
}
|
}
|
||||||
|
@ -70,14 +79,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.augment, .actionSkill {
|
.augment, .actionSkill {
|
||||||
margin: 0.25rem;
|
margin: 0;
|
||||||
padding: 0.25rem;
|
padding: 0.5rem;
|
||||||
height: 3em;
|
height: 3.5em;
|
||||||
width: 3em;
|
width: 3.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.augment {
|
.augment {
|
||||||
outline: calc(0.25em + 1px) solid var(--themeColor);
|
|
||||||
background-color: var(--themeColor);
|
background-color: var(--themeColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,12 +106,35 @@
|
||||||
right: initial;
|
right: initial;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.image {
|
||||||
|
z-index: 3;
|
||||||
|
}
|
||||||
|
|
||||||
.ranks {
|
.ranks {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: -0.25rem;
|
bottom: -0.25rem;
|
||||||
right: -0.25rem;
|
right: -0.25rem;
|
||||||
|
z-index: 4;
|
||||||
font-size: 0.75em;
|
font-size: 0.75em;
|
||||||
background-color: #333;
|
background-color: #333;
|
||||||
color: var(--whiteText);
|
color: var(--whiteText);
|
||||||
padding: 0.1em;
|
padding: 0.1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
display: none;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skill:hover .description {
|
||||||
|
pointer-events: none;
|
||||||
|
font-size: 0.8em;
|
||||||
|
background-color: hsla(0, 0%, 0%, 0.6);
|
||||||
|
color: var(--whiteText);
|
||||||
|
border: 0.15rem solid hsla(0,0%,0%,0.8);
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 110%;
|
||||||
|
width: 20rem;
|
||||||
|
transform: translateX(calc((var(--tree-index) - 1) * -50%));
|
||||||
|
}
|
||||||
|
|
|
@ -1,13 +1,19 @@
|
||||||
// import { useState } from 'preact/hooks';
|
|
||||||
import SKILLS from '@constants/skills';
|
import SKILLS from '@constants/skills';
|
||||||
import style from './index.css';
|
import style from './index.css';
|
||||||
|
|
||||||
function getInitials (string) {
|
function getInitials (string) {
|
||||||
const initials = string.match(/\b\w/g) || [];
|
const numWords = (string.match(/\s/g) || []).length + 1;
|
||||||
return ((initials.shift() || '') + (initials.pop() || '')).toUpperCase();
|
switch (numWords) {
|
||||||
|
case 1:
|
||||||
|
return string.slice(0, 3);
|
||||||
|
case 2:
|
||||||
|
return (string.match(/^\w{1,2}|\s\w{1,2}/g) || []).join('').replace(/\s/g, '');
|
||||||
|
default:
|
||||||
|
return (string.match(/^\w|\s\w/g) || []).join('').replace(/\s/g, '');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function VaultHunter ({
|
export default function Skill ({
|
||||||
name = '?',
|
name = '?',
|
||||||
text = 'Long description',
|
text = 'Long description',
|
||||||
ranks = 0,
|
ranks = 0,
|
||||||
|
@ -15,6 +21,7 @@ export default function VaultHunter ({
|
||||||
effect = rank => `Rank ${rank} effect`,
|
effect = rank => `Rank ${rank} effect`,
|
||||||
type = null,
|
type = null,
|
||||||
enabled = true,
|
enabled = true,
|
||||||
|
onChange = (oldValue, newValue) => console.log(`Skill from ${oldValue} to ${newValue}`),
|
||||||
}) {
|
}) {
|
||||||
const isAugment = [
|
const isAugment = [
|
||||||
SKILLS.AUGMENT_CHEVRON,
|
SKILLS.AUGMENT_CHEVRON,
|
||||||
|
@ -25,16 +32,40 @@ export default function VaultHunter ({
|
||||||
if (type === SKILLS.AUGMENT_CHEVRON) { shapeStyle = style.chevron; }
|
if (type === SKILLS.AUGMENT_CHEVRON) { shapeStyle = style.chevron; }
|
||||||
if (type === SKILLS.AUGMENT_DIAMOND) { shapeStyle = style.diamond; }
|
if (type === SKILLS.AUGMENT_DIAMOND) { shapeStyle = style.diamond; }
|
||||||
if (type === SKILLS.ACTION_SKILL) { shapeStyle = style.actionSkill; }
|
if (type === SKILLS.ACTION_SKILL) { shapeStyle = style.actionSkill; }
|
||||||
|
function clickListener (event) {
|
||||||
|
var newValue;
|
||||||
|
if (event.type === 'click') {
|
||||||
|
newValue = Math.min(invested + 1, ranks);
|
||||||
|
} else { // (event.type === 'contextmenu')
|
||||||
|
newValue = Math.max(invested - 1, 0);
|
||||||
|
}
|
||||||
|
if (enabled && invested !== newValue) {
|
||||||
|
onChange(invested, newValue);
|
||||||
|
}
|
||||||
|
event.preventDefault();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div class={[
|
<div
|
||||||
|
class={[
|
||||||
style.skill,
|
style.skill,
|
||||||
isAugment ? style.augment : '',
|
isAugment ? style.augment : '',
|
||||||
shapeStyle,
|
shapeStyle,
|
||||||
enabled ? style.enabled : '',
|
enabled ? style.enabled : '',
|
||||||
enabled && (ranks === 0 || invested > 0) ? style.usable : '',
|
enabled && (ranks === 0 || invested > 0) ? style.usable : '',
|
||||||
].join(' ')}>
|
].join(' ')}
|
||||||
{ getInitials(name) }
|
onClick={clickListener}
|
||||||
|
onContextMenu={clickListener}
|
||||||
|
>
|
||||||
|
<div class={style.image}>{getInitials(name)}</div>
|
||||||
{ enabled && ranks > 0 && <div class={style.ranks}>{invested}/{ranks}</div>}
|
{ enabled && ranks > 0 && <div class={style.ranks}>{invested}/{ranks}</div>}
|
||||||
|
<div class={style.description}>
|
||||||
|
<h3>{name}</h3>
|
||||||
|
{text}
|
||||||
|
<div class={style.effect}>
|
||||||
|
{effect(Math.max(invested, 1))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,6 @@
|
||||||
.tree {
|
.tree {
|
||||||
--themeColor: hsl(var(--themeHue),91%,22%);
|
--themeColor: hsl(var(--themeHue),91%,22%);
|
||||||
display: flex;
|
display: flex;
|
||||||
z-index: 1;
|
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
margin: 1rem;
|
margin: 1rem;
|
||||||
padding: 0 3.5rem;
|
padding: 0 3.5rem;
|
||||||
|
@ -54,7 +53,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.skills:before {
|
.skills:before {
|
||||||
--gradientStop: calc((var(--invested) + 5) * 3.5rem / 5 + 1.65rem);
|
--gradientStop: calc(var(--invested)* 0.7rem + 3.5rem + 1.65rem);
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 1.85rem;
|
top: 1.85rem;
|
||||||
|
@ -63,12 +62,10 @@
|
||||||
right: 0.375rem;
|
right: 0.375rem;
|
||||||
background-image: linear-gradient(
|
background-image: linear-gradient(
|
||||||
to bottom,
|
to bottom,
|
||||||
hsla(0,0%,60%,1) calc(var(--gradientStop) - 1rem),
|
hsl(var(--themeHue),91%,30%) calc(var(--gradientStop) - 1rem),
|
||||||
hsla(0,0%,90%,1) var(--gradientStop),
|
hsl(var(--themeHue),91%,50%) var(--gradientStop),
|
||||||
hsla(0,0%,25%,1) calc(var(--gradientStop) + 0.1px),
|
hsl(var(--themeHue),91%,15%) calc(var(--gradientStop) + 1px)
|
||||||
hsla(0,0%,40%,1) 120%
|
|
||||||
);
|
);
|
||||||
mix-blend-mode: overlay;
|
|
||||||
border: 0.15rem solid hsla(0,0%,0%,0.8);
|
border: 0.15rem solid hsla(0,0%,0%,0.8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,32 +1,69 @@
|
||||||
import { useState } from 'preact/hooks';
|
import { useReducer } from 'preact/hooks';
|
||||||
|
import deepmerge from 'deepmerge';
|
||||||
import Skill from '@components/Skill';
|
import Skill from '@components/Skill';
|
||||||
|
import investmentValidator from './investmentValidator';
|
||||||
import style from './index.css';
|
import style from './index.css';
|
||||||
|
|
||||||
const initialState = {
|
function reducer (state, action) {
|
||||||
invested: [4, 5, 6],
|
switch (action.type) {
|
||||||
|
case 'skillChange':
|
||||||
|
var newInvested = state.invested.slice(0);
|
||||||
|
newInvested[action.treeIndex] += action.newValue - action.oldValue;
|
||||||
|
var newSkills = deepmerge(state.skills, {
|
||||||
|
[action.treeName]: {
|
||||||
|
[action.tierIndex + '']: {
|
||||||
|
[action.skillName]: {
|
||||||
|
invested: action.newValue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (investmentValidator(newSkills)) {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
invested: newInvested,
|
||||||
|
skills: newSkills,
|
||||||
};
|
};
|
||||||
|
} else {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function contextKiller (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
export default function VaultHunter ({
|
export default function VaultHunter ({
|
||||||
name = 'Unnamed',
|
name = 'Unnamed',
|
||||||
discipline = 'Classless',
|
discipline = 'Classless',
|
||||||
skills = {},
|
skills = {},
|
||||||
}) {
|
}) {
|
||||||
const [build] = useState(initialState);
|
const initialState = {
|
||||||
|
invested: [0, 0, 0],
|
||||||
|
skills,
|
||||||
|
};
|
||||||
|
const [state, dispatch] = useReducer(reducer, initialState);
|
||||||
const trees =
|
const trees =
|
||||||
Object.keys(skills).map((treename, treeindex) =>
|
Object.keys(state.skills).map((treeName, treeIndex) =>
|
||||||
<div
|
<div
|
||||||
class={`${style.tree} ${[style.green, style.blue, style.red][treeindex]}`}
|
class={`${style.tree} ${[style.green, style.blue, style.red][treeIndex]}`}
|
||||||
style={{ '--invested': build.invested[treeindex] }}
|
style={{
|
||||||
|
'--invested': state.invested[treeIndex],
|
||||||
|
'--tree-index': treeIndex,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<h2 class={style.treeName}>{ treename }</h2>
|
<h2 class={style.treeName}>{ treeName }</h2>
|
||||||
<div class={style.skills}>
|
<div class={style.skills}>
|
||||||
{ Object.keys(skills[treename]).map((tier, tierindex) =>
|
{ Object.keys(state.skills[treeName]).map((tier, tierIndex) =>
|
||||||
<div class={style.tier}>
|
<div class={style.tier}>
|
||||||
{ Object.keys(skills[treename][tier]).map(
|
{ Object.keys(state.skills[treeName][tier]).map(skillName =>
|
||||||
skillname => <Skill
|
<Skill
|
||||||
{...skills[treename][tier][skillname]}
|
{...state.skills[treeName][tier][skillName]}
|
||||||
name={skillname}
|
name={skillName}
|
||||||
enabled={build.invested[treeindex] >= 5 * tierindex - 5}
|
enabled={state.invested[treeIndex] >= 5 * tierIndex - 5}
|
||||||
|
onChange={skillChangeListenerFactory(skillName, treeIndex, treeName, tierIndex)}
|
||||||
/>
|
/>
|
||||||
) }
|
) }
|
||||||
</div>
|
</div>
|
||||||
|
@ -35,8 +72,22 @@ export default function VaultHunter ({
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
function skillChangeListenerFactory (skillName, treeIndex, treeName, tierIndex) {
|
||||||
|
return (oldValue, newValue) => {
|
||||||
|
dispatch({
|
||||||
|
type: 'skillChange',
|
||||||
|
skillName,
|
||||||
|
treeIndex,
|
||||||
|
treeName,
|
||||||
|
tierIndex,
|
||||||
|
newValue,
|
||||||
|
oldValue,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class={style.VaultHunter}>
|
<div class={style.VaultHunter} onContextMenu={contextKiller}>
|
||||||
<h1 class={style.title}>{ name }
|
<h1 class={style.title}>{ name }
|
||||||
<div class={style.subtitle}>the { discipline }</div>
|
<div class={style.subtitle}>the { discipline }</div>
|
||||||
</h1>
|
</h1>
|
||||||
|
|
27
src/components/VaultHunter/investmentValidator.js
Normal file
27
src/components/VaultHunter/investmentValidator.js
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
export default function investmentValidator (skills) {
|
||||||
|
// ok &= (
|
||||||
|
// (level == thisLevel && total == 0 && treeTotal >= invested + total) ||
|
||||||
|
// (level != thisLevel && total == 0) ||
|
||||||
|
// (total > 0 && (level * 5 <= invested))
|
||||||
|
// );
|
||||||
|
console.log('----------------------');
|
||||||
|
let totalSpent = 0;
|
||||||
|
for (let tree of Object.values(skills)) {
|
||||||
|
let treeTotal = 0;
|
||||||
|
let tierIndex = 0;
|
||||||
|
for (let tier of Object.values(tree)) {
|
||||||
|
let tierTotal = 0;
|
||||||
|
for (let skill of Object.values(tier)) {
|
||||||
|
if (skill.invested < 0 || skill.invested > skill.ranks) { return false; }
|
||||||
|
tierTotal += skill.invested || 0;
|
||||||
|
};
|
||||||
|
// console.log(`${tierTotal} > 0 && ${treeTotal} + 5 < ${tierIndex} * 5 = ${!(tierTotal > 0 && treeTotal + 5 < tierIndex * 5)}`);
|
||||||
|
if (tierTotal > 0 && treeTotal + 5 < tierIndex * 5) { return false; }
|
||||||
|
treeTotal += tierTotal;
|
||||||
|
tierIndex += 1;
|
||||||
|
};
|
||||||
|
totalSpent += treeTotal;
|
||||||
|
};
|
||||||
|
if (totalSpent > 47) { return false; }
|
||||||
|
return true;
|
||||||
|
}
|
|
@ -14,17 +14,17 @@ const skills = {
|
||||||
"Adrenaline": {
|
"Adrenaline": {
|
||||||
text: "Zane gains increased Action Skill Cooldown Rate. This bonus is based on the amount of shield he has. The more percent full, the greater the bonus.",
|
text: "Zane gains increased Action Skill Cooldown Rate. This bonus is based on the amount of shield he has. The more percent full, the greater the bonus.",
|
||||||
ranks: 5,
|
ranks: 5,
|
||||||
effect: rank => `Action Skill Cooldown Rate - Up to +4%`,
|
effect: rank => `Action Skill Cooldown Rate - Up to +${rank * 4}%`,
|
||||||
},
|
},
|
||||||
"Hearty Stock": {
|
"Hearty Stock": {
|
||||||
text: "Zane gains increased Maximum Shield Capacity.",
|
text: "Zane gains increased Maximum Shield Capacity.",
|
||||||
ranks: 5,
|
ranks: 5,
|
||||||
effect: rank => `Max Shield +6%`,
|
effect: rank => `Max Shield +${rank * 6}%`,
|
||||||
},
|
},
|
||||||
"Ready for Action": {
|
"Ready for Action": {
|
||||||
text: "Zane gains improved Shield Recharge Rate and Shield Recharge Delay.",
|
text: "Zane gains improved Shield Recharge Rate and Shield Recharge Delay.",
|
||||||
ranks: 5,
|
ranks: 5,
|
||||||
effect: rank => `Shield Recharge Rate +6%, Shield Recharge Delay -8%`,
|
effect: rank => `Shield Recharge Rate +${rank * 6}%, Shield Recharge Delay -${rank * 8}%`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"2": {
|
"2": {
|
||||||
|
@ -37,17 +37,17 @@ const skills = {
|
||||||
"Brainfreeze": {
|
"Brainfreeze": {
|
||||||
text: "Whenever Zane scores a Critical Hit on an enemy, there's a chance they will be Slowed.",
|
text: "Whenever Zane scores a Critical Hit on an enemy, there's a chance they will be Slowed.",
|
||||||
ranks: 5,
|
ranks: 5,
|
||||||
effect: rank => `Slow Chance 2.5%`,
|
effect: rank => `Slow Chance ${rank * 2.5}%`,
|
||||||
},
|
},
|
||||||
"Stiff Upper Lip": {
|
"Stiff Upper Lip": {
|
||||||
text: "Whenever Zane is damaged, he gains Damage Resistance against that damage type.",
|
text: "Whenever Zane is damaged, he gains Damage Resistance against that damage type.",
|
||||||
ranks: 3,
|
ranks: 3,
|
||||||
effect: rank => `Damage Resistance +5%`,
|
effect: rank => `Damage Resistance +${rank * 5}%`,
|
||||||
},
|
},
|
||||||
"Rise to the Occasion": {
|
"Rise to the Occasion": {
|
||||||
text: "Zane gains Health Regeneration. The lower his shield is, the higher the bonus. While Zane's shields are full, he does not receive any health regeneration.",
|
text: "Zane gains Health Regeneration. The lower his shield is, the higher the bonus. While Zane's shields are full, he does not receive any health regeneration.",
|
||||||
ranks: 5,
|
ranks: 5,
|
||||||
effect: rank => `Health Regeneration up to +1% of Max Health`,
|
effect: rank => `Health Regeneration up to +${rank * 1}% of Max Health`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"3": {
|
"3": {
|
||||||
|
@ -79,12 +79,12 @@ const skills = {
|
||||||
"Really Expensive Jacket": {
|
"Really Expensive Jacket": {
|
||||||
text: "Elemental damage over time effects applied to Zane have reduced duration.",
|
text: "Elemental damage over time effects applied to Zane have reduced duration.",
|
||||||
ranks: 1,
|
ranks: 1,
|
||||||
effect: `Elemental Effect Duration -50%`,
|
effect: rank => `Elemental Effect Duration -50%`,
|
||||||
},
|
},
|
||||||
"Best Served Cold": {
|
"Best Served Cold": {
|
||||||
text: "Whenever Zane kills an enemy, they create a Cryo Nova, dealing damage to all nearby enemies. This skill has a short cooldown.",
|
text: "Whenever Zane kills an enemy, they create a Cryo Nova, dealing damage to all nearby enemies. This skill has a short cooldown.",
|
||||||
ranks: 5,
|
ranks: 5,
|
||||||
effect: rank => `Damage 3, Cooldown 3 sec`,
|
effect: rank => `Damage ${rank * 3}, Cooldown 3 sec`, // TODO level scaling
|
||||||
},
|
},
|
||||||
"Futility Belt": {
|
"Futility Belt": {
|
||||||
text: "Zane gains resistance to non-elemental damage. Kill Skill - All elemental damage Zane takes is converted to non-elemental damage.",
|
text: "Zane gains resistance to non-elemental damage. Kill Skill - All elemental damage Zane takes is converted to non-elemental damage.",
|
||||||
|
@ -102,7 +102,7 @@ const skills = {
|
||||||
"Refreshment": {
|
"Refreshment": {
|
||||||
text: "Whenever Zane damages a frozen enemy with his weapon, he gains some of that damage back as health.",
|
text: "Whenever Zane damages a frozen enemy with his weapon, he gains some of that damage back as health.",
|
||||||
ranks: 3,
|
ranks: 3,
|
||||||
effect: rank => `Life Steal 8% of damage dealt`,
|
effect: rank => `Life Steal ${rank * 8}% of damage dealt`,
|
||||||
},
|
},
|
||||||
"Calm, Cool, Collected": {
|
"Calm, Cool, Collected": {
|
||||||
text: "Whenever Zane Freezes an enemy, his shield instantly begins recharging. If Zane's shields are already full, he regenerates health for a few seconds. If Zane's health is already full, his Action Skill Cooldowns and Durations are immediately reset.",
|
text: "Whenever Zane Freezes an enemy, his shield instantly begins recharging. If Zane's shields are already full, he regenerates health for a few seconds. If Zane's health is already full, his Action Skill Cooldowns and Durations are immediately reset.",
|
||||||
|
@ -112,7 +112,7 @@ const skills = {
|
||||||
"Nerves of Steel": {
|
"Nerves of Steel": {
|
||||||
text: "Zane gains increasing Accuracy and Handling. The longer his shield is full, the greater the bonus.",
|
text: "Zane gains increasing Accuracy and Handling. The longer his shield is full, the greater the bonus.",
|
||||||
ranks: 3,
|
ranks: 3,
|
||||||
effect: rank => `Accuracy +2% per sec, Handling +2.5% per sec, 99 Max Stacks`,
|
effect: rank => `Accuracy +${rank * 2}% per sec, Handling +${rank * 2.5}% per sec, 99 Max Stacks`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"6": {
|
"6": {
|
||||||
|
@ -179,6 +179,7 @@ const skills = {
|
||||||
},
|
},
|
||||||
"Seein' Red": {
|
"Seein' Red": {
|
||||||
text: "Activating an Action Skill automatically activates all of Zane's kill skills.",
|
text: "Activating an Action Skill automatically activates all of Zane's kill skills.",
|
||||||
|
ranks: 1,
|
||||||
},
|
},
|
||||||
"Static Field": {
|
"Static Field": {
|
||||||
text: "SNTNL emits a static field that sends a Shock beam to nearby enemies, draining their shields and replenishing Zane's.",
|
text: "SNTNL emits a static field that sends a Shock beam to nearby enemies, draining their shields and replenishing Zane's.",
|
||||||
|
|
|
@ -44,7 +44,7 @@ const skills = {
|
||||||
text: "After using Action Skill, Amara's arms remain active and grant Damage Reduction.",
|
text: "After using Action Skill, Amara's arms remain active and grant Damage Reduction.",
|
||||||
effect: rank => `Damage Reduction +${rank * 4}%, Duration 8 sec`,
|
effect: rank => `Damage Reduction +${rank * 4}%, Duration 8 sec`,
|
||||||
},
|
},
|
||||||
"Blight Tiger (Action Skill Element)": {
|
"Blight Tiger": {
|
||||||
ranks: 0,
|
ranks: 0,
|
||||||
text: "Converts Amara's Action Skill to Corrosive Damage. This does not take effect until after Amara uses her Action Skill.",
|
text: "Converts Amara's Action Skill to Corrosive Damage. This does not take effect until after Amara uses her Action Skill.",
|
||||||
effect: rank => ``,
|
effect: rank => ``,
|
||||||
|
@ -52,7 +52,7 @@ const skills = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"3": {
|
"3": {
|
||||||
"Fracture (Action Skill)": {
|
"Fracture": {
|
||||||
ranks: 0,
|
ranks: 0,
|
||||||
text: "Amara summons a Handful of Fists that erupt from the ground, dealing damage in front of Amara.",
|
text: "Amara summons a Handful of Fists that erupt from the ground, dealing damage in front of Amara.",
|
||||||
effect: rank => `Damage 124, Cooldown 26 sec`,
|
effect: rank => `Damage 124, Cooldown 26 sec`,
|
||||||
|
@ -73,7 +73,7 @@ const skills = {
|
||||||
text: "Killing an enemy with an Action Skill grants all allies increased Movement Speed, and can be stacked.",
|
text: "Killing an enemy with an Action Skill grants all allies increased Movement Speed, and can be stacked.",
|
||||||
effect: rank => `Team Movement Speed +${rank * 2}%, Duration 8 sec`,
|
effect: rank => `Team Movement Speed +${rank * 2}%, Duration 8 sec`,
|
||||||
},
|
},
|
||||||
"Revelation (Action Skill Augment)": {
|
"Revelation": {
|
||||||
ranks: 0,
|
ranks: 0,
|
||||||
text: "Amara's Action Skill now creates a Nova when it damages enemies, dealing damage to all nearby enemies.",
|
text: "Amara's Action Skill now creates a Nova when it damages enemies, dealing damage to all nearby enemies.",
|
||||||
effect: rank => `Nova Damage 41, Action Skill Damage -15%`,
|
effect: rank => `Nova Damage 41, Action Skill Damage -15%`,
|
||||||
|
@ -81,7 +81,7 @@ const skills = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"4": {
|
"4": {
|
||||||
"Downfall (Action Skill)": {
|
"Downfall": {
|
||||||
ranks: 0,
|
ranks: 0,
|
||||||
text: "Amara leaps into the air and shoots an Elemental Beam below her, followed by a Slam.",
|
text: "Amara leaps into the air and shoots an Elemental Beam below her, followed by a Slam.",
|
||||||
effect: rank => `Damage 141, Beam Damage 21 per sec, Cooldown 36 sec`,
|
effect: rank => `Damage 141, Beam Damage 21 per sec, Cooldown 36 sec`,
|
||||||
|
@ -109,7 +109,7 @@ const skills = {
|
||||||
text: "Upon entering Fight For Your Life, gain immediate Second Wind that restores health, and creates an Action Skill Elemental Nova that knocks back enemies.",
|
text: "Upon entering Fight For Your Life, gain immediate Second Wind that restores health, and creates an Action Skill Elemental Nova that knocks back enemies.",
|
||||||
effect: rank => `Max Health Restored 100% of Max Health, Cooldown 60 sec`,
|
effect: rank => `Max Health Restored 100% of Max Health, Cooldown 60 sec`,
|
||||||
},
|
},
|
||||||
"Glamour (Action Skill Augment)": {
|
"Glamour": {
|
||||||
ranks: 0,
|
ranks: 0,
|
||||||
text: "Enemies damaged by Amara's Action Skill become confused and attack their allies, but Action Skill Cooldown is increased. If enemies are target of Phasegrasp, nearby enemies become confused.",
|
text: "Enemies damaged by Amara's Action Skill become confused and attack their allies, but Action Skill Cooldown is increased. If enemies are target of Phasegrasp, nearby enemies become confused.",
|
||||||
effect: rank => `Damage -60%, Confuse Duration 6 sec, Cooldown +20%`,
|
effect: rank => `Damage -60%, Confuse Duration 6 sec, Cooldown +20%`,
|
||||||
|
@ -166,7 +166,7 @@ const skills = {
|
||||||
text: "Gain increased Action Skill Cooldown Rate.",
|
text: "Gain increased Action Skill Cooldown Rate.",
|
||||||
effect: rank => `Cooldown Rate +${rank * 4}%`,
|
effect: rank => `Cooldown Rate +${rank * 4}%`,
|
||||||
},
|
},
|
||||||
"Soul Sap (Action Skill Augment)": {
|
"Soul Sap": {
|
||||||
ranks: 0,
|
ranks: 0,
|
||||||
text: "A portion of all damage dealt by Action Skills is returned to her or nearby allies as Health.",
|
text: "A portion of all damage dealt by Action Skills is returned to her or nearby allies as Health.",
|
||||||
effect: rank => `Life Steal +30% of Skill damage dealt`,
|
effect: rank => `Life Steal +30% of Skill damage dealt`,
|
||||||
|
@ -174,7 +174,7 @@ const skills = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"3": {
|
"3": {
|
||||||
"Reverberation (Action Skill)": {
|
"Reverberation": {
|
||||||
ranks: 0,
|
ranks: 0,
|
||||||
text: "Amara sends forward an Astral Projection of herself that damages everything in its path. Deals increased damage for every enemy hit.",
|
text: "Amara sends forward an Astral Projection of herself that damages everything in its path. Deals increased damage for every enemy hit.",
|
||||||
effect: rank => `Damage 116, Damage Bonus +50% per enemy hit, Cooldown 24 sec`,
|
effect: rank => `Damage 116, Damage Bonus +50% per enemy hit, Cooldown 24 sec`,
|
||||||
|
@ -185,7 +185,7 @@ const skills = {
|
||||||
text: "All Action Skill Augments gain increased effects.",
|
text: "All Action Skill Augments gain increased effects.",
|
||||||
effect: rank => `Soul Sap Lifesteal +20%, Allure Radius +100%, Glamour Duration +50%, Bright Star Damage +25%, Stillness of Mind breaks 0.75 sec after being damaged`,
|
effect: rank => `Soul Sap Lifesteal +20%, Allure Radius +100%, Glamour Duration +50%, Bright Star Damage +25%, Stillness of Mind breaks 0.75 sec after being damaged`,
|
||||||
},
|
},
|
||||||
"Stillness of Mind (Action Skill Augment)": {
|
"Stillness of Mind": {
|
||||||
ranks: 0,
|
ranks: 0,
|
||||||
text: "Enemies damaged by Action Skills becomes Phaselocked until they are damaged or duration ends, but Action Skill Cooldown is increased. If an enemy is the target of Phasegrasp, nearby enemies are also Phaselocked.",
|
text: "Enemies damaged by Action Skills becomes Phaselocked until they are damaged or duration ends, but Action Skill Cooldown is increased. If an enemy is the target of Phasegrasp, nearby enemies are also Phaselocked.",
|
||||||
effect: rank => `Damage -35%, Max Duration 6 sec, Cooldown +15%`,
|
effect: rank => `Damage -35%, Max Duration 6 sec, Cooldown +15%`,
|
||||||
|
@ -193,7 +193,7 @@ const skills = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"4": {
|
"4": {
|
||||||
"Deliverance (Action Skill)": {
|
"Deliverance": {
|
||||||
ranks: 0,
|
ranks: 0,
|
||||||
text: "Amara sends forward an Astral Projection of herself that deals damage to everything in its path. Upon hitting enemies, it releases homing Elemental Projectiles that trigger Action Skill Elemental Effect on enemies.",
|
text: "Amara sends forward an Astral Projection of herself that deals damage to everything in its path. Upon hitting enemies, it releases homing Elemental Projectiles that trigger Action Skill Elemental Effect on enemies.",
|
||||||
effect: rank => `Damage 124, Elemental Projectiles 3 per enemy hit, Cooldown 24 sec`,
|
effect: rank => `Damage 124, Elemental Projectiles 3 per enemy hit, Cooldown 24 sec`,
|
||||||
|
@ -226,7 +226,7 @@ const skills = {
|
||||||
text: "Rush stacks gain increased effectiveness.",
|
text: "Rush stacks gain increased effectiveness.",
|
||||||
effect: rank => `Rush Stack Effectiveness +${rank * 10}%`,
|
effect: rank => `Rush Stack Effectiveness +${rank * 10}%`,
|
||||||
},
|
},
|
||||||
"Tandava (Action Skill)": {
|
"Tandava": {
|
||||||
ranks: 0,
|
ranks: 0,
|
||||||
text: "Amara sends forward an Astral Projection of herself that explodes when it hits a target, damaging all nearby enemies.",
|
text: "Amara sends forward an Astral Projection of herself that explodes when it hits a target, damaging all nearby enemies.",
|
||||||
effect: rank => `Damage: 166, Cooldown 28 sec`,
|
effect: rank => `Damage: 166, Cooldown 28 sec`,
|
||||||
|
@ -283,14 +283,14 @@ const skills = {
|
||||||
text: "Whenever Elemental Effects are applies to an enemy, increases chance to spread to a nearby enemy.",
|
text: "Whenever Elemental Effects are applies to an enemy, increases chance to spread to a nearby enemy.",
|
||||||
effect: rank => `Spread Chance ${rank * 8}%`,
|
effect: rank => `Spread Chance ${rank * 8}%`,
|
||||||
},
|
},
|
||||||
"Soulfire (Action Skill Element)": {
|
"Soulfire": {
|
||||||
ranks: 0,
|
ranks: 0,
|
||||||
text: "Converts Action Skill to Fire Damage. This does not take effect until after Action Skill is used.",
|
text: "Converts Action Skill to Fire Damage. This does not take effect until after Action Skill is used.",
|
||||||
type: SKILLS.AUGMENT_DIAMOND,
|
type: SKILLS.AUGMENT_DIAMOND,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"3": {
|
"3": {
|
||||||
"The Eternal Fist (Action Skill)": {
|
"The Eternal Fist": {
|
||||||
ranks: 0,
|
ranks: 0,
|
||||||
text: "Amara summons a giant fist that bursts into the ground and locks targeted enemy in place. If Grasped enemy is killed, up to 3 new targets can be Grasped as well.",
|
text: "Amara summons a giant fist that bursts into the ground and locks targeted enemy in place. If Grasped enemy is killed, up to 3 new targets can be Grasped as well.",
|
||||||
effect: rank => `Bonus Targets up to +4, Cooldown 23 sec, Grasp Immune Damage 66`,
|
effect: rank => `Bonus Targets up to +4, Cooldown 23 sec, Grasp Immune Damage 66`,
|
||||||
|
@ -301,7 +301,7 @@ const skills = {
|
||||||
text: "Gun Damage is increased after an enemy is Grasped. If a Grasped Enemy is killed, current weapon is instantly reloaded.",
|
text: "Gun Damage is increased after an enemy is Grasped. If a Grasped Enemy is killed, current weapon is instantly reloaded.",
|
||||||
effect: rank => `Weapon Damage +10%, Duration 8 sec`,
|
effect: rank => `Weapon Damage +10%, Duration 8 sec`,
|
||||||
},
|
},
|
||||||
"Allure (Action Skill Augment)": {
|
"Allure": {
|
||||||
ranks: 0,
|
ranks: 0,
|
||||||
text: "Amara's Action Skill creates singularities that pull in enemies.",
|
text: "Amara's Action Skill creates singularities that pull in enemies.",
|
||||||
effect: rank => `Action Skill Damage -25%, Duration 2.5 sec`,
|
effect: rank => `Action Skill Damage -25%, Duration 2.5 sec`,
|
||||||
|
@ -324,7 +324,7 @@ const skills = {
|
||||||
text: "When Elemental Effect is applied on an enemy that dies, enemy explodes an deals attuned element damage along with any other inflicted elements.",
|
text: "When Elemental Effect is applied on an enemy that dies, enemy explodes an deals attuned element damage along with any other inflicted elements.",
|
||||||
effect: rank => `Damage ${rank * 13}, Cooldown 8 sec`, // TODO level scaling
|
effect: rank => `Damage ${rank * 13}, Cooldown 8 sec`, // TODO level scaling
|
||||||
},
|
},
|
||||||
"Ties That Bind (Action Skill)": {
|
"Ties That Bind": {
|
||||||
ranks: 0,
|
ranks: 0,
|
||||||
text: "Amara summons a giant fist that bursts from the ground and locks targeted enemy in place. Enemies near Grasped target are linked, and any damage dealt to a linked target is shared between all links.",
|
text: "Amara summons a giant fist that bursts from the ground and locks targeted enemy in place. Enemies near Grasped target are linked, and any damage dealt to a linked target is shared between all links.",
|
||||||
effect: rank => `Link Damage 35% of damage dealt, Cooldown 17 sec, Grasp Immune Damage 80`,
|
effect: rank => `Link Damage 35% of damage dealt, Cooldown 17 sec, Grasp Immune Damage 80`,
|
||||||
|
@ -332,7 +332,7 @@ const skills = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"5": {
|
"5": {
|
||||||
"Fist Over Matter (Action Skill)": {
|
"Fist Over Matter": {
|
||||||
ranks: 0,
|
ranks: 0,
|
||||||
text: "Amara summons a giant fist that bursts from the ground and locks targeted enemy in place. After Grasping, large fists appear to smash the area, dealing damage to nearby enemies.",
|
text: "Amara summons a giant fist that bursts from the ground and locks targeted enemy in place. After Grasping, large fists appear to smash the area, dealing damage to nearby enemies.",
|
||||||
effect: rank => `Damage 21, Cooldown 31 sec, Grasp Immune Damage 93`,
|
effect: rank => `Damage 21, Cooldown 31 sec, Grasp Immune Damage 93`,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue