|
|
|
@@ -177,7 +177,7 @@ function handleMatchBuilds(
|
|
|
|
participantIndex: number,
|
|
|
|
participantIndex: number,
|
|
|
|
builds: Builds,
|
|
|
|
builds: Builds,
|
|
|
|
platform?: string
|
|
|
|
platform?: string
|
|
|
|
): Build {
|
|
|
|
): { build: Build; startItemId: number | undefined } {
|
|
|
|
const timeline: Timeline = match.timeline
|
|
|
|
const timeline: Timeline = match.timeline
|
|
|
|
|
|
|
|
|
|
|
|
// Find or create the build for this participant's rune configuration
|
|
|
|
// Find or create the build for this participant's rune configuration
|
|
|
|
@@ -185,6 +185,7 @@ function handleMatchBuilds(
|
|
|
|
build.count += 1
|
|
|
|
build.count += 1
|
|
|
|
|
|
|
|
|
|
|
|
const items: Array<{ itemId: number; goldAdvantage: GoldAdvantageTag; platform?: string }> = []
|
|
|
|
const items: Array<{ itemId: number; goldAdvantage: GoldAdvantageTag; platform?: string }> = []
|
|
|
|
|
|
|
|
let startItemId: number | undefined = undefined
|
|
|
|
for (const frame of timeline.info.frames) {
|
|
|
|
for (const frame of timeline.info.frames) {
|
|
|
|
for (const event of frame.events) {
|
|
|
|
for (const event of frame.events) {
|
|
|
|
if (event.participantId != participantIndex) continue
|
|
|
|
if (event.participantId != participantIndex) continue
|
|
|
|
@@ -267,9 +268,11 @@ function handleMatchBuilds(
|
|
|
|
// This tree includes start item as the root, then branching paths
|
|
|
|
// This tree includes start item as the root, then branching paths
|
|
|
|
if (items.length > 0) {
|
|
|
|
if (items.length > 0) {
|
|
|
|
treeMerge(build.items, items)
|
|
|
|
treeMerge(build.items, items)
|
|
|
|
|
|
|
|
// The first item is the starter item
|
|
|
|
|
|
|
|
startItemId = items[0].itemId
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return build
|
|
|
|
return { build, startItemId }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function handleMatch(match: Match, champions: Map<number, ChampionData>, platform?: string) {
|
|
|
|
function handleMatch(match: Match, champions: Map<number, ChampionData>, platform?: string) {
|
|
|
|
@@ -365,14 +368,22 @@ function handleMatch(match: Match, champions: Map<number, ChampionData>, platfor
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Items and runes (builds)
|
|
|
|
// Items and runes (builds)
|
|
|
|
const build = handleMatchBuilds(match, participant, participantIndex, lane.builds, platform)
|
|
|
|
const { build, startItemId } = handleMatchBuilds(
|
|
|
|
|
|
|
|
match,
|
|
|
|
|
|
|
|
participant,
|
|
|
|
|
|
|
|
participantIndex,
|
|
|
|
|
|
|
|
lane.builds,
|
|
|
|
|
|
|
|
platform
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// First back data - store at build level
|
|
|
|
// First back data - store at build level with start item tracking
|
|
|
|
const firstBackData = extractFirstBackFromMatch(match, participantIndex)
|
|
|
|
const firstBackData = extractFirstBackFromMatch(match, participantIndex)
|
|
|
|
if (firstBackData) {
|
|
|
|
if (firstBackData) {
|
|
|
|
if (!build.firstBacksRaw) {
|
|
|
|
if (!build.firstBacksRaw) {
|
|
|
|
build.firstBacksRaw = []
|
|
|
|
build.firstBacksRaw = []
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Include the starter item ID for proper filtering when splitting builds
|
|
|
|
|
|
|
|
firstBackData.startItemId = startItemId
|
|
|
|
build.firstBacksRaw.push(firstBackData)
|
|
|
|
build.firstBacksRaw.push(firstBackData)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -445,16 +456,44 @@ function splitMergeOnStarterItem(build: Build, championName: string): BuildWithS
|
|
|
|
console.log(`Warning: for champion ${championName}, start item splits build variant.`)
|
|
|
|
console.log(`Warning: for champion ${championName}, start item splits build variant.`)
|
|
|
|
const builds = []
|
|
|
|
const builds = []
|
|
|
|
for (const c of build.items.children) {
|
|
|
|
for (const c of build.items.children) {
|
|
|
|
|
|
|
|
// Calculate the ratio for proportional distribution
|
|
|
|
|
|
|
|
const ratio = c.count / build.count
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Proportionally distribute boots counts
|
|
|
|
|
|
|
|
const scaledBoots = build.boots.map(b => ({
|
|
|
|
|
|
|
|
data: b.data,
|
|
|
|
|
|
|
|
count: Math.round(b.count * ratio)
|
|
|
|
|
|
|
|
}))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Proportionally distribute suppItems counts
|
|
|
|
|
|
|
|
const scaledSuppItems = build.suppItems.map(s => ({
|
|
|
|
|
|
|
|
data: s.data,
|
|
|
|
|
|
|
|
count: Math.round(s.count * ratio)
|
|
|
|
|
|
|
|
}))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Proportionally distribute bootsFirstCount
|
|
|
|
|
|
|
|
const scaledBootsFirstCount = Math.round(build.bootsFirstCount * ratio)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Filter firstBacksRaw by starter item
|
|
|
|
|
|
|
|
let filteredFirstBacksRaw: FirstBackData[] | undefined
|
|
|
|
|
|
|
|
if (build.firstBacksRaw && build.firstBacksRaw.length > 0) {
|
|
|
|
|
|
|
|
// Filter by the starter item ID that was tracked when storing firstBacksRaw
|
|
|
|
|
|
|
|
filteredFirstBacksRaw = build.firstBacksRaw.filter(fb => fb.startItemId === c.data)
|
|
|
|
|
|
|
|
if (filteredFirstBacksRaw.length === 0) {
|
|
|
|
|
|
|
|
filteredFirstBacksRaw = undefined
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
builds.push({
|
|
|
|
builds.push({
|
|
|
|
runeKeystone: build.runeKeystone,
|
|
|
|
runeKeystone: build.runeKeystone,
|
|
|
|
runes: build.runes,
|
|
|
|
runes: build.runes,
|
|
|
|
items: c,
|
|
|
|
items: c,
|
|
|
|
bootsFirstCount: build.bootsFirstCount,
|
|
|
|
bootsFirstCount: scaledBootsFirstCount,
|
|
|
|
count: c.count,
|
|
|
|
count: c.count,
|
|
|
|
startItems: [{ data: c.data!, count: c.count }],
|
|
|
|
startItems: [{ data: c.data!, count: c.count }],
|
|
|
|
suppItems: build.suppItems,
|
|
|
|
suppItems: scaledSuppItems,
|
|
|
|
boots: build.boots,
|
|
|
|
boots: scaledBoots,
|
|
|
|
firstBacksRaw: build.firstBacksRaw
|
|
|
|
firstBacksRaw: filteredFirstBacksRaw
|
|
|
|
})
|
|
|
|
})
|
|
|
|
c.data = undefined
|
|
|
|
c.data = undefined
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|