// ==UserScript==
// @name Kronos Utils
// @author Merwin
// @email merwinkronos@gmail.com
// @namespace Kronos
// @description Divers petits utilitaires.
// @include http://ikariam.tld/
// @include http://s*.ikariam.*/*
// @exclude http://*.ikariam.*/index.php?view=renameCity*
// ==/UserScript==
/*-------------------------------------
Propriétés du script
--------------------------------------*/
var DEBUT = new Date();
// En fonction du language du naviguateur on va utiliser un langage associé.
var language = 0, finished = 1, langUsed = 11, execTime = 12, wood = 14;
var researching = 16, shown = 17, full = 19, monthshort = 20, empty = 21;
var startExpand = 22, enqueue = 23, shiftClick = 24, shop = 25, left = 26;
var unreplenished = 27, popupInfo = 28;
var langs = {
"fr": ["Français", " Fini à ", "Fermer", "Upgrader plus tard.",
"File de construction", "Ajouter un bâtiment.", "Construire dans",
"heures", "minutes et", "secondes",
"valider", "Langue utilisée", "Temps d'exécution",
"Pas de bâtiment en attente.", "Bois", "Luxe",
"Recherches", "Visible", "Invisible", "plein: ",
"JanFévMarAvrMaiJunJuiAoûSepOctNovDéc", "vide: ",
"; commencer avant que ", "Enqueue",
"Shift-clique, peut-être?",
"Acheter ça, s'il vous plaît", "Même available après ",
"Il faut attendre pour ces ressources",
"Clique pour bàtiment information"],
"en": ["English", " Finished ", "Close", "Upgrade later.",
"Building list", "Add building.", "Build at",
"hours", "minutes and", "seconds",
"confirm", "Language used", "Time of execution",
"No building in waiting.", "Wood", "Luxe",
"Researching", "Shown", "Hidden", "full: ",
"JanFebMarAprMayJunJulAugSepOctNovDec", "empty: ",
"; start expanding before ", "Enqueue",
"Shift click to put at the head of the queue",
"Shopping list", "Resources left ",
"Resources unavailable by build time (and replenish time)",
"Click for building info, use scroll wheel to browse levels"],
// By pusteblume:
"de": ["Deutsch", " Beendet ", "Schliessen", "Später upgraden.",
"Bauliste", "Gebäude hinzufügen.", "Fertiggestellt in",
"Stunden", "Minuten und", "Sekunden",
"bestätigen", "Gewählte Sprache", "benötigte Zeit zum Ausführen",
"Kein Gebäude in der Warteschleife.", "Holz", "Luxusgut",
"aktuelle Forschung", "Sichtbar", "Versteckt", "voll: ",
"JanFebMärAprMaiJunJulAugSepOktNovDez", "leer: ",
"; beginne den Ausbau vor ", "Einreihen",
"Shift Klick um es an den Anfang der Warteschleife zu platzieren",
"Einkaufsliste", "verbliebene Ressourcen ",
"Ressourcen nicht erreichbar während der Bauzeit "+
"(und Regenerieungszeit)",
"Klicken um Gebäudeinfo zu erhalten, benutze das Mausrad um die "+
"Stufen zu sehen"],
// By Tico:
"pt": ["Portuguès", " acaba às ", "Fechar", "Evoluir mais tarde.",
"Lista de construção", "Adicionar edificio.", "Construir em",
"horas", "minutos e", "segundos",
"confirmar", "Lingua usada", "Tempo de execução",
"Nenhum Edificio em espera.", "Madeira", "Luxo"],
"da": ["Dansk", " Færdig kl. ", "Luk", "Opgrader senere.",
"Bygnings liste", "Tilføj bygning.", "Byg kl.",
"timer", "minutter og", "sekunder",
"bekræft", "Sprog brugt", "Udførelsestid",
"Ingen bygning venter.", "Træ", "Luxe"],
// By A.Rosemary:
"es": ["Espagnol", " termina a las ", "Cerrar", "Actualizar más tarde.",
"Lista de construcción", "Añadir edificio.", "Construir en",
"horas", "minutos y", "segundos",
"confirmar", "Idioma usado", "Tiempo de ejecución",
"Nenhum Edificio em espera.", "Madera", "Luxe",
"Investigación"],
"sv": ["Svenska", " Färdigt ", "Stäng", "Uppgradera senare",
"Byggnadslista", "Lägg till byggnad", "Bygg klockan",
"timmar", "minuter och", "sekunder",
"bekräfta", "Språk", "Exekveringstid",
"Inga byggnader väntar.", "Trä", "Lyx",
"Forskning", "Visas", "Gömda", "fullt: ",
"janfebmaraprmajjunjulaugsepoktnovdec", "tomt: ",
"; börja bygg ut före ", "Köa upp",
"Shift-klicka för att lägga först i kön",
"Inköpslista", "Resurser kvar efter ",
"Resurser som kommer saknas vid byggstart, och inskaffningstid",
"Klicka för byggnadsinfo, använd scrollhjulet för andra nivåer"],
// By Sisel:
"cs": ["Czech", " Dokončeno ", "Zavřít", "Rozšířit později.",
"Stavební seznam", "Přidat budovu.", "Postavit na",
"hodiny", "minuty a", "sekundy",
"potvrdit", "Použitý jazyk", "Čas dokončení",
"Žádné budovy ve výstavbě.", "Dřevo", "Luxusní",
"Zkoumání", "Ukázat", "Skrytý", "Plno: ",
"LedÚnoBřeDubKvěČerČecSrpZařŘíjLisPro",
"; začít expandovat dříve ", "Do fronty",
"Shift+kliknuti pro přidání na začátek fronty",
"Nákupní seznam", "Zbylé suroviny ",
"Nedostatek surovin ve stavebním čase (i obnovovacím čase)",
"Klikni pro info o budově, použij scrolovací kolečko pro " +
"prohlížení levelů"],
// By drivex:
"pl": ["Polski", "Koniec o: ", "Zamknij", "Unowocześnij później.",
"Lista budynków", "Dodaj budynek.", "Wybuduj o",
"godzin", "minut i", "sekund",
"zatwierdź", "Wybrany język", "Czas wykonania",
"Brak dalszych budynków.", "Drewno", "Luxusowy",
"Aktualne badanie", "Pokaż", "Ukryty", "pełny: ",
"JanFebMarAprMayJunJulAugSepOctNovDec", "pusty: ",
"; zacznij kolonizować przed ", "Dodaj do kolejki",
"Shift+Klik na budynku aby dodać go na samym początku",
"Surowce, które należy kupić by natychmiast rozpocząć budowe",
"Pozostanie surowców: ", "Surowce niedostępne",
"Kliknij aby dowiedzieć się więcej lub zacznij kręcić rolką aby " +
"dowiedzieć się o kosztach następnych poziomów"],
// By n00bster:
"ro" : ["Romana", " > ", "Inchide", "Construieste mai tarziu.",
"Lista cladiri", "Adauga cladirea.", "Construit la",
"ore", "minute si", "secunde",
"confirma", "Limba", "Timp",
"Nicio cladire asteptand.", "Lemn", "Lux",
"Cercetand", "Arada", "Ascuns", "Plin: ",
"IanFebMarAprMaiJunJulAugSepOctNovDec", "gol: ",
"; Incepe expansiunea inainte ", "Pune la rand",
"Shift click pentru a pune in fata sirului",
"Lista de cumparaturi", "Resurse ramase ",
"Resurse indisponibile la timpul construictiei",
"Click pentru informatiile cladiri, foloseste scroolul sa cauti " +
"levelele"],
};
var lang;
function nth(n) {
var th = [, "st", "nd", "rd"];
return n + (th[n] || "th");
}
var name = "Kronos";
var version = " 0.5";
/*-------------------------------------
Création de div, br, link etc...
-------------------------------------*/
function url(query) {
return (location.search || "").replace(/(\?.*)?$/, query||"");
}
function jsVariable(nameValue) {
var resourceScript = $X('id("cityResources")//script');
if (resourceScript) {
var text = resourceScript.innerHTML;
text = text.substr(text.indexOf(nameValue+" = "),
text.length);
text = text.substr(nameValue.length+3,
text.indexOf(";")-(nameValue.length+3));
return text;
}
}
function luxuryType(type) {
var script = $X('id("cityResources")/script').textContent.replace(/\s+/g," ");
var what = script.match(/currTradegood.*?value_([^\x22\x27]+)/)[1];
switch (type) {
case undefined:
case 0: return resourceIDs[what];
case "name":
case 1: return what;
case "glass": return what.replace("crystal", "glass");
case "english":
case 2:
what = $X('id("value_'+ what +'")/preceding-sibling::span');
return what.textContent.replace(/:.*/, "");
}
}
// on récupére une des valeurs get d'une url(son nom est le param.
function urlParse(param, url) {
if (!url) url = location.search || ""; // On récupére l'url du site.
if (!url && param == "view") {
var view = document.body.id;
if (view) return view;
}
var keys = {};
url.replace(/([^=&?]+)=([^&]*)/g, function(m, key, value) {
keys[decodeURIComponent(key)] = decodeURIComponent(value);
});
return param ? keys[param] : keys;
}
var expandos = { id: 1, className: 1, title: 1, type: 1, checked: 1 };
function node(opt) {
function attr(name) {
var value = opt[name];
delete opt[name];
return value;
}
var id = opt.id;
var n = $(id);
if (!n) {
n = document.createElement(attr("tag") || "div");
var after = attr("after");
var before = opt.prepend ? opt.prepend.firstChild : attr("before");
var parent = attr("prepend") || attr("append") ||
(before || after || {}).parentNode;
if (parent) {
if (before)
parent.insertBefore(n, before);
else if (after)
parent.insertBefore(n, after.nextSibling);
else
parent.appendChild(n);
}
if (id) n.id = id;
}
var html = attr("html");
if (isDefined(html)) n.innerHTML = html;
var text = attr("text");
if (isDefined(text)) n.textContent = text;
var style = attr("style");
if (style)
for (var prop in style)
n.style[prop] = style[prop];
for (prop in opt)
if (expandos[prop])
n[prop] = opt[prop];
else
n.setAttribute(prop, opt[prop]+"");
return n;
}
function createLink(nom, href) {
var lien = document.createElement('a');//création d'un lien
lien.setAttribute('href', href);//On ajoute le href
lien.appendChild(document.createTextNode(nom));//On ajoute le text.
return lien;
}
function goto(href) {
//console.log("aborted goto %x", href); return;
location.href = href.match(/\?/) ? href : urlTo(href);
}
function gotoCity(url, id) {
// console.log("aborted gotoCity(%x, %x)", url, id); return;
var city = $("citySelect");
var ids = cityIDs();
if (isDefined(id)) {
city.selectedIndex = ids.indexOf(id);
} else {
var index = referenceCityID("index");
city.selectedIndex = index;
id = ids[index];
}
var form = city.form;
if (isDefined(url)) form.action = url;
form.elements.namedItem("oldView").value = "city";
form.elements.namedItem("id").value = id;
form.submit()
}
function css(rules, disabled) {
var head = $X('/html/head');
var style = document.createElement("style");
style.type = "text/css";
style.textContent = isString(rules) ? rules : rules.toString();
if (isBoolean(disabled))
style.disabled = disabled;
return head.appendChild(style);
}
function addCSSBubbles() { css(<>>); }
// Military stuff:
function militaryAdvisorMilitaryMovementsView() {
function project(div) {
var li = $X('ancestor::li', div)
projectCompletion(div);
li.style.height = "52px";
}
$x('//li/div/div[contains(@id,"CountDown")]').forEach(project);
}
// returns a function that only runs expensive function fn after no call to it
// has been made for n ms, or 100, if not given, or the time fn took last time,
// if it has been run at least once and no n was given.
function expensive(fn, n) {
function run() {
fn.timeout = null;
var time = n || new Date;
fn();
if (!n) duration = (new Date) - time;
}
var duration;
return function() {
if (fn.timeout)
clearTimeout(fn.timeout);
fn.timeout = setTimeout(run, n || duration || 100);
};
}
function makeLootTable(table, reports) {
function changedFilters(e) {
function filterRow(tr) {
var values = $x('td[position() > 3 and position() < 12]', tr).map(value);
var show = visiblep.apply(this, values);
if (0 && !zzz++) {
console.log(unsafeWindow.names = names);
console.log(unsafeWindow.values = values);
console.log(show);
}
tr.className = tr.className.replace(/( not-filtered)?$/, show ?
" not-filtered" : "");
}
function value(td, n) {
var value, time = 6 == n;
if (time)
value = td.getAttribute("time");
else
value = td.firstChild;
return integer(value || "0");
return !value && n > 5 ? -Infinity : value;
}
var tests = [], names = [];
for (var i = 0; i < filters.length; i++) {
var f = filters[i];
var time = "vT" == f.id;
var bash = "vbash" == f.id;
var v = time ? parseTime(f.value) : integer(f.value || "0");
if (!v && (time || bash)) v = "Infinity";
var n = f.id.replace(/^v/, "");
var op = $("op"+ n).textContent == "≤" ? "<=" : ">=";
var check = $(f.id.slice(1));
if (check) {
if (v) {
if (!check.checked)
check.setAttribute("auto", "yep");
check.checked = true;
} else if (check.getAttribute("auto")) {
check.checked = false;
check.removeAttribute("auto");
}
}
if (!v) f.value = "";
var expr = "("+ n + op + v +")";
tests.push(expr);
names.push(n);
}
//console.log("return "+ tests.join(" && ") +";");
var visiblep = new Function(names, "return "+ tests.join(" && ") +";");
$x('tr[starts-with(@class,"loot")]', body).forEach(filterRow);
}
function listen(input) {
input.addEventListener("click", expensive(changedFilters), false);
input.addEventListener("keyup", function(e) { click(e.target); }, false);
}
function filterView(e) {
if (e) {
var node = e.target;
var text = $("v"+ node.id);
if (text) {
if (node.checked) {
if (!text.value) {
text.value = "1";
text.setAttribute("auto", "yep");
var changed = true;
}
} else {
if (text.getAttribute("auto")) {
text.removeAttribute("auto");
text.value = "";
changed = true;
}
}
if (changed)
changedFilters();
}
}
var visible = show.filter(function(x) { return x.checked; });
//if ((hide.disabled = 0 == visible.length)) return;
hide.textContent = hideMost + "#loot-report tr.loot." +
pluck(visible, "id").join(".") + " { display: table-row; }";
}
function filter(check) {
return function() { click(check); };
}
function sort(col, key) {
function move(junk, i, all) {
var pos = keys[i] % all.length;
buffer.insertBefore(tr[pos], buffer.firstChild);
}
var td = $x('tr[starts-with(@class,"loot")]/td['+ (col+1) +']', body);
if (!td.length) return;
var tr = pluck(td, "parentNode");
var keys = td.map(key);
var direction = col == 9 ?
function descending(a, b) { return a < b ? 1 : -1; } :
function ascending(a, b) { return a > b ? 1 : -1; };
keys.sort(direction);
var last = tr[tr.length-1].nextSibling;
var buffer = document.createDocumentFragment();
tr.forEach(move);
body.insertBefore(buffer, last);
}
function sortByCity() {
function key(td, i, all) {
var a = $X('a[2]', td);
var id = a ? integer(urlParse("selectCity", a.search)) : 0;
return id * all.length + i;
}
sort(11, key);
}
function sortByWhen() {
function key(td, i, all) {
var M, D, h, m;
[D, M, h, m] = trim(td.textContent).split(/\D+/g);
return integer([M, D, h, m].join("")) * all.length + i;
}
sort(2, key);
}
function sortByDistance() {
function key(td, i, all) {
var t = td.getAttribute("time") || 1000000;
return t * all.length + i;
}
sort(9, key);
}
function sortByLoot(col) {
return function(e) {
function key(td, i, all) {
var value = integer(td.firstChild || 0);
return value * all.length + i;
}
sort(col, key);
};
}
function showLoot(report) {
var tr = report.tr;
delete report.tr;
var loot = report.l;
var has = ["loot"];
for (var c = 3; c < cols.length; c++) {
var td = tr.insertCell(c);
var r = cols[c];
if ("T" == r && report.c) {
var t = travelTime(report.c);
if (t) {
td.setAttribute("time", Math.round(t)+"");
td.innerHTML = secsToDHMS(t, 1);
td.className = "time";
}
continue;
}
if ("#" == r) {
var wonToday = hits[report.c] || ""
td.innerHTML = wonToday;
if (wonToday > 5)
td.className = "warn"; // bash alert!
}
if (!loot || !loot[r]) continue;
td.className = "number";
var got = {}; got[r] = loot[r];
td.innerHTML = visualResources(got, { size: 0.5 });
has.push(r);
}
tr.className = (tr.className||"").replace(/^.*( non-filtered)?$/,
has.join(" ") + "$1");
}
table.id = "loot-report";
var override = "#container #mainview #troopsOverview #finishedReports ";
var hideMost = "#loot-report tr.loot { display:none; } " + override +
"#loot-report tr.loot.not-filtered { display: table-row }";
var hide = css("", true);
var body = $X('tbody', table);
var head = body.insertRow(0);
var only = body.insertRow(0);
var cols = [, , , "g", "w", "W", "M", "C", "S", "T", "#"];
var hits = {}; // indexed on city id, values are attacks today
var show = [];
var title = [, , "When",
"$gold", "$wood", "$wine", "$marble", "$glass", "$sulfur",
"Time", "#", "City"];
for (var i = 0; i < 13; i++) {
var r = cols[i];
var t = title[i] || "";
var th = node({ className: r ? "Time" == t ? "": "number" : "",
tag: i && i < 12 ? "th" : "td",
html: visualResources(t),
append: head });
if (1 == i) th.style.minWidth = "25px";
if (2 == i) th.style.minWidth = "68px";
if (11 == i) th.style.width = "400px";
if (r) { // only show filter for cols with relevant data
var id = "#" == r ? "bash" : r;
var op = config.getCity("report.op."+ r, /[T#]/.test(r) ? "≤" : "≥");
var val = config.getCity("report.val."+ r, "");
var html = <>{op}>;
var filter = node({ tag: "th", className: "filter", html: html,
append: only });
only[r] = $X('input', filter);
} else {
only.insertCell(i);
}
if ("When" == t) clickTo(th, sortByWhen);
if ("City" == t) clickTo(th, sortByCity);
if ("Time" == t) { clickTo(th, sortByDistance); continue; }
if ("#" == t || !r) continue;
var check = node({ tag: "input", type: "checkbox", id: r, prepend: th });
show.push(check);
//var img = $X('img', th);
clickTo(th, sortByLoot(i), 'not(self::input)');
//clickTo(check, filterView); -- (preventDefault:s)
check.addEventListener("click", filterView, false);
dblClickTo(th, filter(check), "", true);
}
var filters = cols.filter(I).map(function(r) { return only[r]; });
scrollWheelable(filters);
filters.forEach(listen);
var yesterday = Date.now() - (25 * 36e5);
for (var i = 0; r = reports[i]; i++) {
var recent = r.t > yesterday;
if (recent && r.w && r.c)
hits[r.c] = 1 + (hits[r.c] || 0);
}
reports.forEach(showLoot);
unsafeWindow.markAll = safeMarkAll;
changedFilters();
filterView();
// need to restow these a bit not to break the layout:
var selection = $X('tr[last()]/td[@class="selection"]', body);
var go = $X('tr[last()]/td[@class="go"]', body);
go.parentNode.removeChild(go);
selection.innerHTML += go.innerHTML;
selection.setAttribute("colspan", "7");
selection.className += " go";
go = $X('input[@type="submit"]', selection);
go.style.marginLeft = "6px";
}
function safeMarkAll(cmd) {
//console.log("safe %x!", cmd);
var boxes = $x('id("finishedReports")//input[@type="checkbox" and not(@id)]');
for (var i = 0; i < boxes.length; i++) {
var box = boxes[i], tr = $X('ancestor::tr[1]', box);
if ("none" != getComputedStyle(tr, "").display) {
if ("checked" == cmd) box.checked = true;
if ("reverse" == cmd) box.checked = !box.checked;
}
}
}
function copy(object) {
// Doug Crockford
var fn = function() {};
fn.prototype = object;
return new fn;
}
function researchAdvisorView() {
function learnTech(a) {
var name = a.textContent.match(/['"]([^'"]+)['"]/);
if (!name) return;
name = name[1];
var tech = techs.filter(function(t) { return t.name == name; });
if (!tech.length) return;
tech = tech[0];
config.setServer("tech"+ tech.id, 1);
a.title = a.textContent;
a.href = urlTo("research", tech.id);
a.innerHTML = name.bold() +" "+
tech.does[0].toLowerCase() + tech.does.slice(1);
}
updateCurrentResearch();
var techs = techinfo();
$x('id("inboxResearch")/tbody/tr/td[@class="text"]/a').forEach(learnTech);
}
function diplomacyAdvisorView() {
function span(td) { td.setAttribute("colspan", "8"); }
function showIslandInfo(a) {
var td = a.parentNode, x, y, t, id = urlParse("id", a.search);
var t = /\[(\d+):(\d+)\]$/.exec(trim(a.textContent || "")) || "";
if (t) {
[t, x, y] = t;
t = travelTime(x, y);
t = t && secsToDHMS(t);
}
node({ tag: "td", className: "tt", text: t, before: td });
var r = config.getIsle("r", "x", id||0);
node({ tag: "td", className: "tradegood " + r, after: td });
}
//[contains(translate(.,"0123456789:",""),"[]")]').
var body = $X('id("messages")//tbody[tr[count(th) = 6]]');
var date = $X('tr[1]/th[6]', body);
var town = $X('tr[1]/th[5]', body);
node({ tag: "th", className: "tradegood", before: date });
node({ tag: "th", className: "tt", text: "Travel Time", before: town }); // I18N
$x('tr/td[5]/a', body).forEach(showIslandInfo);
$x('tr/td[@colspan="6"]', body).forEach(span);
}
function militaryAdvisorCombatReportsView() {
function parseDate(t) {
var Y, M, D, h, m;
if ((t = t && trim(t.textContent).split(/\D+/))) {
[D, M, h, m] = t.map(integer);
Y = (new Date).getFullYear();
return (new Date(Y, M - 1, D, h, m)).getTime();
}
}
function fileReport(tr, n) {
var a = $X('td[contains(@class,"subject")]/a', tr);
var w = $X('contains(../@class,"won")', a);
var r = parseInt(urlParse("combatId", a.search));
var d = $X('td[@class="date"]', tr);
var t = parseDate(d);
repId[n] = r;
if (!allreps[r]) {
w ? history.won++ : history.lost++;
newreps[r] = { t: t, w: 0 + w };
allreps[r] = newreps[r];
}
rows[n] = copy(allreps[r]);
rows[n].tr = tr;
}
var table = $X('id("finishedReports")/table[@class="operations"]');
if (!table) return;
var history = eval(config.getServer("war", "({ won: 0, lost: 0 })"));
var allreps = eval(config.getServer("reports", "({})"));
var reports = $x('tbody/tr[td[contains(@class,"subject")]]', table);
var newreps = {};
var cities = {};
var repId = [];
var rows = [];
reports.forEach(fileReport);
var city = config.getServer("cities", {});
for (var i = reports.length; --i >= 0;) {
var a = $X('.//a', reports[i]);
var r = allreps[repId[i]];
var recent = r.t > Date.now() - (25 * 36e5);
// we won, we don't know what city, it's the past 24h (+ DST safety margin)
if (r.w && !r.c && recent) {
a.style.fontStyle = "italic"; // Warn about it! Read that report, please.
a.innerHTML = "?: "+ a.innerHTML;
}
if (r.c) {
if (recent)
var c = cities[r.c] = 1 + (cities[r.c] || 0);
var name = city[r.c].n;
var text = a.textContent;
text = text.slice(0, text.lastIndexOf(name));
a.textContent = text;
var island = linkTo(urlTo("island", { island: city[r.c].i, city: r.c }),
null, null, { text: name });
a.parentNode.appendChild(island);
}
}
var header = $X('id("troopsOverview")/div/h3');
var loot = node({ tag: "a", text: "Show loot table", // I18N
style: { marginLeft: "8px" }, append: header });
clickTo(loot, function() { rm(loot); makeLootTable(table, rows); });
config.setServer("war", history);
config.setServer("reports", allreps);
//console.log(history.toSource());
//console.log(allreps.toSource());
}
function militaryAdvisorReportViewView() {
var loot = parseResources('//td[@class="winner"]/ul[@class="resources"]/li');
var a = $X('id("battleReportDetail")//a');
var cities = config.getServer("cities", {});
var city = parseInt(urlParse("selectCity", a.search));
var island = parseInt(urlParse("id", a.search));
var reports = config.getServer("reports", {});
var r = urlParse("combatId");
var report = reports[r];
if (report) {
if (loot) report.l = loot;
report.c = city;
}
if (!cities.hasOwnProperty(city))
cities[city] = {};
var c = cities[city];
c.n = a.textContent;
c.i = island;
config.setServer("cities", cities);
config.setServer("reports", reports);
//console.log(cities.toSource());
//console.log(reports[r].toSource());
}
function plunderView() {
scrollWheelable();
dontSubmitZero(2, 'id("selectArmy")//input[@type="submit"]');
}
function add(fmt) {
for (var i = 1; i < arguments.length; i++) {
var id = arguments[i];
xpath[id] = fmt.replace("%s", id);
}
}
var xpath = {
ship: 'id("globalResources")/ul/li[@class="transporters"]/a',
citynames: 'id("changeCityForm")//ul[contains(@class,"optionList")]/li'
};
add('id("value_%s")', "wood", "wine", "marble", "crystal", "sulfur");
function get(what, context) {
var many = { citynames: 1 };
var func = many[what] ? $x : $X;
return what in xpath ? func(xpath[what], context) : undefined;
}
var resourceIDs = {
wood: "w", wine: "W", marble: "M", glass: "C", crystal: "C", sulfur: "S",
gold: "g", time: "t", inhabitants: "p", maxActionPoints: "a"
};
function currentResources() {
var inhab = $("value_inhabitants").textContent.split(/\s+/);
return {
p: getFreeWorkers(), P: getPopulation(),
g: integer($("value_gold")), w: integer($("value_wood")),
W: integer($("value_wine")), M: integer($("value_marble")),
C: integer($("value_crystal")), S: integer($("value_sulfur"))
};
}
function addResources(a, b, onlyIterateA) {
return opResources(a, b, function(a, b) { return a + b; }, onlyIterateA);
}
function subResources(a, b, onlyIterateA) {
return opResources(a, b, function(a, b) { return a - b; }, onlyIterateA);
}
function mulResources(a, n, op) {
return opResources(a, n, function(a) { return (op||Math.round)(a * n); }, 1);
}
function opResources(a, b, op, onlyIterateA) {
//console.log("a: %x, b: %x", a?a.toSource():a, isObject(b)?b.toSource():b);
//console.log(op.toSource());
var o = {}, r, A, B;
for (r in a) {
if (isNumber(A = a[r]) && (!isObject(b) || isNumber(B = b[r] || 0)))
o[r] = op(A, B);
}
//if (onlyIterateA) console.log("o: %x", o?o.toSource():o);
if (onlyIterateA) return o;
for (r in b) {
if (o.hasOwnProperty(r)) continue;
if (isNumber(A = a[r] || 0) && isNumber(B = b[r] || 0))
o[r] = op(A, B);
}
//console.log("o: %x", o?o.toSource():o);
return o;
}
function parseResources(res) {
if (isString(res))
res = $x(res);
var o = {}, r, id;
if (res.length)
for (var i = 0; i < res.length; i++) {
r = res[i];
id = resourceIDs[r.className.split(" ")[0]];
o[id] = integer(r);
}
else
return null;
return o;
}
function haveResources(needs) {
var have = currentResources();
for (var r in needs)
if (needs[r] > (have[r] || 0))
return false;
return true;
}
function reapingPace() {
var pace = reapingPace.pace;
if (!pace) {
// FIXME: This just gives city income; ought to do a pace.G for totals too
var preciseCityIncome = $("valueWorkCosts") ||
$X('//li[contains(@class,"incomegold")]/span[@class="value"]');
var sciCost = 8 - config.getServer("tech3110", 0); // Letter chute bonus
var gold = preciseCityIncome ? integer(preciseCityIncome) :
getFreeWorkers() * 4 - sciCost * config.getCity("researchers", 0);
reapingPace.pace = pace = {
g: gold,
w: secondsToHours(jsVariable("startResourcesDelta"))
};
pace[luxuryType()] = secondsToHours(jsVariable("startTradegoodDelta"));
var wineUse = config.getCity("wine", 0);
if (wineUse)
pace.W = (pace.W || 0) - wineUse;
}
return pace;
}
var buildingIDs = {
townHall: 0, townhall: 0, port: 3, academy: 4, shipyard: 5, barracks: 6,
warehouse: 7, wall: 8, tavern: 9, museum: 10, palace: 11, embassy: 12,
branchOffice:13, "workshop-army": 15, "workshop-fleet": 15, safehouse: 16,
palaceColony: 17
};
function buildingClass(id) {
id = buildingID(id);
for (var name in buildingIDs)
if (buildingIDs[name] == id)
return name;
}
function buildingID(a) {
if (isNumber(a)) return a;
var building = isString(a) ? a : a.parentNode.className;
return buildingIDs[building];
}
function haveBuilding(b) {
return buildingLevel(b, 0) && ("-" != buildingPosition(b, "-"));
}
function buildingPosition(b, otherwise) {
var p = config.getCity("posbldg"+ buildingID(b), "?");
return "?" == p ? otherwise : p;
}
function buildingLevel(b, otherwise, saved) {
b = buildingID(b);
if (!saved && "city" == urlParse("view")) {
var div = $("position" + buildingPosition(b));
var a = $X('a[@title]', div);
if (a && !integer(a.title))
a = null; // a ghost house we set up to visualize the queue
}
if (saved || !a)
b = config.getCity("building"+ b, "?");
else
b = number(a.title);
return "?" == b ? otherwise : b;
}
function buildingLevels() {
var levels = {};
for (var name in buildingIDs) {
var id = buildingIDs[name];
var level = config.getCity("building"+ id, 0);
if (level)
levels[id] = level;
}
return levels;
}
var buildingCapacities = {
townHall:
[, 60, 96, 143, 200, 263, 333, 410, 492, 580, 672, 769, 871, 977, 1087,
1201, 1320, 1441, 1567, 1696, 1828, 1964, 2103, 2246, 2391, 2691, 2845,
3003, 3163, 3326, 3492, 3360],
academy:
[0, 8, 12, 16, 22, 28, 35, 43, 51, 60, 69, 79, 89, 100, 111, 122, 134, 146,
159, 172, 185, 198, 212, 227, 241],
tavern:
[0, 3, 5, 8, 11, 14, 17, 21, 25, 29, 33, 38, 42, 47, 52, 57, 63, 68, 73, 79,
85, 91, 97, 103, 109],
port:
[ 3, 10, 30, 58, 92, 131, 176, 225, 279, 336, 398, 464,
533, 606, 682, 762, 844, 931, 1020, 1112, 1207, 1305, 1406, 1509,
1616],
warehouse: {
wood: [ 100, 140, 190, 240, 310, 380, 470, 560, 670, 790, 930,
1090, 1260, 1450, 1670, 1910, 2180],
rest: [ 50, 70, 90, 120, 150, 190, 230, 280, 330, 390, 460,
540, 630, 720, 830, 950, 1090]
}
}
function buildingCapacity(b, l, warehouse) {
b = buildingClass(b);
var c = buildingCapacities[b];
c = c && c[l];
return isDefined(warehouse) ? c && c[warehouse] : c;
}
function buildingExpansionNeeds(b, level) {
level = isDefined(level) ? level : buildingLevel(b);
var needs = costs[b = buildingID(b)][level];
var value = {};
var factor = 1.00;
if (config.getServer("tech2020")) factor -= 0.02; // Pulley
if (config.getServer("tech2060")) factor -= 0.04; // Geometry
if (config.getServer("tech2100")) factor -= 0.08; // Spirit Level
for (var r in needs)
if ("t" == r) // no time discount
value[r] = needs[r];
else
value[r] = Math.floor(needs[r] * factor);
return value;
}
function haveEnoughToUpgrade(b, level, have) {
var upgrade = buildingExpansionNeeds(b, level);
have = have || currentResources();
for (var resource in upgrade)
if (resource != "t" && have[resource] < upgrade[resource])
return false;
return true;
}
function buildingExtraInfo(div, id, name, level) {
function annotate(msg) {
node({ tag: "span", className: "ellipsis", text: msg, append: div,
style: { position: "relative" }});
div.style.padding = "0 3px 0 5px";
div.style.width = "auto";
}
if (-1 == cityIDs().indexOf(cityID()) && "wall" != name) return;
var originalLevel = buildingLevel(id, 0, "saved");
switch (name) {
case "townHall":
if (originalLevel != level) {
var delta = getMaxPopulation(level) - getMaxPopulation(originalLevel);
if (delta > 0) delta = "+" + delta;
annotate(delta);
}
break;
case "wall":
var wall = buildingLevel("townHall", 0);
if (wall)
annotate(Math.floor(10 * level * level / wall) + "%");
break;
case "tavern":
var wineMax = buildingCapacity(name, level);
var wineCur = config.getCity("wine", 0);
if (wineCur != wineMax)
annotate(wineCur +"/"+ wineMax);
break;
case "museum":
var museum = buildingLevel(name) || 0;
var culture = config.getCity("culture", 0);
if (culture != museum)
annotate(culture +"/"+ museum);
break;
case "academy":
var seats = buildingCapacity(name, level);
var working = config.getCity("researchers", 0);
if (working != seats)
annotate(working +"/"+ seats);
break;
case "warehouse":
var wood = buildingCapacity("warehouse", "wood", level);
var rest = buildingCapacity("warehouse", "rest", level);
if (originalLevel != level && wood && rest)
annotate(wood +"/"+ rest);
break;
}
}
function annotateBuilding(li, level) {
var a = $X('a', li);
if (!a) return;
$x('div[@class="pointsLevelBat"]', li).forEach(rm);
var id = buildingID(a);
if (isNumber(id) && li.id && isUndefined(level)) {
config.setCity("building"+ id, number(a.title));
config.setCity("posbldg"+ id, number(li.id));
}
if ("original" == level) {
level = buildingLevel(id, 0, "saved");
a.title = a.title.replace(/\d+/, level);
} else {
level = level || number(a.title);
}
var div = node({ className: "pointsLevelBat", text: level, append: li });
if (haveEnoughToUpgrade(a, level)) {
div.style.backgroundColor = "#FEFCE8";
div.style.borderColor = "#B1AB89";
}
clickTo(div, a.href);
div.style.visibility = "visible";
buildingExtraInfo(div, id, buildingClass(id), level);
div.title = a.title;
}
function showResourceNeeds(needs, parent, div, top, left) {
if (div)
rm(div);
else
div = node({ className: "pointsLevelBat toBuild" });
div.innerHTML = visualResources(needs, { nonegative: true });
if (parent.id == "position3") { // far right
div.style.top = top || "";
div.style.left = "auto";
div.style.right = "-17px";
div.style.margin = "0";
} else if ("position7" == parent.id) { // far left
div.style.top = top || "";
div.style.left = "-11px";
div.style.right = "auto";
div.style.margin = "0";
} else {
div.style.top = top || "";
div.style.left = "0";
div.style.right = "auto";
div.style.margin = "0 0 0 -50%";
}
if (isUndefined(left))
div.style.left = left;
show(div);
parent.appendChild(div);
return div;
}
function levelBat() { // Ajout d'un du level sur les batiments.
function hoverHouse(e) {
var a = $X('(ancestor-or-self::li)/a[@title and @href]', e.target);
if (a && a.title.match(/ \d+$/i)) {
var li = a && a.parentNode;
var top = $X('div[@class="timetofinish"]', li) ? "73px": "";
if (top && li.id == "position0") top = "0";
var div = showResourceNeeds(buildingExpansionNeeds(a), li, hovering, top);
clickTo(div, urlTo("building", buildingID(a)));
var enough = haveEnoughToUpgrade(a);
hovering.style.borderColor = enough ? "#B1AB89" : "#918B69";
hovering.style.backgroundColor = enough ? "#FEFCE8" : "#FDF8C1";
} else {
hide(hovering);
if (hovering.parentNode)
annotateBuilding(hovering.parentNode, "original");
}
}
var places = $("locations");
if (places) {
var hovering = node({ id: "hovering", className: "pointsLevelBat toBuild",
title: lang[popupInfo], append: $('position0') });
hide(hovering);
places.addEventListener("mouseover", hoverHouse, false);
hovering.addEventListener("DOMMouseScroll", function(e) {
var li = hovering.parentNode;
var a = $X('a', li), b = buildingID(a);
var l = Math.min(Math.max(!b, number(a.title) + (e.detail < 0 ? 1 : -1)),
costs[b].length - 1);
a.title = a.title.replace(/\d+/, l);
annotateBuilding(li, l);
hoverHouse({ target: hovering });
}, false);
}
for (var name in buildingIDs)
config.remCity("building"+ buildingIDs[name]); // clear old broken config
var all = $x('id("locations")/li[not(contains(@class,"buildingGround"))]');
all.forEach(function(li) { annotateBuilding(li); });
}
function worldmap_isoView() {
function showResources() {
drawMap();
var w = unsafeWindow;
var cx = w.center_x, dim = w.MAXSIZE;
var cy = w.center_y, mid = w.halfMaxSize;
for (var i = 0; i < dim; i++)
for (var j = 0; j < dim; j++) {
var x = cx + mid - i;
var y = cy + mid - j;
var r = resources[x+":"+y] || [];
var R = r[tradegood];
var t = $("tradegood_"+ i +"_"+ j);
t.innerHTML = !R ? "" : <>
{R}
{r[wood]}
>.toXMLString();
}
return islands;
}
function dropTooltip(x) { x.removeAttribute("title"); }
function mark(x, y) {
var v = setMark(x, y);
travelDistanceBreadcrumbs();
return v;
}
$x('//area[@title]').forEach(dropTooltip);
$x('id("worldmap")/text()').forEach(rm);
var resources = {}, tradegood = 0, wood = 1;
var islands = upgradeConfig0()["s11.ikariam.org"].islands;
for (var id in islands) {
var i = islands[id];
if (i.R)
resources[i.x+":"+i.y] = [i.R, i.w];
}
//var islands = config.get("islands", {});
var drawMap = unsafeWindow.center_map;
unsafeWindow.center_map = showResources;
var setMark = unsafeWindow.mark;
unsafeWindow.mark = mark;
}
function focusCity(city) {
var a = $("city_" + city);
var other = $X('//a[starts-with(@id,"city_") and not(@id="city_'+city+'")]');
if (other) {
click(other);
return click(a);
}
location.href = "javascript:selectedCity = -1; try { (function() {" +
a.getAttribute("onclick") + "}).call(document.getElementById('city_" +
city +"')) } catch(e) {}; void 0";
setTimeout(function() { a.parentNode.className += " selected"; }, 1e3);
}
function travelDistanceBreadcrumbs(island) {
var breadcrumbs = $X('id("breadcrumbs")/span[.//text()[contains(translate' +
'(.,"0123456789:",""),"[]")]]'), x, y, t, junk;
if (breadcrumbs) {
[junk, x, y] = breadcrumbs.textContent.match(/\[(\d+):(\d+)\]/);
if (island) {
config.setIsle("x", x = integer(x), island);
config.setIsle("y", y = integer(y), island);
}
//console.log("isle %x at %x:%y", island, x, y);
//while (isTextNode(breadcrumbs.lastChild)) rm(breadcrumbs.lastChild);
if ((t = travelTime(x, y)))
breadcrumbs.innerHTML += " ("+ secsToDHMS(t) +")";
}
}
var nonIslands = { 3:1, 8:1, 10:1, 12:1, 14:1, 21:3, 138:1, 191:12,
236:7, 245:1, 251:1, 255:7, 284:1, 317:1, 402:10, 541:3, 599:1, 611:3,
644:3, 651:1, 662:1, 689:2, 707:1, 709:1, 748:4, 797:2, 805:2, 914:5,
923:8, 988:2, 1053:1, 1244:2, 1267:1, 1299:1, 1339:60,1519:2, 1756:1, 1830:2,
1848:1, 1850:1, 2011:1, 2024:4, 2062:6, 2268:1, 2357:1, 2449:9, 2459:1, 2463:1,
2471:10,2513:2, 2700:5, 2735:2, 2806:6, 2834:4, 2970:1, 3014:20,3040:1, 3104:6,
3163:4, 3187:1, 3240:6, 3331:1, 3408:2, 3472:1, 3529:7, 3778:2, 3781:2, 3791:2,
3808:2, 3870:1, 3994:1, 4102:4, 4146:1, 4188:2, 4205:19,4234:1, 4242:2, 4268:20,
4307:1, 4388:1, 4424:1, 4426:1, 4428:1, 4430:1, 4432:1, 4434:1, 4436:1, 4462:1,
4464:1, 4474:1, 4537:2, 4543:2, 4546:1, 4557:2, 4564:1, 4566:1, 4568:1, 4570:1,
4572:3, 4605:1, 4677:1, 4697:2, 4718:1, 4720:3, 4734:2, 4737:1, 4747:4, 4753:2,
4801:2, 4956:2, 4959:1, 4961:2, 4965:2, 5012:4, 5018:4, 5061:1, 5066:2, 5069:1,
5071:1, 5185:2, 5189:1, 5255:2, 5296:2, 5322:1, 5530:1, 5548:1, 5550:1, 5552:1,
5554:1, 5578:1, 5580:2, 5654:8, 5669:4, 5697:2, 5701:2, 5709:2, 5722:-5721 };
function nextIsland(id) {
var skip = nonIslands[++id];
return skip ? id + skip : id;
}
function prevIsland(id) {
id = integer(id);
var i = 0;
while (--i >= -60) {
var skip = nonIslands[i + id];
if (skip)
if (skip == -i)
return id - skip - 1;
else
break;
}
return --id > 0 ? id : 5721;
}
function islandView() {
function nextprev(event) {
var n = event.charCode || event.keyCode;
var next = { 37: prevIsland, 39: nextIsland }[n];
if (next) {
event.stopPropagation();
event.preventDefault();
goto(urlTo("island", next(island)));
}
}
var city = urlParse("selectCity");
if (city)
setTimeout(focusCity, 200, city);
levelTown();
levelResources();
var island = urlParse("id", $X('id("advCities")/a').search);
travelDistanceBreadcrumbs(island);
if (island)
addEventListener("keypress", nextprev, false);
}
function travelTime(x1, y1, x2, y2) {
if (arguments.length == 1) { // a city id
var city = isleForCity(x1);
x1 = config.getIsle("x", 0, city);
y1 = config.getIsle("y", 0, city);
//console.log("isle %x at %x:%y", city, x1, y1);
if (!x1 || !y1) return 0;
}
if (arguments.length < 4) {
city = referenceIslandID();
x2 = config.getIsle("x", 0, city);
y2 = config.getIsle("y", 0, city);
//console.log("to isle %x at %x:%y", city, x2, y2);
if (!x2 || !y2) return 0;
}
var dx = x2 - x1, dy = y2 - y1;
return 60 * 20 * (1 + Math.sqrt(dx*dx + dy*dy));
}
function click(node) {
var event = node.ownerDocument.createEvent("MouseEvents");
event.initMouseEvent("click", true, true, node.ownerDocument.defaultView,
1, 0, 0, 0, 0, false, false, false, false, 0, node);
node.dispatchEvent(event);
}
function levelResources() {
function annotate(what) {
what = $X('id("islandfeatures")/li['+ what +']');
if (!what) return;
var level = number(what.className);
node({ className: "pointsLevelBat", text: level, append: what });
var id = urlParse("id");
if (id) {
var res = what.className.split(" ")[0];
var rid = resourceIDs[res];
if ("w" == rid) {
config.setIsle("w", level, id);
} else {
config.setIsle("R", level, id);
config.setIsle("r", rid, id);
}
}
}
annotate('contains(@class,"wood")');
annotate('not(contains(@class,"wood")) and not(@id)');
}
function levelTown() {
function addToFriendList(e) {
var flName = $("flNewName"), flLink = $("flNewLink");
if (flName && flLink) {
var player = e.target;
flName.value = player.childNodes[1].textContent;
var city = number(player.parentNode.id);
var isle = urlParse("id", $X('id("islandfeatures")/li/a').search);
flLink.value = "http://" + location.hostname + "/index.php?" +
"view=island&id="+ isle +"&selectCity="+ city;
location.href = "javascript:void(flToggleFrame(1))";
}
}
function level(li) {
var level = li.className.match(/\d+/)[0];
var city = $X('a[@onclick]/span', li);
if (!city) return; // new city site
var name = $X('text()[preceding-sibling::span]', city);
if (name) {
name.nodeValue = level +":"+ name.nodeValue;
name = name.parentNode;
name.style.left = Math.round((name.offsetWidth) / -2 + 34) + "px";
}
var player = city.cloneNode(true);
player.innerHTML = 'Player name' +
'';
name = trim($X('ul/li[@class="owner"]/text()[1]', li).textContent);
player.childNodes[1].nodeValue = name;
city.parentNode.insertBefore(player, city.nextSibling);
player.style.top = "84px";
player.style.left = Math.round((player.offsetWidth) / -2 + 34) + "px";
var msg = $X('ul/li[@class="owner"]/a', li);
//player.title = msg.title;
clickTo(player, addToFriendList);
dblClickTo(player, msg.href);
}
$x('//li[starts-with(@class,"cityLocation city level")]').forEach(level);
}
function linkTo(url, node, styles, opts) {
if (!url.match(/\?/))
url = urlTo(url);
if (!url) return;
if (isString(node))
node = $X(node, opts && opts.context);
if (!url)
return;
var a = document.createElement("a");
a.href = url;
if (node) {
while (node.lastChild)
a.insertBefore(node.lastChild, a.firstChild);
if (node.id)
a.id = node.id;
if (node.title)
a.title = node.title;
if (node.className)
a.className = node.className;
if (node.hasAttribute("style"))
a.setAttribute("style", node.getAttribute("style"));
}
if (styles)
for (var prop in styles)
a.style[prop] = styles[prop];
if (opts) {
if (opts.saveParent) {
while (node.lastChild)
a.appendChild(node.removeChild(node.firstChild));
return node.appendChild(a);
}
if (opts.text)
a.textContent = opts.text;
}
if (node)
node.parentNode.replaceChild(a, node);
return a;
}
function urlTo(what, id, opts) {
function building() {
var id = buildingID(what);
if ("-" != buildingLevel(id, "-"))
return url("?view="+ what +"&id="+ c +"&position="+ buildingPosition(id));
}
var c = cityID(), i = islandID();
if (what == "workshop")
what = "workshop-army";
switch (what) {
default: return url("?view="+ what);
case "wood": return url("?view=resource&type=resource&id="+ i);
case "luxe": return url("?view=tradegood&type=tradegood&id="+ i);
case "townHall": case "port": case "academy":
case "shipyard": case "wall": case "warehouse":
case "barracks": case "museum": case "branchOffice":
case "embassy": case "palace": case "palaceColony":
case "safehouse": case "tavern": case "workshop-army":
return building();
case "city": return url('?view=city&id='+ c);
case "building": return url("?view=buildingDetail&buildingId="+ id);
case "research": return url("?view=researchDetail&researchId="+ id);
case "library":
return urlTo("academy").replace("academy", "researchOverview");
case "island":
var city = "";
if (isObject(id)) {
if ((city = id.city)) {
if (!id.island && !(id.island = config.getCity("i", 0, city)))
return "#";
city = "&selectCity="+ city;
}
id = id.island;
}
return url('?view=island&id='+ id + city);
}
}
function getQueue() {
return eval(config.getCity("q", "[]"));
}
function setQueue(q) {
return config.setCity("q", uneval(q));
}
function addToQueue(b, first) {
var q = getQueue();
if (first)
q.unshift(b);
else
q.push(b);
setTimeout(drawQueue, 10);
return setQueue(q);
}
function changeQueue(e) {
var enqueued = $X('ancestor-or-self::li[parent::ul[@id="q"]]', e.target);
if (enqueued) { // drop from queue
var q = getQueue();
q.splice(enqueued.getAttribute("rel"), 1);
setQueue(q);
drawQueue();
} else if (!e.altKey) {
return;
} else { // enqueue
var a = $X('parent::li[parent::ul[@id="locations"]]/a', e.target);
if (a) {
addToQueue(buildingID(a), e.shiftKey);
setTimeout(processQueue, 10);
}
}
if (a || enqueued) {
e.stopPropagation();
e.preventDefault();
}
}
function reallyUpgrade(name) {
//console.log("upgrading %x", name);
var i = cityID();
var q = getQueue();
var b = buildingID(name);
var l = buildingLevel(b, 0);
var p = buildingPosition(b);
if (q.shift() != b) { // some other window got there before us; abort
location.hash = "#q:in-progress";
return;
}
if (haveResources(buildingExpansionNeeds(b, l))) {
return setTimeout(function() {
config.remCity("build");
setQueue(q);
if (!l)
return goto(url("?action=CityScreen&function=build&id="+ i +
"&position="+ p +"&building="+ b));
post("/index.php", {
action: "CityScreen",
function: "upgradeBuilding",
id: i,
position: p,
level: l });
}, 3e3);
}
}
function upgrade() {
//console.log("upgrade: %x", getQueue().length);
var q = getQueue();
if (!q.length) return;
var b = q.shift();
var l = buildingLevel(b, 0);
if (haveResources(buildingExpansionNeeds(b, l))) // ascertain we're in a good
return gotoCity("/#q:"+ buildingClass(b)); // view -- and in the right city
var t = replenishTime(b, l);
if (t && isFinite(t)) {
setTimeout(upgrade, Math.max(++t * 1e3, 60e3));
console.log("Waiting %d seconds...", t);
}
}
// iterates through lack, updating accumulate with goods used, zeroing have for
// all missing resources, and adds lack.t with the time it took to replenish it
function replenishTime(b, level, lack, have, accumulate) {
if (haveEnoughToUpgrade(b, level, have))
return 0;
lack = lack || {};
have = have || currentResources();
accumulate = accumulate || {};
// what is missing?
var need = buildingExpansionNeeds(b, level);
for (var r in need) {
if (r == "t") continue;
if (need[r] > have[r])
lack[r] = need[r] - have[r];
}
// how far do we have to move the clock forward to get everything needed?
var t = 0, takesMin = 0, takesMax = 0;
var pace = reapingPace();
var all = addResources(lack, pace); // used as a union operator only here
for (var r in all) {
var n = lack[r] || 0;
var p = pace[r] || 0;
accumulate[r] = (accumulate[r] || 0) + n;
if (p > 0) {
var time = Math.ceil(3600 * n / p);
takesMin = Math.max(takesMin, time);
} else if (n) {
takesMax = Infinity;
}
}
takesMax = accumulate.t = Math.max(takesMin, takesMax);
// replenish all resources (that can be replenished in finite time)
var replenish = mulResources(pace, takesMin / 3600, Math.floor);
for (r in replenish)
have[r] = Math.max(0, have[r] + replenish[r]);
for (r in accumulate)
if (!accumulate[r])
delete accumulate[r];
return takesMax;
}
function copyObject(o) {
var copy = {};
for (var n in o)
copy[n] = o;
return copy;
}
function hoverQueue(have, e) {
var node = e.target;
if ($X('self::li[@rel]', node)) {
var n = li.getAttribute("rel");
var h = $("qhave");
div = showResourceNeeds(have, $("container2"), div);
div.title = lang[left]+ resolveTime((t-Date.now())/1e3, 1);
}
var last = $("q").lastChild.have;
}
function drawQueue() {
var q = getQueue();
var t = Math.max(Date.now(), config.getCity("build")); // in ms
var dt = (t - Date.now()) / 1e3; // in s
var have = currentResources();
var pace = reapingPace();
var miss = {};
var level = buildingLevels();
// add a level for what is being built now, if anything
var building = config.getCity("buildurl");
var buildEnd = config.getCity("build", 0);
if (buildEnd > Date.now() && building) {
building = buildingID(urlParse("view", building));
level[building] = 1 + (level[building] || 0);
var replenished = mulResources(pace, (buildEnd - Date.now()) / 3600e3);
have = addResources(have, replenished);
}
var ul = node({ tag: "ul", id: "q", append: document.body });
ul.innerHTML = "";
for (var i = 0; i < q.length; i++) {
var b = q[i];
var what = buildingClass(b);
var li = node({ tag: "li", className: what, rel: i + "", append: ul,
html: '' });
li.have = copyObject(have);
// erecting a new building, not upgrading an old
if (!level.hasOwnProperty(b)) {
level[b] = 0;
if ("city" == document.body.id) { // erect a placeholder ghost house
var pos = buildingPosition(b);
var spot = $("position"+ pos);
spot.className = buildingClass(b);
$X('a', spot).title = "Level 0";
if ((spot = $X('div[@class="flag"]', spot))) {
spot.className = "buildingimg";
spot.style.opacity = "0.5";
}
}
}
// calculate leading stall time, if any, moving clock/resources forwards:
var stalledOn = {};
var time = replenishTime(b, level[b], stalledOn, have, miss);
//console.log("Stalled %x seconds on %s", stall.t, buildingClass(b));
if (time) {
if (time == Infinity) {
time = "∞"; // FIXME: this merits a more clear error message
} else {
dt += time;
t += time * 1e3;
stalledOn.t = secsToDHMS(time, 1, " ");
}
var div = showResourceNeeds(stalledOn, li, null, "112px", "");
div.style.backgroundColor = "#FCC";
div.style.borderColor = "#E88";
div.title = lang[unreplenished];
}
// FIXME? error condition when storage[level[warehouse]] < need[resource]
// Upgrade and move clock forwards upgradeTime seconds
annotateBuilding(li, ++level[b]);
var need = buildingExpansionNeeds(b, level[b] - 1);
have = subResources(have, need); // FIXME - improve (zero out negative)
dt = parseTime(need.t) + 1;
li.title = "Start time: "+ resolveTime((t - Date.now())/1000+1, 1); // I18N
t += dt * 1000;
var done = trim(resolveTime((t - Date.now()) / 1000));
done = node({ className: "timetofinish", text: done, append: li });
node({ tag: "span", class: "before", prepend: done });
node({ tag: "span", class: "after", append: done });
setTimeout(bind(function(done, li) {
done.style.left = 4 + Math.round( (li.offsetWidth -
done.offsetWidth) / 2) + "px";
}, this, done, li), 10);
}
var div = $("qhave") || undefined;
if (!q.length) {
if (div) hide(div);
return;
}
delete have.p; delete have.g; delete have.P;
div = showResourceNeeds(have, $("container2"), div);
div.title = lang[left] + resolveTime((t-Date.now())/1e3, 1);
div.style.left = div.style.top = "auto";
div.style.margin = "0";
div.style.right = "20px";
div.style.bottom = "35px";
div.style.zIndex = "5000";
div.style.position = "absolute";
if (haveBuilding("branchOffice"))
clickTo(div, sellStuff);
div.id = "qhave";
div = $("qmiss") || undefined;
stalled = false;
for (var r in miss)
stalled = true;
if (!stalled)
return div && hide(div);
// t = secsToDHMS(miss.t);
delete miss.t;
// miss.t = t;
drawQueue.miss = miss;
drawQueue.have = have;
div = showResourceNeeds(miss, $("container2"), div);
if (haveBuilding("branchOffice"))
clickTo(div, goShopping);
div.title = lang[shop];
div.style.top = "auto";
div.style.margin = "0";
div.style.left = "240px";
div.style.bottom = "35px";
div.style.zIndex = "5000";
div.style.position = "absolute";
div.id = "qmiss";
}
// Figure out what our current project and next action are. Returns 0 when idle,
// "building" when building something (known or unknown), "unknown" when data is
// unconclusive (we're in a view without the needed information) after a project
// has been completed, and otherwise the time in milliseconds to build complete
// or resources expected to be available to start building something now queued.
function queueState() {
var v = urlParse("view");
var u = config.getCity("buildurl");
var t = config.getCity("build", Infinity);
var busy = $X('id("buildCountDown") | id("upgradeCountDown")');
//console.log("u: %x, t: %x, b: %x, ql: %x", u, t, busy, getQueue().length);
if (t < Date.now()) { // last known item is completed by now
if ("city" == v)
return busy ? "building" : 0;
return "unknown";
} else if (t == Infinity) { // no known project going
var q = getQueue();
if (!q.length)
return 0;
var b = q.shift();
var l = buildingLevel(b, 0);
console.log("Building %x [%d]: %xs", b, l, replenishTime(b, l));
return replenishTime(b, l);
} // busy building something; return time until completion
return t - Date.now() + 3e3;
}
function processQueue(mayUpgrade) {
var state = queueState(), time = isNumber(state) && state;
//console.log("q: "+ state + " ("+ secsToDHMS(time/1e3) +")", mayUpgrade);
if (time) {
setTimeout(processQueue, Math.max(time, 30e3));
} else if (0 === time) {
if (mayUpgrade) upgrade();
} // else FIXME? This might be safe, if unrelated pages don't self-refresh:
//setTimeout(goto, 3e3, "city"); // May also not be needed at all there
drawQueue();
if (!processQueue.css)
processQueue.css = css(<>>);
}
function alreadyAllocated(pos, building) {
function isOnThisSpot(b) {
return buildingPosition(b) == pos;
}
function alreadyEnqueued(b) {
return b == building;
}
var q = getQueue();
return q.some(isOnThisSpot) || q.some(alreadyEnqueued);
}
function buildingGroundView() {
function build(id, pos, e) {
buts.forEach(rm);
config.setCity("posbldg"+ id, pos);
var prepend = e.shiftKey;
addToQueue(id, prepend);
}
function addEnqueueButton(p) {
var pos = parseInt(urlParse("position"), 10);
var img = $X('preceding-sibling::div[@class="buildinginfo"]/img', p);
var id = img && buildingID(img.src.match(/([^\/.]+).gif$/)[1]);
if (id && pos && !alreadyAllocated(pos, id)) {
var but = node({ tag: "input", className: "button", append: p,
value: lang[enqueue], title: lang[shiftClick],
style: { width: "100px" }});
clickTo(but, bind(build, this, id, pos));
return but;
}
}
projectBuildStart("mainview");
var buts = $x('//p[@class="cannotbuild"]').map(addEnqueueButton);
}
function sumPrices(table, c1, c2) {
function price(tr) {
var prefixes = { G:1e9, M:1e6, k:1e3 };
var td = $x('td', tr);
if (td.length <= Math.max(c1, c2)) return;
var n = number(td[c1]);
var p = number(td[c2]);
if (isNaN(n) || isNaN(p)) return;
n *= p;
for (var e in prefixes)
if (!(n % prefixes[e])) {
n /= prefixes[e];
n += e;
break;
} else if (!(n % (prefixes[e]/10))) {
n /= prefixes[e];
n += e;
break;
}
node({ tag: "span", className: "ellipsis", text: n+"", append: td[c1],
style: { position: "static", verticalAlign: "top", marginLeft: "3px" }
});
}
$x('tbody/tr[td]', table).forEach(price);
}
function branchOfficeView() {
function factor(table) {
sumPrices(table, 1, 3);
}
scrollWheelable();
$x('id("mainview")//table[@class="tablekontor"]').forEach(factor);
clickResourceToSell();
}
function portView() {
setTimeout(projectCompletion, 4e3, "outgoingOwnCountDown");
}
function evenShips(nodes) {
function sum(a, b) {
return integer(a || 0) + integer(b || 0);
}
function fillNextEvenShip(e) {
var input = e.target;
var value = number(input);
var count = nodes.reduce(sum, 0);
var remainder = count % 300;
if (remainder) {
input.value = value + (300 - remainder);
e.stopPropagation();
}
}
function listen(input) {
input.addEventListener("dblclick", fillNextEvenShip, false);
}
if (stringOrUndefined(nodes))
nodes = $x(nodes || '//input[@type="text" and @name]');
nodes.forEach(listen);
}
function scrollWheelable(nodes) {
function getCount(node) {
return $X('preceding-sibling::input[@type="text"] |' +
'self::input[@type="text"]', node);
}
function add(node, sign, event) {
if (!node || !sign) return;
event.preventDefault();
var alt = event.altKey ? 100 : 1;
var ctrl = event.ctrlKey ? 3 : 1;
var meta = event.metaKey ? 1000 : 1;
var shift = event.shiftKey ? 10 : 1;
var factor = meta * alt * ctrl * shift;
var value = node.value || "0";
var time = "vT" == node.id;
if (time) {
value = parseTime(value + "");
if (1 == factor)
factor = 5;
factor *= 60;
} else {
value = integer(value);
}
value = Math.max(0, value + sign * factor);
if (time && value < 40 * 60) { // special case for < 40 minute clustering
if (sign == 1) { // adding
if (value < 20 * 60)
value = 20 * 60;
else if (value < 40 * 60)
value = 40 * 60;
} else { // subtracting
if (value < 20 * 60)
value = 0;
else if (value < 40 * 60)
value = 20 * 60;
}
}
node.value = time ? secsToDHMS(value) : value;
click(node);
}
function groksArrows(event) {
var sign = {};
var key = unsafeWindow.KeyEvent;
sign[key.DOM_VK_UP] = 1;
sign[key.DOM_VK_DOWN] = -1;
add(event.target, sign[event.charCode || event.keyCode], event);
}
function onScrollWheel(event) {
add(getCount(event.target), event.detail > 0 ? -1 : 1, event);
}
function listen(input) {
input.addEventListener("keydown", groksArrows, false);
input.addEventListener("DOMMouseScroll", onScrollWheel, false);
}
if (stringOrUndefined(nodes))
nodes = $x(nodes || '//input[@type="text" and @name]');
nodes.forEach(listen);
}
function stringOrUndefined(what) {
return { undefined: 1, string: 1 }[typeof what] || 0;
}
function dontSubmitZero(but, nodes) {
function getCount(submit) {
var count = $X('preceding-sibling::input[@type="text"]|' +
'self::input[@type="text"]', submit);
if (count) return count;
var inputs = submit.form.elements;
for (var i = 0; i
1010: Deck Weapons
Allows: Building ballista ships in the shipyard
1h 5m 27s (24)
Dry-Dock
1020: Ship Maintenance
Effect: 2% less upkeep for ships
1h 5m 27s (24)
Deck Weapons
1030: Expansion
Allows: Building palaces, founding colonies
20h (440)
Ship Maintenance, Wealth
1040: Foreign Cultures
Allows: Construction of Embassies
1D 10h 54m 32s (768)
Expansion, Espionage
1050: Pitch
Effect: 4% less upkeep for ships
2D 4h (1,144)
Foreign Cultures
1060: Greek Fire
Allows: Building Flame Ships
4D 12h (2,376)
Pitch, Culinary Specialities
1070: Counterweight
Allows: Building catapult-ships at the shipyard
10D 4h 21m 49s (5,376)
Greek Fire, Invention
1080: Diplomacy
Allows: Military Treaties
19D 2h 10m 54s (10,080)
Counterweight
1090: Sea Charts
Effect: 8% less upkeep for ships
39D 18h 32m 43s (21,000)
Diplomacy
1100: Paddle Wheel Engine
Allows: Building steam rams in the shipyard
136D 19h 38m 10s (72,240)
Sea Charts, Helping Hands
1110: Mortar Attachment
Allows: Building mortar ships in the shipyard
231D 19h 38m 10s (122,400)
Paddle Wheel Engine, Glass
1999: Seafaring Future
Seafaring Future
831D 19h 38m 10s (439,200)
The Archimedic Principle, Canon Casting, Utopia, Mortar Attachment
2010: Conservation
Allows: Building of Warehouses
54m 32s (20)
2020: Pulley
Effect: 2% less building costs
1h 5m 27s (24)
Conservation
2030: Wealth
Effect: Allows the mining of trade goods and the building of trading posts
6h 32m 43s (144)
Pulley
2040: Wine Press
Allows: Building of taverns
16h (352)
Wealth, Well Digging
2050: Culinary Specialities
Allows: Training of chefs in the barracks
1D 10h 54m 32s (768)
Wine Press, Expansion, Professional Army
2060: Geometry
Effect: 4% less building costs
2D 4h (1,144)
Culinary Specialities
2070: Market
Allows: Trade Agreements
4D 12h (2,376)
Geometry, Foreign Cultures
2080: Holiday
Effect: Increases the satisfaction in all towns
11D 10h 54m 32s (6,048)
Market
2090: Helping Hands
Allows: Overloading of resources
25D 10h 54m 32s (13,440)
Holiday
2100: Spirit Level
Effect: 8% less costs for the construction of buildings
39D 18h 32m 43s (21,000)
Helping Hands
2110: Bureaucracy
Allows: An additional building space in the towns
117D 6h 32m 43s (61,920)
Spirit Level
2120: Utopia
Utopia
606D 19h 38m 10s (320,400)
Bureaucracy, Diplomacy, Letter Chute, Gunpowder
2999: Economy Future
Economy Future
831D 19h 38m 10s (439,200)
The Archimedic Principle, Canon Casting, Utopia, Mortar Attachment
3010: Well Digging
Effect: +50 housing space, +50 happiness in the capital
1h 27m 16s (32)
3020: Paper
Effect: 2% more research points
1h 21m 49s (30)
Well Digging
3030: Espionage
Allows: Building hideouts
16h (352)
Paper, Wealth
3040: Invention
Allows: Building of workshops
1D 16h 43m 38s (896)
Espionage, Wine Press, Professional Army
3050: Ink
Effect: 4% more research points
2D 4h (1,144)
Invention
3060: Cultural Exchange
Allows: building museums
5D 12h (2,904)
Ink, Culinary Specialities
3070: Anatomy
Allows: Recruiting Doctors in the Barracks
11D 10h 54m 32s (6,048)
Cultural Exchange
3080: Glass
Allows: Usage of crystal glass in order to accelerate research in the academy
25D 10h 54m 32s (13,440)
Anatomy, Market
3090: Mechanical Pen
Effect: 8% more research points
39D 18h 32m 43s (21,000)
Glass
3100: Bird´s Flight
Allows: Gyrocopter
127D 1h 5m 27s (67,080)
Mechanical Pen, Governor
3110: Letter Chute
Effect: 1 Gold upkeep less per scientist
313D 15h 16m 21s (165,600)
Bird´s Flight, Helping Hands
3120: Pressure Chamber
Allows: Building diving boats in the shipyard
404D 13h 5m 27s (213,600)
Letter Chute, Utopia, Robotics
3130: The Archimedic Principle
Allows: Building Bombardiers in the Barracks
272D 17h 27m 16s (144,000)
Pressure Chamber
3999: Knowledge Future
Knowledge Future
831D 19h 38m 10s (439,200)
The Archimedic Principle, Canon Casting, Utopia, Mortar Attachment
4010: Dry-Dock
Allows: Building Shipyards
1h 5m 27s (24)
4020: Maps
Effect: 2% less upkeep for soldiers
1h 5m 27s (24)
Dry-Dock
4030: Professional Army
Allows: Training swordsmen and phalanxes in the barracks
20h (440)
Maps, Wealth
4040: Siege
Allows: Building battering rams in the barracks
1D 10h 54m 32s (768)
Professional Army, Espionage
4050: Code of Honour
Effect: 4% less upkeep
2D 4h (1,144)
Siege
4060: Ballistics
Allows: Archers
4D 12h (2,376)
Code of Honour, Culinary Specialities
4070: Law of the Lever
Allows: Building catapults in the barracks
10D 4h 21m 49s (5,376)
Ballistics, Invention
4080: Governor
Allows: Occupation
19D 2h 10m 54s (10,080)
Law of the Lever, Market
4090: Logistics
Effect: 8% less upkeep for soldiers
39D 18h 32m 43s (21,000)
Governor
4100: Gunpowder
Allows: Building marksmen in the barracks
127D 1h 5m 27s (67,080)
Logistics, Glass
4110: Robotics
Allows: Building steam giants in the barracks
272D 17h 27m 16s (144,000)
Gunpowder
4120: Canon Casting
Allows: Building mortars in the barracks
404D 13h 5m 27s (213,600)
Robotics, Greek Fire
4999: Military Future
Military Future
831D 19h 38m 10s (439,200)
The Archimedic Principle, Canon Casting, Utopia, Mortar Attachment
>.toString().split(/\n\n+/).map(makeTech);
if (what)
return tree.filter(function(t) { return t.name == what; })[0];
if ("researchOverview" != urlParse("view"))
return tree;
if (!techinfo.cssed)
techinfo.cssed = css(<>>);
var tech = {}, byName = {}, hr;
while (!tree.map(unwindDeps).every(I));
tree.forEach(indent);
var div = $X('id("mainview")/div/div[@class="content"]');
$x('br', div).forEach(rm);
var maxLevel = Math.max.apply(Math, pluck(tree.filter(isKnown), "level"));
vr(maxLevel);
if (!techinfo.hide) {
var hide = document.createElement("style");
hide.type = "text/css";
hide.textContent = "ul.explored { display:none; }";
document.documentElement.firstChild.appendChild(hide);
hide.disabled = true;
var header = $X('preceding-sibling::h3/span', div);
header.innerHTML += ": ";
var toggle = node({ tag: "span", id: "hideshow", append: header,
text: lang[shown] });
clickTo(header, function() {
hide.disabled = !hide.disabled;
toggle.textContent = lang[shown + (hide.disabled ? 0 : 1)];
hr.style.height = (div.offsetHeight - 22) + "px";
});
}
function addTimeSpan(a, t) {
var li = a.parentNode;
var id = "t"+ urlParse("researchId", a.search);
var span = node({ id: id, tag: "span", before: a });
}
$x('ul/li/a', div).forEach(addTimeSpan);
div.addEventListener("mousemove", hover, false);
return tree;
}
var costs = [
[{}, {w:70, t:"34m 48s"}, {w:98, t:"56m 24s"}, {w:65, M:17, t:"1h 24m"}, {w:129, M:28, t:"1h 58m"}, {w:236, M:66, t:"2h 40m"}, {w:402, M:95, t:"3h 29m"}, {w:594, M:156, t:"4h 25m"}, {w:849, M:243, t:"5h 30m"}, {w:1176, M:406, t:"6h 43m"}, {w:1586, M:579, t:"8h 5m"}, {w:2101, M:799, t:"9h 35m"}, {w:3280, M:1348, t:"11h 15m"}, {w:4937, M:2124, t:"13h 3m"}, {w:7171, M:2951, t:"15h 1m"}, {w:10139, M:4409, t:"17h 9m"}, {w:14537, M:6461, t:"20h 11m"}, {w:18420, M:8187, t:"22h 44m"}, {w:22896, M:10176, t:"1D 1h"}, {w:28047, M:12466, t:"1D 4h"}, {w:33934, M:15082, t:"1D 7h"}, {w:40623, M:18055, t:"1D 10h"}, {w:48107, M:21381, t:"1D 14h"}, {w:56511, M:25116, t:"1D 17h"}, {w:226044, M:100464, t:"6D 23h"}, {w:452088, M:200928, t:"13D 22h"}, {w:904176, M:401856, t:"27D 21h"}, {w:1808352, M:803712, t:"55D 19h"}, {w:3616704, M:1607424, t:"111D 15h"}, {w:7233408, M:3214848, t:"223D 6h"}, {w:14466816, M:6429696, t:"446D 12h"}, {w:28933632, M:12859392, t:"893D 19m"}],,,
[{w:18, t:"10m 48s"}, {w:31, t:"24m 29s"}, {w:44, t:"50m 24s"}, {w:87, M:33, t:"1h 26m"}, {w:156, M:48, t:"2h 18m"}, {w:266, M:93, t:"2h 58m"}, {w:425, M:126, t:"3h 41m"}, {w:653, M:215, t:"4h 52m"}, {w:963, M:344, t:"5h 37m"}, {w:1381, M:529, t:"7h 6m"}, {w:1915, M:777, t:"7h 48m"}, {w:2604, M:1100, t:"9h 30m"}, {w:3790, M:1731, t:"10h 36s"}, {w:5349, M:2301, t:"11h 53m"}, {w:7333, M:3017, t:"11h 59m"}, {w:9808, M:4265, t:"13h 56m"}, {w:39232, M:17060, t:"2D 7h"}, {w:78464, M:34120, t:"4D 15h"}, {w:156928, M:68240, t:"9D 7h"}, {w:313856, M:136480, t:"18D 14h"}, {w:627712, M:272960, t:"37D 4h"}, {w:1255424, M:545920, t:"74D 8h"}, {w:2510848, M:1091840, t:"148D 17h"}, {w:5021696, M:2183680, t:"297D 11h"}],
[{w:36, t:"14m 24s"}, {w:58, t:"28m 48s"}, {w:84, t:"48m"}, {w:79, C:30, t:"1h 19m"}, {w:159, C:73, t:"1h 57m"}, {w:302, C:210, t:"2h 48m"}, {w:535, C:285, t:"3h 52m"}, {w:889, C:467, t:"5h 6m"}, {w:1423, C:712, t:"6h 36m"}, {w:2174, C:999, t:"8h 16m"}, {w:3221, C:1307, t:"10h 16m"}, {w:4639, C:1960, t:"12h 27m"}, {w:7155, C:3267, t:"15h"}, {w:10630, C:4573, t:"17h 45m"}, {w:15224, C:6264, t:"20h 44m"}, {w:20358, C:8853, t:"1D 7m"}, {w:81432, C:35412, t:"4D 28m"}, {w:162864, C:70824, t:"8D 57m"}, {w:325728, C:141648, t:"16D 1h"}, {w:651456, C:283296, t:"32D 3h"}, {w:1302912, C:566592, t:"64D 7h"}, {w:2605824, C:1133184, t:"128D 15h"}, {w:5211648, C:2266368, t:"257D 6h"}, {w:10423296, C:4532736, t:"514D 13h"}],
[{w:38, t:"22m 41s"}, {w:67, t:"52m 49s"}, {w:96, t:"1h 50m"}, {w:152, M:57, t:"2h 31m"}, {w:272, M:83, t:"4h 1m"}, {w:388, M:135, t:"4h 20m"}, {w:609, M:180, t:"5h 17m"}, {w:810, M:266, t:"6h 2m"}, {w:1091, M:390, t:"6h 22m"}, {w:1551, M:594, t:"7h 58m"}, {w:1921, M:780, t:"7h 50m"}, {w:2600, M:1098, t:"9h 29m"}, {w:3530, M:1612, t:"9h 19m"}, {w:4555, M:1960, t:"10h 7m"}, {w:6228, M:2563, t:"10h 10m"}, {w:7702, M:3349, t:"10h 57m"}, {w:30808, M:13396, t:"1D 19h"}, {w:61616, M:26792, t:"3D 15h"}, {w:123232, M:53584, t:"7D 7h"}, {w:246464, M:107168, t:"14D 14h"}, {w:492928, M:214336, t:"29D 4h"}, {w:985856, M:428672, t:"58D 9h"}, {w:1971712, M:857344, t:"116D 19h"}, {w:3943424, M:1714688, t:"233D 14h"}, {w:7886848, M:3429376, t:"467D 4h"}, {w:15773696, M:6858752, t:"934D 9h"}, {w:31547392, M:13717504, t:"1868D 19h"}, {w:63094784, M:27435008, t:"3737D 14h"}, {w:126189568, M:54870016, t:"7475D 4h"}, {w:252379136, M:109740032, t:"14950D 9h"}, {w:504758272, M:219480064, t:"29900D 19h"}, {w:1009516544, M:438960128, t:"59801D 14h"}],
[{w:35, t:"6m 58s"}, {w:45, t:"16m 12s"}, {w:68, t:"31m 12s"}, {w:76, t:"56m 24s"}, {w:67, M:22, t:"1h 39m"}, {w:76, M:24, t:"1h 44m"}, {w:124, M:46, t:"2h 3m"}, {w:183, M:56, t:"2h 15m"}, {w:235, M:82, t:"2h 23m"}, {w:336, M:100, t:"2h 55m"}, {w:455, M:150, t:"3h 23m"}, {w:616, M:220, t:"4h"}, {w:755, M:289, t:"4h 18m"}, {w:980, M:398, t:"5h"}, {w:1170, M:494, t:"4h 48m"}, {w:1477, M:650, t:"5h 29m"}, {w:1797, M:821, t:"5h 25m"}, {w:2120, M:991, t:"5h 50m"}, {w:2435, M:1048, t:"5h 24m"}, {w:2831, M:1254, t:"5h 48m"}, {w:3208, M:1320, t:"5h 14m"}, {w:3763, M:1595, t:"5h 43m"}, {w:4296, M:1869, t:"5h 5m"}, {w:4874, M:2166, t:"5h 24m"}, {w:19496, M:8664, t:"21h 39m"}, {w:38992, M:17328, t:"1D 19h"}, {w:77984, M:34656, t:"3D 14h"}, {w:155968, M:69312, t:"7D 5h"}, {w:311936, M:138624, t:"14D 10h"}, {w:623872, M:277248, t:"28D 21h"}, {w:1247744, M:554496, t:"57D 18h"}, {w:2495488, M:1108992, t:"115D 12h"}, {w:4990976, M:2217984, t:"231D 57m"}, {w:9981952, M:4435968, t:"462D 1h"}, {w:19963904, M:8871936, t:"924D 3h"}, {w:39927808, M:17743872, t:"1848D 7h"}, {w:79855616, M:35487744, t:"3696D 15h"}, {w:159711232, M:70975488, t:"7393D 6h"}, {w:319422464, M:141950976, t:"14786D 13h"}, {w:638844928, M:283901952, t:"29573D 2h"}, {w:1277689856, M:567803904, t:"59146D 5h"}, {w:2555379712, M:1135607808, t:"118292D 11h"}, {w:5110759424, M:2271215616, t:"236584D 23h"}, {w:10221518848, M:4542431232, t:"473169D 22h"}, {w:20443037696, M:9084862464, t:"946339D 20h"}, {w:40886075392, M:18169724928, t:"1892679D 16h"}, {w:81772150784, M:36339449856, t:"3785359D 8h"}, {w:163544301568, M:72678899712, t:"7570718D 17h"}, {w:327088603136, M:145357799424, t:"15141437D 10h"}, {w:654177206272, M:290715598848, t:"30282874D 21h"}, {w:1308354412544, M:581431197696, t:"60565749D 18h"}, {w:2616708825088, M:1162862395392, t:"121131499D 12h"}, {w:5233417650176, M:2325724790784, t:"242262999D 57m"}, {w:10466835300352, M:4651449581568, t:"484525998D 1h"}, {w:20933670600704, M:9302899163136, t:"969051996D 3h"}, {w:41867341201408, M:18605798326272, t:"1938103992D 7h"}],
[{w:42, t:"27m 36s"}, {w:91, t:"1h 7m"}, {w:79, M:13, t:"1h 40m"}, {w:145, M:43, t:"2h 25m"}, {w:255, M:62, t:"3h 8m"}, {w:396, M:110, t:"4h 2m"}, {w:565, M:134, t:"4h 54m"}, {w:799, M:237, t:"5h 57m"}, {w:1203, M:387, t:"7h 6m"}, {w:1619, M:558, t:"8h 24m"}, {w:2135, M:780, t:"9h 54m"}, {w:2761, M:1167, t:"11h 27m"}, {w:4198, M:1917, t:"13h 12m"}, {w:5746, M:2472, t:"15h 12m"}, {w:7655, M:3150, t:"17h 22m"}, {w:10032, M:5235, t:"19h 48m"}, {w:40128, M:20940, t:"3D 7h"}, {w:80256, M:41880, t:"6D 14h"}, {w:160512, M:83760, t:"13D 4h"}, {w:321024, M:167520, t:"26D 9h"}, {w:642048, M:335040, t:"52D 19h"}, {w:1284096, M:670080, t:"105D 15h"}, {w:2568192, M:1340160, t:"211D 7h"}, {w:5136384, M:2680320, t:"422D 14h"}, {w:10272768, M:5360640, t:"845D 5h"}, {w:20545536, M:10721280, t:"1690D 10h"}, {w:41091072, M:21442560, t:"3380D 21h"}, {w:82182144, M:42885120, t:"6761D 19h"}, {w:164364288, M:85770240, t:"13523D 15h"}, {w:328728576, M:171540480, t:"27047D 6h"}, {w:657457152, M:343080960, t:"54094D 12h"}, {w:1314914304, M:686161920, t:"108189D 19m"}],
[{w:72, t:"1h 12m"}, {w:74, M:13, t:"1h 50m"}, {w:100, M:32, t:"2h 29m"}, {w:155, M:58, t:"3h 16m"}, {w:227, M:69, t:"4h 12m"}, {w:324, M:113, t:"4h 37m"}, {w:442, M:131, t:"4h 59m"}, {w:593, M:195, t:"5h 18m"}, {w:777, M:278, t:"5h 32m"}, {w:998, M:382, t:"5h 42m"}, {w:1255, M:509, t:"6h 24m"}, {w:1564, M:661, t:"7h 8m"}, {w:2159, M:950, t:"8h 55m"}, {w:2317, M:1058, t:"8h 44m"}, {w:2784, M:1301, t:"9h 7m"}, {w:3308, M:1423, t:"9h 27m"}, {w:3902, M:1728, t:"9h 43m"}, {w:4559, M:1876, t:"9h 56m"}, {w:5296, M:2245, t:"10h 4m"}, {w:6119, M:2661, t:"10h 9m"}, {w:7020, M:3120, t:"10h 8m"}, {w:7533, M:3348, t:"10h 2m"}, {w:8065, M:3584, t:"9h 51m"}, {w:8613, M:3828, t:"9h 34m"}, {w:34452, M:15312, t:"1D 14h"}, {w:68904, M:30624, t:"3D 4h"}, {w:137808, M:61248, t:"6D 9h"}, {w:275616, M:122496, t:"12D 18h"}, {w:551232, M:244992, t:"25D 12h"}, {w:1102464, M:489984, t:"51D 57m"}, {w:2204928, M:979968, t:"102D 1h"}, {w:4409856, M:1959936, t:"204D 3h"}, {w:8819712, M:3919872, t:"408D 7h"}, {w:17639424, M:7839744, t:"816D 15h"}, {w:35278848, M:15679488, t:"1633D 6h"}, {w:70557696, M:31358976, t:"3266D 13h"}, {w:141115392, M:62717952, t:"6533D 2h"}, {w:282230784, M:125435904, t:"13066D 5h"}, {w:564461568, M:250871808, t:"26132D 11h"}, {w:1128923136, M:501743616, t:"52264D 23h"}, {w:2257846272, M:1003487232, t:"104529D 22h"}, {w:4515692544, M:2006974464, t:"209059D 20h"}, {w:9031385088, M:4013948928, t:"418119D 16h"}, {w:18062770176, M:8027897856, t:"836239D 8h"}, {w:36125540352, M:16055795712, t:"1672478D 17h"}, {w:72251080704, M:32111591424, t:"3344957D 10h"}, {w:144502161408, M:64223182848, t:"6689914D 21h"}, {w:289004322816, M:128446365696, t:"13379829D 18h"}, {w:578008645632, M:256892731392, t:"26759659D 12h"}, {w:1156017291264, M:513785462784, t:"53519319D 57m"}, {w:2312034582528, M:1027570925568, t:"107038638D 1h"}, {w:4624069165056, M:2055141851136, t:"214077276D 3h"}, {w:9248138330112, M:4110283702272, t:"428154552D 7h"}, {w:18496276660224, M:8220567404544, t:"856309104D 15h"}, {w:36992553320448, M:16441134809088, t:"1712618209D 6h"}, {w:73985106640896, M:32882269618176, t:"3425236418D 13h"}],
[{w:25, t:"13m 20s"}, {w:112, M:12, t:"55m 12s"}, {w:196, M:46, t:"1h 49m"}, {w:297, M:88, t:"3h 5m"}, {w:494, M:162, t:"4h 2m"}, {w:766, M:274, t:"4h 58m"}, {w:1127, M:432, t:"5h 47m"}, {w:1588, M:644, t:"7h 17m"}, {w:2177, M:920, t:"7h 57m"}, {w:2895, M:1274, t:"9h 34m"}, {w:3756, M:1715, t:"9h 55m"}, {w:4803, M:2244, t:"11h 35m"}, {w:6030, M:2594, t:"11h 29m"}, {w:7468, M:3307, t:"13h 8m"}, {w:9117, M:3751, t:"12h 25m"}, {w:11804, M:5133, t:"13h 59m"}, {w:47216, M:20532, t:"2D 7h"}, {w:94432, M:41064, t:"4D 15h"}, {w:188864, M:82128, t:"9D 7h"}, {w:377728, M:164256, t:"18D 15h"}, {w:755456, M:328512, t:"37D 7h"}, {w:1510912, M:657024, t:"74D 14h"}, {w:3021824, M:1314048, t:"149D 4h"}, {w:6043648, M:2628096, t:"298D 8h"}],
[{w:282, M:84, t:"1h 28m"}, {w:760, M:272, t:"2h 57m"}, {w:1616, M:656, t:"4h 56m"}, {w:2996, M:1319, t:"7h 25m"}, {w:5035, M:2353, t:"8h 40m"}, {w:7901, M:3499, t:"11h 35m"}, {w:11746, M:4979, t:"14h 54m"}, {w:16776, M:7456, t:"18h 38m"}, {w:67104, M:29824, t:"3D 2h"}, {w:134208, M:59648, t:"6D 5h"}, {w:268416, M:119296, t:"12D 10h"}, {w:536832, M:238592, t:"24D 20h"}, {w:1073664, M:477184, t:"49D 16h"}, {w:2147328, M:954368, t:"99D 9h"}, {w:4294656, M:1908736, t:"198D 19h"}, {w:8589312, M:3817472, t:"397D 15h"}, {w:17178624, M:7634944, t:"795D 7h"}, {w:34357248, M:15269888, t:"1590D 14h"}, {w:68714496, M:30539776, t:"3181D 5h"}, {w:137428992, M:61079552, t:"6362D 10h"}, {w:274857984, M:122159104, t:"12724D 21h"}, {w:549715968, M:244318208, t:"25449D 19h"}, {w:1099431936, M:488636416, t:"50899D 15h"}, {w:2198863872, M:977272832, t:"101799D 6h"}],
#1=[{w:648, t:"4h"}, {w:5600, M:536, t:"8h"}, {w:20880, M:7317, C:4878, t:"9h"}, {w:57600, W:12800, M:32000, C:25600, t:"8h"}, {w:230400, W:102400, M:153600, C:102400, t:"8h"}, {w:460800, W:204800, M:307200, C:204800, t:"8h"}, {w:921600, W:409600, M:614400, C:409600, t:"8h"}, {w:1843200, W:819200, M:1228800, C:819200, t:"8h"}, {w:3686400, W:1638400, M:2457600, C:1638400, t:"8h"}, {w:7372800, W:3276800, M:4915200, C:3276800, t:"8h"}, {w:14745600, W:6553600, M:9830400, C:6553600, t:"8h"}, {w:29491200, W:13107200, M:19660800, C:13107200, t:"8h"}],
[{w:46, M:14, t:"50m 25s"}, {w:120, M:42, t:"1h 42m"}, {w:212, M:63, t:"2h 23m"}, {w:334, M:110, t:"2h 59m"}, {w:489, M:175, t:"3h 29m"}, {w:681, M:261, t:"3h 53m"}, {w:1001, M:406, t:"4h 38m"}, {w:1428, M:603, t:"5h 25m"}, {w:1967, M:866, t:"6h 15m"}, {w:2635, M:1203, t:"7h 6m"}, {w:3472, M:1622, t:"7h 58m"}, {w:4481, M:1928, t:"8h 53m"}, {w:5693, M:2521, t:"9h 49m"}, {w:7122, M:2931, t:"10h 46m"}, {w:8804, M:3732, t:"11h 45m"}, {w:10770, M:4683, t:"12h 45m"}, {w:43080, M:18732, t:"2D 3h"}, {w:86160, M:37464, t:"4D 6h"}, {w:172320, M:74928, t:"8D 12h"}, {w:344640, M:149856, t:"17D 19m"}, {w:689280, M:299712, t:"34D 38m"}, {w:1378560, M:599424, t:"68D 1h"}, {w:2757120, M:1198848, t:"136D 2h"}, {w:5514240, M:2397696, t:"272D 5h"}, {w:11028480, M:4795392, t:"544D 10h"}, {w:22056960, M:9590784, t:"1088D 20h"}, {w:44113920, M:19181568, t:"2177D 16h"}, {w:88227840, M:38363136, t:"4355D 9h"}, {w:176455680, M:76726272, t:"8710D 19h"}, {w:352911360, M:153452544, t:"17421D 15h"}, {w:705822720, M:306905088, t:"34843D 7h"}, {w:1411645440, M:613810176, t:"69686D 14h"}],
[{w:15, t:"17m 17s"}, {w:38, t:"43m 12s"}, {w:104, M:32, t:"1h 32m"}, {w:222, M:66, t:"2h 7m"}, {w:426, M:152, t:"2h 45m"}, {w:643, M:246, t:"3h 40m"}, {w:933, M:417, t:"4h 45m"}, {w:1301, M:660, t:"5h 56m"}, {w:1765, M:1010, t:"7h 17m"}, {w:2317, M:1481, t:"8h 44m"}, {w:3002, M:2104, t:"10h 21m"}, {w:3799, M:2615, t:"12h 3m"}, {w:4754, M:3579, t:"13h 56m"}, {w:5839, M:4325, t:"15h 54m"}, {w:7618, M:6294, t:"18h 3m"}, {w:9131, M:8116, t:"20h 17m"}, {w:36524, M:32464, t:"3D 9h"}, {w:73048, M:64928, t:"6D 18h"}, {w:146096, M:129856, t:"13D 12h"}, {w:292192, M:259712, t:"27D 1h"}, {w:584384, M:519424, t:"54D 2h"}, {w:1168768, M:1038848, t:"108D 5h"}, {w:2337536, M:2077696, t:"216D 10h"}, {w:4675072, M:4155392, t:"432D 20h"}, {w:9350144, M:8310784, t:"865D 16h"}, {w:18700288, M:16621568, t:"1731D 9h"}, {w:37400576, M:33243136, t:"3462D 19h"}, {w:74801152, M:66486272, t:"6925D 15h"}, {w:149602304, M:132972544, t:"13851D 7h"}, {w:299204608, M:265945088, t:"27702D 14h"}, {w:598409216, M:531890176, t:"55405D 5h"}, {w:1196818432, M:1063780352, t:"110810D 10h"}],,
[{w:26, M:8, t:"18m 36s"}, {w:55, M:20, t:"33m 37s"}, {w:102, M:30, t:"52m 48s"}, {w:163, M:54, t:"1h 12m"}, {w:236, M:85, t:"1h 31m"}, {w:277, M:106, t:"1h 34m"}, {w:371, M:151, t:"1h 53m"}, {w:465, M:197, t:"2h 7m"}, {w:545, M:240, t:"2h 15m"}, {w:682, M:311, t:"2h 34m"}, {w:810, M:379, t:"2h 47m"}, {w:980, M:422, t:"3h 6m"}, {w:1037, M:460, t:"3h 2m"}, {w:1197, M:493, t:"3h 15m"}, {w:1509, M:640, t:"3h 28m"}, {w:1925, M:837, t:"3h 48m"}, {w:2352, M:1046, t:"4h 1m"}, {w:2672, M:1188, t:"4h 14m"}, {w:2883, M:1281, t:"4h 16m"}, {w:3089, M:1373, t:"4h 17m"}, {w:3305, M:1469, t:"4h 19m"}, {w:3913, M:1739, t:"4h 49m"}, {w:4233, M:1881, t:"4h 57m"}, {w:4563, M:2028, t:"5h 4m"}, {w:18252, M:8112, t:"20h 16m"}, {w:36504, M:16224, t:"1D 16h"}, {w:73008, M:32448, t:"3D 9h"}, {w:146016, M:64896, t:"6D 18h"}, {w:292032, M:129792, t:"13D 12h"}, {w:584064, M:259584, t:"27D 57m"}, {w:1168128, M:519168, t:"54D 1h"}, {w:2336256, M:1038336, t:"108D 3h"}],
[{w:20, t:"15m 8s"}, {w:49, M:10, t:"42m"}, {w:95, M:27, t:"1h 8m"}, {w:163, M:48, t:"1h 41m"}, {w:266, M:88, t:"2h 10m"}, {w:407, M:146, t:"2h 38m"}, {w:594, M:228, t:"3h 23m"}, {w:867, M:352, t:"4h 25m"}, {w:1179, M:498, t:"5h 22m"}, {w:1559, M:686, t:"6h 26m"}, {w:2012, M:919, t:"7h 12m"}, {w:2674, M:1250, t:"8h 17m"}, {w:3343, M:1438, t:"9h 1m"}, {w:4127, M:1828, t:"9h 40m"}, {w:5021, M:2066, t:"10h 15m"}, {w:6304, M:2672, t:"11h 12m"}, {w:7533, M:3276, t:"11h 36m"}, {w:8910, M:3960, t:"11h 52m"}, {w:9833, M:4370, t:"12h 1m"}, {w:11232, M:4992, t:"12h 28m"}, {w:44928, M:19968, t:"2D 1h"}, {w:89856, M:39936, t:"4D 3h"}, {w:179712, M:79872, t:"8D 7h"}, {w:359424, M:159744, t:"16D 15h"}, {w:718848, M:319488, t:"33D 6h"}, {w:1437696, M:638976, t:"66D 13h"}, {w:2875392, M:1277952, t:"133D 2h"}, {w:5750784, M:2555904, t:"266D 5h"}, {w:11501568, M:5111808, t:"532D 11h"}, {w:23003136, M:10223616, t:"1064D 23h"}, {w:46006272, M:20447232, t:"2129D 22h"}, {w:92012544, M:40894464, t:"4259D 20h"}, {w:184025088, M:81788928, t:"8519D 16h"}, {w:368050176, M:163577856, t:"17039D 8h"}, {w:736100352, M:327155712, t:"34078D 17h"}, {w:1472200704, M:654311424, t:"68157D 10h"}, {w:2944401408, M:1308622848, t:"136314D 21h"}, {w:5888802816, M:2617245696, t:"272629D 18h"}, {w:11777605632, M:5234491392, t:"545259D 12h"}, {w:23555211264, M:10468982784, t:"1090519D 57m"}],
#1#
];
function visualResources(what, opt) {
var gold = ;
var wood = ;
var wine = ;
var glass =;
var marble=;
var sulfur=;
var bulb = ;
function replace(m, icon) {
var margin = { glass: -3 }[icon] || -5;
icon = eval(icon);
if (opt && opt.size) {
var h0 = icon.@height, h1 = Math.ceil(opt.size * h0);
var w0 = icon.@width, w1 = Math.ceil(opt.size * w0);
margin = margin + Math.floor((h0 - h1) / 2);
icon.@height = h1;
icon.@width = w1;
}
if (!opt || !opt.noMargin)
icon.@style = "margin-bottom: "+ margin +"px";
return icon.toXMLString();
}
if (typeof what == "object") {
var name = { w: "wood", g: "gold",
M: "marble", C: "glass", W: "wine", S: "sulfur" };
var html = []
for (var id in what) {
var count = what[id];
if (count < 0 && opt.nonegative)
count = 0;
if (name[id])
html.push(count +" $"+ name[id]);
else
html.push(count); // (build time)
}
what = html.join(" \xA0 ");
}
return what.replace(/\$([a-z]{4,6})/g, replace);
}
/*--------------------------------------------------------
Création des fonctions de temps.
---------------------------------------------------------*/
function getServerTime(offset) {
var Y, M, D, h, m, s, t;
[D, M, Y, h, m, s] = $("servertime").textContent.split(/[. :]+/g);
t = new Date(Y, parseInt(M, 10)-1, D, h, m, s);
return offset ? new Date(t.valueOf() + offset*1e3) : t;
}
function resolveTime(seconds, timeonly) { // Crée le temps de fin.
function z(t) { return (t < 10 ? "0" : "") + t; }
var t = getServerTime(seconds);
var d = "";
if (t.getDate() != (new Date).getDate()) {
var m = lang[monthshort].slice(t.getMonth()*3);
d = t.getDate() +" "+ m.slice(0, 3) +", ";
}
var h = z(t.getHours());
var m = z(t.getMinutes());
var s = z(t.getSeconds());
t = d + h + ":" + m + ":" + s;
return timeonly ? t : lang[finished] + t;
}
function secondsToHours(bySeconds) {
return isNaN(bySeconds) ? 0 : Math.round(bySeconds * 3600);
}
var locale = unsafeWindow.LocalizationStrings;
var units = { day: 86400, hour: 3600, minute: 60, second: 1 };
// input: "Nd Nh Nm Ns", output: number of seconds left
function parseTime(t) {
function parse(what, mult) {
var count = t.match(new RegExp("(\\d+)" + locale.timeunits.short[what]));
if (count)
return parseInt(count[1], 10) * mult;
return 0;
}
var s = 0;
for (var unit in units)
s += parse(unit, units[unit]);
return s;
}
function secsToDHMS(t, rough, join) {
if (t == Infinity) return "∞";
var result = [];
var minus = t < 0 ? "-" : "";
if (minus)
t = -t;
for (var unit in units) {
var u = locale.timeunits.short[unit];
var n = units[unit];
var r = t % n;
if (r == t) continue;
if ("undefined" == typeof rough || rough--)
result.push(((t - r) / n) + u);
else {
result.push(Math.round(t / n) + u);
break;
}
t = r;
}
return minus + result.join(join || " ");
}
function number(n) {
if (isNumber(n)) return n;
if (isObject(n))
if (/input/i.test(n.nodeName||""))
n = n.value;
else if (n.textContent)
n = n.textContent;
return parseFloat(n.replace(/[^\d.-]+/g, ""));
}
function integer(n) {
if (isNumber(n)) return n;
if (isObject(n))
if (/input/i.test(n.nodeName||""))
n = n.value;
else if (n.textContent)
n = n.textContent;
return parseInt(n.replace(/[^\d-]+/g, ""), 10);
}
function colonizeView() {
function annotate(what, time) {
what.innerHTML += " ("+ time +")";
}
css("#container .resources li { white-space: nowrap; }");
var have = currentResources();
var pace = reapingPace();
var needPop = $X('//ul/li[@class="citizens"]'); // the div to annotate
if (have.p < 40) { // need more colonists!
var busyPop = have.P - have.p;
var wantPop = 40 - have.p;
var people = projectPopulation({ popgte: busyPop + wantPop });
if (people && people != Infinity)
annotate(needPop, resolveTime(people, 1));
}
var needGold = $X('//ul/li[@class="gold"]');
if (have.g < 12e3 && pace.g > 0)
annotate(needGold, resolveTime((12e3 - have.g) / (pace.g / 3600), 1));
var woodadd = secondsToHours(jsVariable("startResourcesDelta"));
var needWood = $X('//ul/li[@class="wood"]');
if (have.w < 1250 && woodadd > 0)
annotate(needWood, resolveTime((1250 - have.w) / (woodadd / 3600), 1));
}
function dblClickTo(node, action, condition, capture) {
clickTo(node, action, condition, capture, "dblclick");
}
function clickTo(node, action, condition, capture, event) {
if (node) {
node.addEventListener(event || "click", function(e) {
if (!condition || $X(condition, e.target)) {
e.stopPropagation();
e.preventDefault();
if (isFunction(action))
action(e);
else
goto(action);
}
}, !!capture);
node.style.cursor = "pointer";
}
}
function post(url, args) {
var form = document.createElement("form");
form.method = "POST";
form.action = url;
for (var item in args) {
var input = document.createElement("input");
input.type = "hidden";
input.name = item;
input.value = args[item];
form.appendChild(input);
}
document.body.appendChild(form);
form.submit();
}
function sellStuff(e) { buysell(e, sell); }
function goShopping(e) { buysell(e, buy); }
function buysell(e, func) {
var img = e.target;
if (img.src) {
var what = img.src.match(/_([^.]+).gif$/)[1];
if (what) func(what);
} else if (haveBuilding("branchOffice")) {
goto("branchOffice");
}
}
function buy(what, amount) {
trade("buy", what, amount);
}
function sell(what, amount) {
trade("sell", what, amount);
}
function trade(operation, what, amount) {
var id = { w: "resource", W: 1, M: 2, C: 3, S: 4 }[resourceIDs[what]];
post(urlTo("branchOffice"), { type: { buy:"444", sell:"333" }[operation],
searchResource: id, range: "99" });
}
function sign(n) {
if ("undefined" == typeof n) n = 0;
return (n > 0 ? "+" : n == 0 ? "±" : "") + n;
}
// projects wine shortage time and adds lots of shortcut clicking functionality
function improveTopPanel() {
function tradeOnClick(li) {
var what = trim(li.className).split(" ")[0]; // "glass", for instance
if (!haveBuilding("branchOffice")) return;
clickTo(li, bind(buy, this, what), 'not(self::a or self::span)');
dblClickTo(li, bind(sell, this, what));
}
function projectWarehouseFull(node, what, have, pace) {
var capacity = number($X('../*[@class="tooltip"]', get(what)));
var time = resolveTime((capacity - have) / (pace/3600), 1);
node.title = (node.title ? node.title +", " : "") + lang[full] + time;
}
css(<>>);
// wine flow calculation
var flow = reapingPace();
var span = $("value_wine");
var time = flow.W < 0 ? Math.floor(number(span)/-flow.W) +"h" : sign(flow.W);
time = node({ tag: "span", className: "ellipsis", text: time, after: span });
if (flow.W < 0)
time.title = lang[empty] + resolveTime(number(span)/-flow.W * 3600, 1);
else if (flow.W > 0) {
var reap = secondsToHours(jsVariable("startTradegoodDelta"));
time.title = "+"+ reap +"/-"+ (reap - flow.W);
projectWarehouseFull(time, "wine", number(span), flow.W);
}
linkTo("tavern", time, { color: "#542C0F" });
// other resource flow
var income = { wood:flow.w };
var type = luxuryType();
var luxe = flow[type];
var name = luxuryType("name");
if (name != "wine") // already did that
income[name] = luxe;
// city resource type, for the city selection pane:
config.setCity("r", type, referenceCityID());
config.setCity("i", islandID(), referenceCityID());
for (name in income) {
var amount = income[name];
span = get(name);
var div = node({ tag: "span", className: "ellipsis", after: span,
text: sign(amount) });
if (amount > 0)
projectWarehouseFull(div, name, number(span), income[name]);
linkTo("port", div, { color: "#542C0F" });
}
var cityNav = $("cityNav");
if (flow.g) {
node({ id: "income", className: flow.g < 0 ? "negative" : "",
text: sign(flow.g), append: cityNav, title: " " });
var ap = $("value_maxActionPoints").parentNode;
ap.style.top = "-49px";
ap.style.left = "-67px";
ap.addEventListener("mouseover", hilightShip, false);
ap.addEventListener("mouseout", unhilightShip, false);
clickTo(ap, urlTo("merchantNavy"));
}
$x('id("cityResources")/ul[@class="resources"]/li/div[@class="tooltip"]').
forEach(function(tooltip) {
var a = linkTo("warehouse", tooltip);
if (a)
hideshow(a, [a, a.parentNode]);
});
clickTo(cityNav, urlTo("townHall"), 'self::*[@id="cityNav" or @id="income"]');
linkTo("luxe", $X('id("value_'+ luxuryType("name") +'")'));
linkTo("wood", $X('id("value_wood")'));
$x('id("cityResources")/ul/li[contains("wood wine marble glass sulfur",'+
'@class)]').forEach(tradeOnClick);
if (!isMyCity()) return;
var build = config.getCity("build", 0), now = Date.now();
if (build > now) {
time = $X('id("servertime")/ancestor::li[1]');
var a = node({ tag: "a", href: config.getCity("buildurl"), append: time });
node({ tag: "span", id: "done", className: "textLabel", append: a,
text: trim(resolveTime(Math.ceil((build-now)/1e3))) });
}
showHousingOccupancy();
showSafeWarehouseLevels();
showCityBuildCompletions();
}
function showCityBuildCompletions() {
var focused = referenceCityID();
var isles = ;
var isle = $X('id("changeCityForm")/ul/li[@class="viewIsland"]');
var lis = get("citynames");
var ids = cityIDs();
var names = cityNames();
if (names.length > 1) {
var images = [];
var links = ;
}
for (var i = 0; i < lis.length; i++) {
var id = ids[i];
var url = config.getCity("buildurl", 0, ids[i]);
var res = config.getCity("r", "", id);
var li = lis[i];
var t = config.getCity("build", 0, ids[i]);
if (t && t > Date.now() && url) {
t = resolveTime((t - Date.now()) / 1e3, 1);
node({ tag: "a", className: "ellipsis", href: url, append: li, text: t,
style: { marginLeft: "3px", background: "none", position: "static",
display: "inline", color: "#542C0F" }});
}
li.title = " "; // to remove the town hall tooltip
if (res) {
var has = {}; has[res] = "";
var img = visualResources(has, { size: 0.5, noMargin: 1 });
li.innerHTML = img + li.innerHTML;
if (links) { // island link bar
var a = ;
images.push(img); // as we can't set innerHTML of an E4X node
links.* += a;
}
img = $X('img', li);
img.style.margin = "0 3px";
img.style.background = "none";
if (id == focused) {
var current = $X('preceding::div[@class="dropbutton"]', li);
current.insertBefore(img.cloneNode(true), current.firstChild);
if (a) a.@style += " outline: 1px solid #FFF;";
}
}
}
if (!links || !isle) return;
var width = "width: "+ (lis.length * 33) + "px";
isles.@style += width;
links.@style += width;
isles += links;
//prompt(!links || !isle, isles);
isle.innerHTML += isles.toXMLString();
$x('div/div/a', isle).forEach(kludge);
$x('div/div//text()', isle).forEach(rm);
function kludge(a, i) {
a.innerHTML = images[i];
img = $X('img', a);
img.height = 10;
img.width = 13;
}
}
function hilightShip() {
var ship = get("ship");
if (ship) ship.style.backgroundPosition = "0 -53px";
}
function unhilightShip() {
var ship = get("ship");
if (ship) ship.style.backgroundPosition = "";
}
var troops = { // land units:
301: {n:"Slinger",p:1,w:40,b:"12m",u:8,m:1,o:4,a:7,d:7,A:2,D:2,s:10,c:"Human",v:20},
302: {n:"Swordsman",p:2,w:47,S:16,b:"17m",u:16,m:3,o:5,a:18,d:14,A:4,D:3,s:12,c:"Human",v:20,x:"Assault"},
303: {n:"Phalanx",p:4,w:104,S:64,b:"40m",u:24,m:4,o:7,a:24,d:40,A:6,D:10,s:14,c:"Human",v:20,x:"Resistance"},
307: {n:"Ram",p:8,w:198,S:128,b:"42m",u:52,m:5,o:8,a:14,d:18,A:3,D:4,s:16,c:"Machina",v:20,x:"Ram"},
313: {n:"Archer",p:4,w:172,S:86,b:"49m",u:32,m:7,o:10,a:40,d:40,A:10,D:10,s:12,c:"Human",v:20},
306: {n:"Catapult",p:10,w:342,S:232,b:"49m",u:72,m:10,o:14,a:36,d:28,A:9,D:7,s:16,c:"Machina",v:20,x:"Ram"},
304: {n:"Gunsman",p:7,w:355,S:154,b:"1h 23m",u:58,m:14,o:18,a:80,d:64,A:18,D:14,s:10,c:"Human",v:20},
305: {n:"Mortar",p:12,w:1325,S:938,b:"1h 53m",u:128,m:19,o:21,a:64,d:64,A:15,D:15,s:16,c:"Machina",v:20,x:"Ram"},
308: {n:"Steam Giant",p:6,w:1150,S:716,b:"1h 45m",u:68,m:16,o:20,a:100,d:140,A:20,D:30,s:14,c:"Machina",v:20,x:"Resistance"},
312: {n:"Gyrocopter",p:8,w:1250,S:670,b:"1h 2m",u:97,m:12,o:16,a:112,d:112,A:25,D:25,s:12,c:"Machina",v:20},
309: {n:"Bombardier",p:24,w:2270,S:878,b:"2h 10m",u:228,m:22,o:24,a:200,d:165,A:45,D:35,s:14,c:"Machina",v:20,x:"Assault"},
311: {n:"Doctor",p:6,w:640,C:361,b:"1h 2m",u:244,m:11,o:12,a:4,d:28,A:0,D:0,s:14,c:"Human",v:20,x:"Healer"},
310: {n:"Cook",p:4,w:520,W:103,b:"38m",u:138,m:8,o:8,a:6,d:26,A:0,D:0,s:16,c:"Human",v:20,x:"Regeneration"}
};
var ships = { // sea units:
201: {n:"Cargo Ship",a:0,d:0,s:4,c:"Steamship",v:20,A:0,D:0},
210: {n:"Ram-Ship",p:6,w:56,S:21,b:"34m",u:20,m:1,o:3,a:16,d:16,A:4,D:4,s:10,c:"Sailer",v:10},
213: {n:"Ballista Ship",p:5,w:72,S:29,b:"47m",u:24,m:3,o:5,a:20,d:28,A:5,D:7,s:11,c:"Sailer",v:8,x:"Resistance"},
211: {n:"Flamethrower",p:5,w:105,S:77,b:"1h 55m",u:45,m:5,o:7,a:40,d:40,A:10,D:10,s:12,c:"Steamship",v:8,x:"Assault"},
214: {n:"Catapult Ship",p:10,w:173,S:76,b:"3h 11m",u:57,m:7,o:10,a:60,d:60,A:12,D:12,s:16,c:"Sailer",v:6},
215: {n:"Mortar Ship",p:22,w:456,S:282,b:"3h 38m",u:130,m:12,o:15,a:160,d:160,A:35,D:35,s:14,c:"Steamship",v:4},
216: {n:"Paddle Wheel Ram",p:12,w:513,S:167,b:"4h 8m",u:114,m:10,o:13,a:100,d:90,A:20,D:18,s:13,c:"Steamship",v:8,x:"Assault"},
212: {n:"Diving Boat",p:16,w:493,C:378,b:"5h 5m",u:126,m:15,o:16,a:110,d:155,A:20,D:30,s:10,c:"Steamship",v:2,x:"Resistance"}
};
function showUnitLevels(specs) {
function level(what, unit, li, pre, post) {
var l = 0;
var is = what.charAt(), up = is.toUpperCase();
var img = $X('img[@class="'+ what +'-icon"]', li);
if (img)
l = 4 - img.src.match(/(\d)\.gif$/)[1];
$X('div/h4', li).innerHTML += pre + (unit[is] + unit[up]*l) + post;
}
function augmentUnit(li) {
var stats = unitStatsFromImage($X('div[@class="unitinfo"]//img', li));
level("att", stats, li, " (", "");
level("def", stats, li, "/", ")");
}
$x('id("units")/li[div[@class="unitinfo"]]').forEach(augmentUnit);
}
function shipyardView() {
dontSubmitZero();
showUnitLevels(ships);
}
function barracksView() {
dontSubmitZero();
showUnitLevels(troops);
var u = $x('id("unitConstructionList")//div[starts-with(@id,"queueEntry")]');
u.forEach(projectCompletion);
}
function unitStatsFromImage(img) {
function normalize(name) {
return name.replace(/[ -].*/, "").toLowerCase();
}
var name = img.src.split("/").pop();
if (name) {
var junk = /^(ship|y\d+)_|_(r|\d+x\d+)(?=[_.])|_faceright|\....$/g;
name = name.replace(junk, "");
for (var id in troops)
if (normalize(troops[id].n) == name)
return troops[id];
for (var id in ships)
if (normalize(ships[id].n) == name)
return ships[id];
}
}
function workshopView() {
function show(td, base, delta) {
var img = $X('img', td);
var type = img && img.src.match(/_([^.]+).gif$/)[1];
var level = { bronze: 0, silber: 1, gold: 2 }[type];
for (var l = 0; l < 3; l++) {
var l1 = base + delta * l; // \uFFEB \u27A0 #906646
var l2 = l1 + delta;
node({ className: "stats", append: td, text: l1 +" \u27A1 "+ l2,
style: { opacity: l == level ? "1.0" : "0.5" }});
}
}
function augment(tr) {
var stats = unitStatsFromImage($X('td[@class="object"]/img', tr))
if (stats) {
var cells = $x('td/table[@class="inside"]/tbody/tr[1]/td[1]', tr);
show(cells[0], stats.a, stats.A);
show(cells[1], stats.d, stats.D);
}
}
$x('id("demo")//tr[td[@class="object"]]').forEach(augment);
projectCompletion("upgradeCountdown", "done");
}
function showSafeWarehouseLevels() {
function showSafeLevel(div) {
var n = "wood" == div.parentNode.className ? wood : rest;
node({ tag: "span", className: "ellipsis", text: n, append: div });
}
var level = config.getCity("building7", 0);
var wood = buildingCapacity("warehouse", "wood", level);
var rest = buildingCapacity("warehouse", "rest", level);
$x('id("cityResources")/ul/li/*[@class="tooltip"]').map(showSafeLevel);
}
function showHousingOccupancy(opts) {
var txt = $("value_inhabitants").firstChild;
var text = txt.nodeValue.replace(/\s/g, "\xA0");
var pop = projectPopulation(opts);
var time = ":∞";
if (pop.upgradeIn)
time = ":" + secsToDHMS(pop.upgradeIn, 0);
else //if (pop.asymptotic > pop.maximum)
time = "/" + sign(pop.maximum - pop.current);
//console.log(pop.toSource());
txt.nodeValue = text.replace(new RegExp("[:)/].*$"), time +")");
var townSize = $X('id("information")//ul/li[@class="citylevel"]');
if (townSize)
node({ id: "townhallfits", className: "ellipsis", append: townSize,
text: pop.current +"/"+ pop.maximum +"; "+ sign(pop.growth) +"/h" });
return pop;
}
function getFreeWorkers() {
return integer($("value_inhabitants").textContent.match(/^[\d,.]+/)[0]);
}
function getPopulation() {
return integer($("value_inhabitants").textContent.match(/\(([\d,.]+)/)[1]);
}
function getMaxPopulation(townHallLevel) {
if ("undefined" == typeof townHallLevel)
townHallLevel = buildingLevel("townHall", 1);
var maxPopulation = buildingCapacity("townHall", townHallLevel);
if (config.getServer("tech2080"))
maxPopulation += 50; // Holiday bonus
if (config.getServer("tech3010") && isCapital())
maxPopulation += 50; // Well Digging bonus (capital city only)
return maxPopulation;
}
function projectPopulation(opts) {
function getGrowth(population) {
return (happy - Math.floor(population)) / 50;
}
var wellDigging = isCapital() && config.getServer("tech3010") ? 50 : 0;
var holiday = config.getServer("tech2080") ? 25 : 0;
var tavern = 12 * buildingLevel("tavern", 0);
var wineLevel = opts && opts.hasOwnProperty("wine") ? opts.wine :
config.getCity("wine", 0);
var wine = 80 * buildingCapacities.tavern.indexOf(wineLevel);
var museum = 20 * buildingLevel("museum", 0);
var culture = 50 * config.getCity("culture", 0);
var happy = 196 + wellDigging + holiday + tavern + wine + museum + culture;
//console.log(wellDigging, holiday, tavern, wine, museum, culture, happy);
var population = opts && opts.population || getPopulation();
var initialGrowth = getGrowth(population);
var growthSignSame = initialGrowth > 0 ? function plus(p) { return p > 0; } :
function minus(p) { return p < 0; };
var currentPopulation = population;
var asymptoticPopulation = population, asymAt;
var change = initialGrowth > 0 ? 1 : -1;
while (growthSignSame(getGrowth(asymptoticPopulation)))
asymptoticPopulation += change;
var time = 0;
var currentGrowth = initialGrowth;
var maximumPopulation = getMaxPopulation();
while (growthSignSame(currentGrowth) && (population < maximumPopulation)) {
currentGrowth = getGrowth(population);
population += currentGrowth / 4; // add 15 minutes of growth
time += 60 * 15;
if (opts && opts.popgte && population >= opts.popgte)
return time;
}
if (opts && opts.popgte) return Infinity;
var hint = $("cityNav"), warn = true;
if (asymptoticPopulation <= maximumPopulation) {
hint.title = "Reaches asymptotic population "+ asymptoticPopulation +"/"+
maximumPopulation +" at "+ resolveTime(time, 1);
warn = false;
}
var people = $("value_inhabitants");
var hqLevel = config.getCity("building0", 1);
var nextHQUpgradeTime = parseTime(costs[0][hqLevel].t);
var upgradingTownHall = $X('id("done")/../@href = "'+ urlTo("townHall") +'"');
hint.title = lang[full] + resolveTime(time, 1);
// < 15 min left for expanding Town Hall ahead of time to meet growth?
if (warn) {
if (time < 15 * 60 + nextHQUpgradeTime && !upgradingTownHall)
people.className = "storage_danger";
if (population == maximumPopulation)
people.className = "storage_full";
else
people.className = "";
if (!upgradingTownHall)
hint.title += lang[startExpand] +
resolveTime(time - nextHQUpgradeTime, 1);
} else
people.className = "";
var upgrade = !upgradingTownHall && asymptoticPopulation > maximumPopulation;
return {
happy: happy - currentPopulation,
growth: initialGrowth,
current: currentPopulation,
asymptotic: asymptoticPopulation,
maximum: maximumPopulation,
finalAt: Date.now() + time * 1e3,
upgradeIn: upgrade ? time - nextHQUpgradeTime : 0,
final: Math.min(asymptoticPopulation, maximumPopulation),
time: time
};
}
function projectBuildStart(root, result) {
function projectWhenWeHaveResourcesToStartBuilding(ul) {
if (!result) return;
var time = 0;
var need = {};
var pace = reapingPace();
var have = currentResources();
var woodNode = $X('li[starts-with(@class,"wood")]', ul);
if (woodNode)
need.w = woodNode;
var needRest = $x('id("buildingUpgrade")//ul[@class="resources"]/li[not('+
'contains(@class,"wood") or contains(@class,"time"))]');
for (var i = 0; i < needRest.length; i++) {
var what = needRest[i];
var id = resourceIDs[what.className.split(" ")[0]];
need[id] = what;
}
for (var r in need) {
var node = need[r];
var amount = number(node);
if (amount <= have[r]) continue;
if (!pace[r]) {
node.title += ": (∞)";
time = Infinity;
} else {
what = 3600 * (amount - have[r]) / pace[r];
node.title += ": ("+ resolveTime(what, 1) +")";
time = Math.max(time, what);
}
}
if (time && (node = $X(result, ul))) {
if (Infinity == time)
time = "\xA0(∞)";
else
time = "\xA0("+ resolveTime(time, 1) +")";
node.appendChild(document.createTextNode(time));
}
}
if ($("donateForm")) return; // is a resource, not something you build/upgrade
result = result || 'li[@class="time"]';
$x('.//ul[@class="resources"][not(ancestor::*[@id="cityResources"])]',
$(root)).forEach( projectWhenWeHaveResourcesToStartBuilding );
}
function projectHaveResourcesToUpgrade() {
// $X('ul[@class="actions"]/li[@class="upgrade"]/a').className = "disabled";
projectBuildStart("buildingUpgrade", 'preceding-sibling::h4');
}
function projectCompletion(id, className, loc) {
var tag = "string" == typeof id ? $(id) : id, set = false;
if (isNumber(className)) className = loc = undefined; // called by forEach/map
if (tag) {
id = tag.id;
// console.log("T: %x", $("servertime").textContent);
// console.log("L: %x", tag.textContent);
// console.log("D: %x", parseTime(tag.textContent));
// console.log("F: %x", resolveTime(parseTime(tag.textContent)));
var time = parseTime(tag.textContent);
var done = resolveTime(time);
var div = node({ tag: tag.nodeName.toLowerCase(), className: className,
after: tag, text: done });
time = time * 1e3 + Date.now();
if ("upgradeCountDown" == id)
set = config.setCity("build", time);
if ("cityCountdown" == id) {
set = config.setCity("build", time);
var move = $X('ancestor::*[contains(@class,"timetofinish")]', tag);
if (move)
move.style.marginLeft = "-40%";
}
if (set) {
if ("string" == typeof loc)
loc = $X(loc, tag);
else if ("undefined" == typeof loc)
if (location.search.match(/\?/))
loc = location;
else
loc = { href: urlTo(document.body.id) };
if (loc)
config.setCity("buildurl", loc.href);
}
}
return time;
}
function tavernView() {
function amount() {
return number(wine.options[wine.selectedIndex]) || 0;
}
function makeGrowthrate() {
var style = {
position: "absolute",
textAlign: "center",
margin: "0 auto",
width: "100%"
};
return node({ tag: "span", id: "growthRate", className: "value",
style: style, before: $("citySatisfaction") });
}
function recalcTownHall() {
var pop = showHousingOccupancy({ wine: amount() });
var rate = $("growthRate") || makeGrowthrate();
rate.innerHTML = sign(pop.growth.toFixed(2));
}
var wine = $("wineAmount").form.elements.namedItem("amount");
wine.parentNode.addEventListener("DOMNodeInserted", function() {
setTimeout(recalcTownHall, 10);
}, false);
config.setCity("wine", amount());
recalcTownHall();
}
function title(detail) {
var server = location.hostname.match(/^s(\d+)\.(.*)/), host = "";
if (server) {
host = " "+ server[2];
server = "αβγδεζηθικλμνξοπρστυφχψω".charAt(parseInt(server[1], 10)-1);
}
if (!detail)
detail = $X('id("breadcrumbs")/*[last()]');
document.title = (server ? server + " " : "") + detail.textContent + host;
}
function clickResourceToSell() {
function haveHowMuch(e) {
var img = e.target;
var resource = img.src.match(/([^_]+).gif$/)[1].replace("glass", "crystal");
return number(get(resource));
}
function sell100(e) {
var have = haveHowMuch(e);
var sell = $x('following::input[@type="text"]', e.target);
sell[0].value = Math.min(have, 100 + integer(sell[0].value || 0));
sell[1].value = Math.max(25, integer(sell[1].value));
}
function sellAll(e) {
var have = haveHowMuch(e);
var sell = $x('following::input[@type="text"]', e.target);
sell[0].value = have;
sell[1].value = Math.max(25, integer(sell[1].value || 0));
}
function clickToSell(img) {
img.addEventListener("click", sell100, false);
//img.addEventListener("dblclick", sellAll, false);
img.style.cursor = "pointer";
}
$x('//table[@class="tablekontor"]/tbody/tr/td[1]/img').forEach(clickToSell);
}
/*---------------------
Ajout du panel dans le menu
---------------------*/
function panelInfo() { // Ajoute un element en plus dans le menu.
var panel = node({ className: "dynamic", before: $("mainview") });
var titre = node({ tag: "h3", className: "header", append: panel,
text: name + version +": " });
var langChoice = node({ tag: "a", text: lang[language], append: titre,
href: "#" });
langChoice.addEventListener("click", promptLanguage, false);
var corps = node({ id:"Kronos", className: "content", append: panel,
style: { margin: "3px 10px" }});
node({ className: "footer", append: panel });
return langChoice;
}
function islandID(city) {
return urlParse("id", $X('//li[@class="viewIsland"]/a').search);
}
function referenceIslandID(city) {
city = city || referenceCityID();
city = config.getServer("cities", {})[city];
return city && city.i;
}
function referenceCityID(index) {
if (index) return $("citySelect").selectedIndex;
var names = get("citynames"), name;
for (var i = 0; name = names[i]; i++)
if (/active/.test(name.className||""))
return cityIDs()[i];
}
function cityID() {
var id = urlParse("id");
var view = urlParse("view");
if (id)
if (buildingIDs.hasOwnProperty(view) ||
{ city:1 }[view])
return integer(id);
return integer(urlParse("id", $X('//li[@class="viewCity"]/a').search));
}
function cityIDs() {
return pluck($x('id("citySelect")/option'), "value").map(integer);
}
function cityName() {
return cityNames()[referenceCityID("index")];
}
function cityNames() {
return pluck(get("citynames"), "textContent");
}
function isCapital() {
var city = cityID();
var capital = config.getServer("capital", 0);
if (capital)
return city == capital;
return city == cityIDs()[0];
}
function isMyCity() {
return cityIDs().indexOf(cityID()) != -1;
}
/*------------------------
/ \
/ ! \ Function Principal.
-------
------------------------*/
function principal() {
if (innerWidth > 1003) document.body.style.overflowX = "hidden"; // !scrollbar
if (!location.hostname.match(/^s\d+\./)) return login();
var langChoice = panelInfo();
var chemin = $("Kronos");
addCSSBubbles();
var view = urlParse("view");
var action = urlParse("action");
var building = view && buildingID(view);
if (isDefined(building)) {
var level = $X('id("buildingUpgrade")//div[@class="buildingLevel"]');
if (level)
config.setCity("building"+ building, number(level));
}
var help = $X('id("buildingUpgrade")/h3/a[@class="help"]');
if (help)
linkTo(urlTo("building", buildingID(urlParse("view"))), help);
switch (view || action) {
case "tavern": tavernView(); break;
case "resource": // fall-through:
case "tradegood": highlightMeInTable(); // fall-through:
case "deployment": // fall-through:
case "takeOffer": scrollWheelable(); break;
case "transport": scrollWheelable(); evenShips(); break;
case "loginAvatar":// &function=login
case "CityScreen": // &function=build&id=...&position=4&building=13
case "city": cityView(); break;
case "buildingDetail": buildingDetailView(); break;
case "port": portView(); break;
case "island": islandView(); break;
case "worldmap_iso": worldmap_isoView(); break;
case "townHall": townHallView(); break;
case "culturalPossessions_assign": // fall-through:
case "museum": museumView(); break;
case "fleetGarrisonEdit": // fall-through:
case "armyGarrisonEdit": dontSubmitZero(); break;
case "shipyard": shipyardView(); break;
case "barracks": barracksView(); break;
case "workshop-army": workshopView("troops"); break;
case "workshop-fleet": workshopView("ships"); break;
case "buildingGround": buildingGroundView(); break;
case "branchOffice": branchOfficeView(); break;
case "researchOverview": researchOverviewView(); break;
case "colonize": scrollWheelable(); colonizeView(); break;
case "merchantNavy": merchantNavyView(); break;
case "militaryAdvisorReportView":
militaryAdvisorReportViewView(); break;
case "militaryAdvisorCombatReports":
militaryAdvisorCombatReportsView(); break;
case "militaryAdvisorMilitaryMovements":
militaryAdvisorMilitaryMovementsView(); break;
case "researchAdvisor": researchAdvisorView(); break;
case "diplomacyAdvisor": diplomacyAdvisorView(); break;
case "plunder": plunderView(); break;
case "Espionage":
case "safehouse": safehouseView(); break;
case "safehouseReports": safehouseReportsView(); break;
case "academy": academyView(); break;
}
var upgradeDiv = $("upgradeCountDown");
var buildDiv = $("buildCountDown");
projectCompletion(upgradeDiv, "time")
projectCompletion(buildDiv);
projectHaveResourcesToUpgrade();
var queued;
if ((queued = (location.hash||"").match("#q:(.*)")))
reallyUpgrade(queued[1]);
processQueue(!queued);
document.addEventListener("click", changeQueue, true);
var research = config.getServer("research", "");
if (research) {
var a = node({ tag: "a", href: urlTo("academy"), append: chemin,
text: lang[researching] +": "+ research });
var tech = techinfo(research);
if (tech)
a.title = tech.does +" ("+ tech.points + " points)"; // I18N
var done = config.getServer("researchDone");
if (done) a.title += resolveTime((done-Date.now()) / 1e3);
node({ tag: "br", append: chemin });
}
improveTopPanel();
if ({ city: 1, island: 1 }[view])
unsafeWindow.friends = eval(config.getServer("culturetreaties", "([])"));
title();
var FIN = new Date();
langChoice.title = lang[execTime] +": "+ (FIN - DEBUT) +"ms";
}
function login() {
var uni = $("universe");
if (!uni || location.pathname != "/" || !document.referrer ||
!document.referrer.indexOf(location.href))
return;
var site = /^http:..s(\d+)\.ikariam/.exec(document.referrer);
if (site)
uni.selectedIndex = integer(site[1]) - 1;
}
GM_registerMenuCommand("Ikariam Kronos Tools: Your language", promptLanguage);
function promptLanguage() {
var help = [];
for (var id in langs)
help.push(id+": "+langs[id][language]);
while (!langs.hasOwnProperty(newLanguage)) {
var newLanguage = prompt("Ikariam Kronos Tools: " +
"Which language do you prefer?\n(" +
help.join(", ") + ")", getLanguage());
if (!newLanguage) return;
if (langs.hasOwnProperty(newLanguage))
config.set("language", newLanguage);
}
location.reload();
}
function getLanguage() {
function guess() {
var guess = navigator.language.replace(/-.*/,"");
return langs.hasOwnProperty(guess) ? guess : "en";
}
if (config.get("language") == "sp") config.set("language", "es");
return config.get("language", guess());
}
function $(id) {
return document.getElementById(id);
}
function $x( xpath, root ) {
var doc = root ? root.evaluate ? root : root.ownerDocument : document, next;
var got = doc.evaluate( xpath, root||doc, null, 0, null ), result = [];
switch (got.resultType) {
case got.STRING_TYPE:
return got.stringValue;
case got.NUMBER_TYPE:
return got.numberValue;
case got.BOOLEAN_TYPE:
return got.booleanValue;
default:
while (next = got.iterateNext())
result.push( next );
return result;
}
}
function $X( xpath, root ) {
var got = $x( xpath, root );
return got instanceof Array ? got[0] : got;
}
function upgradeConfig0() {
function makeNWO() {
var obj = { v: 1 };
for (var name in nwo)
obj[name] = {};
return obj;
}
var ns = {};
var nwo = { capital:1, treaties:1, cities:1, islands:1, techs:1, battles:1 };
var old = config.keys(), key, server, isle, city, tech, r, b, save, junk;
for (var i = 0; key = old[i]; i++) {
var value = config.get(save = key);
// belongs to which server scope?
if ((server = key.match(/(.*):([^:]+\D[^.])$/))) {
[junk, key, server] = server;
} else { // data sanitization; probably drop
if ("language" == key) {
ns.config = ns.config || {};
ns.config[key] = value;
}
continue;
}
var scope = ns[server] || makeNWO();
ns[server] = scope;
// server global stuff first:
if ((tech = key.match(/^tech(\d+)$/))) {
[junk, tech] = tech;
scope.techs[tech] = 1;
continue;
}
if (/^research(|Done)$/.test(key)) {
scope = scope.techs;
scope.research = scope.research || {};
}
switch (key) {
case "war":
scope = scope.battles;
scope.won = value.won;
scope.lost = value.lost;
continue;
case "cities":
scope = scope.cities;
for (var id in value) {
city = scope[id] = scope[id] || {};
city.i = value[id].i;
city.n = value[id].n;
}
continue;
case "capital": scope.capital = integer(value); continue;
case "reports": scope.battles.reports = value; continue;
case "research": scope.research.n = value; continue;
case "researchDone": scope.research.t = value; continue;
case "culturetreaties": scope.treaties.culture = value; continue;
}
// island global stuff; only resource levels at this time:
if ((isle = key.match(/^(.*)\/(\d+)$/))) {
[junk, r, isle] = isle;
scope.islands[isle] = scope.islands[isle] || {};
scope = scope.islands[isle];
if (/^[WMCS]$/.test(r)) {
scope.r = r;
scope.R = value;
} else {
scope[r] = value;
}
continue;
}
// the rest; city local stuff:
if (!(city = key.match(/^(.*):(\d+)$/)))
continue;
[junk, key, city] = city;
city = scope.cities[city] = scope.cities[city] || {};
if ((b = key.match(/^(building|posbldg)(\d+)$/))) {
[junk, key, b] = b;
if (!city.l) city.l = [];
if (!city.p) city.p = [];
if ("building" == key)
city.l[b] = value;
else
city.p[b] = value;
continue;
}
switch (key) {
case "q": city.q = eval(value); continue;
case "r": city.r = value; continue; // temporary hack
case "gold": city.g = value; continue;
case "build": city.t = value; continue;
case "buildurl": city.u = value; continue;
default:
continue;
case "wine": b = buildingIDs.tavern; break;
case "culture": b = buildingIDs.museum; break;
case "researchers": b = buildingIDs.academy; break;
}
if (!city.x) city.x = {};
city.x[b] = integer(value);
}
return ns;
}
/*
s10.org: {
techs: {all tech id:s we have},
.research: {
i: 2090,
n: "Helping hands",
t: 1207648486127
},
battles: {
won: 113,
lost: 8,
reports: {
BID: {
t:1207175100000, w:1, l:{g:136, W:59}, c:38714, a:attackingCity?},
}
},
cities: {
4711: {
n: name,
i: iID,
g: gold net income,
t: completionTime,
u: buildURL,
l: [ lvl0, ...], // building level
p: [ pos0, ...], // building position
q: [ bID, ...], // buildings sceduled to be built
x: { bID:building-special - tavern: wine level; academy: scientists,
r: resourceWorkers, w: woodWorkers },
b: { bID:time busy to }
}, ...
}
}
*/
// config.get() and config.set() store config data in (near-)json in prefs.js.
var config = (function(data) {
function get(name, value) {
return data.hasOwnProperty(name) ? data[name] : value;
}
function getCity(name, value, id) {
return getServer(name +":"+ (id || cityID()), value);
}
function getIsle(name, value, id) {
return getServer(name +"/"+ (id || cityID()), value);
}
function getServer(name, value) {
return get(name +":"+ location.hostname, value);
}
function set(name, value) {
if (value === undefined)
delete data[name];
else
data[name] = value;
//console.count("set");
GM_setValue("config", uneval(data));
return value;
}
function setCity(name, value, id) {
return setServer(name +":"+ (id || cityID()), value);
}
function setIsle(name, value, id) {
return setServer(name +"/"+ (id || islandID()), value);
}
function setServer(name, value) {
return set(name +":"+ location.hostname, value);
}
function remCity(name) {
return remServer(name +":"+ cityID());
}
function remIsle(name) {
return remServer(name +"/"+ islandID());
}
function remServer(name) {
return remove(name +":"+ location.hostname);
}
function keys(re) {
re = re || /./;
var list = [];
for (var id in data)
if (data.hasOwnProperty(id) && re.test(id))
list.push(id);
return list;
}
function remove(id) {
if (/function|object/.test(typeof id)) {
var value = [], re = id;
for (id in data)
if (data.hasOwnProperty(id) && id.test(re)) {
value.push(data[id]);
delete data[id];
}
} else {
value = data[id];
delete data[id];
}
return value;
}
return { get:get, set:set, remove:remove, keys:keys,
setCity:setCity, getCity:getCity, remCity:remCity,
setIsle:setIsle, getIsle:getIsle, remIsle:remIsle,
setServer:setServer, getServer:getServer, remServer:remServer };
})(eval(GM_getValue("config", "({})")));
function bind(fn, self) {
var args = [].slice.call(arguments, 2);
return function() {
fn.apply(self, args.concat([].slice.call(arguments)));
};
}
function isNull(n) { return null === n; }
function isArray(a) { return a && a.hasOwnProperty("length"); }
function isString(s) { return "string" == typeof s; }
function isNumber(n) { return "number" == typeof n; }
function isObject(o) { return "object" == typeof o; }
function isBoolean(b) { return "boolean" == typeof b; }
function isDefined(v) { return "undefined" != typeof v; }
function isFunction(f) { return "function" == typeof f; }
function isUndefined(u) { return "undefined" == typeof u; }
function isTextNode(n) { return isObject(n) && n.nodeType == 3; }
function rm(node) {
node && node.parentNode && node.parentNode.removeChild(node);
}
function hide(node) {
if (node) return node.style.display = "none";
}
function show(node) {
if (node) return node.style.display = "block";
}
function hideshow(node, nodes) {
function listen(on) {
on.addEventListener("mouseover", function(){ show(node); }, false);
on.addEventListener("mouseout", function(){ hide(node); }, false);
}
nodes = nodes || [node];
nodes.forEach(listen);
}
lang = langs[getLanguage()];
XML.setSettings({
ignoreProcessingInstructions:false,
ignoreWhitespace:false,
ignoreComments:false,
prettyPrinting:false, prettyIndent:2
});
//prompt(1, upgradeConfig0()["s11.ikariam.org"].toSource());
principal(); // Appel de la fonction principal.