// ==UserScript==
// @name ModernBot PT-BR JoeMan
// @author Sau1707
// @description A modern grepolis bot
// @version 1.18.5
// @match http://*.grepolis.com/game/*
// @match https://*.grepolis.com/game/*
// @updateURL https://github.com/Sau1707/ModernBot/blob/main/dist/merged.user.js
// @downloadURL https://github.com/Sau1707/ModernBot/blob/main/dist/merged.user.js
// @icon https://raw.githubusercontent.com/Sau1707/ModernBot/main/img/gear.png
// @require http://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js
// ==/UserScript==
var uw;
if (typeof unsafeWindow == 'undefined') {
uw = window;
} else {
uw = unsafeWindow;
}
var style = document.createElement("style");
style.textContent = `.auto_build_up_arrow{background:url(https://gpit.innogamescdn.com/images/game/academy/up.png) no-repeat -2px -2px;width:18px;height:18px;position:absolute;right:-2px;bottom:12px;transform:scale(.8);cursor:pointer}.auto_build_down_arrow{background:url(https://gpit.innogamescdn.com/images/game/academy/up.png) no-repeat -2px -2px;width:18px;height:18px;position:absolute;right:-2px;bottom:-3px;transform:scale(.8) rotate(180deg);cursor:pointer}.auto_build_box{background:url(https://gpit.innogamescdn.com/images/game/academy/tech_frame.png) no-repeat 0 0;width:58px;height:59px;position:relative;overflow:hidden;display:inline-block;vertical-align:middle}.auto_build_building{position:absolute;top:4px;left:4px;width:50px;height:50px;background:url(https://gpit.innogamescdn.com/images/game/main/buildings_sprite_50x50.png) no-repeat 0 0}.auto_trade_troop{position:absolute;top:4px;left:4px;width:50px;height:50px;background:url(https://gpit.innogamescdn.com/images/game/autogenerated/units/unit_icons_50x50_654368f.png) no-repeat 0 0}.auto_build_lvl{position:absolute;bottom:3px;left:3px;margin:0;font-weight:700;font-size:12px;color:#fff;text-shadow:0 0 2px #000,1px 1px 2px #000,0 2px 2px #000}#buildings_lvl_buttons{padding:5px;max-height:400px;user-select:none}#troops_lvl_buttons{padding:5px;max-height:400px;user-select:none}.progress_bar_auto{position:absolute;z-index:1;height:100%;left:0;top:0;background-image:url(https://gpit.innogamescdn.com/images/game/border/header.png);background-position:0 -1px;filter:brightness(100%) saturate(186%) hue-rotate(241deg)}.modern_bot_settings{z-index:10;position:absolute;top:52px!important;right:116px!important}.console_modernbot{width:100%;height:100%;background-color:#164d06;color:#fff;font-family:monospace;font-size:16px;padding:20px;box-sizing:border-box;overflow-y:scroll;display:flex;flex-direction:column-reverse}#MODERN_BOT_content{height:100%}.console_modernbot p{margin:1px}.population_icon{background:url(https://gpit.innogamescdn.com/images/game/autogenerated/layout/layout_095495a.png) no-repeat -697px -647px;width:25px;height:20px;position:absolute;right:2px}.population_icon p{text-align:end;position:absolute;right:30px;padding:0;margin:0;color:#000;font-weight:700}.split_content{width:100%;display:inline-flex;justify-content:space-between}`;
document.head.appendChild(style);
class ModernUtil {
/* CONSTANTS */
REQUIREMENTS = {
sword: {},
archer: { research: 'archer' },
hoplite: { research: 'hoplite' },
slinger: { research: 'slinger' },
catapult: { research: 'catapult' },
rider: { research: 'rider', building: 'barracks', level: 10 },
chariot: { research: 'chariot', building: 'barracks', level: 15 },
big_transporter: { building: 'docks', level: 1 },
small_transporter: { research: 'small_transporter', building: 'docks', level: 1 },
bireme: { research: 'bireme', building: 'docks', level: 1 },
attack_ship: { research: 'attack_ship', building: 'docks', level: 1 },
trireme: { research: 'trireme', building: 'docks', level: 1 },
colonize_ship: { research: 'colonize_ship', building: 'docks', level: 10 },
manticore: { research: 'manticore', god: 'zeus', building: 'temple', level: 15 }, // tentar acrescentar as manticore na lista
};
constructor(console, storage) {
this.console = console;
this.storage = storage;
}
/* Usage async this.sleep(ms) -> stop the code for ms */
sleep = ms => {
return new Promise(resolve => setTimeout(resolve, ms));
};
/**
* Gere uma lista de IDs de cidades localizadas em ilhas grandes.
* Uma ilha grande é definida como uma ilha que possui pelo menos uma cidade que não está em uma ilha pequena..
* @returns {Array} -Matriz de IDs de cidade.
*/
generateList = () => {
const townList = uw.MM.getOnlyCollectionByName('Town').models;
const islandsList = [];
const polisList = [];
for (const town of townList) {
const { island_id, id, on_small_island } = town.attributes;
if (on_small_island) continue; // Evite cidades em pequenas ilhas
if (!islandsList.includes(island_id)) {
islandsList.push(island_id);
polisList.push(id);
}
}
return polisList;
};
/**
* Retorna o código HTML para um botão com um ID, texto, função e propriedades opcionais especificados.
*
* @param {string} id - O ID do botão.
* @param {string} text - O texto a ser exibido no botão.
* @param {Function} fn - A função a ser chamada quando o botão é clicado.
* @param {string} [props] - Propriedades opcionais para passar para a função.
* @returns {string} - O código HTML do botão.
*/
getButtonHtml(id, text, fn, props) {
const name = this.constructor.name.charAt(0).toLowerCase() + this.constructor.name.slice(1);
props = isNaN(parseInt(props)) ? `'${props}'` : props;
const click = `window.modernBot.${name}.${fn.name}(${props || ''})`;
return `
${text}
`;
}
/**
* Retorna o HTML de um título de jogo com um cabeçalho clicável que alterna uma função.
*
* @param {string} id - O ID do elemento HTML.
* @param {string} text - O texto a ser exibido no título.
* @param {function} fn - A função para alternar.
* @param {string|number} props - As propriedades a serem passadas para a função.
* @param {boolean} enable - Se o título está habilitado ou não.
* @param {string} [desc='(clique para alterna)'] - A descrição a ser exibida.
* @returns {string} O HTML do título do jogo.
*/
getTitleHtml(id, text, fn, props, enable, desc = '') {
const name = this.constructor.name.charAt(0).toLowerCase() + this.constructor.name.slice(1);
props = isNaN(parseInt(props)) && props ? `"${props}"` : props;
const click = `window.modernBot.${name}.${fn.name}(${props || ''})`;
const filter = 'brightness(100%) saturate(186%) hue-rotate(241deg)';
return `
${text}
${desc}
`;
}
/**
* Calcula a população total de uma coleção de unidades.
*
* @param {Object} unidades - A coleção de unidades para contar a população.
* @returns {number} - A população total de todas as unidades da coleção.
*/
countPopulation(obj) {
const data = GameData.units;
let total = 0;
for (let key in obj) {
total += data[key].population * obj[key];
}
return total;
}
isActive(type) {
return uw.GameDataPremium.isAdvisorActivated(type);
}
}
// A SER FINALIZADO
class About {
constructor() {
this.checkVersion();
}
settings = () => {};
checkVersion = async () => {
if (!GM_info) return;
/* Verifique se a versão é a atual */
const installedVersion = GM_info.script.version;
const file = await fetch('https://raw.githubusercontent.com/Sau1707/ModernBot/main/version.txt');
const lastVersion = await file.text();
if (lastVersion != installedVersion) {
console.log('Versions differents');
}
console.log(lastVersion, installedVersion);
};
}
class AntiRage extends ModernUtil {
GOODS_ICONS = {
athena: 'js-power-icon.animated_power_icon.animated_power_icon_45x45.power_icon45x45.power.strength_of_heroes',
zeus: 'js-power-icon.animated_power_icon.animated_power_icon_45x45.power_icon45x45.power.fair_wind',
artemis: 'js-power-icon.animated_power_icon.animated_power_icon_45x45.power_icon45x45.power.effort_of_the_huntress',
};
constructor(c, s) {
super(c, s);
this.loop_funct = null;
this.active_god_el = null;
let commandId;
const oldCreate = GPWindowMgr.Create;
GPWindowMgr.Create = function (type, title, params, id) {
if (type === GPWindowMgr.TYPE_ATK_COMMAND && id) commandId = id;
return oldCreate.apply(this, arguments);
};
/* Attach event to attack opening */
uw.$.Observer(uw.GameEvents.window.open).subscribe((e, data) => {
if (data.context != 'atk_command') return;
//const id = data.wnd.getID();
let max = 10;
const addSpell = () => {
let spellMenu = $('#command_info-god')[0];
if (!spellMenu) {
if (max > 0) {
max -= 1;
setTimeout(addSpell, 50);
}
return;
}
$(spellMenu).on('click', this.trigger);
this.command_id = commandId;
};
setTimeout(addSpell, 50);
});
}
handleGod = good => {
const godEl = $(`.god_mini.${good}.${good}`).eq(0);
if (!godEl.length) return;
const powerClassName = this.GOODS_ICONS[good];
godEl.css({
zIndex: 10,
cursor: 'pointer',
borderRadius: '100%',
outline: 'none',
boxShadow: '0px 0px 10px 5px rgba(255, 215, 0, 0.5)',
});
const powerEl = $(`.${powerClassName}`).eq(0);
if (!powerEl.length) return;
godEl.click(() => {
// desativar o deus anteriormente ativo
if (this.active_god_el && this.active_god_el.get(0) === godEl.get(0)) {
clearInterval(this.loop_funct);
this.loop_funct = null;
this.setColor(this.active_god_el.get(0), false);
this.active_god_el = null;
return;
}
if (this.active_god_el && this.active_god_el.get(0) !== godEl.get(0)) {
clearInterval(this.loop_funct);
this.setColor(this.active_god_el.get(0), false);
}
this.loop_funct = setInterval(this.clicker, 1000, powerEl);
this.active_god_el = godEl;
this.setColor(godEl.get(0), true);
});
};
setColor = (elm, apply) => {
if (apply) {
elm.style.filter = 'brightness(100%) sepia(100%) hue-rotate(90deg) saturate(1500%) contrast(0.8)';
} else {
elm.style.filter = '';
}
};
trigger = () => {
setTimeout(() => {
this.handleGod('athena');
this.handleGod('zeus');
this.handleGod('artemis');
$('.js-god-box[data-god_id="zeus"]').find('.powers').append(`
`);
const html = `
Fúria Encantamento
Uma versão Encantada da raiva normal
feita para quem tenta trollar com o autoclick
Lance Purificação e Fúria ao mesmo tempo
300 zeus + 200 artemis
`;
const default_popup = `
`;
const { artemis_favor, zeus_favor } = uw.ITowns.player_gods.attributes;
const enable = artemis_favor >= 200 && zeus_favor >= 300;
if (!enable) $('#enchanted_rage').css('filter', 'grayscale(1)');
// TODO: desabilitar se não habilitar
$('#enchanted_rage').on({
click: () => {
if (!enable) return;
this.enchanted('zeus');
},
mouseenter: event => {
$('#popup_div_curtain').html(html);
const $popupDiv = $('#popup_div');
const offset = $popupDiv.offset();
const height = $popupDiv.outerHeight();
const width = $popupDiv.outerWidth();
const left = event.pageX + 10;
const top = event.pageY + 10;
if (left + width > $(window).width()) {
offset.left -= width;
} else {
offset.left = left;
}
if (top + height > $(window).height()) {
offset.top -= height;
} else {
offset.top = top;
}
$popupDiv.css({
left: offset.left + 'px',
top: offset.top + 'px',
display: 'block',
});
},
mousemove: event => {
const $popupDiv = $('#popup_div');
if ($popupDiv.is(':visible')) {
const offset = $popupDiv.offset();
const height = $popupDiv.outerHeight();
const width = $popupDiv.outerWidth();
const left = event.pageX + 10;
const top = event.pageY + 10;
if (left + width > $(window).width()) {
offset.left -= width;
} else {
offset.left = left;
}
if (top + height > $(window).height()) {
offset.top -= height;
} else {
offset.top = top;
}
$popupDiv.css({
left: offset.left + 'px',
top: offset.top + 'px',
});
}
},
mouseleave: () => {
$('#popup_div_curtain').html(default_popup);
},
});
}, 100);
};
clicker = el => {
let check = $('.js-power-icon.animated_power_icon.animated_power_icon_45x45.power_icon45x45.power').eq(0);
if (!check.length) {
clearInterval(this.loop_funct);
this.loop_funct = null;
this.active_god_el = null;
return;
}
el.click();
let delta_time = 500;
let rand = 500 + Math.floor(Math.random() * delta_time);
clearInterval(this.loop_funct);
this.loop_funct = setInterval(this.clicker, rand, el);
};
enchanted = async type => {
if (type === 'zeus') {
this.cast(this.command_id, 'cleanse');
//await this.sleep(1);
this.cast(this.command_id, 'transformation');
}
};
cast = (id, type) => {
let data = {
model_url: 'Commands',
action_name: 'cast',
arguments: {
id: id,
power_id: type,
},
};
uw.gpAjax.ajaxPost('frontend_bridge', 'execute', data);
};
}
/*
''+
''+
''+
' '+
' '+
' '+
' '+
''+
' '+
''+
''+
''+
''+
'Vento favorevole
'+
'La voce di Zeus risuona nell aria, il vento fa gonfiare le vele delle navi e frecce e dardi sibilanti vengono lanciati con precisione verso il nemico.
'+
'Le forze navali attaccanti ottengono un bonus del 10% alla loro forza durante il loro prossimo attacco.
'+
''+
''+
'250 favore'+
''+
''+
''+
' '+
' '+
' '+
''+
' '+
' '+
' '+
' '+
'
'+
''+
*/
class AutoBootcamp extends ModernUtil {
constructor(c, s) {
super(c, s);
if (this.storage.load('ab_active', false)) this.toggle();
if (this.storage.load('bootcamp_use_def', false)) this.triggerUseDef();
}
settings = () => {
requestAnimationFrame(() => {
if (this.use_def) {
uw.$('#autobootcamp_off').addClass('disabled');
uw.$('#autobootcamp_def').removeClass('disabled');
} else {
uw.$('#autobootcamp_def').addClass('disabled');
uw.$('#autobootcamp_off').removeClass('disabled');
}
});
return `
${this.getTitleHtml('auto_autobootcamp', 'Auto ataque aldeia (so no inicio do jogo quando 1 cidade)', this.toggle, '', this.enable_auto_bootcamp)}
`;
};
triggerUseDef = () => {
this.use_def = !this.use_def;
if (this.use_def) {
uw.$('#autobootcamp_off').addClass('disabled');
uw.$('#autobootcamp_def').removeClass('disabled');
} else {
uw.$('#autobootcamp_def').addClass('disabled');
uw.$('#autobootcamp_off').removeClass('disabled');
}
this.storage.save('bootcamp_use_def', this.use_def);
};
toggle = () => {
if (!this.enable_auto_bootcamp) {
uw.$('#auto_autobootcamp').css('filter', 'brightness(100%) saturate(186%) hue-rotate(241deg)');
this.enable_auto_bootcamp = setInterval(this.main, 4000);
this.console.log('Auto ataque aldeia -> On');
} else {
uw.$('#auto_autobootcamp').css('filter', '');
clearInterval(this.enable_auto_bootcamp);
this.enable_auto_bootcamp = null;
this.console.log('Auto ataque aldeia -> Off');
}
this.storage.save('ab_active', !!this.enable_auto_bootcamp);
};
attackBootcamp = () => {
let cooldown = uw.MM.getModelByNameAndPlayerId('PlayerAttackSpot').getCooldownDuration();
if (cooldown > 0) return false;
let { MovementsUnits } = uw.MM.getModels();
/* Verifique se já não existe um ataque ativo */
if (MovementsUnits != null) {
if (Object.keys(MovementsUnits).length > 0) {
var attack_list = Object.keys(MovementsUnits);
for (var i = 0; i < Object.keys(MovementsUnits).length; i++) {
if (MovementsUnits[attack_list[i]].attributes.destination_is_attack_spot) {
return false;
}
if (MovementsUnits[attack_list[i]].attributes.origin_is_attack_spot) {
return false;
}
}
}
}
var units = { ...uw.ITowns.towns[uw.Game.townId].units() };
delete units.militia;
for (let unit in units) {
if (uw.GameData.units[unit].is_naval) delete units[unit];
}
if (!this.use_def) {
delete units.sword;
delete units.archer;
}
/* Pare se não houver mais unidades disponíveis */
if (Object.keys(units).length === 0) {
this.toggle();
return;
}
this.postAttackBootcamp(units);
return true;
};
rewardBootcamp = () => {
let model = uw.MM.getModelByNameAndPlayerId('PlayerAttackSpot');
/* Pare se o nível não for encontrado */
if (typeof model.getLevel() == 'undefined') {
this.console.log('Auto ataque aldeia nao encontrado');
this.toggle();
return true;
}
let hasReward = model.hasReward();
if (!hasReward) return false;
let reward = model.getReward();
if (reward.power_id.includes('instant') && !reward.power_id.includes('favor')) {
this.useBootcampReward();
return true;
}
if (reward.stashable) this.stashBootcampReward();
else this.useBootcampReward();
return true;
};
/* Função principal, chamada em loop */
main = () => {
if (this.rewardBootcamp()) return;
if (this.attackBootcamp()) return;
};
/* Enviar solicitação de postagem para atacar com as unidades fornecidas */
postAttackBootcamp = units => {
var data = {
model_url: `PlayerAttackSpot/${uw.Game.player_id}`,
action_name: 'attack',
arguments: units,
};
uw.gpAjax.ajaxPost('frontend_bridge', 'execute', data);
};
/* Envie solicitação ao servidor para usar a recompensa */
useBootcampReward = () => {
var data = {
model_url: `PlayerAttackSpot/${uw.Game.player_id}`,
action_name: 'useReward',
arguments: {},
};
uw.gpAjax.ajaxPost('frontend_bridge', 'execute', data);
};
/* Envie uma solicitação ao servidor para armazenar a recompensa */
stashBootcampReward = () => {
var data = {
model_url: `PlayerAttackSpot/${uw.Game.player_id}`,
action_name: 'stashReward',
arguments: {},
};
uw.gpAjax.ajaxPost('frontend_bridge', 'execute', data, 0, {
error: this.useBootcampReward,
});
};
}
/*
Ideas:
- mostrar o status atual
- feito
- recursos faltando (por que digitar)
- população faltando
- filacheio
- mostrar ponto final
- edifícios especiais
*/
// var r = Math.round(e.building.points * Math.pow(e.building.points_factor, e.next_level)) - Math.round(e.building.points * Math.pow(e.building.points_factor, e.level))
class AutoBuild extends ModernUtil {
constructor(c, s) {
super(c, s);
/* Carregar configurações, as polis nas configurações são as ativas */
this.towns_buildings = this.storage.load('buildings', {});
/* Verifique se shift está pressionado */
this.shiftHeld = false;
/* Ativo sempre, verifique se as cidades estão na lista ativa */
this.enable = setInterval(this.main, 20000);
}
settings = () => {
/* Aplicar evento ao turno */
requestAnimationFrame(() => {
uw.$('#buildings_lvl_buttons').on('mousedown', e => {
this.shiftHeld = e.shiftKey;
});
this.setPolisInSettings(uw.ITowns.getCurrentTown().id);
this.updateTitle();
uw.$.Observer(uw.GameEvents.town.town_switch).subscribe(() => {
this.setPolisInSettings(uw.ITowns.getCurrentTown().id);
this.updateTitle();
});
});
return `
Fila de construcao Automatica
`;
};
/* Dado o ID da cidade, defina a polis no menu de configurações */
setPolisInSettings = town_id => {
let town = uw.ITowns.towns[town_id];
/* Se a cidade estiver na lista ativa definida*/
let town_buildings = this.towns_buildings?.[town_id] ?? { ...town.buildings()?.attributes } ?? {};
let buildings = { ...town.buildings().attributes };
const getBuildingHtml = (building, bg) => {
let color = 'lime';
if (buildings[building] > town_buildings[building]) color = 'red';
else if (buildings[building] < town_buildings[building]) color = 'orange';
return `
${town_buildings[building]}
`;
};
/* Se a cidade estiver em grupo, os grupos */
const groups =
`(${Object.values(uw.ITowns.getTownGroups())
.filter(group => group.id > 0 && group.id !== -1 && group.towns[town_id])
.map(group => group.name)
.join(', ')})` || '';
uw.$('#buildings_lvl_buttons').html(`
${getBuildingHtml('main', [450, 0])}
${getBuildingHtml('storage', [250, 50])}
${getBuildingHtml('farm', [150, 0])}
${getBuildingHtml('academy', [0, 0])}
${getBuildingHtml('temple', [300, 50])}
${getBuildingHtml('barracks', [50, 0])}
${getBuildingHtml('docks', [100, 0])}
${getBuildingHtml('market', [0, 50])}
${getBuildingHtml('hide', [200, 0])}
${getBuildingHtml('lumber', [400, 0])}
${getBuildingHtml('stoner', [200, 50])}
${getBuildingHtml('ironer', [250, 0])}
${getBuildingHtml('wall', [50, 100])}
SenadoArmazemQuinta
Academia
TemploQuarteloPortoMercadoGrutaSeracaoPedreiraMina pMuralha
`);
};
/* ligue com town_id, tipo de construção e nível a ser adicionado */
editBuildingLevel = (town_id, name, d) => {
const town = uw.ITowns.getTown(town_id);
const { max_level, min_level } = uw.GameData.buildings[name];
const town_buildings = this.towns_buildings?.[town_id] ?? { ...town.buildings()?.attributes } ?? {};
const townBuildings = town.buildings().attributes;
const current_lvl = parseInt(uw.$(`#build_lvl_${name}`).text());
if (d) {
/* se shift for pressionado, adicione ou remova 10 */
d = this.shiftHeld ? d * 10 : d;
/* Verifique se há transbordamento inferior ou superior */
town_buildings[name] = Math.min(Math.max(current_lvl + d, min_level), max_level);
} else {
if (town_buildings[name] == current_lvl) town_buildings[name] = Math.min(Math.max(50, min_level), max_level);
else town_buildings[name] = townBuildings[name];
}
const color = town_buildings[name] > townBuildings[name] ? 'orange' : town_buildings[name] < townBuildings[name] ? 'red' : 'lime';
uw.$(`#build_settings_${town_id} #build_lvl_${name}`).css('color', color).text(town_buildings[name]);
if (town_id.toString() in this.towns_buildings) {
this.towns_buildings[town_id] = town_buildings;
this.storage.save('buildings', this.towns_buildings);
}
};
isActive = town_id => {
let town = uw.ITowns.towns[town_id];
return !this.towns_buildings?.[town.id];
};
updateTitle = () => {
let town = uw.ITowns.getCurrentTown();
if (town.id.toString() in this.towns_buildings) {
uw.$('#auto_build_title').css('filter', 'brightness(100%) saturate(186%) hue-rotate(241deg)');
} else {
uw.$('#auto_build_title').css('filter', '');
}
};
/* Ligue para ligar e desligar (acionar a cidade atual) */
toggle = () => {
let town = uw.ITowns.getCurrentTown();
if (!(town.id.toString() in this.towns_buildings)) {
this.console.log(`${town.name}: Fila de construcao auto On`);
this.towns_buildings[town.id] = {};
let buildins = ['main', 'storage', 'farm', 'academy', 'temple', 'barracks', 'docks', 'market', 'hide', 'lumber', 'stoner', 'ironer', 'wall'];
buildins.forEach(e => {
let lvl = parseInt(uw.$(`#build_lvl_${e}`).text());
this.towns_buildings[town.id][e] = lvl;
});
this.storage.save('buildings', this.towns_buildings);
} else {
delete this.towns_buildings[town.id];
this.console.log(`${town.name}: Fila de construcao auto Off`);
}
this.updateTitle();
};
/* Loop principal para construção */
main = async () => {
for (let town_id of Object.keys(this.towns_buildings)) {
/* Se a cidade não existir na lista, remova-a para evitar erros*/
if (!uw.ITowns.towns[town_id]) {
delete this.towns_buildings[town_id];
this.storage.save('buildings', this.towns_buildings);
continue;
}
if (this.isFullQueue(town_id)) continue;
/* Se a cidade estiver pronta, remova da lista */
if (this.isDone(town_id)) {
delete this.towns_buildings[town_id];
this.storage.save('buildings', this.towns_buildings);
this.updateTitle();
const town = uw.ITowns.getTown(town_id);
this.console.log(`${town.name}: Fila de construcao auto Done`);
continue;
}
await this.getNextBuild(town_id);
}
};
/* Faça uma solicitação posterior ao servidor para construir o edifício */
postBuild = async (type, town_id) => {
const town = uw.ITowns.getTown(town_id);
let { wood, stone, iron } = town.resources();
let { resources_for, population_for } = uw.MM.getModels().BuildingBuildData[town_id].attributes.building_data[type];
if (town.getAvailablePopulation() < population_for) return;
const m = 20;
if (wood < resources_for.wood + m || stone < resources_for.stone + m || iron < resources_for.iron + m) return;
let data = {
model_url: 'BuildingOrder',
action_name: 'buildUp',
arguments: { building_id: type },
town_id: town_id,
};
uw.gpAjax.ajaxPost('frontend_bridge', 'execute', data);
this.console.log(`${town.getName()}: buildUp ${type}`);
await this.sleep(500);
};
/* Faça uma solicitação de postagem para demolir o prédio */
postTearDown = async (type, town_id) => {
let data = {
model_url: 'BuildingOrder',
action_name: 'tearDown',
arguments: { building_id: type },
town_id: town_id,
};
uw.gpAjax.ajaxPost('frontend_bridge', 'execute', data);
await this.sleep(500);
};
/* retorne verdadeiro se a fila estiver cheia */
isFullQueue = town_id => {
const town = uw.ITowns.getTown(town_id);
if (uw.GameDataPremium.isAdvisorActivated('curator') && town.buildingOrders().length >= 7) {
return true;
}
if (!uw.GameDataPremium.isAdvisorActivated('curator') && town.buildingOrders().length >= 2) {
return true;
}
return false;
};
/* retorne verdadeiro se estiver construindo uma polis de correspondência */
isDone = town_id => {
const town = uw.ITowns.getTown(town_id);
let buildings = town.getBuildings().attributes;
for (let build of Object.keys(this.towns_buildings[town_id])) {
if (this.towns_buildings[town_id][build] != buildings[build]) {
return false;
}
}
return true;
};
/* */
getNextBuild = async town_id => {
let town = ITowns.towns[town_id];
/* nivelo atuale */
let buildings = { ...town.getBuildings().attributes };
/* Adicione a lista do progresso atual da construção */
for (let order of town.buildingOrders().models) {
if (order.attributes.tear_down) {
buildings[order.attributes.building_type] -= 1;
} else {
buildings[order.attributes.building_type] += 1;
}
}
/* nível que precisa atingir */
let target = this.towns_buildings[town_id];
/* Verifique se o edifício é duável, se sim, construa-o e retorne verdadeiro, caso contrário, falso */
const check = async (build, level) => {
/* se o dado for um array, tente aleatoriamente todo o array */
if (Array.isArray(build)) {
build.sort(() => Math.random() - 0.5);
for (let el of build) {
if (await check(el, level)) return true;
}
return false;
}
if (target[build] <= buildings[build]) return false;
else if (buildings[build] < level) {
await this.postBuild(build, town_id);
return true;
}
return false;
};
const tearCheck = async build => {
if (Array.isArray(build)) {
build.sort(() => Math.random() - 0.5);
for (let el of build) {
if (await tearCheck(el)) return true;
}
return false;
}
if (target[build] < buildings[build]) {
await this.postTearDown(build, town_id);
return true;
}
return false;
};
/* SE as docas ainda não estiverem construídas, siga o tutorial */
if (buildings.docks < 1) {
if (await check('lumber', 3)) return;
if (await check('stoner', 3)) return;
if (await check('farm', 4)) return;
if (await check('ironer', 3)) return;
if (await check('storage', 4)) return;
if (await check('temple', 3)) return;
if (await check('main', 5)) return;
if (await check('barracks', 5)) return;
if (await check('storage', 5)) return;
if (await check('stoner', 6)) return;
if (await check('lumber', 6)) return;
if (await check('ironer', 6)) return;
if (await check('main', 8)) return;
if (await check('farm', 8)) return;
if (await check('market', 6)) return;
if (await check('storage', 8)) return;
if (await check('academy', 7)) return;
if (await check('temple', 5)) return;
if (await check('farm', 12)) return;
if (await check('main', 15)) return;
if (await check('storage', 12)) return;
if (await check('main', 25)) return;
if (await check('hide', 10)) return;
}
/* Resouces */
// WALLS!
if (await check('farm', 15)) return;
if (await check(['storage', 'main'], 25)) return;
if (await check('market', 4)) return;
if (await check('hide', 10)) return;
if (await check(['lumber', 'stoner', 'ironer'], 15)) return;
if (await check(['academy', 'farm'], 36)) return;
if (await check(['docks', 'barracks'], 10)) return;
if (await check('wall', 25)) return;
// terme
if (await check(['docks', 'barracks', 'market'], 20)) return;
if (await check('farm', 45)) return;
if (await check(['docks', 'barracks', 'market'], 30)) return;
if (await check(['lumber', 'stoner', 'ironer'], 40)) return;
if (await check('temple', 30)) return;
if (await check('storage', 35)) return;
/* Demolish */
let lista = ['lumber', 'stoner', 'ironer', 'docks', 'barracks', 'market', 'temple', 'academy', 'farm', 'hide', 'storage', 'wall'];
if (await tearCheck(lista)) return;
if (await tearCheck('main')) return;
};
}
/*
TODO:
- Autotrade: o conserto rural não pertence a você, matéria-prima que você possui + log no console
- AutoRuralLevel: ainda por implementar
- AutoFarm: verifique a hora de começar
*/
class AutoFarm extends ModernUtil {
BUTTONHTML =
'';
YELLOW = 'brightness(294%) sepia(100%) hue-rotate(15deg) saturate(1000%) contrast(0.8)';
GREEN = 'brightness(110%) sepia(100%) hue-rotate(100deg) saturate(1500%) contrast(0.8)';
TIMINGS = {
1: 300000,
2: 600000,
3: 1200000,
4: 2400000,
};
constructor(c, s) {
super(c, s);
this.delta_time = 10000;
this.timing = this.storage.load('af_level', 1);
this.percentual = this.storage.load('af_percentuals', 3);
if (this.storage.load('af', false)) {
this.lastTime = Date.now();
this.timer = 0; // TODO: verifique se é realmente 0
this.active = setInterval(this.main, 1000);
}
this.polislist = this.generateList();
}
settings = () => {
requestAnimationFrame(() => {
this.setAutoFarmLevel(this.timing);
this.setAutoFarmPercentual(this.percentual);
});
return `
${this.getTitleHtml('auto_farm', 'Coletar recursos automaticamente nas aldeias', this.toggle, '', this.enable_auto_farming)}
${this.getButtonHtml('percentuals_1', '80%', this.setAutoFarmPercentual, 1)}
${this.getButtonHtml('percentuals_2', '90%', this.setAutoFarmPercentual, 2)}
${this.getButtonHtml('percentuals_3', '100%', this.setAutoFarmPercentual, 3)}
`;
};
/* gerar a lista contendo 1 polis por ilha */
generateList = () => {
const islandsList = new Set();
const polisList = [];
const { models: towns } = uw.MM.getOnlyCollectionByName('Town');
for (const town of towns) {
const { on_small_island, island_id, id } = town.attributes;
if (on_small_island) continue;
if (islandsList.has(island_id)) continue;
islandsList.add(island_id);
polisList.push(id);
}
return polisList;
};
toggle = () => {
if (this.active) {
uw.$('#autofarm_timer').remove();
uw.$('#autofarm_timer_divider').remove();
uw.$('#auto_farm').css('filter', '');
clearInterval(this.active);
this.active = null;
} else {
uw.$('#auto_farm').css('filter', 'brightness(100%) saturate(186%) hue-rotate(241deg)');
this.lastTime = Date.now();
this.timer = 0; // TODO: verifique se é realmente 0
this.active = setInterval(this.main, 1000);
}
this.storage.save('af', !!this.active);
};
setAutoFarmLevel = n => {
uw.$('#farming_lvl_buttons .button_new').addClass('disabled');
uw.$(`#farming_lvl_${n}`).removeClass('disabled');
if (this.timing != n) {
this.timing = n;
this.storage.save('af_level', n);
const rand = Math.floor(Math.random() * this.delta_time);
this.timer = this.TIMINGS[this.timing] + rand;
}
};
setAutoFarmPercentual = n => {
const box = uw.$('#rural_lvl_percentuals');
box.find('.button_new').addClass('disabled');
uw.$(`#percentuals_${n}`).removeClass('disabled');
if (this.percentual != n) {
this.percentual = n;
this.storage.save('af_percentuals', n);
}
};
/* retornar o tempo antes da próxima coleta */
getNextCollection = () => {
const { models } = uw.MM.getCollections().FarmTownPlayerRelation[0];
const lootCounts = {};
for (const model of models) {
const { lootable_at } = model.attributes;
lootCounts[lootable_at] = (lootCounts[lootable_at] || 0) + 1;
}
let maxLootableTime = 0;
let maxValue = 0;
for (const lootableTime in lootCounts) {
const value = lootCounts[lootableTime];
if (value < maxValue) continue;
maxLootableTime = lootableTime;
maxValue = value;
}
const seconds = maxLootableTime - Math.floor(Date.now() / 1000);
return seconds > 0 ? seconds * 1000 : 0;
};
/* Ligue para atualizar o cronômetro */
updateTimer = () => {
const currentTime = Date.now();
this.timer -= currentTime - this.lastTime;
this.lastTime = currentTime;
/* Adicionar temporizador de não estar lá */
const timerDisplay = uw.$('#autofarm_timer_p');
if (!timerDisplay.length) uw.$('.tb_activities, .toolbar_activities').find('.middle').append(this.BUTTONHTML);
else {
timerDisplay.html(Math.round(Math.max(this.timer, 0) / 1000));
}
const isCaptainActive = uw.GameDataPremium.isAdvisorActivated('captain');
uw.$('#autofarm_timer').css('filter', isCaptainActive ? this.GREEN : this.YELLOW);
};
claim = async () => {
const isCaptainActive = uw.GameDataPremium.isAdvisorActivated('captain');
if (isCaptainActive) {
await this.fakeOpening();
await this.sleep(Math.random() * 2000 + 1000); // aleatório entre 1 segundo e 3
await this.fakeSelectAll();
await this.sleep(Math.random() * 2000 + 1000);
if (this.timing <= 2) await this.claimMultiple(300, 600);
if (this.timing > 2) await this.claimMultiple(1200, 2400);
await this.fakeUpdate();
} else {
const { models: player_relation_models } = uw.MM.getOnlyCollectionByName('FarmTownPlayerRelation');
const { models: farm_town_models } = uw.MM.getOnlyCollectionByName('FarmTown');
const now = Math.floor(Date.now() / 1000);
let max = 60;
for (let town_id of this.polislist) {
let town = uw.ITowns.towns[town_id];
let x = town.getIslandCoordinateX();
let y = town.getIslandCoordinateY();
for (let farmtown of farm_town_models) {
if (farmtown.attributes.island_x != x) continue;
if (farmtown.attributes.island_y != y) continue;
for (let relation of player_relation_models) {
if (farmtown.attributes.id != relation.attributes.farm_town_id) continue;
if (relation.attributes.relation_status !== 1) continue;
if (relation.attributes.lootable_at !== null && now < relation.attributes.lootable_at) continue;
this.claimSingle(town_id, relation.attributes.farm_town_id, relation.id, Math.ceil(this.timing / 2));
await this.sleep(500);
if (!max) return;
else max -= 1;
}
}
}
}
setTimeout(() => uw.WMap.removeFarmTownLootCooldownIconAndRefreshLootTimers(), 2000);
};
/* Retornar o total de recursos da polis na lista */
getTotalResources = () => {
let total = {
wood: 0,
stone: 0,
iron: 0,
storage: 0,
};
for (let town_id of this.polislist) {
const town = uw.ITowns.getTown(town_id);
const { wood, stone, iron, storage } = town.resources();
total.wood += wood;
total.stone += stone;
total.iron += iron;
total.storage += storage;
}
return total;
};
main = async () => {
/* Reclamar que o tempo dos recursos já passou */
if (this.timer < 1) {
/* Verifique se o percentual atingiu */
const { wood, stone, iron, storage } = this.getTotalResources();
const minResource = Math.min(wood, stone, iron);
const min_percentual = minResource / storage;
/* Se o percentual máximo for atingido, pare e aguarde 30 segundos*/
if (this.percentual == 3 && min_percentual > 0.99) {
this.timer = 30000;
requestAnimationFrame(this.updateTimer);
return;
}
if (this.percentual == 2 && min_percentual > 0.9) {
this.timer = 30000;
requestAnimationFrame(this.updateTimer);
return;
}
if (this.percentual == 1 && min_percentual > 0.8) {
this.timer = 30000;
requestAnimationFrame(this.updateTimer);
return;
}
const rand = Math.floor(Math.random() * this.delta_time);
this.timer = this.TIMINGS[this.timing] + rand;
await this.claim();
}
/* atualizar o cronômetro */
this.updateTimer();
};
claimSingle = (town_id, farm_town_id, relation_id, option = 1) => {
const data = {
model_url: `FarmTownPlayerRelation/${relation_id}`,
action_name: 'claim',
arguments: {
farm_town_id: farm_town_id,
type: 'resources',
option: option,
},
town_id: town_id,
};
uw.gpAjax.ajaxPost('frontend_bridge', 'execute', data);
};
claimMultiple = (base = 300, boost = 600) =>
new Promise((myResolve, myReject) => {
let data = {
towns: this.polislist,
time_option_base: base,
time_option_booty: boost,
claim_factor: 'normal',
};
uw.gpAjax.ajaxPost('farm_town_overviews', 'claim_loads_multiple', data, false, () => myResolve());
});
/* Finja que a janela está abrindo */
fakeOpening = () =>
new Promise((myResolve, myReject) => {
uw.gpAjax.ajaxGet('farm_town_overviews', 'index', {}, false, async () => {
await this.sleep(10);
await this.fakeUpdate();
myResolve();
});
});
/* Falsifique o usuário selecionando a lista */
fakeSelectAll = () =>
new Promise((myResolve, myReject) => {
const data = {
town_ids: this.polislist,
};
uw.gpAjax.ajaxGet('farm_town_overviews', 'get_farm_towns_from_multiple_towns', data, false, () => myResolve());
});
/* Falsifique a atualização da janela*/
fakeUpdate = () =>
new Promise((myResolve, myReject) => {
const town = uw.ITowns.getCurrentTown();
const { attributes: booty } = town.getResearches();
const { attributes: trade_office } = town.getBuildings();
const data = {
island_x: town.getIslandCoordinateX(),
island_y: town.getIslandCoordinateY(),
current_town_id: town.id,
booty_researched: booty ? 1 : 0,
diplomacy_researched: '',
trade_office: trade_office ? 1 : 0,
};
uw.gpAjax.ajaxGet('farm_town_overviews', 'get_farm_towns_for_town', data, false, () => myResolve());
});
}
class AutoGratis extends ModernUtil {
constructor(c, s) {
super(c, s);
if (this.storage.load('enable_autogratis', false)) this.toggle();
}
settings = () => {
return `
Finalizar as Construçoes Gratuitamente
(Construcao Auto Gratis) Permite finalizar as construcoes
Gratis
(finaliza a construcao nos 4 segundos)
`;
};
/* Ligue para acionar o autogratis */
toggle = () => {
if (!this.autogratis) {
uw.$('#auto_gratis_title').css(
'filter',
'brightness(100%) saturate(186%) hue-rotate(241deg)',
);
this.autogratis = setInterval(this.main, 4000);
this.console.log('Construcao Auto Gratis -> On');
} else {
uw.$('#auto_gratis_title').css('filter', '');
clearInterval(this.autogratis);
this.autogratis = null;
this.console.log('Construcao Auto Gratis -> Off');
}
this.storage.save('enable_autogratis', !!this.autogratis);
};
/* Loop principal para o bot autogratis */
main = () => {
const el = uw.$('.type_building_queue.type_free').not('#dummy_free');
if (el.length) {
el.click();
};
const town = uw.ITowns.getCurrentTown();
for (let model of town.buildingOrders().models) {
if (model.attributes.building_time < 300) {
this.callGratis(town.id, model.id)
return;
}
//const now = new Date()
//if ((model.attributes.to_be_completed_at - now / 1000) > 300) continue;
}
};
callGratis = (town_id, order_id) => {
const data = {
"model_url": `BuildingOrder/${order_id}`,
"action_name": "buyInstant",
"arguments": {
"order_id": order_id
},
"town_id": town_id
}
uw.gpAjax.ajaxPost('frontend_bridge', 'execute', data);
}
}
class AutoHide extends ModernUtil {
constructor(c, s) {
super(c, s);
this.activePolis = this.storage.load('autohide_active', 0);
setInterval(this.main, 60000)//intervalo de 10mn (1mn/60000)
const addButton = () => {
let box = $('.order_count');
if (box.length) {
let butt = $('', {
class: 'button_new',
id: 'autoCaveButton',
style: 'float: right; margin: 0px; left: 169px; position: absolute; top: 56px; width: 66px',
html: ' Auto '
});
box.prepend(butt);
this.updateSettings(uw.ITowns.getCurrentTown().id);
} else {
setTimeout(addButton, 100);
}
};
uw.$.Observer(uw.GameEvents.window.open).subscribe((e, i) => {
if (!i.attributes) return
if (i.attributes.window_type != "hide") return
setTimeout(addButton, 100);
})
uw.$.Observer(uw.GameEvents.town.town_switch).subscribe(() => {
this.updateSettings(uw.ITowns.getCurrentTown().id);
let cave = document.getElementsByClassName(
'js-window-main-container classic_window hide',
)[0];
if (!cave) return;
setTimeout(addButton, 1);
});
}
settings = () => {
requestAnimationFrame(() => {
this.updateSettings(uw.ITowns.getCurrentTown().id);
})
return `
Auto gruta
Envia 1K de prata para a gruta, mas mantém 5K no armazem, Verificacao do estatos todos os 1 minute.
`;
};
toggle = (town_id) => {
let town = town_id ? uw.ITowns.towns[town_id] : uw.ITowns.getCurrentTown();
let hide = town.buildings().attributes.hide
if (this.activePolis == town.id) {
this.activePolis = 0
} else {
if (hide == 10) this.activePolis = town.id;
else uw.HumanMessage.error("O Nivel 10 e exigido para todas as grutas das cidades");
}
this.storage.save("autohide_active", this.activePolis)
this.updateSettings(town.id)
}
updateSettings = (town_id) => {
if (town_id == this.activePolis) {
$('#auto_cave_title').css({
'filter': 'brightness(100%) saturate(186%) hue-rotate(241deg)'
});
$('#autoCaveButton').css({
'filter': ' brightness(100%) sepia(100%) hue-rotate(90deg) saturate(1500%) contrast(0.8)'
});
} else {
$('#auto_cave_title, #autoCaveButton').css({
'filter': ''
});
}
}
main = () => {
if (this.activePolis == 0) return;
const town = uw.ITowns.towns[this.activePolis];
const { iron } = town.resources();
const ironToStore = Math.min(iron - 5000, 1000);
if (ironToStore > 0) {
this.storeIron(this.activePolis, ironToStore);
}
}
/*
main = () => {
if (this.activePolis == 0) return;
const town = uw.ITowns.towns[this.activePolis];
const { iron } = town.resources()
if (iron > 5000) {
this.storeIron(this.activePolis, iron)
}
}
*/
storeIron = (town_id, count) => {
const data = {
"model_url": "BuildingHide",
"action_name": "storeIron",
"arguments": {
"iron_to_store": count
},
"town_id": town_id,
}
uw.gpAjax.ajaxPost('frontend_bridge', 'execute', data);
}
}
class AutoParty extends ModernUtil {
constructor(c, s) {
super(c, s);
this.active_types = this.storage.load('ap_types', { festival: false, procession: false });
this.single = this.storage.load('ap_single', true);
if (this.storage.load('ap_enable', false)) {
this.enable = setInterval(this.main, 30000);
}
}
// ${this.getButtonHtml('autoparty_lvl_1', 'Olympic', this.setRuralLevel, 1)}
settings = () => {
requestAnimationFrame(() => {
this.triggerType('festival', false);
this.triggerType('procession', false);
this.triggerSingle(this.single);
});
return `
${this.getTitleHtml('auto_party_title', 'Auto Cultura', this.toggle, '', this.enable)}
${this.getButtonHtml('autoparty_festival', 'Festival', this.triggerType, 'festival')}
${this.getButtonHtml('autoparty_procession', 'Desfile V.', this.triggerType, 'procession')}
${this.getButtonHtml('autoparty_single', 'Unica C.', this.triggerSingle, 0)}
${this.getButtonHtml('autoparty_multiple', 'Todas C.', this.triggerSingle, 1)}
`;
};
triggerType = (type, swap = true) => {
if (swap) {
this.active_types[type] = !this.active_types[type];
this.storage.save('ap_types', this.active_types);
}
if (!this.active_types[type]) uw.$(`#autoparty_${type}`).addClass('disabled');
else uw.$(`#autoparty_${type}`).removeClass('disabled');
};
triggerSingle = type => {
type = !!type;
if (type) {
uw.$(`#autoparty_single`).addClass('disabled');
uw.$(`#autoparty_multiple`).removeClass('disabled');
} else {
uw.$(`#autoparty_multiple`).addClass('disabled');
uw.$(`#autoparty_single`).removeClass('disabled');
}
if (this.single != type) {
this.single = type;
this.storage.save('ap_single', this.single);
}
};
/* Ligue para ligar/desligar */
toggle = () => {
if (!this.enable) {
uw.$('#auto_party_title').css('filter', 'brightness(100%) saturate(186%) hue-rotate(241deg)');
this.enable = setInterval(this.main, 30000);
} else {
uw.$('#auto_party_title').css('filter', '');
clearInterval(this.enable);
this.enable = null;
}
this.storage.save('ap_enable', !!this.enable);
};
/* Lista de retorno da cidade com celebração ativa */
getCelebrationsList = type => {
const celebrationModels = uw.MM.getModels().Celebration;
if (typeof celebrationModels === 'undefined') return [];
const triumphs = Object.values(celebrationModels)
.filter(celebration => celebration.attributes.celebration_type === type)
.map(triumph => triumph.attributes.town_id);
return triumphs;
};
checkParty = async () => {
let max = 10;
let party = this.getCelebrationsList('party');
if (this.single) {
for (let town_id in uw.ITowns.towns) {
if (party.includes(parseInt(town_id))) continue;
let town = uw.ITowns.towns[town_id];
if (town.getBuildings().attributes.academy < 30) continue;
let { wood, stone, iron } = town.resources();
if (wood < 15000 || stone < 18000 || iron < 15000) continue;
this.makeCelebration('party', town_id);
await this.sleep(750);
max -= 1;
/* Evite que a promessa seja muito longa */
if (max <= 0) return;
}
} else {
if (party.length > 1) return;
this.makeCelebration('party');
}
};
checkTriumph = async () => {
let max = 10;
let killpoints = uw.MM.getModelByNameAndPlayerId('PlayerKillpoints').attributes;
let available = killpoints.att + killpoints.def - killpoints.used;
if (available < 300) return;
let triumph = this.getCelebrationsList('triumph');
if (!this.single) {
// único e múltiplo são trocados ...
for (let town_id in uw.ITowns.towns) {
if (triumph.includes(parseInt(town_id))) continue;
this.makeCelebration('triumph', town_id);
await this.sleep(500);
available -= 300;
if (available < 300) return;
max -= 1;
/* Evite que a promessa seja muito longa */
if (max <= 0) return;
}
} else {
if (triumph.length > 1) return;
this.makeCelebration('triumph');
}
};
main = async () => {
if (this.active_types['procession']) await this.checkTriumph();
if (this.active_types['festival']) await this.checkParty();
};
makeCelebration = (type, town_id) => {
if (typeof town_id === 'undefined') {
let data = {
celebration_type: type,
};
uw.gpAjax.ajaxPost('town_overviews', 'start_all_celebrations', data);
} else {
let data = {
celebration_type: type,
town_id: town_id,
};
uw.gpAjax.ajaxPost('building_place', 'start_celebration', data);
}
};
}
class AutoRuralLevel extends ModernUtil {
constructor(c, s) {
super(c, s);
this.rural_level = this.storage.load('enable_autorural_level', 1);
if (this.storage.load('enable_autorural_level_active')) {
this.enable = setInterval(this.main, 20000);
}
}
settings = () => {
requestAnimationFrame(() => {
this.setRuralLevel(this.rural_level);
});
return `
${this.getTitleHtml('auto_rural_level', 'Subir o nível das aldeias bárbaras automaticamente', this.toggle, '', this.enable)}
`;
};
/* gerar a lista contendo 1 polis por ilha */
generateList = () => {
let islands_list = [];
let polis_list = [];
let town_list = uw.MM.getOnlyCollectionByName('Town').models;
for (let town of town_list) {
if (town.attributes.on_small_island) continue;
let { island_id, id } = town.attributes;
if (!islands_list.includes(island_id)) {
islands_list.push(island_id);
polis_list.push(id);
}
}
return polis_list;
};
setRuralLevel = n => {
uw.$('#rural_lvl_buttons .button_new').addClass('disabled');
uw.$(`#rural_lvl_${n}`).removeClass('disabled');
if (this.rural_level != n) {
this.rural_level = n;
this.storage.save('enable_autorural_level', this.rural_level);
}
};
toggle = () => {
if (!this.enable) {
uw.$('#auto_rural_level').css('filter', 'brightness(100%) saturate(186%) hue-rotate(241deg)');
this.enable = setInterval(this.main, 20000);
} else {
uw.$('#auto_rural_level').css('filter', '');
clearInterval(this.enable);
this.enable = null;
}
this.storage.save('enable_autorural_level_active', !!this.enable);
};
main = async () => {
let player_relation_models = uw.MM.getOnlyCollectionByName('FarmTownPlayerRelation').models;
let farm_town_models = uw.MM.getOnlyCollectionByName('FarmTown').models;
let killpoints = uw.MM.getModelByNameAndPlayerId('PlayerKillpoints').attributes;
/* Obtenha controle com todas as áreas rurais bloqueadas */
const locked = player_relation_models.filter(model => model.attributes.relation_status === 0);
/* Get killpoints */
let available = killpoints.att + killpoints.def - killpoints.used;
let unlocked = player_relation_models.length - locked.length;
/* Se algumas zonas rurais ainda precisam ser desbloqueadas */
if (locked.length > 0) {
/* Os 5 primeiros rurais têm desconto*/
const discounts = [2, 8, 10, 30, 50, 100];
if (unlocked < discounts.length && available < discounts[unlocked]) return;
else if (available < 100) return;
let towns = this.generateList();
for (let town_id of towns) {
let town = uw.ITowns.towns[town_id];
let x = town.getIslandCoordinateX(),
y = town.getIslandCoordinateY();
for (let farmtown of farm_town_models) {
if (farmtown.attributes.island_x != x || farmtown.attributes.island_y != y) continue;
for (let relation of locked) {
if (farmtown.attributes.id != relation.attributes.farm_town_id) continue;
this.unlockRural(town_id, relation.attributes.farm_town_id, relation.id);
this.console.log(`Island ${farmtown.attributes.island_xy}: unlocked ${farmtown.attributes.name}`);
return;
}
}
}
} else {
/* caso contrário, verifique cada nível uma vez por vez */
let towns = this.generateList();
let expansion = false;
const levelCosts = [1, 5, 25, 50, 100];
for (let level = 1; level < this.rural_level; level++) {
if (available < levelCosts[level - 1]) return;
for (let town_id of towns) {
let town = uw.ITowns.towns[town_id];
let x = town.getIslandCoordinateX();
let y = town.getIslandCoordinateY();
for (let farmtown of farm_town_models) {
if (farmtown.attributes.island_x != x) continue;
if (farmtown.attributes.island_y != y) continue;
for (let relation of player_relation_models) {
if (farmtown.attributes.id != relation.attributes.farm_town_id) {
continue;
}
if (relation.attributes.expansion_at) {
expansion = true;
continue;
}
if (relation.attributes.expansion_stage > level) continue;
this.upgradeRural(town_id, relation.attributes.farm_town_id, relation.attributes.id);
this.console.log(`Island ${farmtown.attributes.island_xy}: upgraded ${farmtown.attributes.name}`);
return;
}
}
}
}
if (expansion) return;
}
/* Desligamento automático quando o nível é atingido */
this.toggle();
};
/*
Solicitações de postagem
*/
unlockRural = (town_id, farm_town_id, relation_id) => {
let data = {
model_url: `FarmTownPlayerRelation/${relation_id}`,
action_name: 'unlock',
arguments: {
farm_town_id: farm_town_id,
},
town_id: town_id,
};
uw.gpAjax.ajaxPost('frontend_bridge', 'execute', data);
};
upgradeRural = (town_id, farm_town_id, relation_id) => {
let data = {
model_url: `FarmTownPlayerRelation/${relation_id}`,
action_name: 'upgrade',
arguments: {
farm_town_id: farm_town_id,
},
town_id: town_id,
};
uw.gpAjax.ajaxPost('frontend_bridge', 'execute', data);
};
}
class AutoRuralTrade extends ModernUtil {
constructor(c, s) {
super(c, s);
this.ratio = this.storage.load('rt_ratio', 5);
}
settings = () => {
requestAnimationFrame(() => {
this.setMinRatioLevel(this.ratio);
});
return `
Troca de Recursos Automático entre as aldeias
${this.getButtonHtml('min_rural_ratio_1', '0.25', this.setMinRatioLevel, 1)}
${this.getButtonHtml('min_rural_ratio_2', '0.5', this.setMinRatioLevel, 2)}
${this.getButtonHtml('min_rural_ratio_3', '0.75', this.setMinRatioLevel, 3)}
${this.getButtonHtml('min_rural_ratio_4', '1.0', this.setMinRatioLevel, 4)}
${this.getButtonHtml('min_rural_ratio_5', '1.25', this.setMinRatioLevel, 5)}
`;
};
setMinRatioLevel = n => {
uw.$('#min_rural_ratio .button_new').addClass('disabled');
uw.$(`#min_rural_ratio_${n}`).removeClass('disabled');
if (this.ratio != n) {
this.ratio = n;
this.storage.save('rt_ratio', n);
}
};
/* Comércio com todos os rurais*/
main = async resouce => {
if (resouce) {
/* Definir botão desativado */
[1, 2, 3, 4].forEach(i => {
uw.$(`#autotrade_lvl_${i}`).addClass('disabled').css('cursor', 'auto');
});
this.trade_resouce = resouce;
/* Defina o comércio atual para polis no índice 0 */
this.total_trade = Object.keys(uw.ITowns.towns).length;
this.done_trade = 0;
/* Defina o intervalo */
this.auto_trade_resouces_loop = setInterval(this.mainTradeLoop, 1500);
} else {
/* Clear the interval */
clearInterval(this.auto_trade_resouces_loop);
/* Reative os botões e defina o progresso para 0 */
uw.$('#res_progress_bar').css('width', 0);
[1, 2, 3, 4].forEach(i => {
uw.$(`#autotrade_lvl_${i}`).removeClass('disabled').css('cursor', 'pointer');
});
}
};
tradeWithRural = async polis_id => {
let town = uw.ITowns.towns[polis_id];
if (!town) return;
if (town.getAvailableTradeCapacity() < 3000) return;
//if (this.check_for_hide && town.getBuildings().attributes.hide < 10) return;
let farm_town_models = uw.MM.getOnlyCollectionByName('FarmTown').models;
let player_relation_models = uw.MM.getOnlyCollectionByName('FarmTownPlayerRelation').models;
/* Crie uma lista com todas as cidades agrícolas da atual polis insular */
let x = town.getIslandCoordinateX(),
y = town.getIslandCoordinateY();
let resources = town.resources();
for (const farmtown of farm_town_models) {
if (farmtown.attributes.island_x != x || farmtown.attributes.island_y != y) continue;
if (farmtown.attributes.resource_offer != this.trade_resouce) continue;
if (resources[farmtown.attributes.resource_demand] < 3000) continue;
for (const relation of player_relation_models) {
if (farmtown.attributes.id != relation.attributes.farm_town_id) continue;
if (relation.attributes.current_trade_ratio < this.min_rural_ratio * 0.25) continue;
if (town.getAvailableTradeCapacity() < 3000) continue;
this.tradeRuralPost(relation.attributes.farm_town_id, relation.attributes.id, town.getAvailableTradeCapacity(), town.id);
await this.sleep(750);
}
}
};
mainTradeLoop = async () => {
/* Se for a última polis, então acione para parar */
if (this.done_trade >= this.total_trade) {
this.main();
return;
}
/* realizar negociações com o índice atual */
let towns = Object.keys(uw.ITowns.towns);
await this.tradeWithRural(towns[this.done_trade]);
/* atualizar barra de progresso */
uw.$('#res_progress_bar').css('width', `${(this.done_trade / this.total_trade) * 100}%`);
this.done_trade += 1;
};
tradeRuralPost = (farm_town_id, relation_id, count, town_id) => {
if (count < 100) return;
const data = {
model_url: `FarmTownPlayerRelation/${relation_id}`,
action_name: 'trade',
arguments: { farm_town_id: farm_town_id, amount: count > 3000 ? 3000 : count },
town_id: town_id,
};
uw.gpAjax.ajaxPost('frontend_bridge', 'execute', data);
};
}
class AutoTrade extends ModernUtil {
constructor(c, s) {
super(c, s);
}
settings = () => {
return `
${this.getTitleHtml('auto_trade', 'Auto Trade', '', '', this.enable_auto_farming)}
${this.getButtonHtml('farming_lvl_1', '5 min', this.setAutoFarmLevel, 1)}
${this.getButtonHtml('farming_lvl_2', '10 min', this.setAutoFarmLevel, 2)}
${this.getButtonHtml('farming_lvl_3', '20 min', this.setAutoFarmLevel, 3)}
${this.getButtonHtml('farming_lvl_4', '40 min', this.setAutoFarmLevel, 4)}
`;
};
}
function autoTradeBot() {
const uw = unsafeWindow;
const unit_counnt = {
bireme: 2.9,
slinger: 28,
};
this.tradeUntilComplete = async (target = 'active', troop = 'bireme') => {
console.log(troop);
let ammount;
if (target === 'active') target = uw.ITowns.getCurrentTown().id;
do {
console.log('Trade Loop');
ammount = await this.trade(target, troop);
await sleep(30000);
} while (ammount > 0);
console.log('Tradeing Done');
};
this.trade = async function (target = 'active', troop = 'bireme') {
if (target === 'active') target = uw.ITowns.getCurrentTown().id;
let ammount = await calculateAmmount(target, troop);
let current_ammount;
do {
current_ammount = ammount;
for (let town of Object.values(uw.ITowns.towns)) {
if (town.id == target) continue;
if (uw.stopBot) break;
if (ammount <= 0) break;
ammount = await sendBalance(town.id, target, troop, ammount);
}
} while (current_ammount > ammount);
return ammount;
};
/* devolver todas as negociações */
async function getAllTrades() {
return new Promise(function (myResolve, myReject) {
uw.gpAjax.ajaxGet('town_overviews', 'trade_overview', {}, !0, e => {
myResolve(e.movements);
});
});
}
/* Devolva a quantidade de tropas duráveis com os recursos atuais */
function getCount(targtet_id, troop) {
let target_polis = uw.ITowns.towns[targtet_id];
if (!target_polis) return {};
let resources = target_polis.resources();
let wood = resources.wood / uw.GameData.units[troop].resources.wood;
let stone = resources.stone / uw.GameData.units[troop].resources.stone;
let iron = resources.iron / uw.GameData.units[troop].resources.iron;
let min = Math.min(wood, stone, iron);
return min;
}
/* Retorne o id da cidade e do nome */
function getTradeTarget(html) {
const element = document.createElement('div');
element.innerHTML = html;
let name = element.textContent;
for (let town of Object.values(uw.ITowns.towns)) {
if (town.name == name) return town.id;
}
}
/* Devolva a quantidade de tropas devidas por recursos em um comércio */
function getCountFromTrade(trade, troop) {
let wood = trade.res.wood / uw.GameData.units[troop].resources.wood;
let stone = trade.res.stone / uw.GameData.units[troop].resources.stone;
let iron = trade.res.iron / uw.GameData.units[troop].resources.iron;
let min = Math.min(wood, stone, iron);
return min;
}
/* Devolver quantidade de recursos a serem enviados */
async function calculateAmmount(targtet_id, troop) {
let target_polis = uw.ITowns.towns[targtet_id];
if (!target_polis) return {};
let current_count = {};
let discount = uw.GeneralModifications.getUnitBuildResourcesModification(targtet_id, uw.GameData.units[troop]);
let todo = parseInt(target_polis.getAvailablePopulation() / uw.GameData.units[troop].population) * discount;
let in_polis = getCount(targtet_id, troop);
/* Se a polis tiver todos os recursos -> nenhum recurso precisa ser enviado */
todo -= in_polis;
if (todo < 0) return 0;
let trade = uw.MM.getCollections().Trade[0].models;
let trades = await getAllTrades();
for (let trade of trades) {
if (getTradeTarget(trade.to.link) != targtet_id) continue;
todo -= getCountFromTrade(trade, troop);
}
return todo;
}
function getCountWithTrade(targtet_id, troop) {
let target_polis = uw.ITowns.towns[targtet_id];
if (!target_polis) return {};
let resources = target_polis.resources();
let wood = resources.wood / uw.GameData.units[troop].resources.wood;
let stone = resources.stone / uw.GameData.units[troop].resources.stone;
let iron = resources.iron / uw.GameData.units[troop].resources.iron;
let min_resouces = Math.min(wood, stone, iron); // min ammount
let trade = target_polis.getAvailableTradeCapacity();
let max_trade = trade / (uw.GameData.units[troop].resources.wood + uw.GameData.units[troop].resources.stone + uw.GameData.units[troop].resources.iron); // max tradable
if (max_trade < min_resouces) return max_trade;
else return min_resouces;
}
/* Definir aguardar e adicionar promessa */
function sendTradeRequest(from_id, target_id, troop, count) {
let data = {
id: target_id,
wood: uw.GameData.units[troop].resources.wood * count,
stone: uw.GameData.units[troop].resources.stone * count,
iron: uw.GameData.units[troop].resources.iron * count,
town_id: from_id,
nl_init: true,
};
return new Promise(function (myResolve, myReject) {
uw.gpAjax.ajaxPost('town_info', 'trade', data, !0, () => {
setTimeout(() => myResolve(), 500);
});
});
}
/* Envie recursos da polis para o alvo, balanceados para aquela tropa, retorne a contagem atualizadat*/
async function sendBalance(polis_id, target_id, troop, count) {
let troops_ammount = unit_counnt[troop];
if (!troops_ammount) return 0;
if (polis_id == target_id) return count;
let sender_polis = uw.ITowns.towns[polis_id];
let duable = getCount(polis_id, troop);
if (duable < troops_ammount) return count;
if (sender_polis.getAvailableTradeCapacity() < 500) return count;
let duable_with_trade = getCountWithTrade(polis_id, troop);
if (duable_with_trade < troops_ammount) return count;
await sendTradeRequest(polis_id, target_id, troop, troops_ammount);
return count - troops_ammount < 0 ? 0 : count - troops_ammount;
}
function sleep(time) {
return new Promise(function (myResolve, myReject) {
setTimeout(() => myResolve(), time);
});
}
}
class AutoTrain extends ModernUtil {
POWER_LIST = ['call_of_the_ocean', 'spartan_training', 'fertility_improvement'];
GROUND_ORDER = ['catapult', 'sword', 'archer', 'hoplite', 'slinger', 'rider', 'chariot', 'manticore'];
NAVAL_ORDER = ['small_transporter', 'bireme', 'trireme', 'attack_ship', 'big_transporter', 'demolition_ship', 'colonize_ship'];
SHIFT_LEVELS = {
catapult: [5, 5],
sword: [200, 50],
archer: [200, 50],
hoplite: [200, 50],
slinger: [200, 50],
rider: [100, 25],
chariot: [100, 25],
manticore: [2, 1],
small_transporter: [10, 5],
bireme: [50, 10],
trireme: [50, 10],
attack_ship: [50, 10],
big_transporter: [50, 10],
demolition_ship: [50, 10],
colonize_ship: [5, 1],
};
constructor(c, s) {
super(c, s);
this.spell = this.storage.load('at_spell', false);
this.percentual = this.storage.load('at_per', 1);
this.city_troops = this.storage.load('troops', {});
this.shiftHeld = false;
this.interval = setInterval(this.main, 10000);
}
settings = () => {
requestAnimationFrame(() => {
this.setPolisInSettings(uw.ITowns.getCurrentTown().id);
this.updatePolisInSettings(uw.ITowns.getCurrentTown().id);
this.handlePercentual(this.percentual);
this.handleSpell(this.spell);
uw.$.Observer(uw.GameEvents.town.town_switch).subscribe(() => {
this.setPolisInSettings(uw.ITowns.getCurrentTown().id);
this.updatePolisInSettings(uw.ITowns.getCurrentTown().id);
});
uw.$('#troops_lvl_buttons').on('mousedown', e => {
this.shiftHeld = e.shiftKey;
});
});
return `
${this.getButtonHtml('train_passive', 'Normal', this.handleSpell, 0)}
${this.getButtonHtml('train_spell', 'feitiço', this.handleSpell, 1)}
${this.getButtonHtml('train_percentuals_1', '80%', this.handlePercentual, 1)}
${this.getButtonHtml('train_percentuals_2', '90%', this.handlePercentual, 2)}
${this.getButtonHtml('train_percentuals_3', '100%', this.handlePercentual, 3)}
Treinamento Automatico
`;
};
handleSpell = e => {
e = !!e;
if (this.spell != e) {
this.spell = e;
this.storage.save('at_spell', e);
}
if (e) {
$('#train_passive').addClass('disabled');
$('#train_spell').removeClass('disabled');
} else {
$('#train_passive').removeClass('disabled');
$('#train_spell').addClass('disabled');
}
};
handlePercentual = n => {
let box = $('#train_percentuals');
let buttons = box.find('.button_new');
buttons.addClass('disabled');
$(`#train_percentuals_${n}`).removeClass('disabled');
if (this.percentual != n) {
this.percentual = n;
this.storage.save('at_per', n);
}
};
getTotalPopulation = town_id => {
const town = uw.ITowns.towns[town_id];
const data = GameData.units;
const { models: orders } = town.getUnitOrdersCollection();
let used = 0;
for (let order of orders) {
used += data[order.attributes.unit_type].population * (order.attributes.units_left / order.attributes.count) * order.attributes.count;
}
let units = town.units();
for (let unit of Object.keys(units)) {
used += data[unit].population * units[unit];
}
let outher = town.unitsOuter();
for (let out of Object.keys(outher)) {
used += data[out].population * outher[out];
}
return town.getAvailablePopulation() + used;
};
setPolisInSettings = town_id => {
let town = uw.ITowns.towns[town_id];
let researches = town.researches().attributes;
let buildings = town.buildings().attributes;
const isGray = troop => {
if (!this.REQUIREMENTS.hasOwnProperty(troop)) {
return true; // Tipo de tropa não reconhecido
}
const { research, building, level } = this.REQUIREMENTS[troop];
if (research && !researches[research]) return true;
if (building && buildings[building] < level) return true;
return false;
};
const getTroopHtml = (troop, bg) => {// todas os navios troop, researches, buildings implementar de 1 ou -1
let gray = isGray(troop, researches, buildings);
let color = 'red';
if (gray) {
return `
`;
}
return `
0
`;
};
const getTrooppHtml = (troop, bg) => {// todas as tropas terrestres implementar de 10 ou -10 (getTrooppHtml)
let gray = isGray(troop);
let color = 'red';
if (gray) {
return `
`;
}
return `
0
`;
};
uw.$('#troops_lvl_buttons').html(`
${getTrooppHtml('sword', [400, 0])}
${getTrooppHtml('archer', [50, 100])}
${getTrooppHtml('hoplite', [300, 50])}
${getTrooppHtml('slinger', [250, 350])}
${getTrooppHtml('rider', [50, 350])}
${getTrooppHtml('chariot', [200, 100])}
${getTrooppHtml('catapult', [150, 150])}
${getTrooppHtml('manticore', [0, 300])}
${getTroopHtml('big_transporter', [0, 150])}
${getTroopHtml('small_transporter', [300, 350])}
${getTroopHtml('bireme', [50, 150])}
${getTroopHtml('demolition_ship', [250, 0])}
${getTroopHtml('attack_ship', [150, 100])}
${getTroopHtml('trireme', [400, 250])}
${getTroopHtml('colonize_ship', [50, 200])}
`);
};
editTroopCount = (town_id, troop, count) => {
/* reinicie o intervalo para evitar spam*/
clearInterval(this.interval);
this.interval = setInterval(this.main, 10000);
const { units } = GameData;
const { city_troops } = this;
// Adicione a cidade ao objeto city_troops se ela ainda não existir
if (!city_troops.hasOwnProperty(town_id)) city_troops[town_id] = {};
if (count) {
// Modifique a contagem com base no fato de a tecla Shift estar pressionada
const index = count > 0 ? 0 : 1;
count = this.shiftHeld ? count * this.SHIFT_LEVELS[troop][index] : count;
} else {
count = 10000;
}
// Verifique se a contagem de tropas pode ser aumentada sem exceder a capacidade populacional
const total_pop = this.getTotalPopulation(town_id);
const used_pop = this.countPopulation(this.city_troops[town_id]);
const unit_pop = units[troop].population;
if (total_pop - used_pop < unit_pop * count) count = parseInt((total_pop - used_pop) / unit_pop);
// Atualize a contagem de tropas para a cidade e tipo de tropa especificados
if (troop in city_troops[town_id]) city_troops[town_id][troop] += count;
else city_troops[town_id][troop] = count;
/* Clenaup */
if (city_troops[town_id][troop] <= 0) delete city_troops[town_id][troop];
if (uw.$.isEmptyObject(city_troops[town_id])) delete this.city_troops[town_id];
this.updatePolisInSettings(town_id);
this.storage.save('troops', this.city_troops);
};
updatePolisInSettings = town_id => {
const { units } = GameData;
const cityTroops = this.city_troops[town_id];
Object.keys(units).forEach(troop => {
const guiCount = cityTroops?.[troop] ?? 0;
const selector = `#troops_settings_${town_id} #troop_lvl_${troop}`;
if (guiCount > 0) uw.$(selector).css('color', 'orange').text(guiCount);
else uw.$(selector).css('color', '').text('-');
});
const isTownActive = this.city_troops[town_id];
uw.$('#auto_train_title').css('filter', isTownActive ? 'brightness(100%) saturate(186%) hue-rotate(241deg)' : '');
};
trigger = () => {
const town = uw.ITowns.getCurrentTown();
const town_id = town.getId();
if (this.city_troops[town_id]) {
delete this.city_troops[town_id];
[...this.NAVAL_ORDER, ...this.GROUND_ORDER].forEach(troop => {
const selector = `#troops_settings_${town_id} #troop_lvl_${troop}`;
uw.$(selector).css('color', '').text('-');
});
uw.$('#auto_train_title').css('filter', '');
this.storage.save('troops', this.city_troops);
}
};
/* retornar a contagem do tipo de pedido (naval ou terrestre) */
getUnitOrdersCount = (type, town_id) => {
const town = uw.ITowns.getTown(town_id);
return town.getUnitOrdersCollection().where({ kind: type }).length;
};
getNextInList = (unitType, town_id) => {
const troops = this.city_troops[town_id];
if (!troops) return null;
const unitOrder = unitType === 'naval' ? this.NAVAL_ORDER : this.GROUND_ORDER;
for (const unit of unitOrder) {
if (troops[unit] && this.getTroopCount(unit, town_id) !== 0) return unit;
}
return null;
};
getTroopCount = (troop, town_id) => {
const town = uw.ITowns.getTown(town_id);
if (!this.city_troops[town_id] || !this.city_troops[town_id][troop]) return 0;
let count = this.city_troops[town_id][troop];
for (let order of town.getUnitOrdersCollection().models) {
if (order.attributes.unit_type === troop) count -= order.attributes.count;
}
let townUnits = town.units();
if (townUnits.hasOwnProperty(troop)) count -= townUnits[troop];
let outerUnits = town.unitsOuter();
if (outerUnits.hasOwnProperty(troop)) count -= outerUnits[troop];
//TODO: viajar
if (count < 0) return 0;
/*Obtenha o valor devido com os recursos atuais da polis */
let resources = town.resources();
let discount = uw.GeneralModifications.getUnitBuildResourcesModification(town_id, uw.GameData.units[troop]);
let { wood, stone, iron } = uw.GameData.units[troop].resources;
let w = resources.wood / Math.round(wood * discount);
let s = resources.stone / Math.round(stone * discount);
let i = resources.iron / Math.round(iron * discount);
let current = parseInt(Math.min(w, s, i));
/* Verifique se a população libre */
let duable_with_pop = parseInt(resources.population / uw.GameData.units[troop].population); // para cada tropa
/* Obtenha o máximo possível */
let w_max = resources.storage / (wood * discount);
let s_max = resources.storage / (stone * discount);
let i_max = resources.storage / (iron * discount);
let max = parseInt(Math.min(w_max, s_max, i_max) * 0.85); //0,8 é o percentual completo -> 80%
max = max > duable_with_pop ? duable_with_pop : max;
if (max > count) {
return count > current ? -1 : count;
} else {
if (current >= max && current < duable_with_pop) return current;
if (current >= max && current > duable_with_pop) return duable_with_pop;
return -1;
}
};
/* Verifique a cidade indicada, para terreno ou terreno*/
checkPolis = (type, town_id) => {
let order_count = this.getUnitOrdersCount(type, town_id);
if (order_count > 6) return 0;
let count = 1;
while (count >= 0) {
let next = this.getNextInList(type, town_id);
if (!next) return 0;
count = this.getTroopCount(next, town_id);
if (count < 0) return 0;
if (count === 0) continue;
this.buildPost(town_id, next, count);
return true;
}
};
/* Retornar lista de cidades que possuem energia ativa */
getPowerActive = () => {
const { fragments } = MM.getFirstTownAgnosticCollectionByName('CastedPowers');
let towns_list = [];
for (let town_id in this.city_troops) {
const { models } = fragments[town_id];
for (let power of models) {
let { attributes } = power;
if (this.POWER_LIST.includes(attributes.power_id)) {
towns_list.push(town_id);
break;
}
}
}
return towns_list;
};
/*Faça solicitação de build ao servidor */
buildPost = (town_id, unit, count) => {
let data = {
unit_id: unit,
amount: count,
town_id: town_id,
};
uw.gpAjax.ajaxPost('building_barracks', 'build', data);
uw.gpAjax.ajaxPost('building_temple', 'build', data);
};
/* devolver as cidades ativas */
getActiveList = () => {
if (!this.spell) return Object.keys(this.city_troops);
return this.getPowerActive();
};
/* Função principal, chamada no loop */
main = () => {
let town_list = this.getActiveList();
for (let town_id of town_list) {
if (town_id in uw.ITowns.towns) {
if (this.checkPolis('naval', town_id)) return;
if (this.checkPolis('ground', town_id)) return;
} else {
delete this.city_troops[town_id];
}
}
};
}
/*
botConsole.log(message);
ideas:
- add colors
*/
class BotConsole {
constructor() {
this.string = [];
this.updateSettings();
}
renderSettings = () => {
setTimeout(() => {
this.updateSettings();
let interval = setInterval(() => {
this.updateSettings();
if (!uw.$('#modern_console').length) clearInterval(interval);
}, 1000);
}, 100);
return ``;
};
log = (string) => {
const date = new Date();
const time = date.toLocaleTimeString();
this.string.push(`[${time}] ${string}`);
};
updateSettings = () => {
let console = uw.$('#modern_console');
this.string.forEach((e, i) => {
if (uw.$(`#log_id_${i}`).length) return;
console.prepend(`${e}
`);
});
};
}
class Compressor {
NUMBERS = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
SYMBOLS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-./:;<=>?@[]^_`{|}~';
ITEMS = {
// Buildings
academy: 'a',
barracks: 'b',
docks: 'd',
farm: 'f',
hide: 'h',
ironer: 'i',
lumber: 'l',
main: 'm',
market: 'k',
stoner: 'c',
storage: 's',
temple: 't',
wall: 'w',
// Troops
sword: 'A',
archer: 'B',
hoplite: 'C',
slinger: 'D',
rider: 'E',
chariot: 'F',
catapult: 'G',
big_transporter: 'H',
small_transporter: 'I',
manticore: 'J', // tentar acrescentar as manticore na lista
bireme: 'L',
demolition_ship: 'M',
attack_ship: 'N',
trireme: 'O',
colonize_ship: 'P',
};
constructor() {
const swap = json => {
var ret = {};
for (var key in json) {
ret[json[key]] = key;
}
return ret;
};
this.ITEMS_REV = swap(this.ITEMS);
}
/* Passe um objeto de armazenamento, retorne-o codificado */
encode(storage) {
for (let item in storage) {
if (typeof storage[item] !== 'object') continue;
if (item == 'buildings') {
for (let polis_id in storage[item]) {
let obj = storage[item][polis_id];
storage[item][polis_id] = this.encode_building(obj);
}
}
if (item == 'troops') {
for (let polis_id in storage[item]) {
let obj = storage[item][polis_id];
storage[item][polis_id] = this.encode_troops(obj);
}
}
}
return storage;
}
decode(storage) {
for (let item in storage) {
if (typeof storage[item] !== 'object') continue;
if (item == 'buildings') {
for (let polis_id in storage[item]) {
let str = storage[item][polis_id];
storage[item][polis_id] = this.decode_bulding(str);
}
}
if (item === 'troops') {
for (let polis_id in storage[item]) {
let str = storage[item][polis_id];
storage[item][polis_id] = this.decode_troops(str);
}
}
}
return storage;
}
compressNumber(num) {
let base = this.SYMBOLS.length;
let digits = [];
while (num > 0) {
digits.unshift(this.SYMBOLS[num % base]);
num = Math.floor(num / base);
}
if (digits.length == 1) {
digits.unshift('0');
}
return digits.slice(-2).join('');
}
decompressNumber(str) {
let base = this.SYMBOLS.length;
let digits = str.split('');
let num = 0;
for (let i = 0; i < digits.length; i++) {
num += this.SYMBOLS.indexOf(digits[i]) * Math.pow(base, digits.length - i - 1);
}
return num;
}
/* Dê o objeto de construção, retorne a string codificada */
encode_building(obj) {
let str = '';
for (let item in obj) {
str += this.ITEMS[item] + this.NUMBERS[obj[item]];
}
return str;
}
/* Forneça uma string codificada com construção, retorne o objeto correspondente */
decode_bulding(str) {
let json_str = '{';
for (let item of str.match(/.{1,2}/g)) {
json_str += `"${this.ITEMS_REV[item[0]]}"` + ':' + this.NUMBERS.indexOf(item[1]) + ',';
}
json_str = json_str.replace(/,$/, '}');
return JSON.parse(json_str);
}
encode_troops(obj) {
let str = '';
for (let item in obj) {
str += this.ITEMS[item] + this.compressNumber(obj[item]);
}
return str;
}
decode_troops(str) {
let json_str = '{';
for (let item of str.match(/.{1,3}/g)) {
json_str += `"${this.ITEMS_REV[item[0]]}"` + ':' + this.decompressNumber(item.slice(-2)) + ',';
}
json_str = json_str.replace(/,$/, '}');
return JSON.parse(json_str);
}
}
/*
Crie uma nova janela
*/
class createGrepoWindow {
constructor({ id, title, size, tabs, start_tab, minimizable = true }) {
this.minimizable = minimizable;
this.width = size[0];
this.height = size[1];
this.title = title;
this.id = id;
this.tabs = tabs;
this.start_tab = start_tab;
/* Métodos privados */
const createWindowType = (name, title, width, height, minimizable) => {
function WndHandler(wndhandle) {
this.wnd = wndhandle;
}
Function.prototype.inherits.call(WndHandler, uw.WndHandlerDefault);
WndHandler.prototype.getDefaultWindowOptions = function () {
return {
position: ['center', 'center', 100, 100],
width: width,
height: height,
minimizable: minimizable,
title: title,
};
};
uw.GPWindowMgr.addWndType(name, `${name}_75624`, WndHandler, 1);
};
const getTabById = (id) => {
return this.tabs.filter((tab) => tab.id === id)[0];
};
this.activate = function () {
createWindowType(this.id, this.title, this.width, this.height, this.minimizable); //
uw.$(
`
`,
).appendTo('head');
};
this.deactivate = function () {
if (uw.Layout.wnd.getOpenFirst(uw.GPWindowMgr[`TYPE_${this.id}`])) {
uw.Layout.wnd.getOpenFirst(uw.GPWindowMgr[`TYPE_${this.id}`]).close();
}
uw.$(`#${this.id}_custom_window_style`).remove();
};
/* abre a janela */
this.openWindow = function () {
let wn = uw.Layout.wnd.getOpenFirst(uw.GPWindowMgr[`TYPE_${this.id}`]);
/* se open for chamado, mas a janela já estiver aberta minimizada, maximize isso */
if (wn) {
if (wn.isMinimized()) {
wn.maximizeWindow();
}
return;
}
let content = ` `;
uw.Layout.wnd.Create(uw.GPWindowMgr[`TYPE_${this.id}`]).setContent(content);
/* Adicionar e redefinir guias */
this.tabs.forEach((e) => {
let html = `
${e.title}
`;
uw.$(html).appendTo(`#${this.id}`);
});
/* Adicione eventos às guias */
let tabs = '';
this.tabs.forEach((e) => {
tabs += `#${this.id} #${e.id}, `;
});
tabs = tabs.slice(0, -2);
let self = this;
uw.$(tabs).click(function () {
self.renderTab(this.id);
});
/* renderizar guia padrão*/
this.renderTab(this.tabs[this.start_tab].id);
};
this.closeWindow = function () {
uw.Layout.wnd.getOpenFirst(uw.GPWindowMgr[`TYPE_${this.id}`]).close();
};
/* Lidar com guia ativa */
this.renderTab = function (id) {
let tab = getTabById(id);
uw.$(`#${this.id}_content`).html(getTabById(id).render());
uw.$(`#${this.id} .active`).removeClass('active');
uw.$(`#${id}`).addClass('active');
getTabById(id).afterRender ? getTabById(id).afterRender() : '';
};
}
}
// TODO:
// - desabilitar notificação de nota
// - adicionar logs no console
class ModernStorage extends Compressor {
constructor() {
super();
this.check_done = 0;
/* Adicionar evento para adicionar o botão nas notas */
uw.$.Observer(uw.GameEvents.window.open).subscribe((e, i) => {
if (!i.attributes) return;
if (i.attributes.window_type != 'notes') return;
setTimeout(this.addButton, 100);
});
uw.$.Observer(uw.GameEvents.window.tab.rendered).subscribe((e, i) => {
const { attributes } = i.window_model;
if (!attributes) return;
if (attributes.window_type !== 'notes') return;
requestAnimationFrame(this.addButton);
});
}
getStorage = () => {
const worldId = uw.Game.world_id;
const savedValue = localStorage.getItem(`${worldId}_modernBot`);
let storage = {};
if (savedValue !== null && savedValue !== undefined) {
try {
storage = JSON.parse(savedValue);
} catch (error) {
console.error(`Error parsing localStorage data: ${error}`);
}
}
return storage;
};
saveStorage = storage => {
try {
const worldId = uw.Game.world_id;
localStorage.setItem(`${worldId}_modernBot`, JSON.stringify(storage));
this.lastUpdateTime = Date.now();
return true;
} catch (error) {
console.error(`Error saving data to localStorage: ${error}`);
return false;
}
};
save = (key, content) => {
const storage = this.getStorage();
storage[key] = content;
return this.saveStorage(storage);
};
load = (key, defaultValue = null) => {
const storage = this.getStorage();
const savedValue = storage[key];
return savedValue !== undefined ? savedValue : defaultValue;
};
/* Ligue para salvar a configuração no ID da nota fornecido */
saveSettingsNote = note_id => {
const storage = JSON.stringify(this.encode(this.getStorage()));
const data = {
model_url: `PlayerNote/${note_id}`,
action_name: 'save',
arguments: {
id: note_id,
text: storage,
},
};
uw.gpAjax.ajaxPost('frontend_bridge', 'execute', data);
return storage;
};
/* Ligue para adicionar os botões */
addButton = () => {
this.check_done += 1;
if ($('#modern_storage_load').length) return;
const modern_settings_load = $('', {
class: 'button_new',
id: 'modern_storage_load',
style: 'position: absolute; bottom: 5px; left: 6px; ',
onclick: 'modernBot.storage.loadSettings()',
html: ' Load ',
});
const modern_settings_save = $('', {
class: 'button_new',
id: 'modern_storage_save',
style: 'position: absolute; bottom: 5px; left: 75px; ',
onclick: 'modernBot.storage.saveSettings()',
html: ' Save ',
});
const box = $('.notes_container');
if (box.length) {
$('.notes_container').append(modern_settings_load, modern_settings_save);
} else {
if (this.check_done > 10) {
this.check_done = 0;
return;
}
setTimeout(this.addButton, 100);
}
};
saveSettings = () => {
uw.ConfirmationWindowFactory.openSimpleConfirmation(
'ModernStorage',
'Esta operação irá sobrescrever a nota atual com as configurações locais do ModernBot JoeMan',
() => {
// acionado quando o usuário pressiona sim
const note = this.getActiveNote();
if (!note) return; // TODO: exibir um erro
const content = this.saveSettingsNote(note.id);
$('.preview_box').text(content);
},
() => {}
);
};
loadSettings = () => {
// TODO: verifique se a nota atual possui configurações
uw.ConfirmationWindowFactory.openSimpleConfirmation(
'ModernStorage',
'Esta operação carregará as configurações da nota atual e substituirá as configurações locais',
() => {
// Acionado quando o usuário pressiona sim
const note = this.getActiveNote();
const { text } = note.attributes;
let decoded;
try {
decoded = this.decode(JSON.parse(text));
} catch {
HumanMessage.error("Este pedido não contém as configurações");
return;
}
this.saveStorage(decoded);
location.reload();
},
() => {}
);
};
/* Retornar a nota ativa atual */
getActiveNote() {
const noteClass = $('.tab.selected').attr('class');
if (!noteClass) return null;
const noteX = noteClass.match(/note(\d+)/)[1];
const note_index = parseInt(noteX) - 1;
const collection = MM.getOnlyCollectionByName('PlayerNote');
if (!collection) return null;
let { models } = collection;
return models[note_index];
}
}
/* Configure o farm automático no objeto de janela */
class ModernBot {
constructor() {
this.console = new BotConsole();
this.storage = new ModernStorage();
this.autoGratis = new AutoGratis(this.console, this.storage);
this.autoFarm = new AutoFarm(this.console, this.storage);
this.autoRuralLevel = new AutoRuralLevel(this.console, this.storage);
this.autoBuild = new AutoBuild(this.console, this.storage);
this.autoRuralTrade = new AutoRuralTrade(this.console, this.storage);
this.autoBootcamp = new AutoBootcamp(this.console, this.storage);
this.autoParty = new AutoParty(this.console, this.storage);
this.autoTrain = new AutoTrain(this.console, this.storage);
this.autoHide = new AutoHide(this.console, this.storage);
this.antiRage = new AntiRage(this.console, this.storage);
this.autoTrade = new AutoTrade(this.console, this.storage);
this.settingsFactory = new createGrepoWindow({
id: 'MODERN_BOT',
title: 'ModernBot Lang.℗ PT_BR By JoeMan',
size: [845, 300],
tabs: [
{
title: 'Farm',
id: 'farm',
render: this.settingsFarm,
},
{
title: 'Const',
id: 'build',
render: this.settingsBuild,
},
{
title: 'Trein',
id: 'train',
render: this.settingsTrain,
}, /*
{
title: 'Trade',
id: 'trade',
render: this.settingsTrade,
},*/
{
title: 'Mix',
id: 'mix',
render: this.settingsMix,
},
{
title: 'Console',
id: 'console',
render: this.console.renderSettings,
},
],
start_tab: 0,
});
this.setup();
}
settingsFarm = () => {
let html = '';
html += this.autoFarm.settings();
html += this.autoRuralLevel.settings();
html += this.autoRuralTrade.settings();
return html;
};
settingsBuild = () => {
let html = '';
html += this.autoGratis.settings();
html += this.autoBuild.settings();
return html;
};
settingsMix = () => {
let html = '';
html += this.autoBootcamp.settings();
html += this.autoParty.settings();
html += this.autoHide.settings();
return html;
};
settingsTrain = () => {
let html = '';
html += this.autoTrain.settings();
return html;
};
settingsTrade = () => {
let html = ``;
html += this.autoTrade.settings();
return html;
};
setup = () => {
/* Ativar */
this.settingsFactory.activate();
uw.$('.gods_area_buttons').append(" ");
/* Adicionar evento ao menu da lista de polis */
const editController = () => {
const townController = uw.layout_main_controller.sub_controllers.find(controller => controller.name === 'town_name_area');
if (!townController) {
setTimeout(editController, 2500);
return;
}
const oldRender = townController.controller.town_groups_list_view.render;
townController.controller.town_groups_list_view.render = function () {
oldRender.call(this);
const both = ``;
const build = ``;
const troop = ``;
const townIds = Object.keys(uw.modernBot.autoBuild.towns_buildings);
const troopsIds = uw.modernBot.autoTrain.getActiveList().map(entry => entry.toString());
uw.$('.town_group_town').each(function () {
const townId = parseInt(uw.$(this).attr('data-townid'));
const is_build = townIds.includes(townId.toString());
const id_troop = troopsIds.includes(townId.toString());
if (!id_troop && !is_build) return;
if (id_troop && !is_build) uw.$(this).prepend(troop);
else if (is_build && !id_troop) uw.$(this).prepend(build);
else uw.$(this).prepend(both);
});
};
};
setTimeout(editController, 2500);
};
}
setTimeout(async () => {
/* */
uw.modernBot = new ModernBot();
setTimeout(() => uw.modernBot.settingsFactory.openWindow(), 500);
}, 1000);