Lane-dependant stats (fix #5)
This commit is contained in:
@@ -1,18 +1,17 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
championId: string,
|
championId: number,
|
||||||
winrate: number,
|
winrate: number,
|
||||||
pickrate: number,
|
pickrate: number,
|
||||||
gameCount: number
|
gameCount: number
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const championId = Number(props.championId)
|
const winrate = ref((props.winrate * 100).toFixed(2))
|
||||||
|
watch(() => props.winrate, () => {winrate.value = (props.winrate * 100).toFixed(2)})
|
||||||
|
const pickrate = ref((props.pickrate * 100).toFixed(2))
|
||||||
|
watch(() => props.pickrate, () => {pickrate.value = (props.pickrate * 100).toFixed(2)})
|
||||||
|
|
||||||
const winrate = (props.winrate * 100).toFixed(2)
|
const { data: championData } : ChampionResponse = await useFetch(CDRAGON_BASE + "plugins/rcp-be-lol-game-data/global/default/v1/champions/" + props.championId + ".json")
|
||||||
const pickrate = (props.pickrate * 100).toFixed(2)
|
|
||||||
const gameCount = props.gameCount
|
|
||||||
|
|
||||||
const { data: championData } : ChampionResponse = await useFetch(CDRAGON_BASE + "plugins/rcp-be-lol-game-data/global/default/v1/champions/" + championId + ".json")
|
|
||||||
const championName = championData.value.name
|
const championName = championData.value.name
|
||||||
const championDescription = championData.value.title
|
const championDescription = championData.value.title
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
builds: Builds
|
builds: Builds
|
||||||
}>()
|
}>()
|
||||||
const builds = props.builds
|
|
||||||
|
|
||||||
const {data : items} : ItemResponse = await useFetch(CDRAGON_BASE + "plugins/rcp-be-lol-game-data/global/default/v1/items.json")
|
const {data : items} : ItemResponse = await useFetch(CDRAGON_BASE + "plugins/rcp-be-lol-game-data/global/default/v1/items.json")
|
||||||
const itemMap = reactive(new Map())
|
const itemMap = reactive(new Map())
|
||||||
@@ -10,9 +9,15 @@ for(let item of items.value) {
|
|||||||
itemMap.set(item.id, item)
|
itemMap.set(item.id, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
builds.tree.children.splice(1, builds.tree.children.length - 1)
|
watch(() => props.builds, () => trimBuilds(props.builds))
|
||||||
if(builds.tree.children[0] != null && builds.tree.children[0] != undefined)
|
trimBuilds(props.builds)
|
||||||
builds.tree.children[0].children.splice(1, builds.tree.children[0].children.length - 1)
|
|
||||||
|
function trimBuilds(builds : Builds) {
|
||||||
|
builds.tree.children.splice(1, builds.tree.children.length - 1)
|
||||||
|
if(builds.tree.children[0] != null && builds.tree.children[0] != undefined)
|
||||||
|
builds.tree.children[0].children.splice(1, builds.tree.children[0].children.length - 1)
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { LANE_IMAGES, LANE_IMAGES_HOVER, LANE_IMAGES_SELECTED } from '~/utils/cdragon';
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
filterChange: [value: number]
|
filterChange: [value: number]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const POSITIONS = ["top", "jungle", "middle", "bottom", "utility"]
|
|
||||||
const LANE_IMAGES = Array(5).fill("").map((_, index) => "/img/lanes/icon-position-" + POSITIONS[index] + ".png")
|
|
||||||
const LANE_IMAGES_HOVER = Array(5).fill("").map((_, index) => "/img/lanes/icon-position-" + POSITIONS[index] + "-hover.png")
|
|
||||||
const LANE_IMAGES_SELECTED = Array(5).fill("").map((_, index) => "/img/lanes/icon-position-" + POSITIONS[index] + "-blue.png")
|
|
||||||
|
|
||||||
const laneImgs = Array(5).fill(ref("")).map((_, index) => ref(LANE_IMAGES[index]))
|
const laneImgs = Array(5).fill(ref("")).map((_, index) => ref(LANE_IMAGES[index]))
|
||||||
const laneFilter = ref(-1)
|
const laneFilter = ref(-1)
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ const props = defineProps<{
|
|||||||
pickrate: number}>
|
pickrate: number}>
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const runes = props.runes
|
|
||||||
|
|
||||||
const currentlySelectedPage = ref(0)
|
const currentlySelectedPage = ref(0)
|
||||||
const primaryStyles : Ref<Array<PerkStyle>> = ref([])
|
const primaryStyles : Ref<Array<PerkStyle>> = ref([])
|
||||||
const secondaryStyles : Ref<Array<PerkStyle>> = ref([])
|
const secondaryStyles : Ref<Array<PerkStyle>> = ref([])
|
||||||
@@ -21,22 +19,35 @@ for(let perk of perks_data.value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let { data: stylesData } : PerkStylesResponse = await useFetch(CDRAGON_BASE + "plugins/rcp-be-lol-game-data/global/default/v1/perkstyles.json")
|
let { data: stylesData } : PerkStylesResponse = await useFetch(CDRAGON_BASE + "plugins/rcp-be-lol-game-data/global/default/v1/perkstyles.json")
|
||||||
for(let style of stylesData.value.styles) {
|
watch(() => props.runes, (newRunes, oldRunes) => {
|
||||||
for(let rune of runes) {
|
currentlySelectedPage.value = 0
|
||||||
if(style.id == rune.primaryStyle) {
|
primaryStyles.value = []
|
||||||
primaryStyles.value.push(style)
|
secondaryStyles.value = []
|
||||||
for(let perk of style.slots[0].perks) {
|
keystoneIds.value = []
|
||||||
if(rune.selections.includes(perk)) {
|
|
||||||
keystoneIds.value.push(perk)
|
refreshStylesKeystones()
|
||||||
|
})
|
||||||
|
|
||||||
|
function refreshStylesKeystones() {
|
||||||
|
for(let style of stylesData.value.styles) {
|
||||||
|
for(let rune of props.runes) {
|
||||||
|
if(style.id == rune.primaryStyle) {
|
||||||
|
primaryStyles.value.push(style)
|
||||||
|
for(let perk of style.slots[0].perks) {
|
||||||
|
if(rune.selections.includes(perk)) {
|
||||||
|
keystoneIds.value.push(perk)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
if(style.id == rune.secondaryStyle) {
|
||||||
if(style.id == rune.secondaryStyle) {
|
secondaryStyles.value.push(style)
|
||||||
secondaryStyles.value.push(style)
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
refreshStylesKeystones()
|
||||||
|
|
||||||
function runeSelect(index: number) {
|
function runeSelect(index: number) {
|
||||||
currentlySelectedPage.value = index
|
currentlySelectedPage.value = index
|
||||||
}
|
}
|
||||||
@@ -45,8 +56,12 @@ function runeSelect(index: number) {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div style="width: fit-content;">
|
<div style="width: fit-content;">
|
||||||
<RunePage style="margin:auto; width: fit-content;" :primaryStyleId="runes[currentlySelectedPage].primaryStyle" :secondaryStyleId="runes[currentlySelectedPage].secondaryStyle" :selectionIds="runes[currentlySelectedPage].selections" />
|
<RunePage v-if="runes[currentlySelectedPage] != undefined && runes[currentlySelectedPage] != null"
|
||||||
<div style="display: flex; margin-top: 20px;">
|
style="margin:auto; width: fit-content;"
|
||||||
|
:primaryStyleId="runes[currentlySelectedPage].primaryStyle"
|
||||||
|
:secondaryStyleId="runes[currentlySelectedPage].secondaryStyle"
|
||||||
|
:selectionIds="runes[currentlySelectedPage].selections" />
|
||||||
|
<div style="display: flex; margin-top: 20px; justify-content: center;">
|
||||||
<div v-for="(_, i) in runes" :class="'rune-selector-entry ' + (i == currentlySelectedPage ? 'rune-selector-entry-selected' : '')" @click="runeSelect(i)">
|
<div v-for="(_, i) in runes" :class="'rune-selector-entry ' + (i == currentlySelectedPage ? 'rune-selector-entry-selected' : '')" @click="runeSelect(i)">
|
||||||
<div style="display: flex; margin-top: 20px;">
|
<div style="display: flex; margin-top: 20px;">
|
||||||
<NuxtImg v-if="primaryStyles[i] != null && primaryStyles[i] != undefined"
|
<NuxtImg v-if="primaryStyles[i] != null && primaryStyles[i] != undefined"
|
||||||
|
|||||||
@@ -1,27 +1,42 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { LANE_IMAGES, lanePositionToIndex } from '~/utils/cdragon';
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
championName: String
|
championName: String
|
||||||
|
championLanes: any
|
||||||
}>()
|
}>()
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
stateChange: [state: String]
|
stateChange: [state: String, lane: number]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const state = ref("runes")
|
const state = ref("runes")
|
||||||
|
const laneState = ref(0)
|
||||||
|
|
||||||
function handleStateChange(newState : string) {
|
function handleStateChange(newState : string, newLane: number) {
|
||||||
state.value = newState;
|
state.value = newState;
|
||||||
emit('stateChange', newState)
|
laneState.value = newLane;
|
||||||
|
emit('stateChange', newState, newLane)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="sidebar-container">
|
<div class="sidebar-container">
|
||||||
<Logo font-size="2.6rem" img-width="60" style="padding-left: 15px; padding-right: 15px; margin-top: 30px;"/>
|
<Logo font-size="2.6rem" img-width="60" style="padding-left: 15px; padding-right: 15px; margin-top: 30px;"/>
|
||||||
<h1 class="sidebar-link" style="margin-top: 30px; font-size: 2.4rem; padding-left: 20px;">{{ championName }}</h1>
|
|
||||||
<h2 :class="'sidebar-link ' + (state == 'runes' ? 'sidebar-link-selected' : '')"
|
<div v-for="(lane, i) in championLanes">
|
||||||
@click="handleStateChange('runes')" style="margin-top: 10px; font-size: 1.9rem; padding-left: 35px;">Runes</h2>
|
|
||||||
<h2 :class="'sidebar-link ' + (state == 'items' ? 'sidebar-link-selected' : '')"
|
<div style="display: flex; align-items: center; margin-top: 30px;">
|
||||||
@click="handleStateChange('items')" style="margin-top: 10px; font-size: 1.9rem; padding-left: 35px;">Items</h2>
|
<h1 style="font-size: 2.4rem; padding-left: 20px;">{{ championName }}</h1>
|
||||||
|
<img style="margin-left: 10px;" width="40" height="40" :src="LANE_IMAGES[lanePositionToIndex(lane.data)]" />
|
||||||
|
<h2 style="margin-left: 5px; font-size: 1.8rem; font-weight: 200;">{{ lane.data.toLowerCase() }}</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2 :class="'sidebar-link ' + (state == 'runes' && laneState == i ? 'sidebar-link-selected' : '')"
|
||||||
|
@click="handleStateChange('runes', i)" style="margin-top: 10px; font-size: 1.9rem; padding-left: 35px;">Runes</h2>
|
||||||
|
<h2 :class="'sidebar-link ' + (state == 'items' && laneState == i ? 'sidebar-link-selected' : '')"
|
||||||
|
@click="handleStateChange('items', i)" style="margin-top: 10px; font-size: 1.9rem; padding-left: 35px;">Items</h2>
|
||||||
|
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -8,15 +8,12 @@ const emit = defineEmits<{
|
|||||||
refresh: []
|
refresh: []
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const item = props.tree
|
|
||||||
|
|
||||||
const {data : items} : ItemResponse = await useFetch(CDRAGON_BASE + "plugins/rcp-be-lol-game-data/global/default/v1/items.json")
|
const {data : items} : ItemResponse = await useFetch(CDRAGON_BASE + "plugins/rcp-be-lol-game-data/global/default/v1/items.json")
|
||||||
const itemMap = reactive(new Map())
|
const itemMap = reactive(new Map())
|
||||||
for(let item of items.value) {
|
for(let item of items.value) {
|
||||||
itemMap.set(item.id, item)
|
itemMap.set(item.id, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
import type { TreeItem } from '#build/components';
|
|
||||||
import pkg from 'svg-dom-arrows';
|
import pkg from 'svg-dom-arrows';
|
||||||
const { LinePath } = pkg;
|
const { LinePath } = pkg;
|
||||||
|
|
||||||
@@ -28,6 +25,18 @@ onMounted(() => {
|
|||||||
emit('mount', start.value!!)
|
emit('mount', start.value!!)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
onBeforeUpdate(() => {
|
||||||
|
for(let arrow of arrows) {
|
||||||
|
arrow.release()
|
||||||
|
}
|
||||||
|
arrows.splice(0, arrows.length)
|
||||||
|
})
|
||||||
|
|
||||||
|
onUpdated(() => {
|
||||||
|
refreshArrows()
|
||||||
|
emit('mount', start.value!!)
|
||||||
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
for(let arrow of arrows) {
|
for(let arrow of arrows) {
|
||||||
arrow.release()
|
arrow.release()
|
||||||
@@ -84,13 +93,13 @@ function handleRefresh() {
|
|||||||
<template>
|
<template>
|
||||||
<div style="display: flex; align-items: center;">
|
<div style="display: flex; align-items: center;">
|
||||||
|
|
||||||
<div v-if="item.data != undefined && item.data != null" style="width: fit-content; height: fit-content;">
|
<div v-if="tree.data != undefined && tree.data != null" style="width: fit-content; height: fit-content;">
|
||||||
<img ref="start" class="item-img" width="64" height="64" :alt="item.data.toString()" :src="CDRAGON_BASE + mapPath(itemMap.get(item.data).iconPath)" />
|
<img ref="start" class="item-img" width="64" height="64" :alt="tree.data.toString()" :src="CDRAGON_BASE + mapPath(itemMap.get(tree.data).iconPath)" />
|
||||||
<h3 style="width: fit-content; margin:auto; margin-bottom: 10px;">{{ item.count }}</h3>
|
<h3 style="width: fit-content; margin:auto; margin-bottom: 10px;">{{ tree.count }}</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="margin-left: 30px;">
|
<div style="margin-left: 30px;">
|
||||||
<div style="width: fit-content; height: fit-content;" v-for="child in item.children">
|
<div style="width: fit-content; height: fit-content;" v-for="child in tree.children">
|
||||||
<TreeItem @refresh="handleRefresh" @mount="(end) => handleSubtreeMount(end)" :tree="child" />
|
<TreeItem @refresh="handleRefresh" @mount="(end) => handleSubtreeMount(end)" :tree="child" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,11 +1,18 @@
|
|||||||
<script setup>
|
<script setup lang="ts">
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const championAlias = route.params.alias
|
const championAlias = route.params.alias as string
|
||||||
|
|
||||||
const { data : championData } = await useFetch("/api/champion/" + championAlias.toLowerCase())
|
const { data : championData } : {data : Ref<ChampionData>} = await useFetch("/api/champion/" + championAlias.toLowerCase())
|
||||||
const championId = championData.value.id
|
const championId = championData.value.id
|
||||||
|
|
||||||
|
const laneState = ref(0)
|
||||||
const state = ref("runes")
|
const state = ref("runes")
|
||||||
|
const lane = ref(championData.value.lanes[laneState.value])
|
||||||
|
function updateState(newState : string, newLane : number) {
|
||||||
|
state.value = newState
|
||||||
|
laneState.value = newLane
|
||||||
|
lane.value = championData.value.lanes[laneState.value]
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -13,13 +20,15 @@ const state = ref("runes")
|
|||||||
<Title>{{ championData.name }} - BuildPath</Title>
|
<Title>{{ championData.name }} - BuildPath</Title>
|
||||||
</Head>
|
</Head>
|
||||||
|
|
||||||
<SideBar :champion-name="championData.name" @state-change="(newState) => state = newState"/>
|
<SideBar :champion-name="championData.name"
|
||||||
|
:champion-lanes="championData.lanes"
|
||||||
|
@state-change="updateState"/>
|
||||||
|
|
||||||
<!-- <div style="display: flex; width: fit-content; margin: auto; margin-left: 330px;"> -->
|
<!-- <div style="display: flex; width: fit-content; margin: auto; margin-left: 330px;"> -->
|
||||||
<div style="margin-top: 64px; margin-left: 339px;">
|
<div style="margin-top: 64px; margin-left: 339px;">
|
||||||
<ChampionTitle :champion-id="championId" :winrate="championData.winrate" :pickrate="championData.pickrate" :game-count="championData.gameCount" />
|
<ChampionTitle v-if="championData.gameCount > 0" :champion-id="championId" :winrate="lane.winrate" :pickrate="lane.pickrate" :game-count="lane.count" />
|
||||||
<RuneSelector v-if="state == 'runes' && championData.gameCount > 0" style="margin: auto; margin-top: 40px;" :runes="championData.runes" />
|
<RuneSelector v-if="state == 'runes' && championData.gameCount > 0" style="margin: auto; margin-top: 40px;" :runes="lane.runes" />
|
||||||
<ItemViewer v-if="state == 'items' && championData.gameCount > 0" style="margin:auto; margin-top: 40px;" :builds="championData.builds" />
|
<ItemViewer v-if="state == 'items' && championData.gameCount > 0" style="margin:auto; margin-top: 40px;" :builds="lane.builds" />
|
||||||
<h2 v-if="championData.gameCount == 0" style="margin: auto; margin-top: 20px; width: fit-content;">Sorry, there is no data for this champion :(</h2>
|
<h2 v-if="championData.gameCount == 0" style="margin: auto; margin-top: 20px; width: fit-content;">Sorry, there is no data for this champion :(</h2>
|
||||||
</div>
|
</div>
|
||||||
<!-- <ItemViewer v-if="championData.gameCount > 0" style="margin-top: 64px; margin-left: 64px;" :builds="championData.builds" /> -->
|
<!-- <ItemViewer v-if="championData.gameCount > 0" style="margin-top: 64px; margin-left: 64px;" :builds="championData.builds" /> -->
|
||||||
|
|||||||
43
frontend/types/api.ts
Normal file
43
frontend/types/api.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
declare global {
|
||||||
|
type ItemTree = {
|
||||||
|
count: number
|
||||||
|
data: number
|
||||||
|
children: Array<ItemTree>
|
||||||
|
}
|
||||||
|
type Builds = {
|
||||||
|
start: Array<{count: number, data: number}>
|
||||||
|
tree: ItemTree
|
||||||
|
bootsFirst: number
|
||||||
|
boots: Array<{count: number, data: number}>
|
||||||
|
lateGame: Array<{count: number, data: number}>
|
||||||
|
}
|
||||||
|
type Rune = {
|
||||||
|
count: number
|
||||||
|
primaryStyle: number
|
||||||
|
secondaryStyle: number
|
||||||
|
selections: Array<number>
|
||||||
|
pickrate: number
|
||||||
|
}
|
||||||
|
type LaneData = {
|
||||||
|
data: string
|
||||||
|
count: number
|
||||||
|
winningMatches: number
|
||||||
|
losingMatches: number
|
||||||
|
winrate: number
|
||||||
|
pickrate: number
|
||||||
|
runes: Array<Rune>
|
||||||
|
builds: Builds
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChampionData = {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
alias: string
|
||||||
|
gameCount: number
|
||||||
|
winrate: number
|
||||||
|
pickrate: number
|
||||||
|
lanes: Array<LaneData>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {};
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
declare global {
|
|
||||||
type ItemTree = {
|
|
||||||
count: number
|
|
||||||
data: number
|
|
||||||
children: Array<ItemTree>
|
|
||||||
}
|
|
||||||
type Builds = {
|
|
||||||
start: Array<{count: number, data: number}>
|
|
||||||
tree: ItemTree
|
|
||||||
bootsFirst: number
|
|
||||||
boots: Array<{count: number, data: number}>
|
|
||||||
lateGame: Array<{count: number, data: number}>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export {};
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
const CDRAGON_BASE = "https://raw.communitydragon.org/latest/"
|
|
||||||
|
|
||||||
function mapPath(assetPath) {
|
|
||||||
if(assetPath === undefined || assetPath === null) return ""
|
|
||||||
return assetPath.toLowerCase().replace("/lol-game-data/assets/", "plugins/rcp-be-lol-game-data/global/default/")
|
|
||||||
}
|
|
||||||
|
|
||||||
export { mapPath, CDRAGON_BASE}
|
|
||||||
31
frontend/utils/cdragon.ts
Normal file
31
frontend/utils/cdragon.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
const CDRAGON_BASE = "https://raw.communitydragon.org/latest/"
|
||||||
|
|
||||||
|
/* Lanes */
|
||||||
|
const POSITIONS = ["top", "jungle", "middle", "bottom", "utility"]
|
||||||
|
const LANE_IMAGES = Array(5).fill("").map((_, index) => "/img/lanes/icon-position-" + POSITIONS[index] + ".png")
|
||||||
|
const LANE_IMAGES_HOVER = Array(5).fill("").map((_, index) => "/img/lanes/icon-position-" + POSITIONS[index] + "-hover.png")
|
||||||
|
const LANE_IMAGES_SELECTED = Array(5).fill("").map((_, index) => "/img/lanes/icon-position-" + POSITIONS[index] + "-blue.png")
|
||||||
|
function laneIndexToPosition(index : number) {
|
||||||
|
switch(index) {
|
||||||
|
case 0: return "top"
|
||||||
|
case 1: return "jungle"
|
||||||
|
case 2: return "middle"
|
||||||
|
case 3: return "bottom"
|
||||||
|
case 4: return "utility"
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
function lanePositionToIndex(position : string) {
|
||||||
|
const p = position.toLowerCase()
|
||||||
|
for(let i = 0; i < POSITIONS.length; i++) {
|
||||||
|
if(p == POSITIONS[i]) return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapPath(assetPath : string) {
|
||||||
|
if(assetPath === undefined || assetPath === null) return ""
|
||||||
|
return assetPath.toLowerCase().replace("/lol-game-data/assets/", "plugins/rcp-be-lol-game-data/global/default/")
|
||||||
|
}
|
||||||
|
|
||||||
|
export { mapPath, CDRAGON_BASE, laneIndexToPosition, lanePositionToIndex, POSITIONS, LANE_IMAGES, LANE_IMAGES_HOVER, LANE_IMAGES_SELECTED}
|
||||||
@@ -46,6 +46,16 @@ type Champion = {
|
|||||||
name: String
|
name: String
|
||||||
alias: String
|
alias: String
|
||||||
}
|
}
|
||||||
|
type LaneData = {
|
||||||
|
data: string
|
||||||
|
count: number
|
||||||
|
winningMatches: number
|
||||||
|
losingMatches: number
|
||||||
|
winrate: number
|
||||||
|
pickrate: number
|
||||||
|
runes: Array<Rune>
|
||||||
|
builds: Builds
|
||||||
|
}
|
||||||
|
|
||||||
function handleParticipantRunes(participant, runes: Array<Rune>) {
|
function handleParticipantRunes(participant, runes: Array<Rune>) {
|
||||||
const primaryStyle = participant.perks.styles[0].style
|
const primaryStyle = participant.perks.styles[0].style
|
||||||
@@ -141,9 +151,7 @@ async function championInfos(client, patch: number, champion: Champion) {
|
|||||||
let winningMatches = 0;
|
let winningMatches = 0;
|
||||||
let losingMatches = 0;
|
let losingMatches = 0;
|
||||||
let totalMatches = 0;
|
let totalMatches = 0;
|
||||||
const lanes : Array<{data: string, count: number}> = [];
|
const lanes : Array<LaneData> = [];
|
||||||
const runes : Array<Rune> = [];
|
|
||||||
const builds : Builds = {tree:treeInit(), start: [], bootsFirst: 0, boots: [], lateGame: []}
|
|
||||||
for await (let match of allMatches) {
|
for await (let match of allMatches) {
|
||||||
totalMatches += 1;
|
totalMatches += 1;
|
||||||
let participantIndex = 0;
|
let participantIndex = 0;
|
||||||
@@ -158,16 +166,24 @@ async function championInfos(client, patch: number, champion: Champion) {
|
|||||||
losingMatches += 1;
|
losingMatches += 1;
|
||||||
|
|
||||||
// Lanes
|
// Lanes
|
||||||
// TODO: make stats lane-dependant
|
let lane = lanes.find((x) => x.data == participant.teamPosition)
|
||||||
const already = lanes.find((x) => x.data == participant.teamPosition)
|
if(lane == undefined) {
|
||||||
if(already == undefined) lanes.push({count:1, data: participant.teamPosition})
|
const builds : Builds = {tree:treeInit(), start: [], bootsFirst: 0, boots: [], lateGame: []}
|
||||||
else already.count += 1
|
lane = {count:1, data: participant.teamPosition, runes:[], builds:builds, winningMatches: 0, losingMatches: 0, winrate: 0, pickrate: 0}
|
||||||
|
lanes.push(lane)
|
||||||
|
}
|
||||||
|
else lane.count += 1
|
||||||
|
|
||||||
|
if(participant.win)
|
||||||
|
lane.winningMatches += 1;
|
||||||
|
else
|
||||||
|
lane.losingMatches += 1;
|
||||||
|
|
||||||
// Runes
|
// Runes
|
||||||
handleParticipantRunes(participant, runes)
|
handleParticipantRunes(participant, lane.runes)
|
||||||
|
|
||||||
// Items
|
// Items
|
||||||
handleMatchItems(match.timeline, participantIndex, builds)
|
handleMatchItems(match.timeline, participantIndex, lane.builds)
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -179,31 +195,44 @@ async function championInfos(client, patch: number, champion: Champion) {
|
|||||||
lanes.sort((a, b) => b.count - a.count)
|
lanes.sort((a, b) => b.count - a.count)
|
||||||
|
|
||||||
// Filter runes to keep 3 most played
|
// Filter runes to keep 3 most played
|
||||||
runes.sort((a, b) => b.count - a.count)
|
for(let lane of lanes) {
|
||||||
if(runes.length > 3)
|
const runes = lane.runes
|
||||||
runes.splice(3, runes.length - 3)
|
|
||||||
// Compute runes pickrate
|
runes.sort((a, b) => b.count - a.count)
|
||||||
for(let rune of runes)
|
if(runes.length > 3)
|
||||||
rune.pickrate = rune.count / totalChampionMatches;
|
runes.splice(3, runes.length - 3)
|
||||||
|
// Compute runes pickrate
|
||||||
|
for(let rune of runes)
|
||||||
|
rune.pickrate = rune.count / lane.count;
|
||||||
|
}
|
||||||
|
|
||||||
// Cut item tree branches to keep only 4 branches every time and with percentage threshold
|
for(let lane of lanes) {
|
||||||
builds.tree.count = totalChampionMatches;
|
const builds = lane.builds
|
||||||
treeCutBranches(builds.tree, 4, 0.05)
|
|
||||||
treeSort(builds.tree)
|
|
||||||
|
|
||||||
// Cut item start, to only 4 and with percentage threshold
|
// Cut item tree branches to keep only 4 branches every time and with percentage threshold
|
||||||
arrayRemovePercentage(builds.start, totalChampionMatches, 0.05)
|
builds.tree.count = lane.count;
|
||||||
builds.start.sort((a, b) => b.count - a.count)
|
treeCutBranches(builds.tree, 4, 0.05)
|
||||||
if(builds.start.length > 4)
|
treeSort(builds.tree)
|
||||||
builds.start.splice(4, builds.start.length - 4)
|
|
||||||
|
// Cut item start, to only 4 and with percentage threshold
|
||||||
|
arrayRemovePercentage(builds.start, lane.count, 0.05)
|
||||||
|
builds.start.sort((a, b) => b.count - a.count)
|
||||||
|
if(builds.start.length > 4)
|
||||||
|
builds.start.splice(4, builds.start.length - 4)
|
||||||
|
|
||||||
// Remove boots that are not within percentage threshold
|
// Remove boots that are not within percentage threshold
|
||||||
arrayRemovePercentage(builds.boots, totalChampionMatches, 0.05)
|
arrayRemovePercentage(builds.boots, lane.count, 0.05)
|
||||||
builds.boots.sort((a, b) => b.count - a.count)
|
builds.boots.sort((a, b) => b.count - a.count)
|
||||||
|
|
||||||
builds.bootsFirst /= (winningMatches + losingMatches)
|
builds.bootsFirst /= lane.count
|
||||||
|
|
||||||
builds.lateGame.sort((a, b) => b.count - a.count)
|
builds.lateGame.sort((a, b) => b.count - a.count)
|
||||||
|
}
|
||||||
|
|
||||||
|
for(let lane of lanes) {
|
||||||
|
lane.winrate = lane.winningMatches / lane.count
|
||||||
|
lane.pickrate = lane.count / totalMatches
|
||||||
|
}
|
||||||
|
|
||||||
return {name: champion.name,
|
return {name: champion.name,
|
||||||
alias: champion.alias.toLowerCase(),
|
alias: champion.alias.toLowerCase(),
|
||||||
@@ -212,8 +241,6 @@ async function championInfos(client, patch: number, champion: Champion) {
|
|||||||
winrate: winningMatches / totalChampionMatches,
|
winrate: winningMatches / totalChampionMatches,
|
||||||
gameCount: totalChampionMatches,
|
gameCount: totalChampionMatches,
|
||||||
pickrate: totalChampionMatches/totalMatches,
|
pickrate: totalChampionMatches/totalMatches,
|
||||||
runes: runes,
|
|
||||||
builds: builds
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user