{selectedClass &&
diff --git a/src/components/TalentSlot.scss b/src/components/TalentSlot.scss
index 7cd8cb7..457508f 100644
--- a/src/components/TalentSlot.scss
+++ b/src/components/TalentSlot.scss
@@ -1,66 +1,102 @@
+$row-offset: 30px;
$row-distance: 70px;
-@mixin rowStyle($rowNr) {
- top: 30px + (($rowNr) * 70px);
-}
+$col-offset: 44px;
+$col-distance: 56px;
+
+$color-yellow: #ffd100;
+$color-green: #1eff00;
+$color-dark-green: #40bf40;
+$color-subtle: #9d9d9d;
.talent {
position: absolute;
width: 40px;
height: 40px;
- border: 1px solid black;
- border-radius: 2px;
+ border-radius: 5px;
+ transition: filter .1s linear;
+ filter: none;
+ cursor: pointer;
- &:after {
- content: "";
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- z-index: 10;
- }
+ &--available {
+ .talent__status::after {
+ // background-color: rgba($color-green, .8);
+ box-shadow: inset 0px 0px 6px 3px rgba($color-green, .8);
+ }
- small {
- font-size: 8px;
- line-height: 1em;
- }
-
- &--disabled {
- filter: grayscale(100%)
- }
-
- &:not(&--disabled) {
- cursor: pointer;
- }
-
- &--maxed {
-
- }
-
- @for $i from 0 through 6 {
- &[data-row="#{$i}"] {
- @include rowStyle($i)
+ .point-label {
+ color: $color-green;
}
}
+ &--maxed {
+ .talent__status::after {
+ box-shadow: inset 0px 0px 6px 3px rgba($color-yellow, .8);
+ }
- $col-distance-offset: 44px;
- $col-distance: 56px;
+ .point-label {
+ color: $color-yellow;
+ }
+ }
- &[data-col="0"] { left: $col-distance-offset; }
- &[data-col="1"] { left: $col-distance-offset + ($col-distance * 1); }
- &[data-col="2"] { left: $col-distance-offset + ($col-distance * 2); }
- &[data-col="3"] { left: $col-distance-offset + ($col-distance * 3); }
+ &--disabled {
+ filter: grayscale(100%);
- &__points {
+ .talent__status {
+ opacity: .7;
+ }
+ }
+
+ // Rows
+ @for $i from 0 through 6 {
+ &[data-row="#{$i}"] {
+ top: $row-offset + (($i) * $row-distance);
+ }
+ }
+
+ // Columns
+ @for $i from 0 through 3 {
+ &[data-col="#{$i}"] {
+ left: $col-offset + ($col-distance * $i);
+ }
+ }
+
+ &__status {
position: absolute;
- padding: 1px 2px;
- bottom: -2px;
- right: -2px;
+ width: 48px;
+ height: 46px;
+ bottom: -1px;
+ left: -4px;
+ background-image: url('/images/icons/large/default.png');
+ background-size: cover;
+
+ &:after {
+ content: '';
+ position: absolute;
+ width: 44px;
+ height: 44px;
+ top: 2px;
+ left: 2px;
+ border-radius: 5px;
+ }
+ }
+}
+
+.point-label {
+ position: absolute;
+ bottom: -5px;
+ right: -5px;
+ min-width: 7px;
+ text-align: center;
+ padding: 1px 3px;
+ color: #999;
+ font-size: 11px;
+ font-family: Arial, Helvetica, sans-serif;
+ background: #111;
+ border-radius: 4px;
+ user-select: none;
+
+ &--enabled {
color: white;
- font-size: 12px;
- background: black;
- border-radius: 2px;
}
}
\ No newline at end of file
diff --git a/src/components/TalentSlot.tsx b/src/components/TalentSlot.tsx
index 6707bbe..802f7aa 100644
--- a/src/components/TalentSlot.tsx
+++ b/src/components/TalentSlot.tsx
@@ -37,9 +37,14 @@ export const TalentSlot: FC
= (props) => {
const showPoints = points > 0 || availablePoints > 0
const disabled = props.disabled || !showPoints || !isAvailable(talent, specId, knownTalents)
- const cn = classNames('talent', {
+ const containerClassNames = classNames('talent', {
'talent--disabled': !!disabled,
- 'talent--maxed': points >= talent.ranks.length
+ 'talent--available': !disabled && points < talent.ranks.length,
+ 'talent--maxed': points >= talent.ranks.length || (points > 0 && availablePoints === 0)
+ })
+
+ const pointsClassNames = classNames('point-label', {
+ 'point-label--enabled': !disabled
})
const handleContextMenu = (e) => {
@@ -50,17 +55,21 @@ export const TalentSlot: FC = (props) => {
return (
props.onClick(talent.id) : () => {}}
onContextMenu={handleContextMenu}
>
-
+
+
- {showPoints &&
-
{points}/{talent.ranks.length}
+ {showPoints && !disabled &&
+
+ {points}
+ /{talent.ranks.length}
+
}
)
diff --git a/src/lib/tree.ts b/src/lib/tree.ts
index 51ab682..efc7483 100644
--- a/src/lib/tree.ts
+++ b/src/lib/tree.ts
@@ -9,6 +9,13 @@ import { classByName } from '../data/classes'
export const MAX_POINTS = 51
export const MAX_ROWS = 7
+export const SORT_TALENTS = (a: TalentData, b: TalentData) => {
+ if (a.row === b.row) {
+ return a.col - b.col
+ }
+ return a.row - b.row
+}
+
/**
* Returns the overall points spent in the tree.
*/
@@ -76,6 +83,7 @@ export const removeTalentPoint = (known: Map, talent: TalentData
// No points to reduce for this talent
if (currentPoints === 0) {
+ console.warn('no points to reduce')
return known
}
@@ -85,8 +93,11 @@ export const removeTalentPoint = (known: Map, talent: TalentData
known.forEach((points, talentId) => {
const t = talentsBySpec[specId][talentId]
- if (t) {
+ if (t && points > 0) {
isDependency = isDependency || t.requires.some((req) => req.id === talent.id)
+ if (t.row > highestRow) {
+ console.info('new highest row:', t)
+ }
highestRow = t.row > highestRow ? t.row : highestRow
for (let row = t.row; row < MAX_ROWS; row++) {
cumulativePointsPerRow[row] = (cumulativePointsPerRow[row] || 0) + points
@@ -98,11 +109,18 @@ export const removeTalentPoint = (known: Map, talent: TalentData
const pointsUntilHighestRow = cumulativePointsPerRow[highestRow - 1]
const targetPointsHighestRow = highestRow * 5
if (talent.row < highestRow && pointsUntilHighestRow - 1 < targetPointsHighestRow) {
+ console.warn('would not break the requirements for talents spent in later rows', {
+ talent,
+ highestRow,
+ pointsUntilHighestRow,
+ targetPointsHighestRow
+ })
return known
}
// Prevent if another talent depends on this
if (isDependency) {
+ console.warn('is dependency')
return known
}
@@ -143,12 +161,7 @@ export function encodeKnownTalents(known: Map, className: string
const { specs } = classByName[className]
for (let i = 0; i < specs.length; i++) {
const specId = specs[i]
- const talents = talentsBySpecArray[specId].sort((a, b) => {
- if (a.row === b.row) {
- return a.col - b.col
- }
- return a.row - b.row
- })
+ const talents = talentsBySpecArray[specId].sort(SORT_TALENTS)
string += i > 0 ? '-' : ''
string += removeTrailingCharacters(
talents.map((talent) => known.get(talent.id, 0)).join(''),
@@ -162,7 +175,36 @@ export function encodeKnownTalents(known: Map, className: string
* Decodes a string of points into a Map of talents.
*/
export function decodeKnownTalents(pointString: string, className: string): Map {
- return Map()
+ console.log(pointString, className)
+
+ const { specs } = classByName[className]
+ let known = Map()
+
+ // TODO: Make sure we validate the point string
+ const parts = pointString.split('-')
+ for (let i = 0; i < parts.length; i++) {
+ const specId = specs[i]
+ const specPointStr = parts[i]
+ console.log(specPointStr, { specId })
+ const talents = talentsBySpecArray[specId].sort(SORT_TALENTS)
+
+ for (let y = 0; y < specPointStr.length; y++) {
+ const talent = talents[y]
+ const points = parseInt(specPointStr[y], 10)
+
+ // Validation: break out loop if there's more points in the string than this talent can have
+ if (points > talent.ranks.length) {
+ break
+ }
+
+ if (points > 0) {
+ console.log(`Spent ${points} in ${talent.id}`)
+ known = known.set(talent.id, points)
+ }
+ }
+ }
+
+ return known
}
/**