Last active
May 7, 2024 09:16
-
-
Save Telokis/129d0352653d12005491e8517aa77e96 to your computer and use it in GitHub Desktop.
Adds morale information to several places in Grepolis
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==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