diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..68bf4f3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +node_modules/ +dist/ +.idea/ +core-build/ +cache/ +.DS_Store \ No newline at end of file diff --git a/controller/InGameState.ts b/controller/InGameState.ts new file mode 100644 index 0000000..54f5e87 --- /dev/null +++ b/controller/InGameState.ts @@ -0,0 +1,296 @@ +import type { PluginContext } from 'league-prod-toolkit/core/modules/Module' +import { AllGameData, Player, Event } from '../types/AllGameData' +import { Config } from '../types/Config' +import { ItemEpicness } from '../types/Items' +import { InGameState as InGameStateType } from '../types/InGameState' + +export class InGameState { + + public gameState : InGameStateType + public gameData : any[] = [] + public itemEpicness : number[] + + public actions : Array<(allGameData : AllGameData, i: number) => void> = [] + + constructor ( + private namespace: string, + private ctx: PluginContext, + private config: Config, + private statics: any + ) { + this.itemEpicness = this.config.items.map(i => ItemEpicness[i]) + + this.gameState = { + towers : { + 100 : { + L : {}, + C : {}, + R : {} + }, + 200 : { + L : {}, + C : {}, + R : {} + }, + }, + showInhibitors : null, + inhibitors : { + 100 : { + L1 : { + alive : true, + respawnAt : 0, + respawnIn : 0, + percent : 0 + }, + C1 : { + alive : true, + respawnAt : 0, + respawnIn : 0, + percent : 0 + }, + R1 : { + alive : true, + respawnAt : 0, + respawnIn : 0, + percent : 0 + } + }, + 200 : { + L1 : { + alive : true, + respawnAt : 0, + respawnIn : 0, + percent : 0 + }, + C1 : { + alive : true, + respawnAt : 0, + respawnIn : 0, + percent : 0 + }, + R1 : { + alive : true, + respawnAt : 0, + respawnIn : 0, + percent : 0 + } + }, + }, + player : { + 0 : { + level : 0, + items : new Set(), + }, + 1 : { + level : 0, + items : new Set(), + }, + 2 : { + level : 0, + items : new Set(), + }, + 3 : { + level : 0, + items : new Set(), + }, + 4 : { + level : 0, + items : new Set(), + }, + 5 : { + level : 0, + items : new Set(), + }, + 6 : { + level : 0, + items : new Set(), + }, + 7 : { + level : 0, + items : new Set(), + }, + 8 : { + level : 0, + items : new Set(), + }, + 9 : { + level : 0, + items : new Set(), + } + } + } + + this.ctx.LPTE.emit({ + meta: { + namespace: this.namespace, + type: 'update', + version: 1 + }, + state : this.gameState + }) + } + + public handelData (allGameData: AllGameData) : void { + if (this.gameData.length > 0) { + const previousGameData = this.gameData[this.gameData.length -1] + this.checkPlayerUpdate(allGameData) + this.checkEventUpdate(allGameData, previousGameData) + + this.actions.forEach((func, i) => { + func(allGameData, i) + }) + } + + this.gameData.push(allGameData) + } + + private checkPlayerUpdate (allGameData: AllGameData) { + if (this.config.items.length === 0) return + if (allGameData.allPlayers.length === 0) return + + allGameData.allPlayers.forEach((player, i) => { + this.checkItemUpdate(player, i) + this.checkLevelUpdate(player, i) + }) + } + + private checkLevelUpdate (currentPlayerState: Player, id: number) { + if (currentPlayerState.level === this.gameState.player[id].level) return + if (!this.config.level.includes(currentPlayerState.level.toString())) return + + this.gameState.player[id].level = currentPlayerState.level + + this.ctx.LPTE.emit({ + meta: { + type: 'level-update', + namespace: this.namespace, + version: 1 + }, + team: currentPlayerState.team === "ORDER" ? 100 : 200, + player: id, + level: currentPlayerState.level + }) + } + + private checkItemUpdate (currentPlayerState: Player, id: number) { + const previousItems = this.gameState.player[id].items + + for (const item of currentPlayerState.items) { + + const itemID = item.itemID + if (previousItems.has(itemID)) continue + + const itemBinFind = this.statics.itemBin.find((i: any) => i.itemID === itemID) + if (itemBinFind === undefined) continue + + if (!this.itemEpicness.includes(itemBinFind.epicness)) continue + + this.gameState.player[id].items.add(itemID) + + this.ctx.LPTE.emit({ + meta: { + type: 'item-update', + namespace: this.namespace, + version: 1 + }, + team: currentPlayerState.team === "ORDER" ? 100 : 200, + player: id, + item: itemID + }) + } + } + + // --- + + private checkEventUpdate (allGameData: AllGameData, previousGameData: AllGameData) { + if (allGameData.events.Events.length === 0 || previousGameData.events.Events.length === 0) return + + const newEvents = allGameData.events.Events.slice(previousGameData.events.Events.length) + + newEvents.forEach(event => { + if (event.EventName === "InhibKilled") { + this.handleInhibEvent(event) + } else if (event.EventName === "TurretKilled") { + this.handleTowerEvent(event) + } + }) + } + + private handleInhibEvent (event: Event) { + console.log(event) + const split = event.InhibKilled.split('_') as string[] + const team = split[1] === 'T1' ? 100 : 200 + const lane = split[2] as 'L1' | 'C1' | 'R1' + const respawnAt = Math.round(event.EventTime) + (60 * 5) + + console.log(this.gameState.inhibitors[team][lane].alive) + + if (!this.gameState.inhibitors[team][lane].alive) return + + this.gameState.inhibitors[team][lane] = { + alive : false, + respawnAt : respawnAt, + respawnIn : (60 * 5), + percent : 100 + } + + this.actions.push((allGameData, i) => { + const gameState = allGameData.gameData + const diff = respawnAt - Math.round(gameState.gameTime) + const percent = Math.round((diff * 100) / (60 * 5)) + + this.gameState.inhibitors[team][lane] = { + alive : false, + respawnAt : respawnAt, + respawnIn : diff, + percent : 100 + } + + this.ctx.LPTE.emit({ + meta: { + namespace: this.namespace, + type: 'inhib-update', + version: 1 + }, + team, + lane, + percent, + respawnIn: diff + }) + + if (diff <= 0) { + this.gameState.inhibitors[team][lane] = { + alive : true, + respawnAt : 0, + respawnIn : 0, + percent : 0 + } + this.actions.splice(i, 1) + } + }) + } + + private handleTowerEvent (event: Event) { + console.log(event) + const split = event.TurretKilled.split('_') as string[] + const team = split[1] === 'T1' ? 100 : 200 + const lane = split[2] as 'L' | 'C' | 'R' + const turret = split[3] + + console.log(this.gameState.towers[team][lane][turret]) + if (this.gameState.towers[team][lane][turret] === false) return + + this.gameState.towers[team][lane][turret] = false + + this.ctx.LPTE.emit({ + meta: { + namespace: this.namespace, + type: 'tower-update', + version: 1 + }, + team, + lane, + turret + }) + } +} \ No newline at end of file diff --git a/frontend/frontend.js b/frontend/frontend.js new file mode 100644 index 0000000..f6ad304 --- /dev/null +++ b/frontend/frontend.js @@ -0,0 +1,56 @@ +$('#ingame-embed').val(`${location.href}/gfx/ingmae.html`); + +const namespace = 'league-in-game'; + +$('#settings').on('submit', (e) => { + e.preventDefault() + + LPTE.emit({ + meta: { + namespace, + type: 'set-settings', + version: 1 + }, + items: $('#items').val(), + level: $('#level').val() + }) +}) + +function showInhibs (side) { + LPTE.emit({ + meta: { + namespace, + type: 'show-inhibs', + version: 1 + }, + side + }) +} + +function hideInhibs () { + LPTE.emit({ + meta: { + namespace, + type: 'hide-inhibs', + version: 1 + } + }) +} + +function initSettings (settings) { + $('#items').val(settings.items) + $('#level').val(settings.level) +} + +LPTE.onready(async () => { + const settings = await LPTE.request({ + meta: { + namespace, + type: 'get-settings', + version: 1 + } + }) + initSettings(settings) + + LPTE.on(namespace, 'set-settings', initSettings) +}) \ No newline at end of file diff --git a/frontend/gfx/img/bottom.svg b/frontend/gfx/img/bottom.svg new file mode 100644 index 0000000..de927fc --- /dev/null +++ b/frontend/gfx/img/bottom.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/gfx/img/inhibbadge.png b/frontend/gfx/img/inhibbadge.png new file mode 100644 index 0000000..6b16805 Binary files /dev/null and b/frontend/gfx/img/inhibbadge.png differ diff --git a/frontend/gfx/img/mid.svg b/frontend/gfx/img/mid.svg new file mode 100644 index 0000000..bb144da --- /dev/null +++ b/frontend/gfx/img/mid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/gfx/img/top.svg b/frontend/gfx/img/top.svg new file mode 100644 index 0000000..ee5cf7c --- /dev/null +++ b/frontend/gfx/img/top.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/gfx/img/turret.png b/frontend/gfx/img/turret.png new file mode 100644 index 0000000..74c636d Binary files /dev/null and b/frontend/gfx/img/turret.png differ diff --git a/frontend/gfx/ingame.css b/frontend/gfx/ingame.css new file mode 100644 index 0000000..e346aaa --- /dev/null +++ b/frontend/gfx/ingame.css @@ -0,0 +1,246 @@ +*, *::before, *::after { + padding: 0; + margin: 0; + box-sizing: border-box; +} + +body { + width: 1920px; + height: 1080px; + overflow: hidden; + display: flex; + background-size: contain; + background-repeat: no-repeat; + margin-top: 154px; + font-family: var(--secondary-font-family); + font-weight: 700; + position: relative; +} + +.player { + width: 74px; + height: 71px; + overflow: hidden; + position: relative; + transform-origin: center bottom; + display: flex; + justify-content: center; + align-items: center; + transform: scaleY(0); +} + +.level { + display: none; + transform-origin: center; + font-size: 50px; + transform: translateY(-100%) +} + +.levelUp { + animation: backgroundUp 0.4s ease-out forwards, + backgroundDown 0.4s ease-out 2.85s forwards; +} + +.levelUp .level { + display: block; + animation: levelIn 0.4s ease-in 0.05s forwards, + levelOut 0.4s ease-in 2.6s forwards; +} + +.item { + display: none; + object-fit: contain; + width: 60px; + height: 60px; + transform: translateY(-100%) +} + +.itemBuy { + animation: backgroundUp 0.4s ease-out forwards, + backgroundDown 0.4s ease-out 3.45s forwards; +} + +.itemBuy .item { + display: block; + animation: levelIn 0.4s ease-in 0.1s forwards, + levelOut 0.4s ease-in 3.15s forwards; +} + +.player:not(:last-child) { + margin-bottom: 32px; +} + +#blue { + margin-left: 2px; +} + +#blue .player { + background: var(--blue-team); + background: linear-gradient(180deg, var(--blue-team) 0%, var(--blue-team-dark) 250%); + -webkit-mask-image: radial-gradient(circle 18px at 70px 9px, transparent 0, transparent 12px, black 13px); +} + +#blue .player .level { + color: var(--background-color); +} + +#red { + margin-right: 2px; + margin-left: auto; +} + +#red .player { + background: var(--red-team); + background: linear-gradient(180deg, var(--red-team) 0%, var(--red-team-dark) 250%); + -webkit-mask-image: radial-gradient(circle 18px at 4px 9px, transparent 0, transparent 12px, black 13px); +} + +#red .player .level { + color: var(--text-color); +} + +@keyframes backgroundUp { + from { + transform: scaleY(0); + } + to { + transform: scaleY(1); + } +} + +@keyframes backgroundDown { + from { + transform: scaleY(1); + } + to { + transform: scaleY(0); + } +} + +@keyframes levelIn { + from { + transform: translateY(-100%); + } + to { + transform: translateY(0); + } +} + +@keyframes levelOut { + from { + transform: translateY(0); + } + to { + transform: translateY(-100%); + } +} + +#inhibDiv { + opacity: 1; + background-image: url(./img/inhibbadge.png); + background-size: contain; + background-repeat: no-repeat; + width: 310px; + height: 135px; + position: absolute; + left: 0; + bottom: 135px; + transform: translateY(-100%); + transition: opacity 0.3s ease; + color: var(--text-color) +} + +.inhibitors { + transform: scale(1); + width: 100%; + display: flex; + flex-wrap: wrap; + justify-content: space-around; + opacity: 1; + transition: opacity 0.5s ease; + padding: 7px 30px 10px 22px; + position: absolute; +} + +.inhibitors h3 { + text-align: center; + width: 100%; + margin-bottom: 10px; + font-family: var(--primary-font-family); + font-size: 30px; + position: relative; + letter-spacing: 7.5px; +} + +.inhibitor, +.inhibitor svg { + width: 50px; + height: 50px; +} + +.inhibitor { + --percent: 0%; + position: relative; + text-align: center; +} + +.inhibitor.L1::before { + content: url(./img/top.svg); +} +.inhibitor.C1::before { + content: url(./img/mid.svg); +} +.inhibitor.R1::before { + content: url(./img/bottom.svg); +} + +.inhibitor::before { + position: absolute; + left: 0; + right: 0; + top: 0; + bottom: 0; + z-index: 10; + -webkit-mask-image: linear-gradient(to bottom, transparent 0, transparent calc(var(--percent) * 1%), black calc(calc(var(--percent) * 1%) - 1px)); +} + +#blueSide h3 { + color: var(--blue-team); +} + +#redSide h3 { + color: var(--red-team); +} + +.hide { + opacity: 0 !important; +} + +#turrets { + position: absolute; + top: -140px; + left: 50%; + transform: translateX(-50%); + width: 610px; + font-size: 20px; + display: flex; + justify-content: space-between; +} + +.turrets { + display: flex; +} + +.turret { + margin-right: 5px; + object-fit: contain; + width: 18px; +} + +#blueTurrets { + color: #0099e0; +} + +#redTurrets { + color: #e83d3d; +} \ No newline at end of file diff --git a/frontend/gfx/ingame.js b/frontend/gfx/ingame.js new file mode 100644 index 0000000..11e1e83 --- /dev/null +++ b/frontend/gfx/ingame.js @@ -0,0 +1,165 @@ +const namespace = 'league-in-game'; +const blueTeam = document.querySelector('#blue') +const redTeam = document.querySelector('#red') + +function getPlayerId(id) { + if (id > 4) return id - 5 + else return id +} + +function levelUpdate (e) { + const playerId = getPlayerId(e.player) + + const team = e.team === 100 ? blueTeam : redTeam + const playerDiv = team.children[playerId] + + const levelContainer = playerDiv.querySelector('.level') + + if (playerDiv.classList.contains('levelUp') || playerDiv.classList.contains('itemBuy')) { + return setTimeout(() => { + levelUpdate(e) + }, 3000) + } + + levelContainer.innerHTML = e.level + playerDiv.classList.add('levelUp') + setTimeout(() => { + playerDiv.classList.remove('levelUp') + }, 6000) +} + +function itemUpdate (e) { + const playerId = getPlayerId(e.player) + + const team = e.team === 100 ? blueTeam : redTeam + const playerDiv = team.children[playerId] + + const levelContainer = playerDiv.querySelector('.item') + + if (playerDiv.classList.contains('levelUp') || playerDiv.classList.contains('itemBuy')) { + return setTimeout(() => { + itemUpdate(e) + }, 3000) + } + + levelContainer.src = `/serve/static-league/img/item/${e.item}.png` + playerDiv.classList.add('itemBuy') + setTimeout(() => { + playerDiv.classList.remove('itemBuy') + }, 6000) +} + +const inhibDiv = document.querySelector('#inhibDiv') +const blueSide = inhibDiv.querySelector('#blueSide') +const redSide = inhibDiv.querySelector('#redSide') + +function inhibUpdate (e) { + const team = e.team === 100 ? blueSide : redSide + const inhib = team.querySelector(`.${e.lane}`) + inhib.style.setProperty('--percent', e.percent) + inhib.querySelector('p').innerText = convertSecsToTime(e.respawnIn) +} + +const turretDiv = document.querySelector('#turrets') +const blueTurrets = turretDiv.querySelector('#blueTurrets') +const redTurrets = turretDiv.querySelector('#redTurrets') + +function towerUpdate (e) { + const team = e.team === '100' ? redTurrets : blueTurrets + const value = team.querySelector('.value') + const newValue = (Number(value.innerText) || 0) + 1 + value.innerText = newValue +} + +function setGameState (e) { + const state = e.state + + for (const [teamId, team] of Object.entries(state.towers)) { + for (const lane of Object.values(team)) { + const teamDiv = teamId === '100' ? redTurrets : blueTurrets + const value = teamDiv.querySelector('.value') + let newValue = 0 + + for (const alive of Object.values(lane)) { + if (alive) continue + + newValue += 1 + value.textContent = newValue + 1 + } + + value.textContent = (Number(value.innerText) || 0) + } + } + + for (const [teamId, team] of Object.entries(state.inhibitors)) { + for (const [lane, data] of Object.entries(team)) { + const teamDiv = teamId === 100 ? blueSide : redSide + const div = teamDiv.querySelector(`.${lane}`) + + if (data.alive) { + div.style.setProperty('--percent', '0') + div.querySelector('p').innerText = convertSecsToTime(0) + } else { + div.style.setProperty('--percent', data.percent) + div.querySelector('p').innerText = convertSecsToTime(data.respawnIn) + } + } + } + + if (state.showInhibitors !== null) { + inhibDiv.classList.remove('hide') + if (state.showInhibitors === 100) { + blueSide.classList.remove('hide') + redSide.classList.add('hide') + } else { + blueSide.classList.add('hide') + redSide.classList.remove('hide') + } + } else { + inhibDiv.classList.add('hide') + blueSide.classList.add('hide') + redSide.classList.add('hide') + } +} + +function convertSecsToTime (secs) { + const minutes = Math.floor(secs / 60); + const seconds = secs - minutes * 60; + return `${('0' + minutes).slice(-2)}:${('0' + seconds).slice(-2)}` +} + +LPTE.onready(async () => { + LPTE.on(namespace, 'level-update', levelUpdate) + LPTE.on(namespace, 'item-update', itemUpdate) + LPTE.on(namespace, 'inhib-update', inhibUpdate) + LPTE.on(namespace, 'tower-update', towerUpdate) + LPTE.on(namespace, 'update', setGameState) + + LPTE.on(namespace, 'show-inhibs', (e) => { + inhibDiv.classList.remove('hide') + + if (e.side === 100) { + blueSide.classList.remove('hide') + redSide.classList.add('hide') + } else { + blueSide.classList.add('hide') + redSide.classList.remove('hide') + } + }); + + LPTE.on(namespace, 'hide-inhibs', () => { + inhibDiv.classList.add('hide') + blueSide.classList.add('hide') + redSide.classList.add('hide') + }); + + const res = await LPTE.request({ + meta: { + namespace, + type: 'request', + version: 1 + } + }); + + setGameState(res) +}) \ No newline at end of file diff --git a/frontend/gfx/ingmae.html b/frontend/gfx/ingmae.html new file mode 100644 index 0000000..00b5552 --- /dev/null +++ b/frontend/gfx/ingmae.html @@ -0,0 +1,104 @@ + + + + + + + Tournament Tree.gfx + + + + + +
+
+ +
0
+
+
+ +
0
+
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+

Inhibitors

+
+ +

00:00

+
+
+ +

00:00

+
+
+ +

00:00

+
+
+
+

Inhibitors

+
+ +

00:00

+
+
+ +

00:00

+
+
+ +

00:00

+
+
+
+ + + + \ No newline at end of file diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..0e0a632 --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,57 @@ +

In-Game Information

+ +

Preview

+
+ +
+ +

Embed

+ + +
+ + + +
+ +

Settings

+
+
+ + +
+
+ + +
+
+ +
+
+ + \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..16c5e3f --- /dev/null +++ b/package-lock.json @@ -0,0 +1,142 @@ +{ + "name": "league-in-game", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "league-prod-toolkit": "file:../.." + }, + "devDependencies": { + "typescript": "^4.3.2" + } + }, + "../..": { + "name": "league-prod-toolkit-core", + "version": "0.0.1", + "dependencies": { + "bootstrap": "^4.5.0", + "cli-progress": "^3.3.1", + "copy-webpack-plugin": "^9.0.0", + "debug": "~2.6.9", + "express": "^4.17.1", + "jquery": "^3.5.1", + "jspath": "^0.4.0", + "lcu-connector": "^2.1.2", + "leaguejs": "^2.1.3", + "morgan": "~1.9.1", + "needle": "^2.4.0", + "pug": "^2.0.4", + "send": "^0.17.1", + "toastr": "^2.1.4", + "typescript": "^3.6.3", + "uniqid": "^5.3.0", + "winston": "^3.2.1", + "ws": "^7.3.1" + }, + "devDependencies": { + "@types/cli-progress": "^1.8.1", + "@types/express": "^4.17.13", + "@types/minimist": "^1.2.0", + "@types/needle": "^2.0.4", + "@types/node": "^12.7.7", + "@types/send": "^0.14.5", + "@types/uniqid": "^5.3.0", + "@types/ws": "^7.2.9", + "@typescript-eslint/eslint-plugin": "4", + "babel-cli": "^6.26.0", + "babel-preset-es2015": "^6.24.1", + "browser-sync": "^2.26.7", + "concurrently": "^5.2.0", + "copyfiles": "^2.2.0", + "electron": "^7.0.0", + "eslint": "7", + "eslint-config-standard-with-typescript": "^19.0.1", + "eslint-plugin-import": "2", + "eslint-plugin-node": "11", + "eslint-plugin-promise": "4", + "eslint-plugin-standard": "4", + "nodemon": "^2.0.4", + "ts-loader": "^9.2.2", + "webpack": "^5.38.1", + "webpack-cli": "^4.7.0" + } + }, + "node_modules/league-prod-toolkit": { + "resolved": "../..", + "link": true + }, + "node_modules/typescript": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.2.tgz", + "integrity": "sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + } + }, + "dependencies": { + "league-prod-toolkit": { + "version": "file:../..", + "requires": { + "@types/cli-progress": "^1.8.1", + "@types/express": "^4.17.13", + "@types/minimist": "^1.2.0", + "@types/needle": "^2.0.4", + "@types/node": "^12.7.7", + "@types/send": "^0.14.5", + "@types/uniqid": "^5.3.0", + "@types/ws": "^7.2.9", + "@typescript-eslint/eslint-plugin": "4", + "babel-cli": "^6.26.0", + "babel-preset-es2015": "^6.24.1", + "bootstrap": "^4.5.0", + "browser-sync": "^2.26.7", + "cli-progress": "^3.3.1", + "concurrently": "^5.2.0", + "copy-webpack-plugin": "^9.0.0", + "copyfiles": "^2.2.0", + "debug": "~2.6.9", + "electron": "^7.0.0", + "eslint": "7", + "eslint-config-standard-with-typescript": "^19.0.1", + "eslint-plugin-import": "2", + "eslint-plugin-node": "11", + "eslint-plugin-promise": "4", + "eslint-plugin-standard": "4", + "express": "^4.17.1", + "jquery": "^3.5.1", + "jspath": "^0.4.0", + "lcu-connector": "^2.1.2", + "leaguejs": "^2.1.3", + "morgan": "~1.9.1", + "needle": "^2.4.0", + "nodemon": "^2.0.4", + "pug": "^2.0.4", + "send": "^0.17.1", + "toastr": "^2.1.4", + "ts-loader": "^9.2.2", + "typescript": "^3.6.3", + "uniqid": "^5.3.0", + "webpack": "^5.38.1", + "webpack-cli": "^4.7.0", + "winston": "^3.2.1", + "ws": "^7.3.1" + } + }, + "typescript": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.2.tgz", + "integrity": "sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw==", + "dev": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..52fdf14 --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "name": "league-in-game", + "version": "1.0.0", + "description": "Module to display in-game information", + "author": "Himyu", + "license": "MIT", + "toolkit": { + "modes": [ + "PLUGIN" + ], + "plugin": { + "main": "dist/plugin.js" + }, + "needsBuild": true + }, + "dependencies": { + "league-prod-toolkit": "file:../.." + }, + "devDependencies": { + "typescript": "^4.3.2" + }, + "scripts": { + "build": "tsc" + } +} diff --git a/plugin.ts b/plugin.ts new file mode 100644 index 0000000..7f79073 --- /dev/null +++ b/plugin.ts @@ -0,0 +1,128 @@ +import type { PluginContext } from 'league-prod-toolkit/core/modules/Module' +import { InGameState } from './controller/InGameState'; +import { AllGameData } from './types/AllGameData'; +import type { Config } from './types/Config' + +const namespace = 'league-in-game'; + +module.exports = async (ctx: PluginContext) => { + const configRes = await ctx.LPTE.request({ + meta: { + type: 'request', + namespace: 'config', + version: 1 + } + }); + if (configRes === undefined) { + return ctx.log.warn('config could not be loaded') + } + let config = configRes.config as Config; + + ctx.LPTE.on(namespace, 'set-settings', (e) => { + config.items = e.items + config.level = e.level + + ctx.LPTE.emit({ + meta: { + type: 'set', + namespace: 'config', + version: 1 + }, + config: { + items: e.items, + level: e.level + } + }); + }); + + ctx.LPTE.on(namespace, 'get-settings', (e) => { + ctx.LPTE.emit({ + meta: { + type: e.meta.reply!, + namespace: 'reply', + version: 1 + }, + items: config.items, + level: config.level + }); + }); + + ctx.LPTE.emit({ + meta: { + type: 'add-pages', + namespace: 'ui', + version: 1 + }, + pages: [{ + name: 'OP: league-in-game', + frontend: 'frontend', + id : 'op-league-in-game' + }] + }); + + // Emit event that we're ready to operate + ctx.LPTE.emit({ + meta: { + type: 'plugin-status-change', + namespace: 'lpt', + version: 1 + }, + status: 'RUNNING' + }); + + await ctx.LPTE.await('lpt', 'ready', 150000); + + const staticsRes = await ctx.LPTE.request({ + meta: { + type: 'request-constants', + namespace: 'static-league', + version: 1 + } + }) + if (staticsRes === undefined) { + return ctx.log.warn(`statics could not be loaded`) + } + const statics = staticsRes.constants; + + let inGameState : InGameState + + ctx.LPTE.on('state-league', 'live-game-loaded', () => { + inGameState = new InGameState(namespace, ctx, config, statics) + }) + + ctx.LPTE.on(namespace, 'allgamedata', (e) => { + if (inGameState === undefined) { + inGameState = new InGameState(namespace, ctx, config, statics) + } + + const data = e.data as AllGameData + inGameState.handelData(data) + }); + + ctx.LPTE.on(namespace, 'request', (e) => { + if (inGameState === undefined) { + inGameState = new InGameState(namespace, ctx, config, statics) + } + + ctx.LPTE.emit({ + meta: { + type: e.meta.reply as string, + namespace: 'reply', + version: 1 + }, + state: inGameState.gameState + }); + }); + + ctx.LPTE.on(namespace, 'show-inhibs', (e) => { + if (inGameState === undefined) return + + inGameState.gameState.showInhibitors = e.side + }) + + ctx.LPTE.on(namespace, 'hide-inhibs', (e) => { + if (inGameState === undefined) return + + inGameState.gameState.showInhibitors = null + }) +}; \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..a22a90a --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,66 @@ +{ + "compilerOptions": { + /* Basic Options */ + "incremental": true /* Enable incremental compilation */, + "target": "ES2018" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */, + "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */, + // "lib": [], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + // "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + "sourceMap": true /* Generates corresponding '.map' file. */, + // "outFile": "./", + "allowJs": true, /* Concatenate and emit output to single file. */ + "outDir": "./dist" /* Redirect output structure to the directory. */, + "rootDir": "./" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */, + // "composite": true, /* Enable project compilation */ + "tsBuildInfoFile": "dist/.tsbuildinfo" /* Specify file to store incremental compilation information */, + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true /* Enable all strict type-checking options. */, + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + "resolveJsonModule": true, + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": ["./types"], /* List of folders to include type definitions from. */ + // "types": ["./types.ts"], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + } + } + \ No newline at end of file diff --git a/types/AllGameData.d.ts b/types/AllGameData.d.ts new file mode 100644 index 0000000..3f53759 --- /dev/null +++ b/types/AllGameData.d.ts @@ -0,0 +1,90 @@ +export interface AllGameData { + activePlayer: ActivePlayer + allPlayers: Player[] + events: { + Events: Event[] + } + gameData: GameData +} + +export interface ActivePlayer { + "error": "Spectator mode doesn't currently support this feature" +} + +export interface Player { + championName: string + isBot: boolean + isDead: boolean + items: Item[] + level: number + position: Position + rawChampionName: string + respawnTimer: number + runes: { + keystone: Rune + primaryRuneTree: Rune + secondaryRuneTree: Rune + } + scores: Scores + skinID: number + summonerName: string + summonerSpells: { + summonerSpellOne: SummonerSpell + summonerSpellTwo: SummonerSpell + } + team: Team +} + +export interface Item { + canUse: boolean + consumable: boolean + count: number + displayName: string + itemID: number + price: number + rawDescription: string + rawDisplayName: string + slot: number +} + +export interface Rune { + displayName: string + id: number + rawDescription: string + rawDisplayName: string +} + +export interface Scores { + assists: number + creepScore: number + deaths: number + kills: number + wardScore: number +} + +export interface SummonerSpell { + displayName: string + rawDescription: string + rawDisplayName: string +} + +export type Position = "TOP" | "JUNGLE" | "MIDDLE" | "BOTTOM" | "UTILITY" + +export type Team = "ORDER" | "CHAOS" + + +export interface Event { + EventID: number + EventName: string + EventTime: number + [k: string]: any +} + + +export interface GameData { + gameMode: string + gameTime: number + mapName: string + mapNumber: number + mapTerrain: string +} \ No newline at end of file diff --git a/types/Config.d.ts b/types/Config.d.ts new file mode 100644 index 0000000..260d52f --- /dev/null +++ b/types/Config.d.ts @@ -0,0 +1,4 @@ +export interface Config { + level: string[] + items: string[] +} \ No newline at end of file diff --git a/types/InGameState.ts b/types/InGameState.ts new file mode 100644 index 0000000..ef84ab8 --- /dev/null +++ b/types/InGameState.ts @@ -0,0 +1,50 @@ +export interface InGameState { + towers : { + 100 : TowerState + 200 : TowerState + } + showInhibitors : 100 | 200 | null + inhibitors : { + 100 : InhibitorState + 200 : InhibitorState + } + player : { + [id : number] : { + level : number + items : Set + } + } +} + +export interface TowerState { + L : { + [turret : string] : boolean + } + C : { + [turret : string] : boolean + } + R : { + [turret : string] : boolean + } +} + +export interface InhibitorState { + L1 : { + alive : boolean + respawnIn : number + respawnAt : number + percent : number + } + C1 : { + alive : boolean + respawnIn : number + respawnAt : number + percent : number + } + R1 : { + alive : boolean + respawnIn : number + respawnAt : number + percent : number + } +} \ No newline at end of file diff --git a/types/Items.ts b/types/Items.ts new file mode 100644 index 0000000..5a86ed0 --- /dev/null +++ b/types/Items.ts @@ -0,0 +1,7 @@ +export const ItemEpicness : {[k: string]: number} = { + "Consumable": 7, + "Mythic": 6, + "Legendary": 5, + "Epic": 4, + "Starter": 1 +} \ No newline at end of file