您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Helper for TheresMoreGame
当前为
- // ==UserScript==
- // @name TheresMoreHelp
- // @namespace TheresMoreGame.com
- // @match https://www.theresmoregame.com/play/
- // @grant none
- // @version 1.22.1
- // @description Helper for TheresMoreGame
- // @license MIT
- // @run-at document-idle
- // ==/UserScript==
- ;(async () => {
- let cheatsOff = true
- let scriptPaused = true
- let manualGold = false
- let haveManualResourceButtons = true
- let playingMarket = false
- const buildingsList = [
- { name: 'City of Lights', alwaysBuild: true, isSafe: true },
- { name: 'Harbor district', alwaysBuild: true, isSafe: true },
- { name: 'Stock exchange', alwaysBuild: true, isSafe: true },
- { name: 'Mana pit', alwaysBuild: true, isSafe: true },
- { name: 'Great bombard', alwaysBuild: true, isSafe: true },
- { name: 'Refugees district', alwaysBuild: true, isSafe: true },
- { name: 'Academy of Freethinkers', alwaysBuild: true, isSafe: true },
- { name: 'City center', alwaysBuild: true, isSafe: true },
- { name: 'Cathedral', alwaysBuild: true, isSafe: true },
- { name: 'Great fair', alwaysBuild: true, isSafe: true },
- { name: 'Palisade', alwaysBuild: true, isSafe: true },
- { name: 'Wall', alwaysBuild: true, isSafe: true },
- { name: 'City of Lights part', alwaysBuild: true, isSafe: true },
- { name: 'Harbor district part', alwaysBuild: true, isSafe: true },
- { name: 'Stock exchange part', alwaysBuild: true, isSafe: true },
- { name: 'Mana pit part', alwaysBuild: true, isSafe: true },
- { name: 'Great bombard part', alwaysBuild: true, isSafe: true },
- { name: 'Refugees district part', alwaysBuild: true, isSafe: true },
- { name: 'A. of Freethinkers Part', alwaysBuild: true, isSafe: true },
- { name: 'City center part', alwaysBuild: true, isSafe: true },
- { name: 'Great fair unit', alwaysBuild: true, isSafe: true },
- { name: 'Cathedral part', alwaysBuild: true, isSafe: true },
- { name: 'Guild of craftsmen', alwaysBuild: true, isSafe: true },
- { name: 'Machines of gods', alwaysBuild: true, isSafe: true },
- { name: 'Library of Theresmore', alwaysBuild: true, isSafe: true },
- { name: 'Monastery', alwaysBuild: true, isSafe: true },
- { name: 'Watchman Outpost', alwaysBuild: true, isSafe: true },
- { name: 'Hall of the dead', alwaysBuild: true, isSafe: true },
- { name: 'Sawmill', alwaysBuild: true, isSafe: true },
- { name: 'Monument', alwaysBuild: true, isSafe: true },
- { name: 'Foundry', alwaysBuild: true, isSafe: true },
- { name: 'Builder district', alwaysBuild: true, isSafe: true },
- { name: 'Gan Eden', alwaysBuild: true, isSafe: true },
- { name: 'Portal of the dead', alwaysBuild: true, isSafe: true },
- { name: 'Observatory', isSafe: true },
- { name: 'The Vaults', isSafe: true },
- { name: 'Credit union', isSafe: true },
- { name: 'Canava trading post', isSafe: true },
- { name: 'Bank', isSafe: true },
- { name: 'Marketplace', isSafe: true },
- { name: 'Artisan Workshop', isSafe: true },
- { name: 'Granary', isSafe: true },
- { name: 'School', isSafe: true },
- { name: 'University', isSafe: true },
- { name: 'Research plant', isSafe: true },
- { name: 'Undead Herds', isSafe: true },
- { name: 'Guarded warehouse', isSafe: true },
- { name: 'Fiefdom', isSafe: true },
- { name: 'Natronite refinery', isSafe: true },
- { name: 'Alchemical laboratory', isSafe: true },
- { name: 'Lumberjack Camp', isSafe: true },
- { name: 'Quarry', isSafe: true },
- { name: 'Mine', isSafe: true },
- { name: 'Palisade part', isSafe: true },
- { name: 'Wall part', isSafe: true },
- { name: 'Farm', isSafe: true },
- { name: 'Matter transmuter', isSafe: true },
- { name: 'Stable', isSafe: true },
- { name: 'Spiritual garden', isSafe: true },
- { name: 'Conclave', isSafe: true },
- { name: 'Magical tower', isSafe: true },
- { name: 'Temple', isSafe: true },
- { name: 'Altar of sacrifices', isSafe: true },
- { name: 'Fountain of Prosperity', isSafe: true },
- { name: 'Valley of plenty', isSafe: true },
- { name: 'Tax revenue checkpoints', isSafe: true },
- { name: 'Industrial plant', isSafe: true },
- { name: 'Magic Circle', isSafe: true },
- { name: 'Carpenter workshop', isSafe: true },
- { name: 'Grocery', isSafe: true },
- { name: 'Steelworks', isSafe: true },
- { name: 'Military academy', isSafe: true },
- { name: 'Ancient vault', isSafe: true },
- { name: 'Recruit training center', isSafe: true },
- { name: 'Officer training ground', isSafe: true },
- { name: 'Mansion', isSafe: false, requires: { resource: 'Food', parameter: 'speed', minValue: 3 } },
- { name: 'City Hall', isSafe: false, requires: { resource: 'Food', parameter: 'speed', minValue: 1.5 } },
- { name: 'Residential block', isSafe: false, requires: { resource: 'Food', parameter: 'speed', minValue: 5 } },
- { name: 'Common House', isSafe: false, requires: { resource: 'Food', parameter: 'speed', minValue: 1 } },
- { name: 'Storage facility', isSafe: true },
- { name: 'Ballista', isSafe: true },
- { name: 'Large warehouse', isSafe: true },
- { name: 'Large storehouse', isSafe: true },
- { name: 'Store', isSafe: true },
- { name: 'Natronite depot', isSafe: true },
- { name: 'Barracks', isSafe: true },
- { name: 'Minefield', isSafe: true },
- { name: 'Natronite balloon', isSafe: true },
- { name: 'Decryption of the portal', isSafe: true },
- { name: 'Fortune grove', isSafe: true },
- { name: 'Pillars of mana', isSafe: false, requires: { resource: 'Gold', parameter: 'speed', minValue: 100 } },
- ].filter((building) => building.name)
- const lang = {
- pop_artisan: 'Artisan',
- pop_breeder: 'Breeder',
- pop_farmer: 'Farmer',
- pop_lumberjack: 'Lumberjack',
- pop_merchant: 'Merchant',
- pop_trader: 'Trader',
- pop_miner: 'Miner',
- pop_quarryman: 'Quarryman',
- pop_priest: 'Priest',
- pop_carpenter: 'Carpenter',
- pop_steelworker: 'Steelworker',
- pop_professor: 'Professor',
- pop_skymancer: 'Skymancer',
- pop_supplier: 'Supplier',
- pop_alchemist: 'Alchemist',
- pop_unemployed: 'Unemployed',
- pop_natro_refiner: 'Nat-Refiner',
- pop_researcher: 'Researcher',
- res_army: 'Army',
- res_coin: 'Coin',
- res_copper: 'Copper',
- res_cow: 'Cow',
- res_crystal: 'Crystal',
- res_faith: 'Faith',
- res_fame: 'Fame',
- res_food: 'Food',
- res_gold: 'Gold',
- res_horse: 'Horse',
- res_iron: 'Iron',
- res_legacy: 'Legacy',
- res_luck: 'Luck',
- res_mana: 'Mana',
- res_natronite: 'Natronite',
- res_population: 'Population',
- res_stone: 'Stone',
- res_relic: 'Relic',
- res_research: 'Research',
- res_tools: 'Tools',
- res_wood: 'Wood',
- res_building_material: 'Materials',
- res_steel: 'Steel',
- res_supplies: 'Supplies',
- res_saltpetre: 'Saltpetre',
- res_tome_wisdom: 'Tome of Wisdom',
- res_gem: 'Gem',
- }
- const allJobs = [
- {
- id: 'unemployed',
- },
- {
- id: 'farmer',
- req: [
- {
- type: 'building',
- id: 'farm',
- value: 1,
- },
- ],
- gen: [
- {
- type: 'resource',
- id: 'food',
- value: 1.6,
- },
- ],
- },
- {
- id: 'lumberjack',
- req: [
- {
- type: 'building',
- id: 'lumberjack_camp',
- value: 1,
- },
- ],
- gen: [
- {
- type: 'resource',
- id: 'wood',
- value: 0.7,
- },
- ],
- },
- {
- id: 'quarryman',
- req: [
- {
- type: 'building',
- id: 'quarry',
- value: 1,
- },
- ],
- gen: [
- {
- type: 'resource',
- id: 'stone',
- value: 0.6,
- },
- ],
- },
- {
- id: 'miner',
- req: [
- {
- type: 'building',
- id: 'mine',
- value: 1,
- },
- ],
- gen: [
- {
- type: 'resource',
- id: 'copper',
- value: 0.5,
- },
- {
- type: 'resource',
- id: 'iron',
- value: 0.3,
- },
- ],
- },
- {
- id: 'artisan',
- req: [
- {
- type: 'building',
- id: 'artisan_workshop',
- value: 1,
- },
- ],
- gen: [
- {
- type: 'resource',
- id: 'gold',
- value: 0.5,
- },
- {
- type: 'resource',
- id: 'tools',
- value: 0.3,
- },
- ],
- },
- {
- id: 'merchant',
- req: [
- {
- type: 'building',
- id: 'marketplace',
- value: 1,
- },
- ],
- gen: [
- {
- type: 'resource',
- id: 'gold',
- value: 3,
- },
- ],
- },
- {
- id: 'trader',
- req: [
- {
- type: 'building',
- id: 'credit_union',
- value: 1,
- },
- ],
- gen: [
- {
- type: 'resource',
- id: 'gold',
- value: 6,
- },
- ],
- },
- {
- id: 'breeder',
- req: [
- {
- type: 'building',
- id: 'stable',
- value: 1,
- },
- ],
- gen: [
- {
- type: 'resource',
- id: 'cow',
- value: 0.2,
- },
- {
- type: 'resource',
- id: 'horse',
- value: 0.1,
- },
- ],
- },
- {
- id: 'carpenter',
- req: [
- {
- type: 'building',
- id: 'carpenter_workshop',
- value: 1,
- },
- ],
- gen: [
- {
- type: 'resource',
- id: 'building_material',
- value: 0.3,
- },
- {
- type: 'resource',
- id: 'wood',
- value: -3,
- },
- {
- type: 'resource',
- id: 'stone',
- value: -1.5,
- },
- {
- type: 'resource',
- id: 'tools',
- value: -0.5,
- },
- ],
- },
- {
- id: 'steelworker',
- req: [
- {
- type: 'building',
- id: 'steelworks',
- value: 1,
- },
- ],
- gen: [
- {
- type: 'resource',
- id: 'steel',
- value: 0.4,
- },
- {
- type: 'resource',
- id: 'copper',
- value: -1,
- },
- {
- type: 'resource',
- id: 'iron',
- value: -0.5,
- },
- ],
- },
- {
- id: 'professor',
- req: [
- {
- type: 'building',
- id: 'university',
- value: 1,
- },
- ],
- gen: [
- {
- type: 'resource',
- id: 'crystal',
- value: 0.06,
- },
- {
- type: 'resource',
- id: 'research',
- value: 1,
- },
- ],
- },
- {
- id: 'researcher',
- req: [
- {
- type: 'building',
- id: 'research_plant',
- value: 1,
- },
- ],
- gen: [
- {
- type: 'resource',
- id: 'research',
- value: 3,
- },
- ],
- },
- {
- id: 'supplier',
- req: [
- {
- type: 'building',
- id: 'grocery',
- value: 1,
- },
- ],
- gen: [
- {
- type: 'resource',
- id: 'supplies',
- value: 0.4,
- },
- {
- type: 'resource',
- id: 'food',
- value: -2,
- },
- {
- type: 'resource',
- id: 'cow',
- value: -0.2,
- },
- ],
- },
- {
- id: 'skymancer',
- req: [
- {
- type: 'building',
- id: 'observatory',
- value: 1,
- },
- ],
- gen: [
- {
- type: 'resource',
- id: 'faith',
- value: 3,
- },
- {
- type: 'resource',
- id: 'mana',
- value: 3,
- },
- ],
- },
- {
- id: 'alchemist',
- req: [
- {
- type: 'building',
- id: 'alchemic_laboratory',
- value: 1,
- },
- ],
- gen: [
- {
- type: 'resource',
- id: 'saltpetre',
- value: 0.7,
- },
- ],
- },
- {
- id: 'natro_refiner',
- req: [
- {
- type: 'building',
- id: 'natronite_refinery',
- value: 1,
- },
- ],
- gen: [
- {
- type: 'resource',
- id: 'natronite',
- value: 1,
- },
- {
- type: 'resource',
- id: 'mana',
- value: -5,
- },
- {
- type: 'resource',
- id: 'saltpetre',
- value: -0.5,
- },
- ],
- },
- ]
- .filter((job) => job.gen && job.gen.length)
- .map((job) => {
- return {
- id: lang[`pop_${job.id}`],
- gen: job.gen
- .filter((gen) => gen.type === 'resource')
- .map((gen) => {
- return {
- id: lang[`res_${gen.id}`],
- value: gen.value,
- }
- }),
- }
- })
- .map((job) => {
- return {
- id: job.id,
- isSafe: !job.gen.find((gen) => gen.value < 0),
- resourcesGenerated: job.gen
- .filter((gen) => gen.value > 0)
- .map((gen) => {
- return { id: gen.id, value: gen.value }
- }),
- resourcesUsed: job.gen
- .filter((gen) => gen.value < 0)
- .map((gen) => {
- return { id: gen.id, value: gen.value }
- }),
- }
- })
- const modes = {
- RESEARCH: 'Research',
- GAIN_GOLD: 'Gain Gold',
- BUILD: 'Build',
- BUY_BATTLE_ANGEL: 'Buy BA',
- ASSIGN_POPULATION: 'Assign Population',
- DEFAULT: 'Default',
- }
- let mode = modes.DEFAULT
- let marketPlaceAvailable = false
- const pages = {
- BUILD: 'Build',
- RESEARCH: 'Research',
- POPULATION: 'Population',
- ARMY: 'Army',
- MARKETPLACE: 'Marketplace',
- MAGIC: 'Magic',
- UNKNOWN: 'Unknown',
- }
- const resourcesToTrade = ['Cow', 'Horse', 'Food', 'Copper', 'Wood', 'Stone', 'Iron', 'Tools']
- const timeToWaitUntilFullGold = 60
- let takePageActionTimeout
- const sleep = (miliseconds) => new Promise((resolve) => setTimeout(resolve, miliseconds))
- // https://stackoverflow.com/a/55366435
- class NumberParser {
- constructor(locale) {
- const format = new Intl.NumberFormat(locale)
- const parts = format.formatToParts(12345.6)
- const numerals = Array.from({ length: 10 }).map((_, i) => format.format(i))
- const index = new Map(numerals.map((d, i) => [d, i]))
- this._group = new RegExp(`[${parts.find((d) => d.type === 'group').value}]`, 'g')
- this._decimal = new RegExp(`[${parts.find((d) => d.type === 'decimal').value}]`)
- this._numeral = new RegExp(`[${numerals.join('')}]`, 'g')
- this._index = (d) => index.get(d)
- }
- parse(string) {
- let multiplier = 1
- if (string.includes('K')) {
- multiplier = 1000
- } else if (string.includes('M')) {
- multiplier = 1000000
- }
- return (string = string.replace('K', '').replace('M', '').trim().replace(this._group, '').replace(this._decimal, '.').replace(this._numeral, this._index))
- ? +string * multiplier
- : NaN
- }
- }
- const numberParser = new NumberParser()
- const formatTime = (timeToFormat) => {
- const timeValues = {
- seconds: 0,
- minutes: 0,
- hours: 0,
- days: 0,
- }
- let timeShort = ''
- let timeLong = ''
- timeValues.seconds = timeToFormat % 60
- timeToFormat = (timeToFormat - (timeToFormat % 60)) / 60
- timeValues.minutes = timeToFormat % 60
- timeToFormat = (timeToFormat - (timeToFormat % 60)) / 60
- timeValues.hours = timeToFormat % 24
- timeToFormat = (timeToFormat - (timeToFormat % 24)) / 24
- timeValues.days = timeToFormat
- if (timeValues.days) {
- timeShort += `${timeValues.days}d `
- timeLong += `${timeValues.days} days `
- }
- if (timeValues.hours) {
- timeShort += `${timeValues.hours}h `
- timeLong += `${timeValues.hours} hrs `
- }
- if (timeValues.minutes) {
- timeShort += `${timeValues.minutes}m `
- timeLong += `${timeValues.minutes} min `
- }
- if (timeValues.seconds) {
- timeShort += `${timeValues.seconds}s `
- timeLong += `${timeValues.seconds} sec `
- }
- timeShort = timeShort.trim()
- timeLong = timeLong.trim()
- return {
- timeShort,
- timeLong,
- timeValues,
- }
- }
- window.switchScriptState = () => {
- scriptPaused = !scriptPaused
- window.localStorage.setItem('TMH_cheatsOff', JSON.stringify(false))
- window.localStorage.setItem('TMH_scriptPaused', JSON.stringify(scriptPaused))
- if (!scriptPaused) {
- setTimeout(() => {
- window.location.reload()
- }, 60 * 60 * 1000)
- mode = modes.DEFAULT
- switchPage(pages.BUILD)
- }
- }
- window.switchManualGold = () => {
- manualGold = !manualGold
- window.localStorage.setItem('TMH_cheatsOff', JSON.stringify(false))
- window.localStorage.setItem('TMH_manualGold', JSON.stringify(manualGold))
- }
- const getAllButtons = () => {
- let buttonsList = []
- document.querySelectorAll('#maintabs-container > div > div[role=tabpanel] button.btn.btn-dark:not(.btn-off)').forEach((button) => buttonsList.push(button))
- return buttonsList
- }
- const getResource = (resourceName = 'Gold') => {
- let resourceFound = false
- let resourceToFind = { name: resourceName, current: 0, max: 0, speed: 0, ttf: null, ttz: null }
- const resources = document.querySelectorAll('#root div > div > div > table > tbody > tr > td:nth-child(1) > span')
- resources.forEach((resource) => {
- if (resource.textContent.includes(resourceName)) {
- resourceFound = true
- const values = resource.parentNode.parentNode.childNodes[1].textContent
- .split('/')
- .map((x) => numberParser.parse(x.replace(/[^0-9KM\-,\.]/g, '').trim()))
- resourceToFind.current = values[0]
- resourceToFind.max = values[1]
- resourceToFind.speed = numberParser.parse(resource.parentNode.parentNode.childNodes[2].textContent.replace(/[^0-9KM\-,\.]/g, '').trim()) || 0
- resourceToFind.ttf =
- resourceToFind.speed > 0 && resourceToFind.max !== resourceToFind.current
- ? formatTime(Math.ceil((resourceToFind.max - resourceToFind.current) / resourceToFind.speed))
- : null
- resourceToFind.ttz =
- resourceToFind.speed < 0 && resourceToFind.current ? formatTime(Math.ceil(resourceToFind.current / (resourceToFind.speed * -1))) : null
- }
- })
- return resourceFound ? resourceToFind : null
- }
- const getAllResources = () => {
- const resources = document.querySelectorAll('#root div > div > div > table > tbody > tr > td:nth-child(1) > span')
- const resourceNames = []
- resources.forEach((resource) => resourceNames.push(resource.textContent.trim()))
- return resourceNames
- }
- const getPage = () => {
- let page = pages.UNKNOWN
- const pageSelectedSelector = document.querySelector('#main-tabs > div > button[aria-selected="true"]')
- if (pageSelectedSelector) {
- const pageSelected = pageSelectedSelector.textContent
- if (pageSelected.includes('Build')) {
- page = pages.BUILD
- } else if (pageSelected.includes('Research')) {
- page = pages.RESEARCH
- } else if (pageSelected.includes('Population')) {
- page = pages.POPULATION
- } else if (pageSelected.includes('Army')) {
- page = pages.ARMY
- } else if (pageSelected.includes('Marketplace')) {
- page = pages.MARKETPLACE
- } else if (pageSelected.includes('Magic')) {
- page = pages.MAGIC
- }
- }
- return page
- }
- const hasPage = (page) => {
- let foundPage = false
- const navButtons = document.querySelectorAll('#main-tabs > div > button')
- navButtons.forEach((button) => {
- if (button.innerText.includes(page)) {
- foundPage = true
- }
- })
- return foundPage
- }
- const switchPage = async (page) => {
- if (cheatsOff) return
- if (scriptPaused) return
- let foundPage = hasPage(page)
- if (!foundPage) {
- mode = modes.DEFAULT
- switchPage(pages.BUILD)
- return
- }
- let switchedPage = false
- const navButtons = document.querySelectorAll('#main-tabs > div > button')
- navButtons.forEach((button) => {
- if (button.innerText.includes(page) && button.getAttribute('aria-selected') !== 'true') {
- button.click()
- switchedPage = true
- }
- })
- if (switchedPage) {
- console.log(`$-> Switched page to ${page}`)
- await sleep(3500)
- }
- clearTimeout(takePageActionTimeout)
- takePageActionTimeout = setTimeout(takePageAction, 1)
- }
- const takePageAction = async () => {
- if (cheatsOff) return
- if (scriptPaused) return
- let page = getPage()
- if (page !== pages.BUILD && mode === modes.BUILD) {
- switchPage(pages.BUILD)
- return
- } else if (page !== pages.RESEARCH && mode === modes.RESEARCH) {
- switchPage(pages.RESEARCH)
- return
- } else if (page !== pages.MARKETPLACE && mode === modes.GAIN_GOLD) {
- switchPage(pages.MARKETPLACE)
- return
- } else if (page !== pages.ARMY && mode === modes.BUY_BATTLE_ANGEL) {
- switchPage(pages.ARMY)
- return
- } else if (page !== pages.POPULATION && mode === modes.ASSIGN_POPULATION) {
- switchPage(pages.POPULATION)
- return
- }
- if (page === pages.BUILD) {
- setTimeout(spendResourcesBuilding, 1)
- } else if (page === pages.RESEARCH) {
- setTimeout(performReserach, 1)
- } else if (page === pages.MARKETPLACE) {
- setTimeout(playMarket, 1)
- } else if (page === pages.ARMY) {
- setTimeout(buyArmy, 1)
- } else if (page === pages.POPULATION) {
- setTimeout(assignPopulation, 1)
- } else {
- clearTimeout(takePageActionTimeout)
- takePageActionTimeout = setTimeout(takePageAction, 1000)
- }
- }
- const hasUnassignedPopulation = () => {
- let unassignedPopulation = false
- const navButtons = document.querySelectorAll('#main-tabs > div > button')
- navButtons.forEach((button) => {
- if (button.innerText.includes(pages.POPULATION)) {
- unassignedPopulation = !!button.querySelector('span')
- }
- })
- return unassignedPopulation
- }
- const spendResourcesBuilding = async () => {
- if (cheatsOff) return
- if (scriptPaused) return
- if (getPage() !== pages.BUILD) {
- clearTimeout(takePageActionTimeout)
- takePageActionTimeout = setTimeout(takePageAction, 1000)
- return
- }
- const buttonsList = getAllButtons()
- const buildingNames = []
- buttonsList.forEach((button) => buildingNames.push(button.innerText.split('\n').shift()))
- let hasBuilt = false
- let shouldBuild = false
- if (buildingNames.length) {
- for (let i = 0; i < buildingsList.length && !hasBuilt; i++) {
- if (hasBuilt) break
- const building = buildingsList[i]
- shouldBuild = buildingNames.includes(building.name)
- if (!building.isSafe) {
- const requiredResource = getResource(building.requires.resource)
- if (!requiredResource) {
- shouldBuild = false
- } else {
- shouldBuild = shouldBuild && requiredResource[building.requires.parameter] > building.requires.minValue
- }
- }
- if (shouldBuild) {
- if (building.alwaysBuild || mode === modes.BUILD) {
- const buildButton = buttonsList.find((button) => button.innerText.includes(building.name))
- if (buildButton) {
- buildButton.click()
- hasBuilt = true
- console.log(`#-> Started building ${building.name}`)
- await sleep(1000)
- }
- mode = modes.DEFAULT
- break
- } else {
- mode = modes.BUILD
- break
- }
- }
- }
- }
- await sleep(5000)
- if (mode === modes.BUILD || shouldBuild || hasBuilt) {
- await switchPage(pages.BUILD)
- } else {
- const gold = getResource('Gold')
- let shouldSell = resourcesToTrade.find((resName) => {
- const res = getResource(resName)
- if (res && (res.current === res.max || res.current + res.speed * timeToWaitUntilFullGold >= res.max)) return true
- })
- if (hasPage(pages.POPULATION) && hasUnassignedPopulation()) {
- mode = modes.ASSIGN_POPULATION
- await switchPage(pages.POPULATION)
- } else if (hasPage(pages.ARMY) && canAffordBA() && shouldBuyBA()) {
- mode = modes.BUY_BATTLE_ANGEL
- await switchPage(pages.ARMY)
- } else if (marketPlaceAvailable && gold.current + gold.speed * timeToWaitUntilFullGold < gold.max && shouldSell) {
- mode = modes.GAIN_GOLD
- await switchPage(pages.MARKETPLACE)
- } else {
- mode = modes.RESEARCH
- await switchPage(pages.RESEARCH)
- }
- }
- }
- const performReserach = async () => {
- if (cheatsOff) return
- if (scriptPaused) return
- if (getPage() !== pages.RESEARCH) {
- clearTimeout(takePageActionTimeout)
- takePageActionTimeout = setTimeout(takePageAction, 1000)
- return
- }
- const manualResearches = ['A moonlight night', 'Dragon assault', 'Mysterious robbery', 'The Fallen Angel reveal']
- let didResearch = false
- const buttonsList = getAllButtons()
- const researchesList = []
- buttonsList.forEach(
- (button) => !manualResearches.includes(button.innerText.split('\n').shift()) && researchesList.push(button.innerText.split('\n').shift())
- )
- if (researchesList.length) {
- while (!didResearch && researchesList.length) {
- const research = researchesList.shift()
- buttonsList.forEach((button) => {
- if (button.innerText.split('\n').shift() === research) {
- button.click()
- didResearch = true
- console.log(`%-> Researching ${research}`)
- }
- })
- }
- }
- if (didResearch) {
- await sleep(5000)
- } else {
- await sleep(2000)
- }
- mode = modes.DEFAULT
- await switchPage(pages.BUILD)
- }
- const playMarket = async () => {
- if (cheatsOff) return
- if (playingMarket) return
- if (getPage() !== pages.MARKETPLACE) {
- clearTimeout(takePageActionTimeout)
- takePageActionTimeout = setTimeout(takePageAction, 1000)
- return
- }
- let gold = getResource('Gold')
- let shouldSell = resourcesToTrade.find((resName) => {
- const res = getResource(resName)
- if (res && res.current === res.max) return true
- })
- if (gold && gold.current < gold.max && shouldSell) {
- playingMarket = true
- const resourceHoldersQSA = document.querySelectorAll('div > div.tab-container > div > div > div')
- const resourceHolders = []
- if (resourceHoldersQSA) {
- resourceHoldersQSA.forEach((resourceHolder) => {
- const resName = resourceHolder.querySelector('h5').innerText
- const res = getResource(resName)
- if (resourcesToTrade.includes(resName) && res && (res.current === res.max || res.current + res.speed * timeToWaitUntilFullGold >= res.max)) {
- resourceHolders.push(resourceHolder)
- }
- })
- }
- for (let i = 0; i < resourceHolders.length; i++) {
- gold = getResource('Gold')
- const resourceHolder = resourceHolders[i]
- const resName = resourceHolder.querySelector('h5').innerText
- let res = getResource(resName)
- const initialPrice = numberParser.parse(resourceHolder.querySelector('div:nth-child(2) > div > table > tbody > tr > td:nth-child(2)').innerText)
- let price = initialPrice
- let sellButtons = resourceHolder.querySelectorAll('div:nth-child(2) > div.grid.gap-3 button:not(.btn-dark)')
- while (sellButtons && sellButtons.length && gold.current < gold.max && res.current + res.speed * timeToWaitUntilFullGold * 2 >= res.max) {
- let maxSellButton = 2
- const missingResToSell = Math.ceil((gold.max - gold.current) / price)
- if (missingResToSell < 80) {
- maxSellButton = 0
- } else if (missingResToSell < 800) {
- maxSellButton = 1
- }
- maxSellButton = Math.min(maxSellButton, sellButtons.length - 1)
- sellButtons[maxSellButton].click()
- console.log(`$$> Selling ${sellButtons[maxSellButton].innerText} of ${res.name} for ${price}`)
- await sleep(10)
- sellButtons = resourceHolder.querySelectorAll('div:nth-child(2) > div.grid.gap-3 button:not(.btn-dark)')
- gold = getResource('Gold')
- res = getResource(resName)
- price = numberParser.parse(resourceHolder.querySelector('div:nth-child(2) > div > table > tbody > tr > td:nth-child(2)').innerText)
- await sleep(1)
- if (price / initialPrice < 0.1) {
- break
- }
- }
- }
- playingMarket = false
- }
- mode = modes.RESEARCH
- await switchPage(pages.RESEARCH)
- }
- const canAffordBA = () => {
- const faith = getResource('Faith')
- const mana = getResource('Mana')
- if (faith && mana) {
- if (faith.current >= 2000 && mana.current >= 600) {
- return true
- }
- }
- return false
- }
- const shouldBuyBA = () => {
- const faith = getResource('Faith')
- const mana = getResource('Mana')
- if (faith && mana) {
- if (
- faith.current + faith.speed * timeToWaitUntilFullGold >= faith.max &&
- mana.current + mana.speed * timeToWaitUntilFullGold >= mana.max &&
- mana.speed > 10
- ) {
- return true
- }
- }
- return false
- }
- const buyArmy = async () => {
- if (cheatsOff) return
- if (scriptPaused) return
- if (getPage() !== pages.ARMY) {
- clearTimeout(takePageActionTimeout)
- takePageActionTimeout = setTimeout(takePageAction, 1000)
- return
- }
- if (canAffordBA() && shouldBuyBA()) {
- const allButtonsQSA = document.querySelectorAll('div > div > div > div > div > span > button:not(.btn-off)')
- let buyBAButton = null
- allButtonsQSA.forEach((button) => {
- if (button.innerText.includes('Battle Angel')) {
- buyBAButton = button
- }
- })
- if (buyBAButton) {
- buyBAButton.click()
- console.log(`@-> Buying Battle Angel(s)`)
- await sleep(5000)
- }
- }
- mode = modes.RESEARCH
- await switchPage(pages.RESEARCH)
- }
- const assignPopulation = async () => {
- if (cheatsOff) return
- if (scriptPaused) return
- if (getPage() !== pages.POPULATION) {
- clearTimeout(takePageActionTimeout)
- takePageActionTimeout = setTimeout(takePageAction, 1000)
- return
- }
- let canAssignJobs = true
- const container = document.querySelector('#maintabs-container > div > div[role=tabpanel]')
- let availablePop = container
- .querySelector('div > span.ml-2')
- .textContent.split('/')
- .map((pop) => numberParser.parse(pop.trim()))
- const availableJobsQSA = container.querySelectorAll('h5')
- const availableJobs = []
- availableJobsQSA.forEach((job) => {
- const jobTitle = job.textContent.trim()
- availableJobs.push({
- ...allJobs.find((allJob) => allJob.id === jobTitle),
- container: job.parentElement.parentElement,
- })
- })
- if (availablePop[0] > 0) {
- while (canAssignJobs) {
- const jobsWithSpace = availableJobs.filter((job) => !!job.container.querySelector('button.btn-green'))
- canAssignJobs = false
- if (jobsWithSpace.length) {
- if (getResource('Food').speed <= 0) {
- const foodJob = jobsWithSpace.find((job) => job.resourcesGenerated.find((res) => res.id === 'Food'))
- if (foodJob) {
- const addJobButton = foodJob.container.querySelector('button.btn-green')
- if (addJobButton) {
- console.log(`**> Assigning worker as ${foodJob.id}`)
- addJobButton.click()
- canAssignJobs = true
- await sleep(1000)
- }
- }
- } else {
- let unassigned = container
- .querySelector('div > span.ml-2')
- .textContent.split('/')
- .map((pop) => numberParser.parse(pop.trim()))
- .shift()
- if (unassigned > 0) {
- const resources = [
- 'Natronite',
- 'Saltpetre',
- 'Tools',
- 'Wood',
- 'Stone',
- 'Iron',
- 'Copper',
- 'Mana',
- 'Faith',
- 'Research',
- 'Materials',
- 'Steel',
- 'Supplies',
- 'Gold',
- 'Crystal',
- 'Horse',
- 'Cow',
- ]
- .filter((res) => getResource(res))
- .filter((res) => jobsWithSpace.find((job) => job.resourcesGenerated.find((resGen) => resGen.id === res)))
- const resourcesWithNegativeGen = resources.filter((res) => getResource(res) && res.speed < 0)
- const resourcesWithNoGen = resources.filter((res) => !resourcesWithNegativeGen.includes(res) && getResource(res) && !res.speed)
- const resourcesLeft = resources.filter((res) => !resourcesWithNegativeGen.includes(res) && !resourcesWithNoGen.includes(res))
- const resourcesSorted = resourcesWithNegativeGen.concat(resourcesWithNoGen).concat(resourcesLeft)
- if (resourcesSorted.length) {
- for (let i = 0; i < resourcesSorted.length; i++) {
- if (unassigned === 0) break
- const resourceName = resourcesSorted[i]
- const jobsForResource = jobsWithSpace
- .filter((job) => job.resourcesGenerated.find((resGen) => resGen.id === resourceName))
- .sort(
- (a, b) =>
- b.resourcesGenerated.find((resGen) => resGen.id === resourceName).value -
- a.resourcesGenerated.find((resGen) => resGen.id === resourceName).value
- )
- if (jobsForResource.length) {
- for (let i = 0; i < jobsForResource.length; i++) {
- if (unassigned === 0) break
- const job = jobsForResource[i]
- let isSafeToAdd = job.isSafe
- if (!job.isSafe) {
- job.resourcesUsed.forEach((resUsed) => {
- const res = getResource(resUsed.id)
- if (!res || res.speed < Math.abs(resUsed.value * 2)) {
- isSafeToAdd = false
- }
- })
- }
- if (isSafeToAdd) {
- const addJobButton = job.container.querySelector('button.btn-green')
- if (addJobButton) {
- console.log(`*-> Assigning worker as ${job.id}`)
- addJobButton.click()
- unassigned -= 1
- canAssignJobs = !!unassigned
- await sleep(1000)
- }
- }
- }
- }
- }
- }
- }
- }
- }
- const unassigned = container
- .querySelector('div > span.ml-2')
- .textContent.split('/')
- .map((pop) => numberParser.parse(pop.trim()))
- .shift()
- if (unassigned === 0) {
- canAssignJobs = false
- }
- await sleep(10)
- }
- }
- mode = modes.RESEARCH
- await switchPage(pages.RESEARCH)
- }
- const managePanel = async () => {
- if (cheatsOff) return
- const controlPanel = document.querySelector('div#theresMoreHelpControlPanel')
- let scriptState = scriptPaused ? `▶️` : `⏸️`
- let manualGoldState = manualGold ? '🤑' : `😅`
- if (!controlPanel) {
- const controlPanelElement = document.createElement('div')
- controlPanelElement.id = 'theresMoreHelpControlPanel'
- controlPanelElement.classList.add('dark')
- controlPanelElement.classList.add('dark:bg-mydark-300')
- controlPanelElement.style.position = 'fixed'
- controlPanelElement.style.bottom = '10px'
- controlPanelElement.style.left = '10px'
- controlPanelElement.style.zIndex = '99999999'
- controlPanelElement.style.border = '1px black solid'
- controlPanelElement.style.padding = '10px'
- controlPanelElement.innerHTML = `
- <p class="mb-2">TheresMoreHelp:
- <button onClick="window.switchScriptState()" title="Start/stop script" class="scriptState">${scriptState}</button>
- <button onClick="window.switchManualGold()" title="Always play market" class="manualGold">${manualGoldState}</button>
- </p>
- <p class="currentPage text-xs mb-2 text-orange-500">Page: ${getPage()}</p>
- <p class="currentAction text-xs text-orange-500">Mode: ${mode}</p>
- `
- document.querySelector('div#root').insertAdjacentElement('afterend', controlPanelElement)
- } else {
- controlPanel.querySelector('.currentPage').textContent = `Page: ${getPage()}`
- controlPanel.querySelector('.currentAction').textContent = `Mode: ${mode}`
- controlPanel.querySelector('.scriptState').textContent = scriptState
- controlPanel.querySelector('.manualGold').textContent = manualGoldState
- }
- }
- const calculateTTF = () => {
- const resourceTrNodes = document.querySelectorAll('#root > div > div:not(#maintabs-container) > div > div > div > table:not(.hidden) > tbody > tr')
- resourceTrNodes.forEach((row) => {
- const cells = row.querySelectorAll('td')
- const resourceName = cells[0].textContent.trim()
- const resource = getResource(resourceName)
- let ttf = ''
- if (resource && resource.current < resource.max && resource.speed) {
- ttf = resource.ttf ? resource.ttf.timeLong : resource.ttz.timeLong
- }
- if (!cells[3]) {
- const ttfElement = document.createElement('td')
- ttfElement.className = 'px-3 3xl:px-5 py-3 lg:py-2 3xl:py-3 whitespace-nowrap w-1/3 text-right'
- ttfElement.textContent = ttf
- row.appendChild(ttfElement)
- } else {
- cells[3].textContent = ttf
- }
- })
- }
- const calculateTippyTTF = () => {
- let potentialResourcesToFillTable = document.querySelectorAll('div.tippy-box > div.tippy-content > div > div > table')
- if (potentialResourcesToFillTable.length) {
- potentialResourcesToFillTable = potentialResourcesToFillTable[0]
- const rows = potentialResourcesToFillTable.querySelectorAll('tr')
- rows.forEach((row) => {
- const cells = row.querySelectorAll('td')
- const resourceName = cells[0].textContent.trim()
- const resource = getResource(resourceName)
- if (resource) {
- let ttf = '✅'
- const target = numberParser.parse(
- cells[1].textContent
- .split(' ')
- .shift()
- .replace(/[^0-9KM\-,\.]/g, '')
- .trim()
- )
- if (target > resource.max || resource.speed <= 0) {
- ttf = 'never'
- } else if (target > resource.current) {
- ttf = formatTime(Math.ceil((target - resource.current) / resource.speed)).timeShort
- }
- if (!cells[2]) {
- const ttfElement = document.createElement('td')
- ttfElement.className = 'px-4 3xl:py-1 text-right'
- ttfElement.textContent = ttf
- row.appendChild(ttfElement)
- } else {
- cells[2].textContent = ttf
- }
- }
- })
- }
- }
- const checkMarketplaceAvailable = async () => {
- let hasResourcesToTrade = false
- let hasMarketplaceEnabled = false
- resourcesToTrade.forEach((resName) => {
- const res = getResource(resName)
- if (res) {
- hasResourcesToTrade = true
- }
- })
- const navButtons = document.querySelectorAll('#main-tabs > div > button')
- navButtons.forEach((button) => {
- if (button.innerText.includes('Marketplace') && button.getAttribute('aria-selected') !== 'true') {
- hasMarketplaceEnabled = true
- }
- })
- marketPlaceAvailable = hasResourcesToTrade && hasMarketplaceEnabled
- }
- const tossACoinToYourClicker = () => {
- if (cheatsOff) return
- if (!haveManualResourceButtons) return
- if (scriptPaused) return
- const manualResources = ['Food', 'Wood', 'Stone']
- const resourcesToClick = []
- const buttonsToClick = []
- const buttons = document.querySelectorAll(
- '#root > div.flex.flex-wrap.w-full.mx-auto.p-2 > div.w-full.lg\\:pl-2 > div > div.order-2.flex.flex-wrap.gap-3 > button'
- )
- if (!buttons) {
- haveManualResourceButtons = false
- return
- }
- manualResources.forEach((resourceName) => {
- const resource = getResource(resourceName)
- if (resource && resource.current < resource.max && (!resource.speed || resource.speed <= 0)) {
- resourcesToClick.push(resourceName)
- }
- })
- buttons.forEach((button) => {
- if (resourcesToClick.includes(button.innerText.trim())) {
- buttonsToClick.push(button)
- }
- })
- while (buttonsToClick.length) {
- const buttonToClick = buttonsToClick.shift()
- buttonToClick.click()
- }
- }
- const checkToPlayMarket = () => {
- if (cheatsOff) return
- const page = getPage()
- if (scriptPaused && manualGold && marketPlaceAvailable && !playingMarket && page === pages.MARKETPLACE) {
- playMarket()
- }
- if (page !== pages.MARKETPLACE) {
- playingMarket = false
- }
- }
- const performRoutineTasks = () => {
- calculateTTF()
- if (!cheatsOff) {
- checkMarketplaceAvailable()
- managePanel()
- checkToPlayMarket()
- assignPopulation()
- }
- }
- const performFastTasks = () => {
- calculateTippyTTF()
- if (!cheatsOff) {
- if (haveManualResourceButtons) tossACoinToYourClicker()
- }
- }
- window.setInterval(performRoutineTasks, 1000)
- window.setInterval(performFastTasks, 100)
- const loadSettingsFromLocalStorage = () => {
- const TMH_scriptPaused = window.localStorage.getItem('TMH_scriptPaused')
- const TMH_manualGold = window.localStorage.getItem('TMH_manualGold')
- const TMH_cheatsOff = window.localStorage.getItem('TMH_cheatsOff')
- if (TMH_cheatsOff) {
- cheatsOff = JSON.parse(TMH_cheatsOff)
- }
- if (TMH_scriptPaused) {
- scriptPaused = JSON.parse(TMH_scriptPaused)
- }
- if (TMH_manualGold) {
- manualGold = JSON.parse(TMH_manualGold)
- }
- }
- loadSettingsFromLocalStorage()
- if (!cheatsOff) {
- await sleep(5000)
- takePageAction()
- if (!scriptPaused) {
- setTimeout(() => {
- window.location.reload()
- }, 60 * 60 * 1000)
- }
- }
- })()