diff --git a/.terserrc.js b/.terserrc.js index a738b77..bd1d0db 100644 --- a/.terserrc.js +++ b/.terserrc.js @@ -2,7 +2,7 @@ const preambles = { 'extended-player-profile': `// ==UserScript== // @name Extended player profile // @version 1.1.1 -// @description Adds additional info and actions to a player's profile. +// @description Adds additional info and actions on a player overview. // @author Dawid Wysokiński - Kichiyaki - contact@dwysokinski.me // @match https://*/game.php?*screen=info_player* // @downloadURL ${process.env.PUBLIC_URL}/extended-player-profile.user.js @@ -22,6 +22,18 @@ const preambles = { // @icon https://www.google.com/s2/favicons?domain=plemiona.pl // @grant none // @run-at document-end +// ==/UserScript==`, + 'extended-village-profile': `// ==UserScript== +// @name Extended village profile +// @version 1.0.0 +// @description Adds additional info and actions on a village overview. +// @author Dawid Wysokiński - Kichiyaki - contact@dwysokinski.me +// @match https://*/game.php?*screen=info_village* +// @downloadURL ${process.env.PUBLIC_URL}/extended-village-profile.user.js +// @updateURL ${process.env.PUBLIC_URL}/extended-village-profile.user.js +// @icon https://www.google.com/s2/favicons?domain=plemiona.pl +// @grant none +// @run-at document-end // ==/UserScript==`, }; diff --git a/README.md b/README.md index 90e9cd1..4966173 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ This repo contains a variety of scripts for [Tribal Wars](https://www.tribalwars ### Extended player profile -This script adds additional info and actions to a player's profile. +This script adds additional info and actions on a player overview. ![img.png](docs/extended-player-profile.png) @@ -36,6 +36,22 @@ javascript: $.getScript('https://scripts.tribalwarshelp.com/extended-map-popup.quickbar.js') ``` +### Extended village profile + +This script adds additional info and actions on a village overview. + +![img.png](docs/extended-village-profile.png) + +#### Installation + +[User script](https://scripts.tribalwarshelp.com/extended-village-profile.user.js) + +Quick bar: +```javascript +javascript: + $.getScript('https://scripts.tribalwarshelp.com/extended-village-profile.quickbar.js') +``` + ## License Distributed under the MIT License. See ``LICENSE`` for more information. diff --git a/docs/extended-village-profile.png b/docs/extended-village-profile.png new file mode 100644 index 0000000..99c73f4 Binary files /dev/null and b/docs/extended-village-profile.png differ diff --git a/package.json b/package.json index 6a04261..937a10b 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,8 @@ "build:extended-player-profile-quickbar": "yarn build-single ./src/extended-player-profile.quickbar.ts", "build:extended-map-popup-user": "PREAMBLE=extended-map-popup yarn build-single ./src/extended-map-popup.user.ts", "build:extended-map-popup-quickbar": "yarn build-single ./src/extended-map-popup.quickbar.ts", + "build:extended-village-profile-user": "PREAMBLE=extended-village-profile yarn build-single ./src/extended-village-profile.user.ts", + "build:extended-village-profile-quickbar": "yarn build-single ./src/extended-village-profile.quickbar.ts", "build": "npm-run-all build:*", "lint": "eslint src/**/*.ts" }, diff --git a/src/extended-map-popup.user.ts b/src/extended-map-popup.user.ts index b265657..3221dd3 100644 --- a/src/extended-map-popup.user.ts +++ b/src/extended-map-popup.user.ts @@ -225,10 +225,7 @@ class Popup { { id: Popup.ID_LOYALTY, title: t('Loyalty'), - value: calcLoyalty( - ennoblement?.createdAt ?? new Date(0), - this.config.speed - ), + value: calcLoyalty(ennoblement?.createdAt ?? 0, this.config.speed), }, ].forEach(({ id, title, value }) => { let tr = tbody.querySelector('#' + id); diff --git a/src/extended-player-profile.user.ts b/src/extended-player-profile.user.ts index a5f3756..77db4be 100644 --- a/src/extended-player-profile.user.ts +++ b/src/extended-player-profile.user.ts @@ -313,7 +313,6 @@ class UI { a.innerText = name; a.href = '#'; a.addEventListener('click', handler); - a.setAttribute('data-player-id', this.player.id.toString()); td.appendChild(a); tr.appendChild(td); tbody.appendChild(tr); diff --git a/src/extended-village-profile.quickbar.ts b/src/extended-village-profile.quickbar.ts new file mode 100644 index 0000000..9a519fa --- /dev/null +++ b/src/extended-village-profile.quickbar.ts @@ -0,0 +1,2 @@ +// Parcel doesn't have an option to rename output files +import './extended-village-profile.user'; diff --git a/src/extended-village-profile.user.ts b/src/extended-village-profile.user.ts new file mode 100644 index 0000000..62c2f6f --- /dev/null +++ b/src/extended-village-profile.user.ts @@ -0,0 +1,234 @@ +import { Ennoblement, ServerConfig, TWHelpClient } from './lib/twhelp'; +import { createTranslationFunc } from './utils'; +import { DialogTable } from './common/dialog-table'; +import { calcLoyalty } from './lib/tw'; +import { Cache } from './lib/cache'; + +const t = createTranslationFunc({ + pl_PL: { + 'Show ennoblements': 'Pokaż przejęcia', + 'Date/time': 'Data/czas', + Village: 'Wioska', + 'Old owner': 'Stary właściciel', + 'New owner': 'Nowy właściciel', + Points: 'Punkty', + Barbarian: 'Barbarzyńska', + 'Ennobled at': 'Ostatnio przejęta', + Unknown: 'Nieznany', + Loyalty: 'Poparcie', + Never: 'Nigdy', + }, +}); + +class TWHelpConnector { + private static SERVER_CONFIG_CACHE_KEY = + 'extended_village_profile_server_config'; + + private cache = new Cache(localStorage); + constructor( + private readonly client: TWHelpClient, + private readonly version: string, + private readonly server: string, + private readonly id: number + ) {} + + serverConfig() { + return this.cache.load( + TWHelpConnector.SERVER_CONFIG_CACHE_KEY, + 3600, + () => { + return this.client.serverConfig(this.version, this.server); + } + ); + } + + async latestEnnoblement() { + const ennoblements = await this.client.villageEnnoblements( + this.version, + this.server, + this.id, + { + limit: 1, + sort: ['createdAt:DESC'], + } + ); + return ennoblements.data.length > 0 ? ennoblements.data[0] : null; + } + + villageEnnoblements(page: number, limit: number) { + return this.client.villageEnnoblements(this.version, this.server, this.id, { + offset: (page - 1) * limit, + limit, + sort: ['createdAt:desc'], + }); + } +} + +enum DialogId { + ENNOBLEMENTS = 'extended_village_profile_ennoblements', +} + +class UI { + constructor( + private readonly config: ServerConfig, + private readonly ennoblement: Ennoblement | null, + private readonly connector: TWHelpConnector + ) {} + + render() { + this.renderAdditionalInfo(); + this.renderActions(); + } + + private renderAdditionalInfo() { + const tbody = document.querySelector('#embedmap_village')?.closest('tbody'); + if (!(tbody instanceof HTMLTableSectionElement)) { + return; + } + + tbody.insertAdjacentHTML( + 'beforeend', + ` + + ${t('Ennobled at')}: + ${ + this.ennoblement + ? new Date(this.ennoblement.createdAt).toLocaleString() + : t('Never') + } + + + ${t('Loyalty')}: + ${calcLoyalty( + this.ennoblement?.createdAt ?? 0, + this.config.speed + )} + + ` + ); + } + + private renderActions() { + const tbody = document + .querySelector('#content_value a[href*="twstats"]') + ?.closest('tbody'); + if (!(tbody instanceof HTMLTableSectionElement)) { + return; + } + + [ + { + name: t('Show ennoblements'), + handler: this.showEnnoblements.bind(this), + }, + ].forEach(({ name, handler }) => { + const tr = document.createElement('tr'); + const td = document.createElement('td'); + td.colSpan = 2; + const a = document.createElement('a'); + a.innerText = name; + a.href = '#'; + a.addEventListener('click', handler); + td.appendChild(a); + tr.appendChild(td); + tbody.appendChild(tr); + }); + } + + private async showEnnoblements(e: Event) { + e.preventDefault(); + + await new DialogTable( + DialogId.ENNOBLEMENTS, + [ + { + header: t('Village'), + accessor: (e) => + `${e.village.fullName}`, + }, + { + header: t('Points'), + accessor: (e) => e.points.toLocaleString(), + }, + { + header: t('Old owner'), + accessor: ({ village: { player } }) => { + if (!player) { + return t('Barbarian'); + } + return `${player.name}${ + player.tribe + ? ` (${player.tribe.tag})` + : '' + }`; + }, + }, + { + header: t('New owner'), + accessor: ({ newOwner }) => { + if (!newOwner) { + return t('Unknown'); + } + return `${newOwner.name}${ + newOwner.tribe + ? ` (${newOwner.tribe.tag})` + : '' + }`; + }, + }, + { + header: t('Date/time'), + accessor: (e) => new Date(e.createdAt).toLocaleString(), + }, + ], + 30, + (page: number, limit: number) => { + return this.connector.villageEnnoblements(page, limit); + } + ).render(); + } +} + +class ExtendedVillageProfile { + connector: TWHelpConnector; + constructor(client: TWHelpClient) { + this.connector = new TWHelpConnector( + client, + window.game_data.market, + window.game_data.world, + this.getVillageId() + ); + } + + async run() { + const config = await this.connector.serverConfig(); + const ennoblement = await this.connector.latestEnnoblement(); + + new UI(config, ennoblement, this.connector).render(); + } + + private getVillageId() { + const str = new URLSearchParams(window.location.search).get('id'); + if (!str) { + return 0; + } + return parseInt(str); + } +} + +(async () => { + if ( + window.game_data.screen !== 'info_village' || + window.game_data.mode !== null + ) { + return; + } + + await new ExtendedVillageProfile( + new TWHelpClient(process.env.TWHELP_API_BASE_URL ?? '') + ) + .run() + .catch((err) => { + console.log(err); + }); +})();