Skip to content

Instantly share code, notes, and snippets.

@Telokis
Last active May 7, 2024 09:16
Show Gist options
  • Save Telokis/129d0352653d12005491e8517aa77e96 to your computer and use it in GitHub Desktop.
Save Telokis/129d0352653d12005491e8517aa77e96 to your computer and use it in GitHub Desktop.
Adds morale information to several places in Grepolis
// ==UserScript==
// @name Morale everywhere
// @namespace Telokis
// @version 1.1.2
// @author Telokis
// @description Tell me the morale pls
// @match https://*.grepolis.com/game/*
// @include https://*.grepolis.com/game/*
// @exclude view-source://*
// @grant none
// ==/UserScript==
(async function () {
// Helper TeloAjax
// prettier-ignore
class TeloAjax {
constructor() {
this.eventHandlers = {};
this.startListeningForAjax();
}
on(eventName, handlerFunction) {
if (!(eventName in this.eventHandlers)) {
this.eventHandlers[eventName] = [];
}
this.eventHandlers[eventName].push(handlerFunction);
}
startListeningForAjax() {
$(document).ajaxComplete((event, xhr, options) => {
let urlParts = options.url.split("?");
let eventKey = "";
if (urlParts[1] !== undefined && urlParts[1].split(/&/)[1] !== undefined) {
eventKey = urlParts[0].substr(5) + "/" + urlParts[1].split(/&/)[1].substr(7);
}
let focusedWindow = GPWindowMgr.getFocusedWindow() || false;
let windowId = null;
if (focusedWindow) {
windowId = focusedWindow.getID();
focusedWindow = $(focusedWindow.getJQElement().find(".gpwindow_content"));
}
if (this.eventHandlers[eventKey] && this.eventHandlers[eventKey].length) {
for (let handlerIndex = 0; handlerIndex < this.eventHandlers[eventKey].length; handlerIndex++) {
this.eventHandlers[eventKey][handlerIndex](focusedWindow, windowId, eventKey);
}
}
});
}
}
class Cache {
static VALIDITY = 1000 * 60 * 30; // 30 minutes
cache = new Map();
set(key, value) {
this.cache.set(key, [value, Date.now() + Cache.VALIDITY]);
}
get(key) {
if (this.cache.has(key)) {
const entry = this.cache.get(key);
if (entry[1] >= Date.now()) {
return entry[0];
}
}
return null;
}
clear() {
this.cache.clear();
}
}
const cache = new Cache();
function moraleCalc(myPoints, enemyPoints, enemyCities) {
function moraleFloor(cities) {
const raw = {
1: 30,
2: 35,
3: 40,
4: 45,
5: 50,
6: 55,
7: 60,
8: 65,
9: 70,
10: 75,
};
return cities in raw ? raw[cities] : 80;
}
if (Game.features?.casual_world) {
if (enemyPoints <= myPoints * 0.8 || enemyPoints >= myPoints * 1.2) {
return 0;
}
}
const val = [(enemyPoints / myPoints) * 3 + 0.3] * 100;
const floor = moraleFloor(enemyCities);
return val > 100 ? 100 : val < floor ? floor : Math.floor(val);
}
async function fetchUserInfos(name) {
const data = await fetch(
`https://api.grepodata.com/player/search?query=${name}&from=0&size=30&`,
).then((response) => response.json());
return data.results.find(
(p) => p.world === Game.world_id && p.name.toLowerCase() === name.toLowerCase(),
);
}
async function getMorale(name) {
let morale = cache.get(name);
if (morale === null) {
const infos = await fetchUserInfos(name);
if (!infos) {
console.log("No infos for", name);
return -1;
}
if (Game.alliance_id === infos.alliance_id) {
morale = 0;
} else {
morale = moraleCalc(Game.player_points, infos.points, infos.towns);
}
cache.set(name, morale);
}
return morale;
}
$(`
<style id="telo_morale_css" type="text/css">
.telo_morale {
text-align: right;
margin-left: 3px;
font-weight: bold;
}
</style>`).appendTo("head");
function moraleStyle(val) {
// prettier-ignore
const colors = ["#dddddd","#ff0000","#ff0000","#ff0300","#ff0700","#ff0a00","#ff0d00","#ff1100","#ff1400","#ff1700","#ff1a00","#ff1e00","#ff2100","#ff2400","#ff2800","#ff2b00","#ff2e00","#ff3200","#ff3500","#ff3800","#ff3b00","#ff3f00","#ff4200","#ff4500","#ff4900","#ff4c00","#ff4f00","#ff5400","#ff5700","#ff5b00","#ff5e00","#ff6100","#ff6500","#ff6800","#ff6b00","#ff6f00","#ff7200","#ff7500","#ff7800","#ff7c00","#ff7f00","#ff8200","#ff8600","#ff8900","#ff8c00","#ff9000","#ff9300","#ff9600","#ff9900","#ff9d00","#ffa000","#ffa500","#ffa500","#fba502","#f7a504","#f4a506","#f0a608","#eca60a","#e8a60c","#e5a60e","#e1a610","#dda612","#d9a714","#d6a716","#d2a718","#cea71b","#caa71d","#c7a71f","#c3a821","#bfa823","#bba825","#b8a827","#b4a829","#b0a82b","#aca92d","#a9a92f","#a5a931","#a1a933","#a1a933","#a2ab34","#a3ad34","#a4af35","#a5b136","#a5b336","#a6b537","#a7b737","#a8b938","#a9bb39","#aabd39","#abbf3a","#acc13b","#acc33b","#adc53c","#aec73d","#afc93d","#b0cb3e","#b1cd3f","#b2cf3f","#b3d140","#b3d340","#b4d541","#b5d742","#b6d942","#b7db43"];
return "color: " + colors[val];
}
function makeMoraleSpan(morale) {
if (morale === -1) {
return $(
`<span title="Manque d'infos sur le joueur" class="telo_morale" style="color: #dddddd"}">N/A</span>`,
);
}
return $(
`<span title="Moral" class="telo_morale" style="${moraleStyle(
morale,
)}">${morale}%</span>`,
);
}
async function updateIslandView() {
const lines = [
...$("#island_info_towns_left_sorted_by_name").children().toArray(),
...$("#island_info_towns_left_sorted_by_score").children().toArray(),
...$("#island_info_towns_left_sorted_by_player").children().toArray(),
];
for (let i = 0; i < lines.length; i++) {
const $line = $(lines[i]);
const $name = $line.find(".player_name");
let morale = 0;
if ($name.text() === "Ville fantôme") {
morale = 100;
} else {
const name = $name.find("a").text();
morale = await getMorale(name);
}
$line.append(makeMoraleSpan(morale));
}
}
async function updateAllianceView() {
const lines = $("#ally_towns .members_list > li:not([class]) > ul").children().toArray();
for (let i = 0; i < lines.length; i++) {
const $line = $(lines[i]);
const $name = $line.find("a.gp_player_link");
const name = $name.text();
const morale = await getMorale(name);
$line.append(makeMoraleSpan(morale));
}
}
async function updateTownView() {
const $line = $(
"#towninfo_towninfo > .game_border > .game_list .list_item_right > a.write_message:first",
)
.parent()
.parent()
.find("div.list_item_left");
const $name = $line.find("a.gp_player_link");
const name = $name.text();
const morale = await getMorale(name);
$line.append(makeMoraleSpan(morale));
}
async function updateProfileView($wnd) {
const $playerInfo = $wnd.find("#player_info");
const name = $playerInfo.find("h3").text();
const morale = await getMorale(name);
$playerInfo.append(makeMoraleSpan(morale));
}
async function updateReportView() {
const $playerInfo = $("#player_info");
const name = $playerInfo.find("h3").text();
const morale = await getMorale(name);
$playerInfo.append(makeMoraleSpan(morale));
}
const teloAjax = new TeloAjax();
teloAjax.on("/island_info/index", updateIslandView);
teloAjax.on("/player/get_profile_html", updateProfileView);
teloAjax.on("/alliance/profile", updateAllianceView);
teloAjax.on("/town_info/info", updateTownView);
const wrapUpdateReportView = () => {
setTimeout(updateReportView, 200);
};
// teloAjax.on("/message/view", wrapUpdateReportView);
// teloAjax.on("/message/preview", wrapUpdateReportView);
// teloAjax.on("/alliance_forum/forum", wrapUpdateReportView);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment