2023-02-04 11:40:38 +00:00
|
|
|
import addSeconds from 'date-fns/addSeconds';
|
|
|
|
import {
|
|
|
|
Ennoblement,
|
|
|
|
ServerConfig,
|
|
|
|
TWHelpClient,
|
|
|
|
UnitInfo,
|
|
|
|
} from './lib/twhelp';
|
|
|
|
import { Cache, InMemoryStorage } from './lib/cache';
|
|
|
|
import { calcDistance, calcLoyalty } from './lib/tw';
|
|
|
|
import { createTranslationFunc } from './utils';
|
|
|
|
|
|
|
|
declare global {
|
|
|
|
interface Window {
|
|
|
|
TWMap: {
|
|
|
|
popup: {
|
|
|
|
loadVillage: (villageId: string) => void;
|
|
|
|
_loadVillage: (villageId: string) => void;
|
|
|
|
displayForVillage: (
|
|
|
|
village: { id: string },
|
|
|
|
x: number,
|
|
|
|
y: number
|
|
|
|
) => void;
|
|
|
|
_displayForVillage: (
|
|
|
|
village: { id: string },
|
|
|
|
x: number,
|
|
|
|
y: number
|
|
|
|
) => void;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const t = createTranslationFunc({
|
|
|
|
pl_PL: {
|
|
|
|
'Ennobled at': 'Ostatnio przejęta',
|
|
|
|
Loyalty: 'Poparcie',
|
|
|
|
'Can send a nobleman': 'Można wysłać szlachica',
|
|
|
|
Yes: 'Tak',
|
|
|
|
No: 'Nie',
|
|
|
|
Never: 'Nigdy',
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
class TWHelpConnector {
|
|
|
|
private static readonly SERVER_CONFIG_CACHE_KEY =
|
|
|
|
'extended_map_popup_server_config';
|
|
|
|
private static readonly UNIT_INFO_CACHE_KEY = 'extended_map_popup_unit_info';
|
|
|
|
private static readonly VILLAGE_CACHE_KEY_PREFIX =
|
|
|
|
'extended_map_popup_village_';
|
|
|
|
|
|
|
|
private readonly localStorageCache = new Cache(localStorage);
|
|
|
|
private readonly inMemoryCache = new Cache(new InMemoryStorage());
|
|
|
|
constructor(
|
|
|
|
private readonly client: TWHelpClient,
|
|
|
|
private readonly version: string,
|
|
|
|
private readonly server: string
|
|
|
|
) {}
|
|
|
|
|
|
|
|
serverConfig() {
|
|
|
|
return this.localStorageCache.load(
|
|
|
|
TWHelpConnector.SERVER_CONFIG_CACHE_KEY,
|
|
|
|
3600,
|
|
|
|
() => {
|
|
|
|
return this.client.serverConfig(this.version, this.server);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
unitInfo() {
|
|
|
|
return this.localStorageCache.load(
|
|
|
|
TWHelpConnector.UNIT_INFO_CACHE_KEY,
|
|
|
|
3600,
|
|
|
|
() => {
|
|
|
|
return this.client.unitInfo(this.version, this.server);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
async latestEnnoblement(id: number, cacheOnly?: boolean) {
|
|
|
|
const key = TWHelpConnector.VILLAGE_CACHE_KEY_PREFIX + id.toString();
|
|
|
|
|
|
|
|
if (cacheOnly) {
|
|
|
|
return this.inMemoryCache.get<Ennoblement | null>(key);
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.inMemoryCache.load(key, 86400, async () => {
|
|
|
|
const ennoblements = await this.client.villageEnnoblements(
|
|
|
|
this.version,
|
|
|
|
this.server,
|
|
|
|
id,
|
|
|
|
{
|
|
|
|
limit: 1,
|
|
|
|
sort: ['createdAt:DESC'],
|
|
|
|
}
|
|
|
|
);
|
|
|
|
return ennoblements.data.length > 0 ? ennoblements.data[0] : null;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class Popup {
|
|
|
|
private static readonly ID_ENNOBLED_AT = 'extended_map_popup_ennobled_at';
|
|
|
|
private static readonly ID_LOYALTY = 'extended_map_popup_loyalty';
|
|
|
|
|
|
|
|
constructor(
|
|
|
|
private readonly config: ServerConfig,
|
|
|
|
private readonly unitInfo: UnitInfo,
|
|
|
|
private readonly currentVillage: Window['game_data']['village'],
|
|
|
|
private readonly connector: TWHelpConnector
|
|
|
|
) {}
|
|
|
|
|
|
|
|
addHandlers() {
|
|
|
|
window.TWMap.popup._loadVillage = window.TWMap.popup.loadVillage;
|
|
|
|
window.TWMap.popup.loadVillage = this.loadVillage.bind(this);
|
|
|
|
window.TWMap.popup._displayForVillage =
|
|
|
|
window.TWMap.popup.displayForVillage;
|
|
|
|
window.TWMap.popup.displayForVillage = this.display.bind(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
private async loadVillage(villageId: string) {
|
|
|
|
window.TWMap.popup._loadVillage(villageId);
|
|
|
|
|
|
|
|
await this.displayLatestEnnoblementAndLoyalty(parseInt(villageId), false);
|
|
|
|
}
|
|
|
|
|
|
|
|
private async display(village: { id: string }, x: number, y: number) {
|
|
|
|
window.TWMap.popup._displayForVillage(village, x, y);
|
|
|
|
|
|
|
|
this.displayArrivalTimes(x, y);
|
|
|
|
this.displayCanSendNobleman(x, y);
|
2023-02-05 07:00:32 +00:00
|
|
|
await this.displayLatestEnnoblementAndLoyalty(
|
|
|
|
parseInt(village.id),
|
|
|
|
window.game_data.features.Premium.active
|
|
|
|
);
|
2023-02-04 11:40:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private displayArrivalTimes(x: number, y: number) {
|
|
|
|
const dist = calcDistance({ x, y }, this.currentVillage);
|
|
|
|
if (dist <= 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const imgs = document.querySelectorAll(
|
|
|
|
'#map_popup #info_content tbody img[src*="unit/unit_"]'
|
|
|
|
);
|
|
|
|
if (imgs.length === 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const tbody = imgs[0].closest('tbody');
|
|
|
|
if (!(tbody instanceof HTMLTableSectionElement)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const tr = document.createElement('tr');
|
|
|
|
tr.classList.add('center');
|
|
|
|
|
|
|
|
imgs.forEach((img, idx) => {
|
|
|
|
if (!(img instanceof HTMLImageElement)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const [unit, config] of Object.entries(this.unitInfo)) {
|
|
|
|
if (!img.src.includes(unit)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
const td = document.createElement('td');
|
|
|
|
td.style.padding = '2px';
|
|
|
|
td.style.backgroundColor = idx % 2 === 0 ? '#F8F4E8' : '#DED3B9';
|
|
|
|
td.style.maxWidth = '70px';
|
|
|
|
td.innerText = addSeconds(
|
|
|
|
window.Timing.getCurrentServerTime(),
|
|
|
|
Math.round(dist * config.speed * 60)
|
|
|
|
).toLocaleString();
|
|
|
|
tr.appendChild(td);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
tbody.appendChild(tr);
|
|
|
|
}
|
|
|
|
|
|
|
|
private displayCanSendNobleman(x: number, y: number) {
|
|
|
|
const dist = calcDistance({ x, y }, this.currentVillage);
|
|
|
|
if (dist <= 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const tbody = document.querySelector('#map_popup #info_content tbody');
|
|
|
|
if (!(tbody instanceof HTMLTableSectionElement)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const tr = document.createElement('tr');
|
|
|
|
|
|
|
|
tr.innerHTML = `
|
|
|
|
<td>${t('Can send a nobleman')}:</td>
|
|
|
|
<td>${dist <= this.config.snob.maxDist ? t('Yes') : t('No')}</td>
|
|
|
|
`;
|
|
|
|
|
|
|
|
tbody.appendChild(tr);
|
|
|
|
}
|
|
|
|
|
|
|
|
private async displayLatestEnnoblementAndLoyalty(
|
|
|
|
id: number,
|
|
|
|
cacheOnly: boolean
|
|
|
|
) {
|
|
|
|
const ennoblement = await this.connector.latestEnnoblement(id, cacheOnly);
|
|
|
|
|
|
|
|
const tbody = document.querySelector('#map_popup #info_content tbody');
|
|
|
|
if (!(tbody instanceof HTMLTableSectionElement)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
[
|
|
|
|
{
|
|
|
|
id: Popup.ID_ENNOBLED_AT,
|
|
|
|
title: t('Ennobled at'),
|
|
|
|
value: ennoblement
|
|
|
|
? new Date(ennoblement.createdAt).toLocaleString()
|
|
|
|
: t('Never'),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: Popup.ID_LOYALTY,
|
|
|
|
title: t('Loyalty'),
|
2023-02-05 07:33:32 +00:00
|
|
|
value: calcLoyalty(ennoblement?.createdAt ?? 0, this.config.speed),
|
2023-02-04 11:40:38 +00:00
|
|
|
},
|
|
|
|
].forEach(({ id, title, value }) => {
|
|
|
|
let tr = tbody.querySelector('#' + id);
|
|
|
|
if (!tr) {
|
|
|
|
tr = document.createElement('tr');
|
|
|
|
tr.id = id;
|
|
|
|
tbody.appendChild(tr);
|
|
|
|
}
|
|
|
|
|
|
|
|
tr.innerHTML = `
|
|
|
|
<td>${title}:</td>
|
|
|
|
<td>${value}</td>
|
|
|
|
`;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class ExtendedMapPopup {
|
|
|
|
connector: TWHelpConnector;
|
|
|
|
constructor(client: TWHelpClient) {
|
|
|
|
this.connector = new TWHelpConnector(
|
|
|
|
client,
|
|
|
|
window.game_data.market,
|
|
|
|
window.game_data.world
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
async run() {
|
|
|
|
const config = await this.connector.serverConfig();
|
|
|
|
const unitInfo = await this.connector.unitInfo();
|
|
|
|
|
|
|
|
new Popup(
|
|
|
|
config,
|
|
|
|
unitInfo,
|
|
|
|
window.game_data.village,
|
|
|
|
this.connector
|
|
|
|
).addHandlers();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
(async () => {
|
|
|
|
if (window.game_data.screen !== 'map' || window.game_data.mode !== null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
await new ExtendedMapPopup(
|
|
|
|
new TWHelpClient(process.env.TWHELP_API_BASE_URL ?? '')
|
|
|
|
)
|
|
|
|
.run()
|
|
|
|
.catch((err) => {
|
|
|
|
console.log(err);
|
|
|
|
});
|
|
|
|
})();
|