// ==UserScript== // @name Kittens tools // @namespace http://bloodrizer.ru/games/kittens/ // @version 1.113 // @description Kittens tools (visual) // @author Anton // @match http://bloodrizer.ru/games/kittens/ // @require https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.2.22/cytoscape.min.js // @downloadURL none // ==/UserScript== (function() { 'use strict'; var $ = jQuery; var _BotSettings = { autoBuy: false, autoPromoteKittens: true, autoCollectFaith: true, autoCreateSteel: true, autoCreatePlates: true, autoSendHunters: true, autoCreateManuscript: true, autoCreateWood: true, autoCreateBeams: true, autoCreateSlabs: true, autoCreateCompendium: true, init: function () { _BotUI.initSettingsLink(); _BotUI.initSettingsPage(); _BotSettings.restoreAll(); }, toggleSetting: function (settingName) { var name = 'auto' + settingName; if (typeof _BotSettings[name] === 'boolean') { _BotSettings[name] = !_BotSettings[name]; _BotUI.initSettingsPage(); _BotSettings.store(name, _BotSettings[name]); } return false; }, store: function(name, value) { if (typeof(Storage) !== "undefined") localStorage.setItem(name, value); }, restore: function(name, asBool) { if (typeof asBool === 'undefined') asBool = false; if (typeof(Storage) !== "undefined") { var aValue = localStorage.getItem(name); if (asBool && aValue !== null) { return (aValue === 'true' || aValue === true); } else { return aValue; } } else return null; }, restoreAll: function () { for (var x in _BotSettings) { if (_BotSettings.hasOwnProperty(x)) { if (x.indexOf("auto") === 0) { var oldValue = _BotSettings.restore(x, true); if (oldValue !== null) { _BotSettings[x] = oldValue; } } } } } }; var _Helpers = { _log2: [], isGameReady: function() { return $("#game").css('display') === 'block'; }, log: function(message) { var mes = 'BOT: ' + message; if (game && game.msg && game.ui) { game.msg(mes, 'msg'); game.ui.renderConsoleLog(); } }, log2: function(messsage) { if (_Helpers._log2.length >= 100) { _Helpers._log2.shift(); } _Helpers._log2.push(messsage); if (_BotUI.currentPage === 'log2') { _BotUI.initLog2Page(); } }, isResourceUnlocked: function(res) { return game.resPool.get(res).unlocked; }, getBotVersion: function () { return typeof GM_info == 'function' ? GM_info().script.version : (typeof GM_info == 'object' ? GM_info.script.version : '?'); }, getMinCraft: function(res) { // craft no more than 2% of possible craft var allCount = game.workshop.getCraftAllCount(res); var ratioCount = Math.floor(allCount*0.02); return ratioCount < 1 ? 1 : ratioCount; }, canBuyBuilding: function(bldName) { var prices = game.bld.getPrices(bldName); for (var x in prices) { if (prices.hasOwnProperty(x)) { if (prices[x].val > game.resPool.get(prices[x].name).value) { return false; } } } return true; }, getCountKittensForPromote: function() { var x=0; for (var i = 0; i < game.village.sim.kittens.length; i++) { var done = false; if(game.village.sim.kittens[i].engineerSpeciality !== null) { var kitten = game.workshop.getCraft(game.village.sim.kittens[i].engineerSpeciality); if (kitten) { var tier = kitten.tier; if (game.village.sim.kittens[i].rank < tier) { x++; done = true; } } } if (!done) { x++; } } return x; }, getZebraTitanium: function () { if (game.diplomacy.get('zebras').unlocked) { var shipVal = game.resPool.get("ship").value; var shipRate = shipVal * 0.35; //0.35% per ship to get titanium var titaniumAmt = 1.5; titaniumAmt += titaniumAmt * (shipVal / 100) * 2; //2% more titanium per ship return {percent: shipRate + 15, value: titaniumAmt}; } return {percent: 0, value: 0}; }, clone: function (obj) { var copy; // Handle the 3 simple types, and null or undefined if (null == obj || "object" != typeof obj) return obj; // Handle Date if (obj instanceof Date) { copy = new Date(); copy.setTime(obj.getTime()); return copy; } // Handle Array if (obj instanceof Array) { copy = []; for (var i = 0, len = obj.length; i < len; i++) { copy[i] = _Helpers.clone(obj[i]); } return copy; } // Handle Object if (obj instanceof Object) { copy = {}; for (var attr in obj) { if (obj.hasOwnProperty(attr)) copy[attr] = _Helpers.clone(obj[attr]); } return copy; } throw new Error("Unable to copy obj! Its type isn't supported."); }, getGameStandingRatio: function () { var standingRatio = game.getEffect("standingRatio"); standingRatio = standingRatio ? standingRatio : 0; if (game.prestige.getPerk("diplomacy").researched){ standingRatio += 10; } return standingRatio; } }; var _BotActions = { craftAll: function(res) { if (_Helpers.isResourceUnlocked(res)) { _Helpers.log2("Crafting " + res); game.craftAll(res); } }, collectAstronomy: function() { if (game.calendar.observeRemainingTime > 0) { if (typeof game.calendar.observeHandler === 'function') { game.calendar.observeHandler(); } } }, catnipToWood: function() { var catnip = game.resPool.get("catnip"); if (catnip.value >= catnip.maxValue) { var minWood = _Helpers.getMinCraft('wood'); var wood = game.resPool.get("wood"); if (wood.value + minWood <= wood.maxValue) { _Helpers.log2('Catnip to Wood x ' + minWood); game.craft('wood', minWood); } } }, collectFaith: function() { var faith = game.resPool.get("faith"); if (faith.value >= faith.maxValue) { _Helpers.log2('Praise'); game.religion.praise(); } }, sendAllHunters: function() { var manpower = game.resPool.get("manpower"); if (manpower.value >= manpower.maxValue) { _Helpers.log2('Sending hunters'); game.village.huntAll(); _BotActions.craftAll('parchment'); } }, ironToSteel: function() { if (_Helpers.isResourceUnlocked('steel')) { var iron = game.resPool.get("iron"); var coal = game.resPool.get("coal"); if (iron.value >= iron.maxValue || coal.value >= coal.maxValue) { if (coal.value >= 100 && iron.value >= 100) { _Helpers.log2('Iron to Steel x ALL'); game.craftAll('steel'); } } } }, ironToPlates: function() { var iron = game.resPool.get("iron"); var minPlate = _Helpers.getMinCraft('plate'); if (iron.value >= (125 * minPlate) && _Helpers.isResourceUnlocked('plate')) { _Helpers.log2('Iron to Plate x ' + minPlate); game.craft('plate', minPlate); } }, woodToBeams: function() { var wood = game.resPool.get("wood"); if (wood.value >= wood.maxValue && _Helpers.isResourceUnlocked('beam')) { var minVal = _Helpers.getMinCraft('beam'); _Helpers.log2('Wood to Beam x ' + minVal); game.craft('beam', minVal); } }, mineralsToSlabs: function() { var minerals = game.resPool.get("minerals"); if (minerals.value >= minerals.maxValue && _Helpers.isResourceUnlocked('slab')) { var minVal = _Helpers.getMinCraft('slab'); _Helpers.log2('Minerals to Slab x ' + minVal); game.craft('slab', minVal); } }, cultureToManuscript: function() { var culture = game.resPool.get("culture"); var parchment = game.resPool.get("parchment"); var minVal = _Helpers.getMinCraft('manuscript'); if (culture.value >= culture.maxValue && _Helpers.isResourceUnlocked('manuscript') && culture.value >= (400 * minVal) && parchment.value >= (25 * minVal)) { _Helpers.log2('Culture to Manuscript x ' + minVal); game.craft('manuscript', minVal); } }, makeABuy: function(itemName) { var btn = $('.bldGroupContainer').find('div.btnContent').find('span').filter(function(){ var t = $(this).text(); return t.indexOf(itemName + " (") === 0 || t === itemName; }); if (btn&& btn.length === 1) { _Helpers.log('Autobuy ' + itemName); _Helpers.log2('Autobuy ' + itemName); btn.click(); } }, promoteKittens: function() { var gold = game.resPool.get("gold"); if (!gold.unlocked) return; if (gold.value >= 15 && gold.value >= gold.maxValue && _Helpers.getCountKittensForPromote() > 0) { game.village.promoteKittens(); } }, scienceToCompendium: function() { var science = game.resPool.get("science"); var manuscript = game.resPool.get("manuscript"); var minVal = _Helpers.getMinCraft('compedium'); if (science.value >= science.maxValue && _Helpers.isResourceUnlocked('compedium') && manuscript.value >= (50 * minVal) && science.value >= (10000 * minVal)) { _Helpers.log2('Science + Manuscript to Compendium x ' + minVal); game.craft('compedium', minVal); } } }; // noinspection JSUnusedGlobalSymbols var _BotUI = { currentPage: 'log', cy: undefined, cyElements: undefined, fixFontSize: function() { var $midColumn = $('#midColumn'); var $rightColumn = $('#rightColumn'); var fnt1 = $('#leftColumn').css('font-size'); var fnt2 = $midColumn.css('font-size'); var fnt3 = $rightColumn.css('font-size'); if (fnt2 !== fnt1 || fnt3 !== fnt1) { _Helpers.log('Fixing font size'); $midColumn.css('font-size', fnt1); $rightColumn.css('font-size', fnt1); } }, fixStyles: function() { var style = ''; $('body').append($(style)); }, addBotButton: function () { var $a = $('Bot (' + (_BotLogic.isAutoLogicStarted ? 'on' : 'off') + ')'); $a.on("click", function() { _BotLogic.isAutoLogicStarted = !_BotLogic.isAutoLogicStarted; _Helpers.log((_BotLogic.isAutoLogicStarted ? 'Started' : 'Stopped') + ' version ' + _Helpers.getBotVersion()); $('#botbutton').text(_BotLogic.isAutoLogicStarted ? 'Bot (on)' : 'Bot (off)'); }); $('#headerLinks .links-block').append(' | ').append($a); }, initSettingsLink: function () { $('a.chatLink').text('Bot settings').attr('onclick', 'KittenTools.UI.onSettingsButtonClick()'); }, initSettingsPage: function () { var inner = ''; for (var x in _BotSettings) { if (_BotSettings.hasOwnProperty(x)) { if (x.indexOf("auto") === 0) { var name = x.substr(4); var isChecked = _BotSettings[x]; inner += '' + name + ' ' + (isChecked ? 'ON' : 'OFF') + '
'; } } } $("#IRCChatInner").html(inner); }, initInfoPage: function() { var inner = '', br = "
"; inner += 'Show Tech tree' + br; inner += br; var tradeRatio = game.diplomacy.getTradeRatio(); var spiceMax = 25 + 49 + 49 * tradeRatio; inner += 'Trades:' + br; inner += '- Blueprint chance: 10% (fixed)' + br; inner += '- Spice chance: 35% (fixed)' + br; inner += '- Spice amount max: ' + parseFloat(spiceMax).toFixed(2) + br; inner += '- Trade ratio: ' + parseFloat((tradeRatio + 1) * 100).toFixed(2) + '%' + br; var gameStanding = _Helpers.getGameStandingRatio(); inner += br; inner += 'Friendly trades:' + br; for (var rid in game.diplomacy.races) { if (game.diplomacy.races.hasOwnProperty(rid)) { var r = game.diplomacy.races[rid]; if (r.attitude === 'friendly' && r.standing) { var rStanding = r.standing * 100 + gameStanding/2; inner += '- ' + r.title +' +25% res chance: ' + parseFloat(rStanding).toFixed(2) + '%' + br; } } } inner += br; inner += 'Hostile trades:' + br; for (rid in game.diplomacy.races) { if (game.diplomacy.races.hasOwnProperty(rid)) { r = game.diplomacy.races[rid]; if (r.attitude === 'hostile' && r.standing) { rStanding = r.standing * 100 + gameStanding; inner += '- ' + r.title +' trade chance: ' + parseFloat(rStanding).toFixed(2) + '%' + br; } } } inner += br; var zebraTitanium = _Helpers.getZebraTitanium(); inner += 'Zebras trade:' + br; inner += '- Titan chance: ' + parseFloat(zebraTitanium.percent).toFixed(2) + '%' + br; inner += '- Titan amount: ' + parseFloat(zebraTitanium.value).toFixed(2) + br; $("#IRCChatInner").html(inner); }, initInfoButton: function () { $('.right-tab-header').append(' | Info'); }, initLog2Button: function () { $('.right-tab-header').append(' | Log2'); }, initLog2Page: function() { var inner = '', br = "
"; inner += 'Clear log
'; for (var i = (_Helpers._log2.length - 1); i >= 0; i--) { if (_Helpers._log2.hasOwnProperty(i)) { inner += _Helpers._log2[i] + br; } } $("#IRCChatInner").html(inner); }, isCyInited: function() { return typeof _BotUI.cy !== 'undefined'; }, initCy: function() { var el = document.getElementById('scienceTree'); if (!_BotUI.isCyInited() && typeof cytoscape !== 'undefined' && el !== null) { var elems = [], elemsPositions = {}; var _addNode = function (name, connectTo, dx ,dy) { var t = game.science.get(name); if (!t) return; while (typeof elemsPositions[dx + '_' + dy] !== 'undefined') { dy += 100; } elemsPositions[dx + '_' + dy] = 1; var nodeData = { group: 'nodes', data: { id: t.name, researched: t.researched, unlocked: t.unlocked }, position: { x: dx, y: dy }, grabbable: false, locked: false }; /*if (typeof connectTo !== 'undefined') { nodeData.data.parent = connectTo; }*/ elems.push(nodeData); if (typeof connectTo !== 'undefined') { var linkName = t.name + '_' + connectTo; elems.push({ group: 'edges', data: { id: linkName, source: connectTo, target: t.name } }); } if (typeof t.unlocks !== 'undefined' && typeof t.unlocks.tech === 'object') { var offsetY = 0; for (var x in t.unlocks.tech) { if (t.unlocks.tech.hasOwnProperty(x)) { _addNode(t.unlocks.tech[x], name, dx + 150, dy + offsetY); offsetY += 100; } } } }; _addNode('calendar', undefined, 100, 100); _BotUI.cyElements = elems; _BotUI.cy = cytoscape({ container: el, // container to render in zoom: 0.5, wheelSensitivity: 0.25, style: [ { selector: 'node', style: { 'label': 'data(id)', 'background-color': function( ele ) { var unlocked = ele.data('unlocked'); var researched = ele.data('researched'); return researched ? 'green' : (unlocked ? 'lightblue' : 'gray'); } } }, { selector: ':selected', style: { 'background-color': 'white' } } ] }); _BotUI.cy.add(_BotUI.cyElements); _BotUI.cy.on('click', _BotUI.onScienceNodeClick); } }, init: function () { _BotUI.fixStyles(); _BotUI.addBotButton(); _BotUI.initInfoButton(); _BotUI.initLog2Button(); _BotUI.initScienceTree(); }, initScienceTree: function() { var closeDivButton = 'X'; var scienceDiv = ''; $('#gamePageContainer').prepend(scienceDiv); //_BotUI.initCy(); }, onCloseScienceTreeClick: function() { $('#scienceTree').hide(); }, onInfoButtonClick: function () { _BotUI.currentPage = 'info'; game.ui.loadChat(); _BotUI.initInfoPage(); }, onSettingsButtonClick: function () { _BotUI.currentPage = 'settings'; game.ui.loadChat(); _BotUI.initSettingsPage(); }, onLog2ButtonClick: function () { _BotUI.currentPage = 'log2'; game.ui.loadChat(); _BotUI.initLog2Page(); }, initLogLink: function () { $('a.chatLink').attr('onclick', 'KittenTools.UI.onLogButtonClick()'); }, onLogButtonClick: function () { _BotUI.currentPage = 'log'; game.ui.hideChat(); }, onClearLog2Click: function () { _Helpers._log2 = []; _BotUI.initLog2Page(); }, onOpenScienceTreeClick: function () { if (_BotUI.isCyInited()) { $('#scienceTree').show(); } else { _BotUI.initCy(); } }, onScienceNodeClick: function (e) { var clickedNode = e.target; var data = clickedNode.hasOwnProperty('0') ? clickedNode[0]._private.data : undefined; console.log(clickedNode); if (typeof data !== 'undefined') { var techName = data.id; var tech = game.science.get(techName); console.log(tech); } } }; var _BotLogic = { tAutoLogic: undefined, isAutoLogicStarted: true, autoBuyItem: function(bldName) { var bld = game.bld.get(bldName); if (bld.unlocked && _Helpers.canBuyBuilding(bldName)) { var itemName = bld.stages && bld.stages.length > 0 ? bld.stages[bld.stage].label : bld.label; _BotActions.makeABuy(itemName); } }, autoBuyAll: function() { if (!_BotSettings.autoBuy) return; _BotLogic.autoBuyItem('field'); _BotLogic.autoBuyItem('pasture'); _BotLogic.autoBuyItem('unicornPasture'); _BotLogic.autoBuyItem('hut'); _BotLogic.autoBuyItem('logHouse'); _BotLogic.autoBuyItem('workshop'); _BotLogic.autoBuyItem('aqueduct'); _BotLogic.autoBuyItem('library'); _BotLogic.autoBuyItem('academy'); _BotLogic.autoBuyItem('barn'); _BotLogic.autoBuyItem('mine'); _BotLogic.autoBuyItem('lumberMill'); _BotLogic.autoBuyItem('temple'); _BotLogic.autoBuyItem('tradepost'); _BotLogic.autoBuyItem('warehouse'); _BotLogic.autoBuyItem('chapel'); _BotLogic.autoBuyItem('amphitheatre'); _BotLogic.autoBuyItem('smelter'); }, autoClick: function() { var field = game.bld.get('field'); if (field.on < 5) { var btn = $('.bldGroupContainer').find('div.btnContent').find('span').filter(function(){ return $(this).text().indexOf("Gather catnip") === 0; }); if (btn && btn.length) btn.click(); } }, autoLogic: function() { _BotUI.fixFontSize(); _BotActions.collectAstronomy(); if (!_BotLogic.isAutoLogicStarted) return; _BotLogic.autoClick(); _BotLogic.autoBuyAll(); _BotLogic.promoteKittens(); _BotLogic.collectFaith(); _BotLogic.ironToSteelOrPlates(); _BotLogic.sendAllHunters(); _BotLogic.cultureToManuscript(); _BotLogic.catnipToWood(); _BotLogic.woodToBeams(); _BotLogic.mineralsToSlabs(); _BotLogic.scienceToCompendium(); }, promoteKittens: function () { if (_BotSettings.autoPromoteKittens) _BotActions.promoteKittens(); }, collectFaith: function () { if (_BotSettings.autoCollectFaith) _BotActions.collectFaith(); }, ironToSteelOrPlates: function() { var iron = game.resPool.get("iron"); var coal = game.resPool.get("coal"); if (iron.value >= iron.maxValue || coal.value >= coal.maxValue) { var minPlate = _Helpers.getMinCraft('plate'); if (coal.value >= 100 && iron.value >= 100 && _Helpers.isResourceUnlocked('steel')) { _BotLogic.ironToSteel(); } else if (iron.value >= (125 * minPlate) && _Helpers.isResourceUnlocked('plate')) { _BotLogic.ironToPlates(); } } }, ironToSteel: function () { if (_BotSettings.autoCreateSteel) _BotActions.ironToSteel(); }, ironToPlates: function () { if (_BotSettings.autoCreatePlates) _BotActions.ironToPlates(); }, sendAllHunters: function () { if (_BotSettings.autoSendHunters) _BotActions.sendAllHunters(); }, cultureToManuscript: function () { if (_BotSettings.autoCreateManuscript) _BotActions.cultureToManuscript(); }, catnipToWood: function () { if (_BotSettings.autoCreateWood) _BotActions.catnipToWood(); }, woodToBeams: function () { if (_BotSettings.autoCreateBeams) _BotActions.woodToBeams(); }, mineralsToSlabs: function () { if (_BotSettings.autoCreateSlabs) _BotActions.mineralsToSlabs(); }, scienceToCompendium: function () { if (_BotSettings.autoCreateCompendium) _BotActions.scienceToCompendium(); } }; var KittenTools = { Helpers: _Helpers, Actions: _BotActions, UI: _BotUI, Settings: _BotSettings, Logic: _BotLogic }; var _starter = function() { _BotLogic.tAutoLogic = setInterval(_BotLogic.autoLogic, 1000); _BotUI.init(); _BotSettings.init(); _Helpers.log('Started version ' + _Helpers.getBotVersion()); if (typeof game.KittenTools === 'undefined') { game.KittenTools = KittenTools; } if (typeof unsafeWindow !== 'undefined') { unsafeWindow.KittenTools = KittenTools; unsafeWindow.cytoscape = cytoscape; } }; var _waiter = function() { if (_Helpers.isGameReady()) { _starter(); } else { setTimeout(_waiter, 1000); } }; _waiter(); })();