Using WH datasets

This commit is contained in:
Melvin Valster
2019-07-16 23:25:41 +02:00
parent 85f9208b4a
commit 7d1b822830
11 changed files with 12679 additions and 62 deletions
+29 -18
View File
@@ -1,7 +1,10 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import im from 'immutable' import { List, Map, fromJS } from 'immutable'
import { TalentTree } from './TalentTree'; import { TalentTree } from './TalentTree';
import { setPointsInTree } from '../lib/tree'; import { modifyPointsInTree, modifyKnownTalents } from '../lib/tree';
import { talentsBySpec, specNames } from '../data/talents';
import { classByName } from '../data/classes';
import { number } from 'prop-types';
const createTalent = (name: string, row: number, column: number, ranks: string | string[], type: Talent['type'] = 'talent'): Talent => { const createTalent = (name: string, row: number, column: number, ranks: string | string[], type: Talent['type'] = 'talent'): Talent => {
return { return {
@@ -94,33 +97,41 @@ interface Props {
pointString?: string // e.g. 2305302300--001 pointString?: string // e.g. 2305302300--001
} }
const initialSpentPoints: im.List<im.List<number>> = im.fromJS([ const initialSpentPoints: List<List<number>> = fromJS([
[], [], [] [], [], []
]) ])
const initMap = Map<number, number>()
export const Calculator: React.FC<Props> = ({ forClass, pointString = '' }) => { export const Calculator: React.FC<Props> = ({ forClass = 'warlock', pointString = '' }) => {
const [knownTalents, setKnownTalents] = useState(initMap)
const [spentPoints, setSpentPoints] = useState(initialSpentPoints) const [spentPoints, setSpentPoints] = useState(initialSpentPoints)
const points = pointString.split('')
console.log({spentPoints}) const selectedClass = classByName[forClass]
console.log(knownTalents)
const handleTalentPress = (specId: number, talentId: number, modifier: 1 | -1) => {
console.log('onTalentPress', { specId, talentId, modifier })
const talent = talentsBySpec[specId][talentId]
setKnownTalents(modifyKnownTalents(knownTalents, talent, modifier))
const onTalentPress = (treeIndex, talentId, clickType) => {
console.log('onTalentPress')
const newSpentPoints = spentPoints.set(
treeIndex,
setPointsInTree(spentPoints.get(treeIndex), talentId, 9)
)
setSpentPoints(newSpentPoints)
} }
return ( return (
<div className="calculator"> <div className="calculator">
<TalentTree {selectedClass.specs.map((specId, specIndex) => (
tree={warlockTalents[0]} <TalentTree
spentPoints={spentPoints.get(0)} key={specId}
onTalentPress={(talentId, clickType) => onTalentPress(0, talentId, clickType)} specId={specId}
/> knownTalents={knownTalents}
spentPoints={spentPoints.get(specIndex)}
onTalentPress={handleTalentPress}
/>
))}
</div> </div>
) )
} }
+9
View File
@@ -0,0 +1,9 @@
.icon {
background-repeat: no-repeat;
background-size: cover;
&--medium {
width: 40px;
height: 40px;
}
}
+10 -4
View File
@@ -1,12 +1,18 @@
import React from 'react' import React, { FC } from 'react'
import './Icon.scss'
interface Props { interface Props {
name: string
size?: 'small' | 'medium' | 'large'
} }
export const Icon: React.FC<Props> = () => { export const Icon: FC<Props> = ({ name, size = 'medium', children }) => {
const url = `https://wow.zamimg.com/images/wow/icons/${size}/${name}.jpg`
const className = `icon icon--${size}`
return ( return (
<div className="icon"> <div className={className} style={{ backgroundImage: `url(${url})`}}>
icon {children}
</div> </div>
) )
} }
+8 -6
View File
@@ -1,27 +1,29 @@
import React from 'react' import React, { FC } from 'react'
import './TalentSlot.scss' import './TalentSlot.scss'
import { Icon } from './Icon'
import { spells } from '../data/spells'
interface Props { interface Props {
key: number key: number
talent: Talent talent: TalentData
/** Points spent */ /** Points spent */
points: number points: number
onClick?: (e: any) => void onClick?: (e: any) => void
} }
export const TalentSlot: React.FC<Props> = (props) => { export const TalentSlot: FC<Props> = (props) => {
const { talent, points } = props const { talent, points } = props
const requiredPointsSpent = talent.row * 5 const requiredPointsSpent = talent.row * 5
return ( return (
<div <div
className="talent" className="talent"
title={talent.name} title={talent.ranks[0].toString()}
data-row={talent.row} data-row={talent.row}
data-col={talent.column} data-col={talent.col}
onClick={props.onClick} onClick={props.onClick}
> >
<small>{talent.name}</small> <Icon name={talent.icon} />
<div className="talent__rank">{points}/{talent.ranks.length}</div> <div className="talent__rank">{points}/{talent.ranks.length}</div>
</div> </div>
) )
+17 -12
View File
@@ -1,33 +1,38 @@
import React from 'react' import React, { MouseEvent } from 'react'
import { List } from 'immutable' import { List, Map } from 'immutable'
import { TalentSlot } from './TalentSlot'; import { TalentSlot } from './TalentSlot';
import { getTreePointCount } from '../lib/tree';
import { talentsBySpec, specNames } from '../data/talents';
interface Props { interface Props {
tree: TalentTree specId: number
spentPoints: List<number> spentPoints: List<number>
knownTalents: Map<number, number>
onTalentPress: TalentClickHandler onTalentPress: TalentClickHandler
} }
export const TalentTree: React.FC<Props> = ({ tree, spentPoints, onTalentPress }) => { export const TalentTree: React.FC<Props> = ({ specId, spentPoints, knownTalents, onTalentPress }) => {
const { talents } = tree const talents = Object.values(talentsBySpec[specId])
const handleTalentPress = (index) => { const handleTalentPress = (talentId: number) => {
return (e) => { return (e: MouseEvent) => {
onTalentPress(index, 'add') onTalentPress(specId, talentId, e.shiftKey ? -1 : 1)
} }
} }
return ( return (
<div className="tree"> <div className="tree">
<h2>{tree.name}</h2> <h2>{specNames[specId]}</h2>
{talents.map((talent, index) => {talents.map((talent, index) =>
<TalentSlot <TalentSlot
key={index} key={talent.id}
talent={talent} talent={talent}
points={spentPoints.get(index, 0)} points={knownTalents.get(talent.id, 0)}
onClick={handleTalentPress(index)} onClick={handleTalentPress(talent.id)}
/> />
)} )}
Spent: {getTreePointCount(spentPoints)}
</div> </div>
) )
} }
+79
View File
@@ -0,0 +1,79 @@
interface ClassData {
id: number
name: string
icon: string
specs: number[]
}
export const classes: ClassData[] = [
{
id: 1,
name: 'Warrior',
icon: 'class_warrior',
specs: [161, 164, 163]
},
{
id: 2,
name: 'Paladin',
icon: 'class_paladin',
specs: [382, 383, 381]
},
{
id: 3,
name: 'Hunter',
icon: 'class_hunter',
specs: [361, 363, 362]
},
{
id: 4,
name: 'Rogue',
icon: 'class_rogue',
specs: [182, 181, 183]
},
{
id: 5,
name: 'Priest',
icon: 'class_priest',
specs: [201, 202, 203]
},
{
id: 7,
name: 'Shaman',
icon: 'class_shaman',
specs: [261, 263, 262]
},
{
id: 8,
name: 'Mage',
icon: 'class_mage',
specs: [81, 41, 61]
},
{
id: 9,
name: 'Warlock',
icon: 'class_warlock',
specs: [302, 303, 301]
},
{
id: 11,
name: 'Druid',
icon: 'class_druid',
specs: [283, 281, 282]
},
]
export const classById: {[key: number]: ClassData} =
classes.reduce((previousValue: object, currentValue: ClassData) => {
return {
...previousValue,
[currentValue.id]: currentValue
}
}, {})
export const classByName: {[key: string]: ClassData} =
classes.reduce((previousValue: object, currentValue: ClassData) => {
return {
...previousValue,
[currentValue.name.toLowerCase()]: currentValue
}
}, {})
+8637
View File
File diff suppressed because it is too large Load Diff
+3745
View File
File diff suppressed because it is too large Load Diff
+34 -15
View File
@@ -1,21 +1,40 @@
import im from 'immutable' import im from 'immutable'
import { setPointsInTree } from './tree' import {
setTalentPointsInTree,
getTreePointCount
} from './tree'
it('sets points on an empty tree', () => {
const tree = im.List() describe('setTalentPointsInTree', () => {
expect(setPointsInTree(tree, 2, 5).toJS()).toEqual([0, 0, 5]) it('sets points on an empty tree', () => {
const tree = im.List()
expect(setTalentPointsInTree(tree, 2, 5).toJS()).toEqual([0, 0, 5])
})
it('sets points in the end of the current range', () => {
const tree = im.List([0, 1])
expect(setTalentPointsInTree(tree, 2, 5).toJS()).toEqual([0, 1, 5])
})
it('sets points in the middle of the current range', () => {
const tree = im.List([0, 0, 0, 0, 0, 0, 5])
expect(setTalentPointsInTree(tree, 2, 5).toJS()).toEqual([0, 0, 5, 0, 0, 0, 5])
})
it('does not mutate the tree for points already set', () => {
const tree = im.List([0, 3, 2, 0, 5])
expect(setTalentPointsInTree(tree, 1, 3)).toStrictEqual(tree)
})
}) })
it('sets points in the end of the current range', () => { describe('getTreePointCount', () => {
const tree = im.List([0, 1]) it('returns proper count', () => {
expect(setPointsInTree(tree, 2, 5).toJS()).toEqual([0, 1, 5]) const result = getTreePointCount(im.List([0, 0, 4, 5, 3, 0, 0]))
}) expect(result).toBe(12)
it('sets points in the middle of the current range', () => { })
const tree = im.List([0, 0, 0, 0, 0, 0, 5])
expect(setPointsInTree(tree, 2, 5).toJS()).toEqual([0, 0, 5, 0, 0, 0, 5])
})
it('does not mutate the tree for points already set', () => { it('returns 0 for empty list', () => {
const tree = im.List([0, 3, 2, 0, 5]) const result = getTreePointCount(im.List())
expect(setPointsInTree(tree, 1, 3)).toStrictEqual(tree) expect(result).toBe(0)
})
}) })
+89 -5
View File
@@ -1,9 +1,93 @@
import im from 'immutable' import { List, Map, fromJS } from 'immutable'
export function setPointsInTree(tree: im.List<number>, index: number, points: number) { /**
* Sets proper values for a tree, filling in 0s in between.
*/
export function setTalentPointsInTree(tree: List<number>, talentIndex: number, points: number): List<number> {
// Ensure all values until `index` are set, otherwise set to 0 // Ensure all values until `index` are set, otherwise set to 0
for (let i = tree.size; i <= index; i++) { for (let i = tree.size; i < talentIndex; i++) {
tree = tree.set(i, i === index ? points : 0) tree = tree.set(i, 0)
} }
return tree return tree.set(talentIndex, points)
}
/**
* Returns the overall points spent in the tree.
*/
export function getTreePointCount(tree: List<number>): number {
return tree.reduce((reduction, value) => value + reduction, 0)
}
export function canRemovePoint() {
}
export function canSetPoint() {
}
export const modifyPointsInTree = (tree: List<number>, talent: TalentData, talentIndex: number, modifier: 1 | -1): List<number> => {
const currentPoints = tree.get(talentIndex, 0)
// TODO: We should prevent reducing talent points on a row when it is a dependency for points already spent in the next row.
// TODO: Support for specific Talent dependency requirement.
// TODO: Spend a maximum of 51 points
// Check we have the required amount of points spent in the tree for this talent
const requiredPoints = talent.row * 5
const spentPointCount = getTreePointCount(tree)
if (requiredPoints > spentPointCount) return tree
let newPoints = currentPoints
if (modifier === 1) {
if (currentPoints === talent.ranks.length) return tree
newPoints = currentPoints + 1
} else if (modifier === -1) {
if (currentPoints === 0) return tree
newPoints = currentPoints - 1
}
return setTalentPointsInTree(tree, talentIndex, newPoints)
}
export const modifyKnownTalents = (known: Map<number, number>, talent: TalentData, modifier: 1 | -1): Map<number, number> => {
const currentPoints = known.get(talent.id, 0)
// TODO: We should prevent reducing talent points on a row when it is a dependency for points already spent in the next row.
// TODO: Support for specific Talent dependency requirement.
// TODO: Spend a maximum of 51 points
// TODO: Check we have the required amount of points spent in the tree for this talent
const requiredPoints = talent.row * 5
// const spentPointCount = known.get(talent.id, 0)
// if (requiredPoints > spentPointCount) return tree
let newPoints = currentPoints
if (modifier === 1) {
if (currentPoints === talent.ranks.length) return known
newPoints = currentPoints + 1
} else if (modifier === -1) {
if (currentPoints === 0) return known
newPoints = currentPoints - 1
}
return newPoints === 0
? known.remove(talent.id)
: known.set(talent.id, newPoints)
}
export function parsePointString(str: string): List<List<number>> {
const list: Array<number[]> = []
const trees = str.split('-')
trees.map((stringForTree, index) => {
const points = stringForTree.split('').map(a => parseInt(a, 2))
list[index] = points
})
return fromJS(list)
} }
+21 -1
View File
@@ -5,6 +5,26 @@ interface TalentTree {
talents: Talent[] talents: Talent[]
} }
interface TalentData {
/** ID for the Talent */
id: number
/** Row for talent. Starts at 0 */
row: number
/** Column for talent. Starts at 0 */
col: number
/** Icon */
icon: string
/** Array of spell IDs */
ranks: number[]
/** Talent dependencies for this talent */
requires: Array<{
/** Talent ID */
id: number
/** Amount of points required in this talent */
qty: number
}>
}
interface Talent { interface Talent {
name: string name: string
row: number row: number
@@ -14,4 +34,4 @@ interface Talent {
prerequisite?: [number, number] | number // [row, column] OR index prerequisite?: [number, number] | number // [row, column] OR index
} }
type TalentClickHandler = (talentId, clickType: 'add' | 'remove') => void type TalentClickHandler = (specId: number, talentId: number, modifier: 1 | -1) => void