mirror of
https://github.com/vkaelin/LeagueStats.git
synced 2026-03-25 12:57:28 +00:00
feat: add detailed-match feature (without ranks for now)
This commit is contained in:
parent
2f677ab17b
commit
973355c201
14 changed files with 463 additions and 146 deletions
|
|
@ -91,29 +91,29 @@
|
|||
<div
|
||||
:style="{
|
||||
backgroundImage: `url(${
|
||||
player.firstSum ? player.firstSum.icon : null
|
||||
player.summonerSpell1 ? player.summonerSpell1.icon : null
|
||||
})`,
|
||||
}"
|
||||
:class="{ 'cursor-pointer': player.firstSum }"
|
||||
:class="{ 'cursor-pointer': player.summonerSpell1 }"
|
||||
class="w-4 h-4 bg-center bg-cover rounded-md bg-blue-1000"
|
||||
></div>
|
||||
</template>
|
||||
<template v-if="player.firstSum" #default>
|
||||
<template v-if="player.summonerSpell1" #default>
|
||||
<div
|
||||
class="flex max-w-sm p-2 text-xs text-left text-white select-none"
|
||||
>
|
||||
<div
|
||||
:style="{
|
||||
backgroundImage: `url('${player.firstSum.icon}')`,
|
||||
backgroundImage: `url('${player.summonerSpell1.icon}')`,
|
||||
}"
|
||||
class="flex-shrink-0 w-12 h-12 ml-1 bg-center bg-cover rounded-md bg-blue-1000"
|
||||
></div>
|
||||
<div class="ml-2 leading-tight">
|
||||
<div class="text-base leading-none">
|
||||
{{ player.firstSum.name }}
|
||||
{{ player.summonerSpell1.name }}
|
||||
</div>
|
||||
<div class="mt-1 font-light text-blue-200">
|
||||
{{ player.firstSum.description }}
|
||||
{{ player.summonerSpell1.description }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -124,29 +124,29 @@
|
|||
<div
|
||||
:style="{
|
||||
backgroundImage: `url(${
|
||||
player.secondSum ? player.secondSum.icon : null
|
||||
player.summonerSpell2 ? player.summonerSpell2.icon : null
|
||||
})`,
|
||||
}"
|
||||
:class="{ 'cursor-pointer': player.secondSum }"
|
||||
:class="{ 'cursor-pointer': player.summonerSpell2 }"
|
||||
class="w-4 h-4 bg-center bg-cover rounded-md bg-blue-1000"
|
||||
></div>
|
||||
</template>
|
||||
<template v-if="player.secondSum" #default>
|
||||
<template v-if="player.summonerSpell2" #default>
|
||||
<div
|
||||
class="flex max-w-sm p-2 text-xs text-left text-white select-none"
|
||||
>
|
||||
<div
|
||||
:style="{
|
||||
backgroundImage: `url('${player.secondSum.icon}')`,
|
||||
backgroundImage: `url('${player.summonerSpell2.icon}')`,
|
||||
}"
|
||||
class="flex-shrink-0 w-12 h-12 ml-1 bg-center bg-cover rounded-md bg-blue-1000"
|
||||
></div>
|
||||
<div class="ml-2 leading-tight">
|
||||
<div class="text-base leading-none">
|
||||
{{ player.secondSum.name }}
|
||||
{{ player.summonerSpell2.name }}
|
||||
</div>
|
||||
<div class="mt-1 font-light text-blue-200">
|
||||
{{ player.secondSum.description }}
|
||||
{{ player.summonerSpell2.description }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -195,7 +195,7 @@
|
|||
class="flex flex-col items-start justify-center ml-1 leading-none"
|
||||
>
|
||||
<router-link
|
||||
v-if="player.firstSum"
|
||||
v-if="player.summonerSpell1"
|
||||
:to="{
|
||||
name: 'summoner',
|
||||
params: { region: $route.params.region, name: player.name },
|
||||
|
|
|
|||
|
|
@ -175,7 +175,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</Ripple>
|
||||
<DetailedMatch :data="getMatchDetails(data.gameId) || {}" :details-open="showDetails" />
|
||||
<DetailedMatch :data="getMatchDetails(data.matchId) || {}" :details-open="showDetails" />
|
||||
</li>
|
||||
</template>
|
||||
|
||||
|
|
@ -223,8 +223,8 @@ export default {
|
|||
displayDetails() {
|
||||
this.showDetails = !this.showDetails
|
||||
|
||||
if (!this.getMatchDetails(this.data.gameId)) {
|
||||
this.matchDetails(this.data.gameId)
|
||||
if (!this.getMatchDetails(this.data.matchId)) {
|
||||
this.matchDetails(this.data.matchId)
|
||||
}
|
||||
},
|
||||
isSummonerProfile(account_id) {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,20 @@ import store from '@/store'
|
|||
|
||||
const leaguesNumbers = { 'I': 1, 'II': 2, 'III': 3, 'IV': 4 }
|
||||
|
||||
/**
|
||||
* Get the url of the of the player runes
|
||||
* @param {Object} perks : from the API
|
||||
*/
|
||||
export function getPrimaryAndSecondaryRune(perks) {
|
||||
const primaryRune = perks.selected.length ? store.state.cdragon.runes.perks[perks.selected[0]] : null
|
||||
const secondaryRune = store.state.cdragon.runes.perkstyles[perks.secondaryStyle]
|
||||
|
||||
return {
|
||||
primaryRune: primaryRune ? createCDragonAssetUrl(primaryRune.icon) : null,
|
||||
secondaryRune: secondaryRune ? createCDragonAssetUrl(secondaryRune.icon) : null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all the infos about a list of matches built with the Riot API data
|
||||
* @param {Object} RiotData : all data from the Riot API
|
||||
|
|
@ -12,10 +26,9 @@ const leaguesNumbers = { 'I': 1, 'II': 2, 'III': 3, 'IV': 4 }
|
|||
export function createMatchData(matches) {
|
||||
for (const match of matches) {
|
||||
// Runes
|
||||
const primaryRune = match.perks.selected.length ? store.state.cdragon.runes.perks[match.perks.selected[0]] : null
|
||||
const secondaryRune = store.state.cdragon.runes.perkstyles[match.perks.secondaryStyle]
|
||||
match.primaryRune = primaryRune ? createCDragonAssetUrl(primaryRune.icon) : null
|
||||
match.secondaryRune = secondaryRune ? createCDragonAssetUrl(secondaryRune.icon) : null
|
||||
const runes = getPrimaryAndSecondaryRune(match.perks)
|
||||
match.primaryRune = runes.primaryRune
|
||||
match.secondaryRune = runes.secondaryRune
|
||||
|
||||
const date = new Date(match.date)
|
||||
const dateOptions = { day: '2-digit', month: '2-digit', year: 'numeric' }
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@ export const state = {
|
|||
}
|
||||
|
||||
export const mutations = {
|
||||
MATCH_LOADING(state, gameId) {
|
||||
const alreadyIn = state.matches.find(m => m.gameId === gameId)
|
||||
MATCH_LOADING(state, matchId) {
|
||||
const alreadyIn = state.matches.find(m => m.matchId === matchId)
|
||||
if (!alreadyIn) {
|
||||
state.matches.push({ gameId: gameId, status: 'loading' })
|
||||
state.matches.push({ matchId, status: 'loading' })
|
||||
}
|
||||
},
|
||||
MATCH_FOUND(state, matchDetails) {
|
||||
|
|
@ -28,27 +28,27 @@ export const mutations = {
|
|||
}
|
||||
|
||||
export const actions = {
|
||||
async matchDetails({ commit, rootState }, gameId) {
|
||||
commit('MATCH_LOADING', gameId)
|
||||
const region = rootState.regionsList[rootState.settings.region]
|
||||
console.log('MATCH DETAILS STORE', gameId, region)
|
||||
async matchDetails({ commit }, matchId) {
|
||||
commit('MATCH_LOADING', matchId)
|
||||
console.log('MATCH DETAILS STORE', matchId)
|
||||
|
||||
const resp = await axios(({ url: 'match/details', data: { gameId, region }, method: 'POST' })).catch(() => { })
|
||||
const resp = await axios(({ url: 'match/details', data: { matchId }, method: 'POST' })).catch(() => { })
|
||||
console.log('--- DETAILS INFOS ---')
|
||||
console.log(resp.data)
|
||||
commit('MATCH_FOUND', resp.data.matchDetails)
|
||||
|
||||
// If the ranks of the players are not yet known
|
||||
if (resp.data.matchDetails.blueTeam.players[0].rank === undefined) {
|
||||
const ranks = await axios(({ url: 'match/details/ranks', data: { gameId, region }, method: 'POST' })).catch(() => { })
|
||||
if (!ranks) return
|
||||
console.log('--- RANK OF MATCH DETAILS ---')
|
||||
console.log(ranks.data)
|
||||
commit('MATCH_RANKS_FOUND', { gameId, ...ranks.data })
|
||||
}
|
||||
// TODO: add ranks back when it's done on the API
|
||||
// // If the ranks of the players are not yet known
|
||||
// if (resp.data.matchDetails.blueTeam.players[0].rank === undefined) {
|
||||
// const ranks = await axios(({ url: 'match/details/ranks', data: { gameId, region }, method: 'POST' })).catch(() => { })
|
||||
// if (!ranks) return
|
||||
// console.log('--- RANK OF MATCH DETAILS ---')
|
||||
// console.log(ranks.data)
|
||||
// commit('MATCH_RANKS_FOUND', { gameId, ...ranks.data })
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
export const getters = {
|
||||
getMatchDetails: state => gameId => state.matches.find(m => m.gameId === gameId),
|
||||
getMatchDetails: state => matchId => state.matches.find(m => m.matchId === matchId),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
|
||||
import Match from 'App/Models/Match'
|
||||
import DetailedMatchSerializer from 'App/Serializers/DetailedMatchSerializer'
|
||||
import MatchService from 'App/Services/MatchService'
|
||||
import StatsService from 'App/Services/StatsService'
|
||||
import DetailedMatchValidator from 'App/Validators/DetailedMatchValidator'
|
||||
import MatchesIndexValidator from 'App/Validators/MatchesIndexValidator'
|
||||
|
||||
export default class MatchesController {
|
||||
|
|
@ -9,7 +12,6 @@ export default class MatchesController {
|
|||
* @param ctx
|
||||
*/
|
||||
public async index({ request, response }: HttpContextContract) {
|
||||
console.log('More Matches Request')
|
||||
const { puuid, region, matchIds, season } = await request.validate(MatchesIndexValidator)
|
||||
const matches = await MatchService.getMatches(region, matchIds, puuid)
|
||||
|
||||
|
|
@ -19,4 +21,27 @@ export default class MatchesController {
|
|||
stats,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* POST - Return details data for one specific match
|
||||
* @param ctx
|
||||
*/
|
||||
public async show({ request, response }: HttpContextContract) {
|
||||
console.time('MatchDetails')
|
||||
const { matchId } = await request.validate(DetailedMatchValidator)
|
||||
|
||||
const match = await Match.query()
|
||||
.where('id', matchId)
|
||||
.preload('teams')
|
||||
.preload('players')
|
||||
.firstOrFail()
|
||||
|
||||
const matchDetails = DetailedMatchSerializer.serializeOneMatch(match)
|
||||
|
||||
console.timeEnd('MatchDetails')
|
||||
|
||||
return response.json({
|
||||
matchDetails,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ class MatchParser {
|
|||
damage_dealt_champions: player.totalDamageDealtToChampions,
|
||||
damage_taken: player.totalDamageTaken,
|
||||
heal: player.totalHeal,
|
||||
minions: player.totalMinionsKilled,
|
||||
minions: player.totalMinionsKilled + player.neutralMinionsKilled,
|
||||
critical_strike: player.largestCriticalStrike,
|
||||
killing_spree: player.killingSprees,
|
||||
time_spent_living: player.longestTimeSpentLiving,
|
||||
|
|
|
|||
|
|
@ -2,38 +2,10 @@ import { getSeasonNumber, sortTeamByRole } from 'App/helpers'
|
|||
import Match from 'App/Models/Match'
|
||||
import MatchPlayer from 'App/Models/MatchPlayer'
|
||||
import { TeamPosition } from 'App/Parsers/ParsedType'
|
||||
import CDragonService from 'App/Services/CDragonService'
|
||||
import MatchSerializer from './MatchSerializer'
|
||||
import {
|
||||
SerializedMatch,
|
||||
SerializedMatchChampion,
|
||||
SerializedMatchItem,
|
||||
SerializedMatchPerks,
|
||||
SerializedMatchStats,
|
||||
SerializedMatchSummonerSpell,
|
||||
SerializedMatchTeamPlayer,
|
||||
} from './SerializedTypes'
|
||||
import { SerializedMatch, SerializedMatchStats, SerializedMatchTeamPlayer } from './SerializedTypes'
|
||||
|
||||
class BasicMatchSerializer extends MatchSerializer {
|
||||
/**
|
||||
* Get champion specific data
|
||||
* @param id of the champion
|
||||
*/
|
||||
public getChampion(id: number): SerializedMatchChampion {
|
||||
const originalChampionData = CDragonService.champions[id]
|
||||
const icon =
|
||||
CDragonService.BASE_URL +
|
||||
originalChampionData.squarePortraitPath.split('/assets/')[1].toLowerCase()
|
||||
|
||||
return {
|
||||
icon,
|
||||
id: originalChampionData.id,
|
||||
name: originalChampionData.name,
|
||||
alias: originalChampionData.alias,
|
||||
roles: originalChampionData.roles,
|
||||
}
|
||||
}
|
||||
|
||||
protected getPlayerSummary(player: MatchPlayer): SerializedMatchTeamPlayer {
|
||||
return {
|
||||
puuid: player.summonerPuuid,
|
||||
|
|
@ -47,57 +19,6 @@ class BasicMatchSerializer extends MatchSerializer {
|
|||
return players.map((p) => this.getPlayerSummary(p)).sort(sortTeamByRole)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Summoner Spell Data from CDragon
|
||||
* @param id of the summonerSpell
|
||||
*/
|
||||
public getSummonerSpell(id: number): SerializedMatchSummonerSpell | null {
|
||||
const spell = CDragonService.summonerSpells[id]
|
||||
if (id === 0 || !spell) {
|
||||
return null
|
||||
}
|
||||
const spellName = spell.iconPath.split('/assets/')[1].toLowerCase()
|
||||
return {
|
||||
name: spell.name,
|
||||
description: spell.description,
|
||||
icon: `${CDragonService.BASE_URL}${spellName}`,
|
||||
}
|
||||
}
|
||||
|
||||
protected getItems(player: MatchPlayer): Array<SerializedMatchItem | null> {
|
||||
const items: (SerializedMatchItem | null)[] = []
|
||||
for (let i = 0; i < 6; i++) {
|
||||
const id = player['item' + i]
|
||||
if (id === 0) {
|
||||
items.push(null)
|
||||
continue
|
||||
}
|
||||
|
||||
const item = CDragonService.items[id]
|
||||
if (!item) {
|
||||
items.push(null)
|
||||
continue
|
||||
}
|
||||
|
||||
const itemUrl = item.iconPath.split('/assets/')[1].toLowerCase()
|
||||
items.push({
|
||||
image: `${CDragonService.BASE_URL}${itemUrl}`,
|
||||
name: item.name,
|
||||
description: item.description,
|
||||
price: item.priceTotal,
|
||||
})
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
protected getPerks(player: MatchPlayer): SerializedMatchPerks {
|
||||
return {
|
||||
primaryStyle: player.perksPrimaryStyle,
|
||||
secondaryStyle: player.perksSecondaryStyle,
|
||||
selected: player.perksSelected,
|
||||
}
|
||||
}
|
||||
|
||||
protected getStats(player: MatchPlayer): SerializedMatchStats {
|
||||
return {
|
||||
kills: player.kills,
|
||||
|
|
@ -137,27 +58,18 @@ class BasicMatchSerializer extends MatchSerializer {
|
|||
|
||||
return {
|
||||
allyTeam: this.getTeamSummary(allyPlayers),
|
||||
champion: this.getChampion(identity.championId),
|
||||
date: match.date,
|
||||
enemyTeam: this.getTeamSummary(enemyPlayers),
|
||||
matchId: match.id,
|
||||
gamemode: match.gamemode,
|
||||
items: this.getItems(identity),
|
||||
level: identity.champLevel,
|
||||
map: match.map,
|
||||
name: identity.summonerName,
|
||||
newMatch,
|
||||
perks: this.getPerks(identity),
|
||||
region: match.region,
|
||||
result: allyTeam.result,
|
||||
role: TeamPosition[identity.teamPosition],
|
||||
season: getSeasonNumber(match.date),
|
||||
stats: this.getStats(identity),
|
||||
summonerId: identity.summonerId,
|
||||
summonerSpell1: this.getSummonerSpell(identity.summoner1Id),
|
||||
summonerSpell2: this.getSummonerSpell(identity.summoner2Id),
|
||||
summonerPuuid: puuid,
|
||||
time: match.gameDuration,
|
||||
...this.getPlayerBase(identity),
|
||||
}
|
||||
}
|
||||
public serialize(matches: Match[], puuid: string, newMatches = false): SerializedMatch[] {
|
||||
|
|
|
|||
134
server-v2/app/Serializers/DetailedMatchSerializer.ts
Normal file
134
server-v2/app/Serializers/DetailedMatchSerializer.ts
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
import { sortTeamByRole } from 'App/helpers'
|
||||
import Match from 'App/Models/Match'
|
||||
import MatchPlayer from 'App/Models/MatchPlayer'
|
||||
import MatchTeam from 'App/Models/MatchTeam'
|
||||
import MatchSerializer from './MatchSerializer'
|
||||
import {
|
||||
SerializedDetailedMatch,
|
||||
SerializedDetailedMatchBan,
|
||||
SerializedDetailedMatchPlayer,
|
||||
SerializedDetailedMatchStats,
|
||||
SerializedDetailedMatchTeam,
|
||||
SerializedDetailedMatchTeamStats,
|
||||
} from './SerializedTypes'
|
||||
|
||||
class DetailedMatchSerializer extends MatchSerializer {
|
||||
protected getTeamBans(team: MatchTeam): SerializedDetailedMatchBan[] {
|
||||
if (!team.bans || !team.banOrders) {
|
||||
return []
|
||||
}
|
||||
|
||||
return team.bans.map((banId, index) => {
|
||||
return {
|
||||
champion: this.getChampion(banId),
|
||||
championId: banId,
|
||||
pickTurn: team.banOrders![index],
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
protected getTeamStats(players: MatchPlayer[]): SerializedDetailedMatchTeamStats {
|
||||
return players.reduce(
|
||||
(acc, player) => {
|
||||
acc.kills += player.kills
|
||||
acc.deaths += player.deaths
|
||||
acc.assists += player.assists
|
||||
acc.gold += player.gold
|
||||
acc.dmgChamp += player.damageDealtChampions
|
||||
acc.dmgObj += player.damageDealtObjectives
|
||||
acc.dmgTaken += player.damageTaken
|
||||
return acc
|
||||
},
|
||||
{ kills: 0, deaths: 0, assists: 0, gold: 0, dmgChamp: 0, dmgObj: 0, dmgTaken: 0 }
|
||||
)
|
||||
}
|
||||
|
||||
protected getPlayersDetailed(
|
||||
players: MatchPlayer[],
|
||||
teamStats: SerializedDetailedMatchTeamStats,
|
||||
gameDuration: number
|
||||
): SerializedDetailedMatchPlayer[] {
|
||||
return players
|
||||
.map((player) => {
|
||||
const stats: SerializedDetailedMatchStats = {
|
||||
kills: player.kills,
|
||||
deaths: player.deaths,
|
||||
assists: player.assists,
|
||||
minions: player.minions,
|
||||
vision: player.visionScore,
|
||||
gold: player.gold,
|
||||
dmgChamp: player.damageDealtChampions,
|
||||
dmgObj: player.damageDealtObjectives,
|
||||
dmgTaken: player.damageTaken,
|
||||
kp: player.kp.toFixed(1) + '%',
|
||||
kda: player.kills + player.assists !== 0 && player.deaths === 0 ? '∞' : player.kda,
|
||||
realKda: player.kda,
|
||||
}
|
||||
const percentStats = {
|
||||
minions: +(player.minions / (gameDuration / 60)).toFixed(2),
|
||||
vision: +(player.visionScore / (gameDuration / 60)).toFixed(2),
|
||||
gold: +((player.gold * 100) / teamStats.gold).toFixed(1) + '%',
|
||||
dmgChamp: +((player.damageDealtChampions * 100) / teamStats.dmgChamp).toFixed(1) + '%',
|
||||
dmgObj:
|
||||
+(
|
||||
teamStats.dmgObj ? (player.damageDealtObjectives * 100) / teamStats.dmgObj : 0
|
||||
).toFixed(1) + '%',
|
||||
dmgTaken: +((player.damageTaken * 100) / teamStats.dmgTaken).toFixed(1) + '%',
|
||||
}
|
||||
return {
|
||||
...this.getPlayerBase(player),
|
||||
...this.getRuneIcons(player.perksSelected, player.perksSecondaryStyle),
|
||||
stats,
|
||||
percentStats,
|
||||
}
|
||||
})
|
||||
.sort(sortTeamByRole)
|
||||
}
|
||||
|
||||
protected getTeamDetailed(
|
||||
team: MatchTeam,
|
||||
players: MatchPlayer[],
|
||||
gameDuration: number
|
||||
): SerializedDetailedMatchTeam {
|
||||
const teamStats = this.getTeamStats(players)
|
||||
|
||||
return {
|
||||
bans: this.getTeamBans(team),
|
||||
barons: team.barons,
|
||||
color: team.color === 100 ? 'Blue' : 'Red',
|
||||
dragons: team.dragons,
|
||||
inhibitors: team.inhibitors,
|
||||
players: this.getPlayersDetailed(players, teamStats, gameDuration),
|
||||
result: team.result,
|
||||
riftHeralds: team.riftHeralds,
|
||||
teamStats,
|
||||
towers: team.towers,
|
||||
}
|
||||
}
|
||||
|
||||
public serializeOneMatch(match: Match): SerializedDetailedMatch {
|
||||
const blueTeam = match.teams.find((team) => team.color === 100)!
|
||||
const redTeam = match.teams.find((team) => team.color === 200)!
|
||||
|
||||
const bluePlayers: MatchPlayer[] = []
|
||||
const redPlayers: MatchPlayer[] = []
|
||||
|
||||
for (const p of match.players) {
|
||||
p.team === 100 ? bluePlayers.push(p) : redPlayers.push(p)
|
||||
}
|
||||
|
||||
return {
|
||||
blueTeam: this.getTeamDetailed(blueTeam, bluePlayers, match.gameDuration),
|
||||
date: match.date,
|
||||
matchId: match.id,
|
||||
gamemode: match.gamemode,
|
||||
map: match.map,
|
||||
redTeam: this.getTeamDetailed(redTeam, redPlayers, match.gameDuration),
|
||||
region: match.region,
|
||||
season: match.season,
|
||||
time: match.gameDuration,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new DetailedMatchSerializer()
|
||||
|
|
@ -1 +1,102 @@
|
|||
export default abstract class MatchSerializer {}
|
||||
import MatchPlayer from 'App/Models/MatchPlayer'
|
||||
import { TeamPosition } from 'App/Parsers/ParsedType'
|
||||
import CDragonService from 'App/Services/CDragonService'
|
||||
import {
|
||||
SerializedBasePlayer,
|
||||
SerializedMatchChampion,
|
||||
SerializedMatchItem,
|
||||
SerializedMatchPerks,
|
||||
SerializedMatchSummonerSpell,
|
||||
} from './SerializedTypes'
|
||||
|
||||
export default abstract class MatchSerializer {
|
||||
/**
|
||||
* Get champion specific data
|
||||
* @param id of the champion
|
||||
*/
|
||||
public getChampion(id: number): SerializedMatchChampion {
|
||||
const originalChampionData = CDragonService.champions[id]
|
||||
const icon = CDragonService.createAssetUrl(originalChampionData.squarePortraitPath)
|
||||
|
||||
return {
|
||||
icon,
|
||||
id: originalChampionData.id,
|
||||
name: originalChampionData.name,
|
||||
alias: originalChampionData.alias,
|
||||
roles: originalChampionData.roles,
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get Summoner Spell Data from CDragon
|
||||
* @param id of the summonerSpell
|
||||
*/
|
||||
public getSummonerSpell(id: number): SerializedMatchSummonerSpell | null {
|
||||
const spell = CDragonService.summonerSpells[id]
|
||||
if (id === 0 || !spell) {
|
||||
return null
|
||||
}
|
||||
return {
|
||||
name: spell.name,
|
||||
description: spell.description,
|
||||
icon: CDragonService.createAssetUrl(spell.iconPath),
|
||||
}
|
||||
}
|
||||
|
||||
protected getItems(player: MatchPlayer): Array<SerializedMatchItem | null> {
|
||||
const items: (SerializedMatchItem | null)[] = []
|
||||
for (let i = 0; i < 6; i++) {
|
||||
const id = player['item' + i]
|
||||
if (id === 0) {
|
||||
items.push(null)
|
||||
continue
|
||||
}
|
||||
|
||||
const item = CDragonService.items[id]
|
||||
if (!item) {
|
||||
items.push(null)
|
||||
continue
|
||||
}
|
||||
|
||||
items.push({
|
||||
image: CDragonService.createAssetUrl(item.iconPath),
|
||||
name: item.name,
|
||||
description: item.description,
|
||||
price: item.priceTotal,
|
||||
})
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
protected getPerks(player: MatchPlayer): SerializedMatchPerks {
|
||||
return {
|
||||
primaryStyle: player.perksPrimaryStyle,
|
||||
secondaryStyle: player.perksSecondaryStyle,
|
||||
selected: player.perksSelected,
|
||||
}
|
||||
}
|
||||
|
||||
protected getRuneIcons(perksSelected: number[], perksSecondaryStyle: number) {
|
||||
const primaryRune = perksSelected.length ? CDragonService.perks[perksSelected[0]] : null
|
||||
const secondaryRune = CDragonService.perkstyles[perksSecondaryStyle]
|
||||
|
||||
return {
|
||||
primaryRune: primaryRune ? CDragonService.createAssetUrl(primaryRune.iconPath) : null,
|
||||
secondaryRune: secondaryRune ? CDragonService.createAssetUrl(secondaryRune.iconPath) : null,
|
||||
}
|
||||
}
|
||||
|
||||
protected getPlayerBase(player: MatchPlayer): SerializedBasePlayer {
|
||||
return {
|
||||
champion: this.getChampion(player.championId),
|
||||
items: this.getItems(player),
|
||||
level: player.champLevel,
|
||||
name: player.summonerName,
|
||||
perks: this.getPerks(player),
|
||||
role: TeamPosition[player.teamPosition],
|
||||
summonerId: player.summonerId,
|
||||
summonerPuuid: player.summonerPuuid,
|
||||
summonerSpell1: this.getSummonerSpell(player.summoner1Id),
|
||||
summonerSpell2: this.getSummonerSpell(player.summoner2Id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,25 +1,28 @@
|
|||
export interface SerializedMatch {
|
||||
allyTeam: SerializedMatchTeamPlayer[]
|
||||
export interface SerializedBasePlayer {
|
||||
champion: SerializedMatchChampion
|
||||
date: number
|
||||
enemyTeam: SerializedMatchTeamPlayer[]
|
||||
matchId: string
|
||||
gamemode: number
|
||||
items: Array<SerializedMatchItem | null>
|
||||
level: number
|
||||
map: number
|
||||
name: string
|
||||
newMatch: boolean
|
||||
perks: SerializedMatchPerks
|
||||
region: string
|
||||
result: string
|
||||
role: string
|
||||
season: number
|
||||
stats: SerializedMatchStats
|
||||
summonerId: string
|
||||
summonerPuuid: string
|
||||
summonerSpell1: SerializedMatchSummonerSpell | null
|
||||
summonerSpell2: SerializedMatchSummonerSpell | null
|
||||
}
|
||||
|
||||
export interface SerializedMatch extends SerializedBasePlayer {
|
||||
allyTeam: SerializedMatchTeamPlayer[]
|
||||
date: number
|
||||
enemyTeam: SerializedMatchTeamPlayer[]
|
||||
matchId: string
|
||||
gamemode: number
|
||||
map: number
|
||||
newMatch: boolean
|
||||
region: string
|
||||
result: string
|
||||
season: number
|
||||
stats: SerializedMatchStats
|
||||
time: number
|
||||
}
|
||||
|
||||
|
|
@ -80,3 +83,80 @@ export interface SerializedMatchStats {
|
|||
tripleKills: number
|
||||
vision: number
|
||||
}
|
||||
|
||||
/* ============================
|
||||
|
||||
Detailed Match
|
||||
|
||||
============================ */
|
||||
export interface SerializedDetailedMatch {
|
||||
blueTeam: SerializedDetailedMatchTeam
|
||||
date: number
|
||||
matchId: string
|
||||
gamemode: number
|
||||
map: number
|
||||
redTeam: SerializedDetailedMatchTeam
|
||||
region: string
|
||||
season: number
|
||||
time: number
|
||||
}
|
||||
|
||||
export interface SerializedDetailedMatchTeam {
|
||||
bans: SerializedDetailedMatchBan[]
|
||||
barons: number
|
||||
color: string
|
||||
dragons: number
|
||||
inhibitors: number
|
||||
players: SerializedDetailedMatchPlayer[]
|
||||
result: string
|
||||
riftHeralds: number
|
||||
teamStats: SerializedDetailedMatchTeamStats
|
||||
towers: number
|
||||
}
|
||||
|
||||
export interface SerializedDetailedMatchBan {
|
||||
champion: SerializedMatchChampion
|
||||
championId: number
|
||||
pickTurn: number
|
||||
}
|
||||
|
||||
export interface SerializedDetailedMatchPlayer extends SerializedBasePlayer {
|
||||
stats: SerializedDetailedMatchStats
|
||||
percentStats: SerializedDetailedMatchPercentStats
|
||||
primaryRune: string | null
|
||||
secondaryRune: string | null
|
||||
}
|
||||
|
||||
export interface SerializedDetailedMatchTeamStats {
|
||||
assists: number
|
||||
deaths: number
|
||||
dmgChamp: number
|
||||
dmgObj: number
|
||||
dmgTaken: number
|
||||
gold: number
|
||||
kills: number
|
||||
}
|
||||
|
||||
export interface SerializedDetailedMatchStats {
|
||||
assists: number
|
||||
deaths: number
|
||||
dmgChamp: number
|
||||
dmgObj: number
|
||||
dmgTaken: number
|
||||
gold: number
|
||||
kda: string | number
|
||||
kills: number
|
||||
kp: string
|
||||
minions: number
|
||||
realKda: number
|
||||
vision: number
|
||||
}
|
||||
|
||||
export interface SerializedDetailedMatchPercentStats {
|
||||
dmgChamp: string
|
||||
dmgObj: string
|
||||
dmgTaken: string
|
||||
gold: string
|
||||
minions: number
|
||||
vision: number
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,14 @@ class CDragonService {
|
|||
return dto.reduce((obj, item) => ((obj[item.id] = item), obj), {})
|
||||
}
|
||||
|
||||
/**
|
||||
* Give the full CDragon image path from the iconPath field
|
||||
*/
|
||||
public createAssetUrl(iconPath: string) {
|
||||
const name = iconPath.split('/assets/')[1].toLowerCase()
|
||||
return `${this.BASE_URL}${name}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Get global Context with CDragon Data
|
||||
*/
|
||||
|
|
|
|||
42
server-v2/app/Validators/DetailedMatchValidator.ts
Normal file
42
server-v2/app/Validators/DetailedMatchValidator.ts
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import { schema } from '@ioc:Adonis/Core/Validator'
|
||||
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
|
||||
|
||||
export default class DetailedMatchValidator {
|
||||
constructor(protected ctx: HttpContextContract) {}
|
||||
|
||||
/*
|
||||
* Define schema to validate the "shape", "type", "formatting" and "integrity" of data.
|
||||
*
|
||||
* For example:
|
||||
* 1. The username must be of data type string. But then also, it should
|
||||
* not contain special characters or numbers.
|
||||
* ```
|
||||
* schema.string({}, [ rules.alpha() ])
|
||||
* ```
|
||||
*
|
||||
* 2. The email must be of data type string, formatted as a valid
|
||||
* email. But also, not used by any other user.
|
||||
* ```
|
||||
* schema.string({}, [
|
||||
* rules.email(),
|
||||
* rules.unique({ table: 'users', column: 'email' }),
|
||||
* ])
|
||||
* ```
|
||||
*/
|
||||
public schema = schema.create({
|
||||
matchId: schema.string(),
|
||||
})
|
||||
|
||||
/**
|
||||
* Custom messages for validation failures. You can make use of dot notation `(.)`
|
||||
* for targeting nested fields and array expressions `(*)` for targeting all
|
||||
* children of an array. For example:
|
||||
*
|
||||
* {
|
||||
* 'profile.username.required': 'Username is required',
|
||||
* 'scores.*.number': 'Define scores as valid numbers'
|
||||
* }
|
||||
*
|
||||
*/
|
||||
public messages = {}
|
||||
}
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
import { SerializedMatchTeamPlayer } from './Serializers/SerializedTypes'
|
||||
|
||||
/**
|
||||
* All League of Legends regions used in Riot API
|
||||
*/
|
||||
|
|
@ -99,12 +97,16 @@ export function getCurrentSeason(): number {
|
|||
return seasons[lastTimestamp]
|
||||
}
|
||||
|
||||
interface SortableByRole {
|
||||
role: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort array of Players by roles according to a specific order
|
||||
* @param a first player
|
||||
* @param b second player
|
||||
*/
|
||||
export function sortTeamByRole(a: SerializedMatchTeamPlayer, b: SerializedMatchTeamPlayer) {
|
||||
export function sortTeamByRole<T extends SortableByRole>(a: T, b: T) {
|
||||
const sortingArr = ['TOP', 'JUNGLE', 'MIDDLE', 'BOTTOM', 'UTILITY']
|
||||
return sortingArr.indexOf(a.role) - sortingArr.indexOf(b.role)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ Route.post('/summoner/records', 'SummonersController.records')
|
|||
// Route.post('/summoner/live', 'SummonersController.liveMatchDetails')
|
||||
|
||||
Route.post('/match', 'MatchesController.index')
|
||||
// Route.post('/match/details', 'MatchesController.show')
|
||||
Route.post('/match/details', 'MatchesController.show')
|
||||
// Route.post('/match/details/ranks', 'MatchesController.showRanks')
|
||||
|
||||
Route.get('/cdragon/runes', 'CDragonController.runes')
|
||||
|
|
|
|||
Loading…
Reference in a new issue