mirror of
https://github.com/vkaelin/LeagueStats.git
synced 2026-03-25 12:57:28 +00:00
feat: create Match, BasicMatch and LiveMatch Transformers
This commit is contained in:
parent
aa4659b53e
commit
54a0e07474
7 changed files with 522 additions and 28 deletions
|
|
@ -20,10 +20,10 @@ export interface ParticipantDetails {
|
||||||
summonerId: string,
|
summonerId: string,
|
||||||
champion: Champion,
|
champion: Champion,
|
||||||
role: string,
|
role: string,
|
||||||
primaryRune: string,
|
primaryRune: string | null,
|
||||||
secondaryRune: string,
|
secondaryRune: string | null,
|
||||||
level: number,
|
level: number,
|
||||||
items: Item[],
|
items: (Item | null)[],
|
||||||
firstSum: SummonerSpell | number,
|
firstSum: SummonerSpell | number,
|
||||||
secondSum: SummonerSpell | number,
|
secondSum: SummonerSpell | number,
|
||||||
stats: Stats,
|
stats: Stats,
|
||||||
|
|
@ -39,32 +39,32 @@ export interface Champion {
|
||||||
icon: string
|
icon: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SummonerSpell {
|
export interface SummonerSpell {
|
||||||
name: string,
|
name: string,
|
||||||
description: string,
|
description: string,
|
||||||
icon: string
|
icon: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Rank {
|
export interface Rank {
|
||||||
tier: string,
|
tier: string,
|
||||||
shortName: string
|
shortName: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ParticipantBasic {
|
export interface ParticipantBasic {
|
||||||
account_id: string,
|
account_id: string,
|
||||||
name: string,
|
name: string,
|
||||||
role: string,
|
role: string,
|
||||||
champion: Champion
|
champion: Champion
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Item {
|
export interface Item {
|
||||||
image: string,
|
image: string,
|
||||||
name: string,
|
name: string,
|
||||||
description: string,
|
description: string,
|
||||||
price: number
|
price: number
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Stats {
|
export interface Stats {
|
||||||
kills: number,
|
kills: number,
|
||||||
deaths: number,
|
deaths: number,
|
||||||
assists: number,
|
assists: number,
|
||||||
|
|
@ -74,21 +74,21 @@ interface Stats {
|
||||||
dmgChamp: number,
|
dmgChamp: number,
|
||||||
dmgObj: number,
|
dmgObj: number,
|
||||||
dmgTaken: number,
|
dmgTaken: number,
|
||||||
kda: number,
|
kda: number | string,
|
||||||
realKda: number,
|
realKda: number,
|
||||||
criticalStrike: number,
|
criticalStrike?: number,
|
||||||
killingSpree: number,
|
killingSpree?: number,
|
||||||
doubleKills: number,
|
doubleKills?: number,
|
||||||
tripleKills: number,
|
tripleKills?: number,
|
||||||
quadraKills: number,
|
quadraKills?: number,
|
||||||
pentaKills: number,
|
pentaKills?: number,
|
||||||
heal: number,
|
heal?: number,
|
||||||
towers: number,
|
towers?: number,
|
||||||
longestLiving: number,
|
longestLiving?: number,
|
||||||
kp: number,
|
kp: number | string,
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PercentStats {
|
export interface PercentStats {
|
||||||
minions: number,
|
minions: number,
|
||||||
vision: number,
|
vision: number,
|
||||||
gold: string,
|
gold: string,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,14 @@
|
||||||
import Redis from '@ioc:Adonis/Addons/Redis'
|
import Redis from '@ioc:Adonis/Addons/Redis'
|
||||||
import got from 'got/dist/source'
|
import got from 'got/dist/source'
|
||||||
|
|
||||||
|
export interface FinalRoleComposition {
|
||||||
|
'TOP'?: number,
|
||||||
|
'JUNGLE'?: number,
|
||||||
|
'MIDDLE'?: number,
|
||||||
|
'BOTTOM'?: number,
|
||||||
|
'SUPPORT'?: number,
|
||||||
|
}
|
||||||
|
|
||||||
export interface RoleComposition {
|
export interface RoleComposition {
|
||||||
'TOP'?: number,
|
'TOP'?: number,
|
||||||
'JUNGLE'?: number,
|
'JUNGLE'?: number,
|
||||||
|
|
@ -186,7 +194,12 @@ class RoleIdentificationService {
|
||||||
* @param jungle
|
* @param jungle
|
||||||
* @param support
|
* @param support
|
||||||
*/
|
*/
|
||||||
public getRoles (championPositions: ChampionsRates, composition: number[], jungle?: number, support?: number) {
|
public getRoles (
|
||||||
|
championPositions: ChampionsRates,
|
||||||
|
composition: number[],
|
||||||
|
jungle?: number,
|
||||||
|
support?: number
|
||||||
|
): FinalRoleComposition {
|
||||||
// Set composition champion playrate to 0% if not present in the json data
|
// Set composition champion playrate to 0% if not present in the json data
|
||||||
for (const compChamp of composition) {
|
for (const compChamp of composition) {
|
||||||
if (championPositions[compChamp]) {
|
if (championPositions[compChamp]) {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,17 @@ import { SummonerDTO } from 'App/Services/Jax/src/Endpoints/SummonerEndpoint'
|
||||||
import { LeagueEntryDTO } from './Jax/src/Endpoints/LeagueEndpoint'
|
import { LeagueEntryDTO } from './Jax/src/Endpoints/LeagueEndpoint'
|
||||||
import { SummonerModel } from 'App/Models/Summoner'
|
import { SummonerModel } from 'App/Models/Summoner'
|
||||||
|
|
||||||
|
export interface LeagueEntriesByQueue {
|
||||||
|
soloQ?: LeagueEntryByQueue,
|
||||||
|
flex5v5?: LeagueEntryByQueue
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LeagueEntryByQueue extends LeagueEntryDTO {
|
||||||
|
fullRank: string,
|
||||||
|
winrate: string,
|
||||||
|
shortName: string | number
|
||||||
|
}
|
||||||
|
|
||||||
class SummonerService {
|
class SummonerService {
|
||||||
private uniqueLeagues = ['CHALLENGER', 'GRANDMASTER', 'MASTER']
|
private uniqueLeagues = ['CHALLENGER', 'GRANDMASTER', 'MASTER']
|
||||||
private leaguesNumbers = { 'I': 1, 'II': 2, 'III': 3, 'IV': 4 }
|
private leaguesNumbers = { 'I': 1, 'II': 2, 'III': 3, 'IV': 4 }
|
||||||
|
|
@ -11,7 +22,7 @@ class SummonerService {
|
||||||
* Helper to transform League Data from the Riot API
|
* Helper to transform League Data from the Riot API
|
||||||
* @param league raw data of the league from Riot API
|
* @param league raw data of the league from Riot API
|
||||||
*/
|
*/
|
||||||
private getleagueData (league?: LeagueEntryDTO) {
|
private getleagueData (league?: LeagueEntryDTO): LeagueEntryByQueue | null {
|
||||||
if (!league) {
|
if (!league) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
@ -45,7 +56,7 @@ class SummonerService {
|
||||||
* @param account of the summoner
|
* @param account of the summoner
|
||||||
* @param summonerDB summoner in the database
|
* @param summonerDB summoner in the database
|
||||||
*/
|
*/
|
||||||
public getAllSummonerNames (account: SummonerDTO, summonerDB:SummonerModel) {
|
public getAllSummonerNames (account: SummonerDTO, summonerDB: SummonerModel) {
|
||||||
const names = summonerDB.names ? summonerDB.names : []
|
const names = summonerDB.names ? summonerDB.names : []
|
||||||
|
|
||||||
if (!names.find(n => n.name === account.name)) {
|
if (!names.find(n => n.name === account.name)) {
|
||||||
|
|
@ -64,12 +75,11 @@ class SummonerService {
|
||||||
* @param account
|
* @param account
|
||||||
* @param region
|
* @param region
|
||||||
*/
|
*/
|
||||||
public async getRanked (account: SummonerDTO, region: string) {
|
public async getRanked (account: SummonerDTO, region: string): Promise<LeagueEntriesByQueue> {
|
||||||
const ranked = await Jax.League.summonerID(account.id, region)
|
const ranked = await Jax.League.summonerID(account.id, region)
|
||||||
const result = {
|
const result = {
|
||||||
soloQ: this.getleagueData(ranked.find(e => e.queueType === 'RANKED_SOLO_5x5')) || null,
|
soloQ: this.getleagueData(ranked.find(e => e.queueType === 'RANKED_SOLO_5x5')) || undefined,
|
||||||
flex5v5: this.getleagueData(ranked.find(e => e.queueType === 'RANKED_FLEX_SR')) || null,
|
flex5v5: this.getleagueData(ranked.find(e => e.queueType === 'RANKED_FLEX_SR')) || undefined,
|
||||||
flex3v3: this.getleagueData(ranked.find(e => e.queueType === 'RANKED_FLEX_TT')) || null,
|
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
|
||||||
83
server-new/app/Transformers/BasicMatchTransformer.ts
Normal file
83
server-new/app/Transformers/BasicMatchTransformer.ts
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
import { MatchModel, ParticipantBasic } from 'App/Models/Match'
|
||||||
|
import { MatchDto } from 'App/Services/Jax/src/Endpoints/MatchEndpoint'
|
||||||
|
import { SummonerDTO } from 'App/Services/Jax/src/Endpoints/SummonerEndpoint'
|
||||||
|
import MatchTransformer from 'App/Transformers/MatchTransformer'
|
||||||
|
|
||||||
|
class BasicMatchTransformer extends MatchTransformer {
|
||||||
|
/**
|
||||||
|
* Transform raw data for 1 match
|
||||||
|
* @param match
|
||||||
|
* @param account
|
||||||
|
*/
|
||||||
|
private transformOneMatch (match: MatchDto, account: SummonerDTO) {
|
||||||
|
// Global data about the match
|
||||||
|
const globalInfos = super.getGameInfos(match)
|
||||||
|
|
||||||
|
const identity = match.participantIdentities.find((p) => p.player.currentAccountId === account.accountId)
|
||||||
|
const player = match.participants[identity!.participantId - 1]
|
||||||
|
|
||||||
|
let win = match.teams.find((t) => t.teamId === player.teamId)!.win
|
||||||
|
|
||||||
|
// Match less than 5min
|
||||||
|
if (match.gameDuration < 300) {
|
||||||
|
win = 'Remake'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Player data
|
||||||
|
const playerData = super.getPlayerData(match, player, false)
|
||||||
|
|
||||||
|
// Teams data
|
||||||
|
const allyTeam:ParticipantBasic[] = []
|
||||||
|
const enemyTeam:ParticipantBasic[] = []
|
||||||
|
for (let summoner of match.participantIdentities) {
|
||||||
|
const allData = match.participants[summoner.participantId - 1]
|
||||||
|
const playerInfos = {
|
||||||
|
account_id: summoner.player.currentAccountId,
|
||||||
|
name: summoner.player.summonerName,
|
||||||
|
role: super.getRoleName(allData.timeline, match.queueId),
|
||||||
|
champion: super.getChampion(allData.championId),
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allData.teamId === player.teamId) {
|
||||||
|
allyTeam.push(playerInfos)
|
||||||
|
} else {
|
||||||
|
enemyTeam.push(playerInfos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Roles
|
||||||
|
super.getMatchRoles(match, allyTeam, enemyTeam, player.teamId, playerData)
|
||||||
|
|
||||||
|
return {
|
||||||
|
account_id: identity!.player.currentAccountId,
|
||||||
|
summoner_puuid: account.puuid,
|
||||||
|
gameId: match.gameId,
|
||||||
|
result: win,
|
||||||
|
allyTeam,
|
||||||
|
enemyTeam,
|
||||||
|
...globalInfos,
|
||||||
|
...playerData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform raw data from Riot API
|
||||||
|
* @param matches data from Riot API, Array of match or a single match
|
||||||
|
* @param ctx context
|
||||||
|
*/
|
||||||
|
public async transform (matches: MatchDto[] | MatchDto, { account }: { account: SummonerDTO }) {
|
||||||
|
await super.getContext()
|
||||||
|
|
||||||
|
if (Array.isArray(matches)) {
|
||||||
|
const finalMatches:MatchModel[] = []
|
||||||
|
matches.forEach((match, index) => {
|
||||||
|
finalMatches[index] = this.transformOneMatch(match, account)
|
||||||
|
})
|
||||||
|
return finalMatches
|
||||||
|
} else {
|
||||||
|
return this.transformOneMatch(matches, account) as MatchModel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new BasicMatchTransformer()
|
||||||
72
server-new/app/Transformers/LiveMatchTransformer.ts
Normal file
72
server-new/app/Transformers/LiveMatchTransformer.ts
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
import { queuesWithRole } from 'App/helpers'
|
||||||
|
import { CurrentGameInfo, CurrentGameParticipant } from 'App/Services/Jax/src/Endpoints/SpectatorEndpoint'
|
||||||
|
import { FinalRoleComposition } from 'App/Services/RoleIdentiticationService'
|
||||||
|
import SummonerService, { LeagueEntriesByQueue } from 'App/Services/SummonerService'
|
||||||
|
import MatchTransformer, { PlayerRole } from './MatchTransformer'
|
||||||
|
|
||||||
|
class LiveMatchTransformer extends MatchTransformer {
|
||||||
|
/**
|
||||||
|
* Get player soloQ and flex rank from his summonerName
|
||||||
|
* @param participant
|
||||||
|
* @param region
|
||||||
|
*/
|
||||||
|
private async getPlayerRank (participant: CurrentGameParticipant, region: string) {
|
||||||
|
const account = await SummonerService.getAccount(participant.summonerName, region)
|
||||||
|
let ranked: LeagueEntriesByQueue
|
||||||
|
if (account) {
|
||||||
|
ranked = await SummonerService.getRanked(account, region)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...participant,
|
||||||
|
level: account ? account.summonerLevel : undefined,
|
||||||
|
rank: account ? ranked! : undefined,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform raw data from Riot API
|
||||||
|
* @param liveMatch
|
||||||
|
* @param ctx
|
||||||
|
*/
|
||||||
|
public async transform (liveMatch: CurrentGameInfo, { region }: { region: string }) {
|
||||||
|
await super.getContext()
|
||||||
|
|
||||||
|
// Roles
|
||||||
|
const blueTeam: PlayerRole[] = [] // 100
|
||||||
|
const redTeam: PlayerRole[] = [] // 200
|
||||||
|
let blueRoles: FinalRoleComposition = {}
|
||||||
|
let redRoles: FinalRoleComposition = {}
|
||||||
|
const needsRole = this.championRoles && queuesWithRole.includes(liveMatch.gameQueueConfigId)
|
||||||
|
if (needsRole) {
|
||||||
|
liveMatch.participants.map(p => {
|
||||||
|
const playerRole = { champion: p.championId, jungle: p.spell1Id === 11 || p.spell2Id === 11 }
|
||||||
|
p.teamId === 100 ? blueTeam.push(playerRole) : redTeam.push(playerRole)
|
||||||
|
})
|
||||||
|
|
||||||
|
blueRoles = super.getTeamRoles(blueTeam)
|
||||||
|
redRoles = super.getTeamRoles(redTeam)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const participant of liveMatch.participants) {
|
||||||
|
// Perks
|
||||||
|
participant.runes = participant.perks ?
|
||||||
|
super.getPerksImages(participant.perks.perkIds[0], participant.perks.perkSubStyle)
|
||||||
|
: {}
|
||||||
|
|
||||||
|
// Roles
|
||||||
|
if (needsRole) {
|
||||||
|
const roles = participant.teamId === 100 ? blueRoles : redRoles
|
||||||
|
participant.role = Object.entries(roles).find(([, champion]) => participant.championId === champion)![0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ranks
|
||||||
|
const requestsParticipants = liveMatch.participants.map(p => this.getPlayerRank(p, region))
|
||||||
|
liveMatch.participants = await Promise.all(requestsParticipants)
|
||||||
|
|
||||||
|
return liveMatch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new LiveMatchTransformer()
|
||||||
314
server-new/app/Transformers/MatchTransformer.ts
Normal file
314
server-new/app/Transformers/MatchTransformer.ts
Normal file
|
|
@ -0,0 +1,314 @@
|
||||||
|
import { getSeasonNumber, queuesWithRole, sortTeamByRole, supportItems } from 'App/helpers'
|
||||||
|
import Jax from 'App/Services/Jax'
|
||||||
|
import { MatchDto, ParticipantDto, ParticipantTimelineDto } from 'App/Services/Jax/src/Endpoints/MatchEndpoint'
|
||||||
|
import { Item, ParticipantBasic, ParticipantDetails, PercentStats, Stats } from 'App/Models/Match'
|
||||||
|
import RoleIdentificationService from 'App/Services/RoleIdentiticationService'
|
||||||
|
|
||||||
|
export interface PlayerRole {
|
||||||
|
champion: number,
|
||||||
|
jungle?: boolean,
|
||||||
|
support?: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default abstract class MatchTransformer {
|
||||||
|
protected champions: any
|
||||||
|
protected items: any
|
||||||
|
protected perks: any
|
||||||
|
protected perkstyles: any
|
||||||
|
protected summonerSpells: any
|
||||||
|
protected championRoles: any
|
||||||
|
protected sortTeamByRole: (a: ParticipantBasic, b: ParticipantBasic) => number
|
||||||
|
/**
|
||||||
|
* Get global Context with CDragon Data
|
||||||
|
*/
|
||||||
|
public async getContext () {
|
||||||
|
const items = await Jax.CDragon.items()
|
||||||
|
const champions = await Jax.CDragon.champions()
|
||||||
|
const perks = await Jax.CDragon.perks()
|
||||||
|
const perkstyles = await Jax.CDragon.perkstyles()
|
||||||
|
const summonerSpells = await Jax.CDragon.summonerSpells()
|
||||||
|
const championRoles = await RoleIdentificationService.pullData().catch(() => { })
|
||||||
|
|
||||||
|
this.champions = champions
|
||||||
|
this.items = items
|
||||||
|
this.perks = perks
|
||||||
|
this.perkstyles = perkstyles.styles
|
||||||
|
this.summonerSpells = summonerSpells
|
||||||
|
this.championRoles = championRoles
|
||||||
|
this.sortTeamByRole = sortTeamByRole
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get champion specific data
|
||||||
|
* @param id of the champion
|
||||||
|
*/
|
||||||
|
public getChampion (id: number) {
|
||||||
|
const champion = { ...this.champions.find(c => c.id === id) }
|
||||||
|
champion.icon = `
|
||||||
|
https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/
|
||||||
|
${champion.squarePortraitPath.split('/assets/')[1].toLowerCase()}
|
||||||
|
`
|
||||||
|
delete champion.squarePortraitPath
|
||||||
|
return champion
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get global data about the match
|
||||||
|
*/
|
||||||
|
public getGameInfos (match: MatchDto) {
|
||||||
|
return {
|
||||||
|
map: match.mapId,
|
||||||
|
gamemode: match.queueId,
|
||||||
|
date: match.gameCreation,
|
||||||
|
region: match.platformId.toLowerCase(),
|
||||||
|
season: getSeasonNumber(match.gameCreation),
|
||||||
|
time: match.gameDuration,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get player specific data during the match
|
||||||
|
* @param match
|
||||||
|
* @param player
|
||||||
|
* @param detailed : detailed or not stats
|
||||||
|
* @param teamStats : if detailed, the teamStats argument is mandatory
|
||||||
|
*/
|
||||||
|
public getPlayerData (match: MatchDto, player: ParticipantDto, detailed: boolean, teamStats: any = {}) {
|
||||||
|
const identity = match.participantIdentities.find(p => p.participantId === player.participantId)
|
||||||
|
const name = identity!.player.summonerName
|
||||||
|
const champion = this.getChampion(player.championId)
|
||||||
|
const role = this.getRoleName(player.timeline, match.queueId)
|
||||||
|
const level = player.stats.champLevel
|
||||||
|
|
||||||
|
// Regular stats / Full match stats
|
||||||
|
const stats: Stats = {
|
||||||
|
kills: player.stats.kills,
|
||||||
|
deaths: player.stats.deaths,
|
||||||
|
assists: player.stats.assists,
|
||||||
|
minions: player.stats.totalMinionsKilled + player.stats.neutralMinionsKilled,
|
||||||
|
vision: player.stats.visionScore,
|
||||||
|
gold: player.stats.goldEarned,
|
||||||
|
dmgChamp: player.stats.totalDamageDealtToChampions,
|
||||||
|
dmgObj: player.stats.damageDealtToObjectives,
|
||||||
|
dmgTaken: player.stats.totalDamageTaken,
|
||||||
|
kp: 0,
|
||||||
|
kda: 0,
|
||||||
|
realKda: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stats.kills + stats.assists !== 0 && stats.deaths === 0) {
|
||||||
|
stats.kda = '∞'
|
||||||
|
stats.realKda = stats.kills + stats.assists
|
||||||
|
} else {
|
||||||
|
stats.kda = +(stats.deaths === 0 ? 0 : ((stats.kills + stats.assists) / stats.deaths)).toFixed(2)
|
||||||
|
stats.realKda = stats.kda
|
||||||
|
}
|
||||||
|
|
||||||
|
// Percent stats / Per minute stats : only for detailed match
|
||||||
|
let percentStats: PercentStats
|
||||||
|
if (detailed) {
|
||||||
|
percentStats = {
|
||||||
|
minions: +(stats.minions / (match.gameDuration / 60)).toFixed(2),
|
||||||
|
vision: +(stats.vision / (match.gameDuration / 60)).toFixed(2),
|
||||||
|
gold: +(player.stats.goldEarned * 100 / teamStats.gold).toFixed(1) + '%',
|
||||||
|
dmgChamp: +(player.stats.totalDamageDealtToChampions * 100 / teamStats.dmgChamp).toFixed(1) + '%',
|
||||||
|
dmgObj: +(teamStats.dmgObj ? player.stats.damageDealtToObjectives * 100 / teamStats.dmgObj : 0).toFixed(1) + '%',
|
||||||
|
dmgTaken: +(player.stats.totalDamageTaken * 100 / teamStats.dmgTaken).toFixed(1) + '%',
|
||||||
|
}
|
||||||
|
|
||||||
|
stats.kp = teamStats.kills === 0 ? '0%' : +((stats.kills + stats.assists) * 100 / teamStats.kills).toFixed(1) + '%'
|
||||||
|
} else {
|
||||||
|
const totalKills = match.participants.reduce((prev, current) => {
|
||||||
|
if (current.teamId !== player.teamId) {
|
||||||
|
return prev
|
||||||
|
}
|
||||||
|
return prev + current.stats.kills
|
||||||
|
}, 0)
|
||||||
|
|
||||||
|
stats.criticalStrike = player.stats.largestCriticalStrike
|
||||||
|
stats.killingSpree = player.stats.largestKillingSpree
|
||||||
|
stats.doubleKills = player.stats.doubleKills
|
||||||
|
stats.tripleKills = player.stats.tripleKills
|
||||||
|
stats.quadraKills = player.stats.quadraKills
|
||||||
|
stats.pentaKills = player.stats.pentaKills
|
||||||
|
stats.heal = player.stats.totalHeal
|
||||||
|
stats.towers = player.stats.turretKills
|
||||||
|
stats.longestLiving = player.stats.longestTimeSpentLiving
|
||||||
|
stats.kp = totalKills === 0 ? 0 : +((stats.kills + stats.assists) * 100 / totalKills).toFixed(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
let primaryRune: string | null = null
|
||||||
|
let secondaryRune: string | null = null
|
||||||
|
if (player.stats.perkPrimaryStyle) {
|
||||||
|
({ primaryRune, secondaryRune } = this.getPerksImages(player.stats.perk0, player.stats.perkSubStyle))
|
||||||
|
}
|
||||||
|
|
||||||
|
const items: (Item | null)[] = []
|
||||||
|
for (let i = 0; i < 6; i++) {
|
||||||
|
const id = player.stats['item' + i]
|
||||||
|
if (id === 0) {
|
||||||
|
items.push(null)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const item = this.items.find((i: any) => i.id === id)
|
||||||
|
const itemUrl = item.iconPath.split('/assets/')[1].toLowerCase()
|
||||||
|
|
||||||
|
items.push({
|
||||||
|
image: `https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/${itemUrl}`,
|
||||||
|
name: item.name,
|
||||||
|
description: item.description,
|
||||||
|
price: item.priceTotal,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const firstSum = player.spell1Id
|
||||||
|
const secondSum = player.spell2Id
|
||||||
|
|
||||||
|
const playerData: ParticipantDetails = {
|
||||||
|
name,
|
||||||
|
summonerId: identity!.player.summonerId,
|
||||||
|
champion,
|
||||||
|
role,
|
||||||
|
primaryRune,
|
||||||
|
secondaryRune,
|
||||||
|
level,
|
||||||
|
items,
|
||||||
|
firstSum,
|
||||||
|
secondSum,
|
||||||
|
stats,
|
||||||
|
}
|
||||||
|
if (detailed) {
|
||||||
|
playerData.percentStats = percentStats!
|
||||||
|
}
|
||||||
|
|
||||||
|
return playerData
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the icons of the primary rune and secondary category
|
||||||
|
* @param perk0 primary perks id
|
||||||
|
* @param perkSubStyle secondary perks category
|
||||||
|
*/
|
||||||
|
public getPerksImages (perk0: number, perkSubStyle: number) {
|
||||||
|
const firstRune = this.perks.find((p: any) => p.id === perk0)
|
||||||
|
const firstRuneUrl = firstRune.iconPath.split('/assets/')[1].toLowerCase()
|
||||||
|
const primaryRune = `https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/${firstRuneUrl}`
|
||||||
|
|
||||||
|
const secondRuneStyle = this.perkstyles.find((p: any) => p.id === perkSubStyle)
|
||||||
|
|
||||||
|
const secondRuneStyleUrl = secondRuneStyle ? secondRuneStyle.iconPath.split('/assets/')[1].toLowerCase() : null
|
||||||
|
const secondaryRune = secondRuneStyleUrl ?
|
||||||
|
`https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/${secondRuneStyleUrl}`
|
||||||
|
: ''
|
||||||
|
|
||||||
|
return { primaryRune, secondaryRune }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the lane of the summoner according to timeline
|
||||||
|
* @param timeline from Riot Api
|
||||||
|
* @param gamemode of the match to check if a role is needed
|
||||||
|
*/
|
||||||
|
public getRoleName (timeline: ParticipantTimelineDto, gamemode: number) {
|
||||||
|
if (!queuesWithRole.includes(gamemode)) {
|
||||||
|
return 'NONE'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeline.lane === 'BOTTOM' && timeline.role.includes('SUPPORT')) {
|
||||||
|
return 'SUPPORT'
|
||||||
|
}
|
||||||
|
|
||||||
|
return timeline.lane
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the 5 roles of a team based on champions
|
||||||
|
* @param team 5 champions + smite from a team
|
||||||
|
*/
|
||||||
|
public getTeamRoles (team: PlayerRole[]) {
|
||||||
|
const teamJunglers = team.filter(p => p.jungle && !p.support)
|
||||||
|
const jungle = teamJunglers.length === 1 ? teamJunglers[0].champion : undefined
|
||||||
|
const teamSupports = team.filter(p => p.support && !p.jungle)
|
||||||
|
const support = teamSupports.length === 1 ? teamSupports[0].champion : undefined
|
||||||
|
|
||||||
|
return RoleIdentificationService.getRoles(this.championRoles, team.map(p => p.champion), jungle, support)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update roles for a team if Riot's ones are badly identified
|
||||||
|
* @param team 5 players data of the team
|
||||||
|
* @param champs 5 champions + smite from the team
|
||||||
|
* @param playerData data of the searched player, only for basic matches
|
||||||
|
*/
|
||||||
|
public updateTeamRoles (team: ParticipantBasic[], champs: PlayerRole[], playerData?: ParticipantDetails) {
|
||||||
|
// const actualRoles = [...new Set(team.map(p => p.role))]
|
||||||
|
// if (actualRoles.length === 5) {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
const identifiedChamps = this.getTeamRoles(champs)
|
||||||
|
for (const summoner of team) {
|
||||||
|
summoner.role = Object.entries(identifiedChamps).find(([, champion]) => summoner.champion.id === champion)![0]
|
||||||
|
|
||||||
|
if (playerData && summoner.champion.id === playerData.champion.id) {
|
||||||
|
playerData.role = summoner.role
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param match from Riot Api
|
||||||
|
* @param allyTeam 5 players of the first team
|
||||||
|
* @param enemyTeam 5 players of the second team
|
||||||
|
* @param allyTeamId team id of the searched player, only for basic matches
|
||||||
|
* @param playerData data of the searched player, only for basic matches
|
||||||
|
*/
|
||||||
|
public getMatchRoles (
|
||||||
|
match: MatchDto,
|
||||||
|
allyTeam: ParticipantBasic[],
|
||||||
|
enemyTeam: ParticipantBasic[],
|
||||||
|
allyTeamId = 100,
|
||||||
|
playerData?: ParticipantDetails
|
||||||
|
) {
|
||||||
|
if (!this.championRoles || !queuesWithRole.includes(match.queueId)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let allyChamps: PlayerRole[] = []
|
||||||
|
let enemyChamps: PlayerRole[] = []
|
||||||
|
match.participants.map(p => {
|
||||||
|
const items = [p.stats.item0, p.stats.item1, p.stats.item2, p.stats.item3, p.stats.item4, p.stats.item5]
|
||||||
|
const playerRole = {
|
||||||
|
champion: p.championId,
|
||||||
|
jungle: p.spell1Id === 11 || p.spell2Id === 11,
|
||||||
|
support: supportItems.some(suppItem => items.includes(suppItem)),
|
||||||
|
}
|
||||||
|
p.teamId === allyTeamId ? allyChamps.push(playerRole) : enemyChamps.push(playerRole)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.updateTeamRoles(allyTeam, allyChamps, playerData)
|
||||||
|
this.updateTeamRoles(enemyTeam, enemyChamps)
|
||||||
|
|
||||||
|
allyTeam.sort(this.sortTeamByRole)
|
||||||
|
enemyTeam.sort(this.sortTeamByRole)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Summoner Spell Data from CDragon
|
||||||
|
* @param id of the summonerSpell
|
||||||
|
*/
|
||||||
|
public getSummonerSpell (id: number) {
|
||||||
|
if (id === 0) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const spell = this.summonerSpells.find((s: any) => s.id === id)
|
||||||
|
const spellName = spell.iconPath.split('/assets/')[1].toLowerCase()
|
||||||
|
return {
|
||||||
|
name: spell.name,
|
||||||
|
description: spell.description,
|
||||||
|
icon: `https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/${spellName}`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { ParticipantBasic } from './Models/Match'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* League of Legends queues with defined role for each summoner
|
* League of Legends queues with defined role for each summoner
|
||||||
*/
|
*/
|
||||||
|
|
@ -40,7 +42,7 @@ export function getSeasonNumber (timestamp: number) {
|
||||||
* @param a first role
|
* @param a first role
|
||||||
* @param b second role
|
* @param b second role
|
||||||
*/
|
*/
|
||||||
export function sortTeamByRole (a:any, b:any) {
|
export function sortTeamByRole (a:ParticipantBasic, b:ParticipantBasic) {
|
||||||
const sortingArr = ['TOP', 'JUNGLE', 'MIDDLE', 'BOTTOM', 'SUPPORT']
|
const sortingArr = ['TOP', 'JUNGLE', 'MIDDLE', 'BOTTOM', 'SUPPORT']
|
||||||
return sortingArr.indexOf(a.role) - sortingArr.indexOf(b.role)
|
return sortingArr.indexOf(a.role) - sortingArr.indexOf(b.role)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue