Po uprave danich jsem si rekl, co kdyby se usnadnilo a urychlilo klikani o neco vice.
Proklikavanim eventu.
A tak jsem se rozhodl, ze do hry pridam moznost rychlych utoku.
Rychle utoky zautoci na event, jako byste jej sami otevreli a bez nejake zmeny odeslali do utoku.
Tzv. silne klany (nebo klidne i slabe), co vi, ze nejake eventy davaji bez problemu, nemusi zbytecne travit nekolik vterin utocenim na event.
Kdyby mel Nezmar zajem, muze zmeny pridat do hry.
Je to ciste javascript, castecne spaghety, protoze ze zacatku byla mensi vize.
Ale neco z toho by se snad dalo pouzit.
Code: Select all
// ==UserScript==
// @name Land of Ice modifikace
// @version 0.1.10
// @description Menu, rychle utoky, dane, dobyti mesta
// @author yS
// @match *://*.landofice.com/main.php*
// @match *://*.landofice.com/market*
// @match *://*.landofice.com/utok*
// @match *://*.landofice.com/settings*
// @match *://*.landofice.com/klanarmy_vybaveni*
// @grant GM_setValue
// @grant GM_getValue
// @namespace https://greasyfork.org/users/1005892
// @icon https://www.google.com/s2/favicons?sz=64&domain=landofice.com
// ==/UserScript==
// Constants for disabling certain parts of the script
const ZMENIT_MENU = "edit_menu";
const PRIDAT_DANE = "add_vat";
const PRIDAT_DOBYT_MESTO = "add_city";
const PRIDAT_NASTAVENI_PRODUKCE = "add_recruit_buttons";
const PRIDAT_RYCHLE_UTOKY = "add_quick_attacks";
const PRIDAT_SMART_REKRUT = "add_smart_recruit";
const POUZIT_ASYNC_REKRUT = "async_recruit";
const PRIDAT_SMART_MARKET = "add_smart_market";
const ZOBRAZIT_ZTRATY_V_UTOKU = "add_attack_losses";
const REFRESH_CLAN_URL = "/settings/changeClan/";
const REFRESH_MAIN = "/main.php?obnovit";
let configuration_showing = 0;
let active_clan_id = null;
if (GM_getValue(PRIDAT_SMART_MARKET, true) && location.href.indexOf("market") != -1) {
modifyMarket();
return;
}
if (location.href.indexOf("utok") != -1) {
if (GM_getValue(ZOBRAZIT_ZTRATY_V_UTOKU, true) == false) {
return;
}
let konec = document.getElementById("konec");
if (konec != null) {
let results_div = addBattleResult(document, false, false);
let br = document.createElement("br");
results_div.append(br);
br = document.createElement("br");
results_div.append(br);
konec.append(results_div);
}
return;
}
if (location.href.indexOf("settings") != -1) {
modifySettings();
return;
}
if (location.href.indexOf("klanarmy_vybaveni") != -1) {
processEquipment();
return;
}
if (GM_getValue(ZMENIT_MENU, true)) {
modifyMenu();
}
if (GM_getValue(PRIDAT_RYCHLE_UTOKY, true)) {
modifyBattles();
}
if (GM_getValue(PRIDAT_SMART_REKRUT, true) || GM_getValue(POUZIT_ASYNC_REKRUT, true)) {
modifyRecruit();
}
addConfigurationButton();
// FUNCTIONS
// MARKET
function modifyMarket() {
let elements = document.querySelectorAll("input[type='number']");
if (elements.length == 0) {
elements = document.querySelectorAll("input[type='text']");
}
for (let i = 0; i < elements.length; i++) {
let input = elements[i];
input.type = "text";
input.addEventListener("input", filterField);
input.form.addEventListener("submit", e => {
e.preventDefault();
processFinalValueOfInput(input);
if (Math.floor(parseInt(input.value)) == 0) {
input.value = null;
return;
}
input.form.submit();
});
}
}
// MARKET END
// RECRUIT
/**
* Modifies the buildings. Changes recruit from refreshing the page, to sending the form through XHR
*/
function modifyRecruit() {
// modify the form
for (let i = 0; i < document.forms.length; i++) {
const form = document.forms[i];
if (form.action.indexOf("changeClan") != -1) {
continue;
}
let input = form.bu_kolik;
if (input == null) {
console.log("Chybi input na pocet vybaveni");
continue;
}
if (GM_getValue(PRIDAT_SMART_REKRUT, true)) {
input.type = "text";
input.value = null;
input.addEventListener("input", filterField);
}
form.addEventListener("submit", e => {
e.preventDefault();
processFinalValueOfInput(input);
if (Math.floor(parseInt(input.value)) == 0) {
input.value = null;
return;
}
if (GM_getValue(POUZIT_ASYNC_REKRUT, true)) {
handleForm(form, recruitCallback);
input.value = null;
input.blur();
} else {
form.submit();
}
});
}
// if not using async recruit, skip modifying the anchors
if (GM_getValue(POUZIT_ASYNC_REKRUT, true) == false) {
return;
}
// modify the anchors
let elements = document.querySelectorAll(".recrut_btn a");
for (let i = 0; i < elements.length; i++) {
let element = elements[i];
let action = element.href;
element.href = "javascript:void(0)";
let form = element.parentElement.nextElementSibling;
element.addEventListener("click", e => {
httpPostAsync(action, null, recruitCallback, form);
});
}
}
function processFinalValueOfInput(input) {
input.value = input.value.toLowerCase().replace(",", ".").replace("k", "*1000").replace("m", "*1000000");
let values = input.value.split(/[*x]+/);
let value = 0;
if (values.length == 1) {
value = values[0];
} else {
value = 1;
for (let i = 0; i < values.length; i++) {
value *= parseFloat(values[i]);
}
}
input.value = value;
}
function filterField(e) {
let t = e.target;
let badValues = /[^0-9xkm,.*]/gi;
t.value = t.value.replace(badValues, '');
}
function recruitCallback(dom, form) {
let elements = dom.getElementsByClassName("odehraj-ostatni");
if (elements.length != 1) {
return;
}
let div = document.createElement("div");
div.innerText = elements[0].innerText;
let added_value = elements[0].innerText.split(" x");
added_value = added_value[0].substring(added_value[0].lastIndexOf(" "));
added_value = parseInt(added_value);
appendToOdtah(div);
updateMaterials(dom);
let values = form.parentElement.children[0].innerText.split(" ");
form.parentElement.children[0].innerText = "Celkem vybavení: " + (parseInt(values[values.length-1]) + added_value);
}
function updateMaterials(dom) {
let original_element = document.getElementsByClassName("klan-stats")[0];
let updated_element = dom.getElementsByClassName("klan-stats")[0];
original_element.parentElement.replaceChild(updated_element, original_element);
original_element = document.getElementsByClassName("klan-source")[0];
updated_element = dom.getElementsByClassName("klan-source")[0];
original_element.parentElement.replaceChild(updated_element, original_element);
let original_elements = document.querySelectorAll(".odehraj-budovy-akce .build .data p:NOT(.delimit)");
let updated_elements = dom.querySelectorAll(".odehraj-budovy-akce .build .data p:NOT(.delimit)");
for (let i = 0; i < original_elements.length; i++) {
original_elements[i].parentElement.replaceChild(updated_elements[i], original_elements[i]);
}
}
// RECRUIT END
// QUICK BATTLES
/**
* Adds a button for quick battles
*/
function modifyBattles() {
let links_node_list = document.querySelectorAll(".odehraj:NOT(.odehraj-budovy-akce, .odehraj-odtah, .odehraj-new-stavby, .odehraj-valka) a"); // prophet, new buildings and buildings
let links = Array.prototype.slice.call((links_node_list));
let defend_city_link = document.querySelector(".odehraj.odehraj-valka a:nth-of-type(2)"); // sieged city
if (defend_city_link != null) {
links.push(defend_city_link);
}
for (let i = 0; i < links.length; i++) {
addQuickBattle(links[i]);
}
}
function quickAttackRenderBattleResult(dom) {
let div = addBattleResult(dom, true, true);
if (div !== null) {
appendToOdtah(div);
}
}
/**
* Renders all of the battle results. The losses, new units, experience gained and rewards.
* @param HTMLDocument dom
*/
function addBattleResult(dom, show_rewards = true, show_exp = true) {
let div = document.createElement("div");
let br = document.createElement("br");
div.append(br);
if (show_rewards) {
let konec = dom.getElementById("konec");
if (konec == null) {
return null;
}
for (let i=1;; i++) {
konec = konec.nextSibling;
if (konec == null) {
break;
}
let element = konec.cloneNode(true);
div.append(element);
}
}
const results = processLosses(dom);
const losses_map = results[0];
const experience_element = results[1];
if (losses_map != null) {
renderLosses(losses_map, div);
}
if (experience_element != null && show_exp) {
div.append(experience_element);
}
return div;
}
/**
* Renders losses / gained units
* @param Map losses_map
* @param HTMLDivElement div
*/
function renderLosses(losses_map, div) {
let losses_div = document.createElement("div");
let reincarnated_div = document.createElement("div");
let gained_units = false;
let lost_units = false;
let p;
losses_map.forEach((count, unit) => {
p = document.createElement("p");
p.innerText = Math.abs(count) + " x " + unit;
if (count < 0) {
gained_units = true;
reincarnated_div.append(p);
} else {
losses_div.append(p);
lost_units = true;
}
});
if (lost_units == true) {
p = document.createElement("p");
p.style.color = "red";
p.innerText = "Ztráty";
losses_div.prepend(p);
div.append(losses_div);
}
if (gained_units) {
p = document.createElement("p");
p.style.color = "green";
p.innerText = "Získáno";
reincarnated_div.prepend(p);
div.append(reincarnated_div);
}
}
function appendToOdtah(div) {
let odehraj_odtah = document.getElementsByClassName("odehraj-odtah");
if (odehraj_odtah.length == 1) {
odehraj_odtah = odehraj_odtah[0];
odehraj_odtah.append(div);
}
}
function getUnitData(element) {
let unit_data = element.innerText.replaceAll(",", "").split(" x ");
if (unit_data.length == 1) {
return null;
}
let unit_name = unit_data[1].split(" (");
return [unit_data[0], unit_name[0]];
}
function processLosses(dom) {
let konec = dom.getElementById("konec");
let elements = konec.querySelectorAll(".utocnik p");
let ztraty_hodnota_text = elements[elements.length-1].innerText;
ztraty_hodnota_text = ztraty_hodnota_text.split(" / ");
ztraty_hodnota_text = ztraty_hodnota_text[0].split(": ")[1];
if (parseInt(ztraty_hodnota_text) == 0) {
return [null, null];
}
const units_map = new Map();
let losses_map = new Map();
// BEFORE BATTLE
let army_elements = dom.querySelectorAll(".armady p.utocnik");
for (let i = 0; i < army_elements.length; i++) {
let unit_data = getUnitData(army_elements[i]);
let original_count = units_map.get(unit_data[1]);
if (original_count == null) {
// in case more stacks of the same unit exist (general of old imperium)
original_count = 0;
}
units_map.set(unit_data[1], original_count + parseInt(unit_data[0]));
}
// AFTER BATTLE
let experience_element = null;
for (let i = 0; i < elements.length; i++) {
if (elements[i].children[0] == null || elements[i].children[0].className != 'privolane') {
let unit_data = getUnitData(elements[i]);
if (unit_data == null) {
if (elements[i].innerText.indexOf("Za tuhle slavnou bitvu ziskava velitel") != -1) {
experience_element = elements[i].cloneNode(true);
experience_element.style.color = "chartreuse";
}
continue;
}
let original_count = units_map.get(unit_data[1]);
units_map.delete(unit_data[1]);
if (original_count == null) {
// unit was not there at the beginning => added by necromancy
original_count = 0;
}
if ((original_count - unit_data[0]) != 0) {
let current_count = losses_map.get(unit_data[1]);
if (current_count == null) {
current_count = original_count;
}
current_count = current_count - parseInt(unit_data[0]);
if (current_count == 0) {
losses_map.delete(unit_data[1]);
} else {
if (unit_data[1].indexOf("velitel klanu") != -1) {
// Commander is dead -> add back to army
httpGetAsync("/clanarmy/addCommander", null);
losses_map.set("Velitel klanu se vrátil zpátky do armády", -1);
}
losses_map.set(unit_data[1], current_count);
}
}
}
}
if (losses_map.size == 0) {
losses_map = null;
}
return [losses_map, experience_element];
}
function attack(response) {
let responseText = response.responseText;
let dom = new DOMParser().parseFromString(responseText, "text/html");
handleForm(dom.forms[0], quickAttackRenderBattleResult);
}
function addQuickBattle(link) {
let anchor = newAnchor("(Rychle zaútočit)", "Provede útok okamžitě, bez otevírání oken");
addClickEventListener(anchor, link.href, attack);
link.parentElement.insertBefore(anchor, link.nextSibling);
}
function addClickEventListener(element, url, callback) {
element.addEventListener("click", e => {
httpGetAsync(url, callback, null);
});
}
// QUICK BATTLES END
// MENU
function modifyMenu() {
if (!GM_getValue(PRIDAT_DANE, true) && !GM_getValue(PRIDAT_DOBYT_MESTO, true) && !(GM_getValue(PRIDAT_NASTAVENI_PRODUKCE, true))) {
return;
}
let elements = document.getElementsByClassName("action-list");
if (elements == null) {
return;
}
let left_menu = elements[0];
let index = 0;
const sees_valka = left_menu.children[index].innerText == "Válka";
if (GM_getValue(PRIDAT_DANE, true) && sees_valka) {
index = addVatModificationToMenu(left_menu, index);
}
if (GM_getValue(PRIDAT_NASTAVENI_PRODUKCE, true)) {
index = addRecruitButtonsToMenu(left_menu, index);
}
if (GM_getValue(PRIDAT_DOBYT_MESTO, true) && sees_valka) {
let anchor = newAnchor("Dobýt město", "Okamžitě dobyje město bez nových oken");
addClickEventListener(anchor, "utok.php?utok=mesto&", attack);
let li = document.createElement("li");
li.append(anchor);
left_menu.insertBefore(li, left_menu.children[index++]);
}
}
// MENU (VAT)
function addVatModificationToMenu(left_menu, index) {
let li = document.createElement("li");
li.innerText = "Daně:";
left_menu.insertBefore(li, left_menu.children[index++]);
let vat_li = li;
li = document.createElement("li");
left_menu.insertBefore(li, left_menu.children[index++]);
let anchor = newAnchor("<<", "Snížit daně");
anchor.onclick = function() { changeValueAction("klanstats.php?dane=snizit", vat_li, "Daně (snížené):") };
li.append(anchor);
anchor = newAnchor(" --- ", "Normální daně");
anchor.onclick = function() { changeValueAction("klanstats.php?dane=normal", vat_li, "Daně (normální):") };
li.append(anchor);
anchor = newAnchor(">>", "Zvýšit daně");
anchor.onclick = function() { changeValueAction("klanstats.php?dane=zvysit", vat_li, "Daně (zvýšené):") };
li.append(anchor);
// separate Dane from other menu options
li = document.createElement("li");
li.classList.add("empty");
left_menu.insertBefore(li, left_menu.children[index++]);
return index;
}
// MENU (RECRUIT BUTTONS)
function addRecruitButtonsToMenu(left_menu, index) {
let li = document.createElement("li");
li.innerText = "Produkce jednotek:";
left_menu.insertBefore(li, left_menu.children[index++]);
let recruit_li = li;
li = document.createElement("li");
left_menu.insertBefore(li, left_menu.children[index++]);
let anchor = newAnchor("Stop", "Zastavit rekrut");
anchor.onclick = function() { changeValueAction("klanstats.php?produkce=stop", recruit_li, "Produkce jednotek (Zastavená):") };
li.append(anchor);
anchor = newAnchor("Start", "Povolit rekrut");
anchor.onclick = function() { changeValueAction("klanstats.php?produkce=start", recruit_li, "Produkce jednotek (Povolená):") };
li.append(anchor);
// separate recruit from other menu options
li = document.createElement("li");
li.classList.add("empty");
left_menu.insertBefore(li, left_menu.children[index++]);
return index;
}
// MENU END
// Menu actions
function changeValueAction(url, vat_li, text) {
httpGetAsync(url, valueChanged, {vat_li: vat_li, text : text});
}
function valueChanged(xmlHttp, params) {
params.vat_li.innerText = params.text;
}
// VAT END
// CONFIG
function addConfigurationButton() {
let elem = document.createElement("a");
elem.href = "javascript:void(0)";
elem.addEventListener("click", renderSettings);
elem.textContent = "Mód";
elem.title = "Nastavení modifikací LoI";
let target = document.getElementsByClassName("menu-right")[0];
target.insertBefore(elem, target.children[0]);
const width = target.children.length * 75;
target.style = "width: " + width + "px; background-size: 100% 70px;";
}
function renderSettings() {
if (configuration_showing == 2) {
configuration_showing = 1;
document.getElementById("mod_configuration").classList.add("hide");
return;
}
let div;
if (configuration_showing == 1) {
configuration_showing = 2;
document.getElementById("mod_configuration").classList.remove("hide");
return;
}
configuration_showing = 2;
div = document.createElement("div");
div.id = "mod_configuration";
let p = document.createElement("p");
p.innerText = "Změny se provedou po obnovení stránky. Po změnách v levém menu stiskněte \"Obnovit\"";
div.append(p);
div.append(document.createElement("br"));
p = document.createElement("p");
p.innerText = "Chytrý rekrut povoluje znaky x* v inputech. \nNapř. 1863*5 = 9315. \n";
div.append(p);
div.append(document.createElement("br"));
div.append(document.createElement("br"));
appendConfigurationInputToDiv(div, ZMENIT_MENU, "Povolit úpravy levého menu");
appendConfigurationInputToDiv(div, PRIDAT_DANE, "Přidat změnu daní do levého menu");
appendConfigurationInputToDiv(div, PRIDAT_NASTAVENI_PRODUKCE, "Přidat nastavení produkce (povolení / zastavení rekrutování)");
appendConfigurationInputToDiv(div, PRIDAT_DOBYT_MESTO, "Přidat dobytí města do levého menu");
appendConfigurationInputToDiv(div, PRIDAT_RYCHLE_UTOKY, "Povolit rychlé utoky u eventů");
appendConfigurationInputToDiv(div, PRIDAT_SMART_REKRUT, "Povolit chytrý rekrut - použití x* pro násobení, k = násobení tisícem, m = násobení miliónem a ',.' pro desetinná čísla.");
appendConfigurationInputToDiv(div, POUZIT_ASYNC_REKRUT, "Použít asynchroní rekrut - bez obnovení stránky");
appendConfigurationInputToDiv(div, PRIDAT_SMART_MARKET, "Použít chytré tržiště - použití x* pro násobení, k = násobení tisícem, m = násobení miliónem a ',.' pro desetinná čísla.");
appendConfigurationInputToDiv(div, ZOBRAZIT_ZTRATY_V_UTOKU, "Zobrazit přehled ztrát (a získaných jednotek) v normalních útocích.");
appendToOdtah(div);
}
function appendConfigurationInputToDiv(div, input_id, label_text) {
let input = document.createElement("input");
input.id = input_id;
input.type = "checkbox";
input.style.verticalAlign = "middle";
input.checked = GM_getValue(input_id, true);
input.addEventListener("change", e => {
GM_setValue(input.id, input.checked);
});
let label = document.createElement("label");
label.htmlFor = input.id;
label.innerText = label_text;
label.style.marginLeft = "5px";
let container = document.createElement("div");
container.append(input);
container.append(label);
div.append(container);
}
// CONFIG END
// SETTINGS
function modifySettings() {
let headers = document.getElementsByTagName("h3");
if (headers.length == 0) {
return;
}
// add refresh button
let refresh_button = document.createElement("a");
refresh_button.href = "javascript:void(0)";
refresh_button.addEventListener("click", refreshClans);
refresh_button.textContent = "Obnovit tahy";
refresh_button.title = "Obnoví počet tahů u klanů";
refresh_button.classList.add("button");
headers[0].parentElement.insertBefore(refresh_button, headers[0].nextSibling);
}
function refreshClans() {
let clan_id_elements = document.querySelectorAll("td.t-c");
let active_clan_id_element = document.querySelector(".active td.t-c");
active_clan_id = active_clan_id_element.innerText;
let clan_ids = [];
for (let i = 0; i < clan_id_elements.length; i++) {
clan_ids.push(clan_id_elements[i].innerText);
}
// settings, refresh turns for all clans, set active the clan that was active
refreshNextClan(null, clan_ids);
}
function refreshNextClan(xmlHttp, clan_ids) {
if (clan_ids == null || clan_ids.length == 0) {
if (active_clan_id != null) {
httpGetAsync(REFRESH_CLAN_URL + active_clan_id, refreshMain, null);
active_clan_id = null;
} else {
location.reload();
}
return;
}
let clan_id = clan_ids[0];
clan_ids.shift();
httpGetAsync(REFRESH_CLAN_URL + clan_id, refreshMain, clan_ids);
}
function refreshMain(xmlHttp, clan_ids) {
httpGetAsync(REFRESH_MAIN, refreshNextClan, clan_ids);
}
//
// EQUIPMENT
function processEquipment() {
let table = document.getElementsByClassName("equip-items-table")[1];
if (table == null) {
console.log("missing table");
return;
}
if (document.forms[0] == null) {
console.log("missing form");
return;
}
let units_with_equipment = [];
for (let i = 0; i < table.rows.length; i++) {
if (table.rows[i].children[1].innerText != "žádné") {
units_with_equipment.push(table.rows[i].children[0].innerText);
}
}
for (let i = 0; i < document.forms[0].jednotka.options.length; i++) {
let option = document.forms[0].jednotka.options[i];
if (units_with_equipment.includes(option.innerText.trim())) {
option.classList.add("hide");
}
}
}
// EQUIPMENT END
// GENERAL FUNCTIONS
function newAnchor(text, title) {
let element = document.createElement("a");
element.style.marginLeft = "5px";
element.innerText = text;
element.href = "javascript:void(0)";
if (title != null) {
element.title = title;
}
return element;
}
function handleForm(form, func) {
let data = new FormData(form);
let params = new URLSearchParams(data).toString();
httpPostAsync(form.action, params, func, form);
}
function httpPostAsync(theUrl, params, callback, form) {
let xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = function() {
if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
let responseText = xmlHttp.responseText;
let dom = new DOMParser().parseFromString(responseText, "text/html");
callback(dom, form);
}
}
xmlHttp.open("POST", theUrl, true); // true for asynchronous
xmlHttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xmlHttp.send(params);
}
function httpGetAsync(theUrl, callback, params) {
let xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = function() {
if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
if (callback != null) {
callback(xmlHttp, params);
}
}
}
xmlHttp.open("GET", theUrl, true); // true for asynchronous
xmlHttp.send(null);
}
// GENERAL FUNCTIONS END