Greasy Fork

Greasy Fork is available in English.

Kittens tools

Kittens tools (visual)

当前为 2019-01-17 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Kittens tools
// @namespace    http://bloodrizer.ru/games/kittens/
// @version      1.160
// @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
// ==/UserScript==

(function() {
	'use strict';

	var $ = jQuery;

    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)) {
                    var resName = prices[x].name;
                    if (prices[x].val > game.resPool.get(resName).value) {
                        return false;
                    }
                    if (resName === 'catnip') {
                        if (!_Helpers.canSpendMint(prices[x].val)) {
                            return false;
                        }
                    }
                    if (_BotSettings.isCheckedResSetting(resName)) {
                        return false;
                    }
                }
            }
            return true;
        },
        canPromoteKitten: function(kitten, rank) {
            var currentGold = game.resPool.get("gold").value;
            if (currentGold < 25) return false;

            var sim = game.village.sim;
            var kittenRank = kitten.rank;
            if (typeof(rank) == "undefined") {
                rank = kitten.rank + 1;
            }
            var rankDiff = rank - kittenRank;

            if (rankDiff > 0) {
                var expToPromote = sim.expToPromote(kittenRank, rank, kitten.exp);
                var goldToPromote = sim.goldToPromote(kittenRank, rank, currentGold);

                if (expToPromote[0] && goldToPromote[0]) {
                    return true;
                } else if (rankDiff > 1) { // If rank is unreachable, try one rank
                    return _Helpers.canPromoteKitten(kitten);
                }
            }

            return false;
        },
        getCountKittensForPromote: function() {
            var promotedKittens = [];

            for (var i = 0; i < game.village.sim.kittens.length; i++) {
                var done = false;
                if(game.village.sim.kittens[i].engineerSpeciality != null) {
                    var tier = this.game.workshop.getCraft(game.village.sim.kittens[i].engineerSpeciality).tier;
                    if (game.village.sim.kittens[i].rank < tier) {
                        promotedKittens.push({"kitten": game.village.sim.kittens[i], "rank": tier});
                        done = true;
                    }
                }
                if (!done) {
                    promotedKittens.push({"kitten": game.village.sim.kittens[i]});
                }
            }

            var promotedKittensCount = 0;
            if(promotedKittens.length) {
                for (i = 0; i < promotedKittens.length; i++) {
                    if (typeof(promotedKittens[i].rank) == "number") {
                        promotedKittensCount += _Helpers.canPromoteKitten(promotedKittens[i].kitten, promotedKittens[i].rank) ? 1 : 0;
                    } else {
                        promotedKittensCount += _Helpers.canPromoteKitten(promotedKittens[i].kitten) ? 1 : 0;
                    }
                }
            }

            return promotedKittensCount;
        },
        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;
        },
        getBuildingTitle: function (building_type) {
            var bld = game.bld.get(building_type);
            return bld.stages && bld.stages.length > 0 ? bld.stages[bld.stage].label : bld.label;
        },
        canSpendMint: function (mintAmount) {
            var calendar = game.calendar,
                winterDays = calendar.daysPerSeason -
                    (calendar.getCurSeason().name === "winter" ? calendar.day : 0);
            var catnipPerTick = game.calcResourcePerTick("catnip", { modifiers:{
                    "catnip" : 0.25
                }});	//calculate estimate winter per tick for catnip;

            return (game.resPool.get("catnip").value - mintAmount + (winterDays * catnipPerTick / calendar.dayPerTick)) > 0;
        },
        getMagnetoBonus: function (addSteamwork, addMagneto) {
            if (typeof addSteamwork === 'undefined') addSteamwork = 0;
            if (typeof addMagneto === 'undefined') addMagneto = 0;

            var steamworks = game.bld.get("steamworks");
            var steamworksOn = steamworks.on + addSteamwork;
            var magnetoOn = game.bld.get("magneto").effects.magnetoRatio * addMagneto;
            var swRatio = steamworksOn > 0 ? (1+ steamworks.effects["magnetoBoostRatio"] * steamworksOn) : 1;
            return (game.getEffect("magnetoRatio") + magnetoOn) * swRatio;
        },
        getBuildingPrice: function (building_type) {
            return game.bld.getPrices(building_type);
        },
        isBuildingUnlocked: function(building_type) {
            return game.bld.isUnlocked(game.bld.get(building_type));
        },
        canBuildNow: function (building_type) {
            if (_Helpers.isBuildingUnlocked(building_type)) {
                var prices = _Helpers.getBuildingPrice(building_type);
                for (var i in prices) {
                    if (prices.hasOwnProperty(i)) {
                        var resName = prices[i].name;
                        var resVal = prices[i].val;
                        var res = game.resPool.get(resName);

                        if (!res.unlocked) return false;
                        if (res.maxValue > 0 && res.maxValue < resVal) return false;
                    }
                }
                return true;
            }
            return false;
        }
    };

    var _BotSetting = {
	    auto: {
            Buy: false,
            PromoteKittens: true,
            CollectFaith: true,
            CreateSteel: true,
            CreatePlates: true,
            SendHunters: true,
            CreateManuscript: true,
            CreateWood: true,
            CreateBeams: true,
            CreateSlabs: true,
            CreateCompendium: true,
            Trade: false
        },
        buys: {},
        keeps: {}
    };
	var zeroSetting = _Helpers.clone(_BotSetting);

    var _BotSettings = {
	    version: 1,
        settingIdx: 0,
	    settings: [zeroSetting],
	    init: function () {
	        for (var i = 1; i < 5; i++) {
                var newZeroSetting = _Helpers.clone(_BotSetting);
	            _BotSettings.settings.push(newZeroSetting);
            }
            _BotUI.initSettingsLink();
            _BotUI.initSettingsPage();
            _BotSettings.restoreAll();
        },
        toggleSetting: function (settingName) {
            var currentSettingAuto = _BotSettings.settings[_BotSettings.settingIdx].auto;
            if (typeof currentSettingAuto[settingName] === 'boolean') {
                currentSettingAuto[settingName] = !currentSettingAuto[settingName];
                _BotUI.initSettingsPage();
                _BotSettings.storeSetting();
            }
            return false;
        },
        toggleBuySetting: function (name) {
            var currentSettingBuys = _BotSettings.settings[_BotSettings.settingIdx].buys;
            if (typeof currentSettingBuys[name] === 'boolean') {
                currentSettingBuys[name] = !currentSettingBuys[name];
                _BotUI.initSettingsPage();
                _BotSettings.storeSetting();
            } else {
                currentSettingBuys[name] = true;
                _BotUI.initSettingsPage();
                _BotSettings.storeSetting();
            }
            return false;
        },
        toggleResSetting: function (name) {
            var currentSettingKeeps = _BotSettings.settings[_BotSettings.settingIdx].keeps;
            if (typeof currentSettingKeeps[name] === 'boolean') {
                currentSettingKeeps[name] = !currentSettingKeeps[name];
                _BotUI.initSettingsPage();
                _BotSettings.storeSetting();
            } else {
                currentSettingKeeps[name] = true;
                _BotUI.initSettingsPage();
                _BotSettings.storeSetting();
            }
            return false;
        },
        restoreBuySettingOld: function() {
	        var data = _BotSettings.restore('buy', false);
            var currentSetting = _BotSettings.settings[_BotSettings.settingIdx];
	        if (typeof data === 'string') {
                currentSetting.buys = JSON.parse(data);
            } else {
                currentSetting.buys = {};
            }
        },
        restoreResSettingOld: function() {
	        var data = _BotSettings.restore('keeps', false);
            var currentSetting = _BotSettings.settings[_BotSettings.settingIdx];
	        if (typeof data === 'string') {
                currentSetting.keeps = JSON.parse(data);
            } else {
                currentSetting.keeps = {};
            }
        },
        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;
        },
        restoreAllNew: function() {
            var data = _BotSettings.restore('settings', data);
            if (data) {
                _BotSettings.settings = JSON.parse(data);
            }
            var settingIdx = _BotSettings.restore('settingsIdx');
            if (settingIdx) {
                _BotSettings.settingIdx = parseInt(settingIdx);
            }
            var version = _BotSettings.restore('version');
            if (version) {
                _BotSettings.version = parseInt(version);
            }
        },
        storeSetting: function() {
            var data = JSON.stringify(_BotSettings.settings);
            _BotSettings.store('settings', data);
            _BotSettings.store('settingsIdx', _BotSettings.settingIdx);
            _BotSettings.store('version', _BotSettings.version);
        },
        restoreAllOld: function() {
	        var autos = _BotSetting.auto;
	        var currentSetting = _BotSettings.settings[_BotSettings.settingIdx];
            for (var x in autos) {
                if (autos.hasOwnProperty(x)) {
                    var oldValue = _BotSettings.restore('auto' + x, true);
                    if (oldValue !== null) {
                        currentSetting.auto[x] = oldValue;
                    }
                }
            }

            _BotSettings.restoreBuySettingOld();
            _BotSettings.restoreResSettingOld();
        },
        removeOldSettings: function() {
            if (typeof(Storage) === "undefined") return;

            var autos = _BotSetting.auto;
            for (var x in autos) {
                if (autos.hasOwnProperty(x)) {
                    localStorage.removeItem('auto' + x);
                }
            }

            localStorage.removeItem('buys');
            localStorage.removeItem('keeps');
        },
        restoreAll: function () {
	        if (_BotSettings.restore('version') === null) {
	            _BotSettings.restoreAllOld();
	            _BotSettings.removeOldSettings();
	            _BotSettings.storeSetting()
            } else {
                _BotSettings.restoreAllNew();
            }
        },
        isCheckedBuySetting: function (bld) {
            var currentSettingBuys = _BotSettings.settings[_BotSettings.settingIdx].buys;
            if (typeof currentSettingBuys[bld] !== 'undefined') {
                return currentSettingBuys[bld] === true;
            }
            return false;
        },
        isCheckedResSetting: function (res) {
            var currentSettingKeeps = _BotSettings.settings[_BotSettings.settingIdx].keeps;
            if (typeof currentSettingKeeps[res] !== 'undefined') {
                return currentSettingKeeps[res] === true;
            }
            return false;
        },
        isAuto: function (autoSetting) {
            var currentSetting = _BotSettings.settings[_BotSettings.settingIdx];
            var r = currentSetting.auto[autoSetting];
            return typeof r !== 'undefined' && r;
        }
    };

	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);
            }
        },
        sendZebraCaravan: function () {
            var zebras = game.diplomacy.get('zebras');
            if (zebras.unlocked) {
                var titanium = game.resPool.get('titanium');
                if (titanium.value < titanium.maxValue) {
                    var gold = game.resPool.get("gold");
                    var power = game.resPool.get("manpower");
                    var slab = game.resPool.get('slab');
                    if (gold.value >= gold.maxValue && power.value >= 50 && slab.value >= 50) {
                        _Helpers.log2('Zebra trade x 1');
                        game.diplomacy.tradeMultiple(zebras, 1);
                    }
                }
            }
        },
        sendNagasCaravan: function () {
            var nagas = game.diplomacy.get('nagas');
            if (nagas.unlocked) {
                var ivory = game.resPool.get('ivory');
                var gold = game.resPool.get("gold");
                if (ivory.value > 500 && ivory.perTickCached > 0 && gold.value >= gold.maxValue) {
                    var minerals = game.resPool.get("minerals");
                    var power = game.resPool.get("manpower");
                    if (minerals.value < minerals.maxValue && power.value >= 50) {
                        _Helpers.log2('Nagas trade x 1');
                        game.diplomacy.tradeMultiple(nagas, 1);
                    }
                }
            }
        },
        sendDragonsCaravan: function () {
            var dragons = game.diplomacy.get('dragons');
            if (dragons.unlocked) {
                var uranium = game.resPool.get('uranium');
                if (uranium.unlocked && uranium.value < uranium.maxValue) {
                    var titanium = game.resPool.get('titanium');
                    var gold = game.resPool.get("gold");
                    var power = game.resPool.get("manpower");
                    if (titanium.value >= titanium.maxValue && titanium.value >= 250 && gold.value >= gold.maxValue && power.value >= 50) {
                        _Helpers.log2('Dragons trade x 1');
                        game.diplomacy.tradeMultiple(dragons, 1);
                    }
                }
            }
        }
	};

	// noinspection JSUnusedGlobalSymbols
    var _BotUI = {
        currentPage: 'log',
        cy: undefined,
        cyElements: undefined,
        $scienceLegend: undefined,
        $scienceLegendTitle: undefined,
        $scienceLegendDescr: undefined,
        $scienceLegendPrice: undefined,
        $scienceLegendUnlock: undefined,
        buildingsTranslations: {},
        initTranslations: function() {
            var bg = game.bld.buildingGroups;
            for (var x in bg) {
                if (bg.hasOwnProperty(x)) {
                    for (var i in bg[x].buildings) {
                        if (bg[x].buildings.hasOwnProperty(i)) {
                            var b = bg[x].buildings[i];
                            var name = _Helpers.getBuildingTitle(b);
                            _BotUI.buildingsTranslations[name] = b;
                        }
                    }
                }
            }
        },
        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 = '<style type="text/css">' +
                '.modern .btnContent, .btn.bldEnabled.modern div.btnContent, .btn.bldlackResConvert.modern div.btnContent {padding: 5px 0 5px 10px;} '+
                '.btn.modern a {padding: 5px 6px 5px 6px !important;margin:-5px 0;} '+
                '#rightTabLog {max-height: 75vh; overflow-y: scroll; padding-right: 4px;} ' +
                '#IRCChatInner {overflow-y: scroll; height: 75vh;} ' +
                '#IRCChatInner a {text-decoration: none; color: darkmagenta} ' +
                '#IRCChatInner a:hover {text-decoration: underline: color: magenta} ' +
                '.msg {opacity: 1 !important;} ' +
                '.right-tab-header a {padding: 0} ' +
                '#scienceTree {z-index:2;position:fixed;left:2vw;top:2vh;width:96vw;height:96vh;background:azure;border:1px solid;} ' +
                '#closeScienceTree {z-index:3;position:fixed;right:2vw;top:2vh;color:black;font-size:20px;padding:4px;border:1px solid;text-decoration:none;} ' +
                '#closeScienceTree:hover {color:blue} ' +
                '.legendBlock {margin-bottom: 8px;padding: 0 8px 8px 8px;border-bottom: 1px solid gray;} ' +
                '#scienceLegend {z-index:4;position:fixed;left:3vw;top:50vh;width:47vw;height:47vh;background:whitesmoke;border:1px solid;} ' +
                '#scienceLegendInner {z-index:5;position:fixed;left:4vw;top:51vh;width:45vw;height:45vh;overflow-y:auto;} ' +
                '#scienceLegendTitle {text-align:center;margin-top:8px;font-size:140%;} ' +
                '#scienceLegendPrice td, #scienceLegendUnlock td {padding-right: 16px;vertical-align: top;} ' +
                '#scienceLegendUnlock {font-size: 90%;} ' +
                '.btn.nosel.modern.disabled.nochance {border-color: red;}' +

                '.radio-group input[type=radio] {position: absolute; visibility: hidden; display: none;}' +
                '.radio-group label {display: inline-block; cursor: pointer; font-weight: bold; padding: 4px 12px;}' +
                '.radio-group input[type=radio]:checked + label{background: #999}' +
                '.radio-group label + input[type=radio] + label {border-left: solid 1px;}' +
                '.radio-group {border: solid 1px; display: inline-block; border-radius: 10px; overflow: hidden;}' +

                '</style>';
            $('body').append($(style));
        },
        addColorsToBuildingsButtons: function() {
            var $buttons = $('.bldGroupContainer .btn.modern');
            if ($buttons.length) {
                var gatherMintCaption = $I('buildings.gatherCatnip.label');
                var pressMintCaption = $I('buildings.refineCatnip.label');
                for (var i = 0; i < $buttons.length; i++) {
                    var $btn = $buttons[i];
                    var isDisabled = $btn.className.indexOf('disabled') >= 0;
                    if (isDisabled) {
                        var btnText = $btn.innerText;
                        var isSpecial = btnText.indexOf(gatherMintCaption) === 0 || btnText.indexOf(pressMintCaption) === 0;
                        if (!isSpecial) {
                            var idx = btnText.indexOf(' (');
                            if (idx <= 0) {
                                var bldName = btnText;
                            } else {
                                bldName = btnText.substr(0, idx);
                            }
                            var building_type = _BotUI.buildingsTranslations[bldName];
                            if (typeof building_type !== 'undefined') {
                                var canBuild = _Helpers.canBuildNow(building_type);
                                //console.log(bldName,building_type,canBuild);
                                if (!canBuild) {
                                    $($btn).addClass('nochance');
                                }
                            }
                        }
                    }
                }
            }
        },
        addBotButton: function () {
            var $a = $('<a href="#" id="botbutton">Bot (' + (_BotLogic.isAutoLogicStarted ? 'on' : 'off') + ')</a>');
            $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 = '', br = '<br/>';

            inner += '<form><div class="radio-group">';
            for (var i = 0; i < _BotSettings.settings.length; i++) {
                var idx = 'option' + i;
                inner += '<input type="radio" id="' + idx + '" name="selector" '
                    + (i === _BotSettings.settingIdx ? 'checked="checked"' : '') + '>'+
                    '<label for="' + idx + '" onclick="KittenTools.UI.onChangeSettingIdxClick(' + i + ')">' + i + '</label>';
            }
            inner += '</div></form>' + br;

            inner += 'Logic<hr/>';
            var currentSettingAuto = _BotSettings.settings[_BotSettings.settingIdx].auto;
            for (var x in currentSettingAuto) {
                if (currentSettingAuto.hasOwnProperty(x)) {
                    var isChecked = currentSettingAuto[x];
                    inner += '<a href="#" onclick="KittenTools.Settings.toggleSetting(\'' + x + '\')">'
                        + x + '</a> : ' + (isChecked ? 'ON' : '-') + br;
                }
            }

            inner += br + 'Autobuid<hr/>';
            var bg = game.bld.buildingGroups;
            for (x in bg) {
                if (bg.hasOwnProperty(x)) {
                    inner += bg[x].title + br;
                    for (i in bg[x].buildings) {
                        if (bg[x].buildings.hasOwnProperty(i)) {
                            var b = bg[x].buildings[i];
                            var name = _Helpers.getBuildingTitle(b);
                            isChecked = _BotSettings.isCheckedBuySetting(b);
                            inner += '<a href="#" onclick="KittenTools.Settings.toggleBuySetting(\'' + b + '\')">'
                                + name + '</a> : ' + (isChecked ? 'ON' : '-') + br;
                        }
                    }
                    inner += br;
                }
            }

            inner += br + 'Keep resource<hr/>';
            var rm = game.resPool.resourceMap;
            for (var r in rm) {
                if (rm.hasOwnProperty(r)) {
                    if (rm[r].unlocked) {
                        name = rm[r].title;
                        isChecked = _BotSettings.isCheckedResSetting(r);
                        inner += '<a href="#" onclick="KittenTools.Settings.toggleResSetting(\'' + r + '\')">'
                            + name + '</a> : ' + (isChecked ? 'ON' : '-') + br;
                    }
                }
            }

            $("#IRCChatInner").html(inner);
        },
        initInfoPage: function() {
            var inner = '', br = "<br/>";

            inner += '<a href="#" onclick="KittenTools.UI.onOpenScienceTreeClick()">Show Tech tree</a>' + 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;
            inner += 'Neutral trades:' + br;
            var dragons = game.diplomacy.get('dragons');
            var dragonsUranium = dragons.sells[0];
            var minDragonsUranium = (1 - dragonsUranium.delta / 2) * (1 + tradeRatio);
            var maxDragonsUranium = (1 + dragonsUranium.delta / 2) * (1 + tradeRatio);
            inner += '- ' + dragons.title +' uranium: ' + parseFloat(minDragonsUranium).toFixed(2)
                + ' - ' + parseFloat(maxDragonsUranium).toFixed(2) + br;
            inner += '- ' + dragons.title +' trade chance: 95%' + 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;

            inner += br;
            inner += 'Magneto bonus:' + br;
            var magnetoBonus = _Helpers.getMagnetoBonus();
            var magnetoBonusAddSteam = _Helpers.getMagnetoBonus(1);
            var magnetoBonusAddMagneto = _Helpers.getMagnetoBonus(0, 1);
            inner += '- Current: ' + parseInt(magnetoBonus * 100) + '%' + br;
            inner += '- On +1 Steam: ' + parseInt(magnetoBonusAddSteam * 100) + '%' + br;
            inner += '- On +1 Magneto: ' + parseInt(magnetoBonusAddMagneto * 100) + '%' + br;

            $("#IRCChatInner").html(inner);
        },
        initInfoButton: function () {
            $('.right-tab-header').append('&nbsp;| <a href="#" onclick="KittenTools.UI.onInfoButtonClick()">Info</a>');
        },
        initLog2Button: function () {
            $('.right-tab-header').append('&nbsp;| <a href="#" onclick="KittenTools.UI.onLog2ButtonClick()">Log2</a>');
        },
        initLog2Page: function() {
            var inner = '', br = "<br/>";

            inner += '<a href="#" onclick="KittenTools.UI.onClearLog2Click()">Clear log</a><hr/>';

            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',
                                'border-width': '2px',
                                'border-style': 'solid',
                                'border-color': 'red'
                            }
                        }
                    ]
                });

                _BotUI.cy.add(_BotUI.cyElements);

                _BotUI.cy.on('click', _BotUI.onScienceNodeClick);
            }
        },
        init: function () {
            _BotUI.fixStyles();
            _BotUI.addBotButton();
            _BotUI.initInfoButton();
            _BotUI.initLog2Button();
            _BotUI.initScienceTree();
            _BotUI.initScienceLegend();
            _BotUI.initTranslations();
        },
        initScienceTree: function() {
            var closeDivButton = '<a href="#" id="closeScienceTree" onclick="KittenTools.UI.onCloseScienceTreeClick()">X</a>';
            var scienceDiv = '<div id="scienceTree" style="display:none">' + closeDivButton + '</div>';
            $('#gamePageContainer').prepend(scienceDiv);

            //_BotUI.initCy();
        },
        initScienceLegend: function() {
            var header = '<div id="scienceLegendTitle" class="legendBlock">Название</div>';
            var descr = '<div id="scienceLegendDescr" class="legendBlock">Описание длинное</div>';
            var prices = '<div id="scienceLegendPrice" class="legendBlock">Цена: 1 crystal</div>';
            var unlocks = '<div id="scienceLegendUnlock" class="legendBlock">Открывает: Religion</div>';
            var blocks = header + descr + prices + unlocks;
            var inner = '<div id="scienceLegendInner">' + blocks + '</div>';
            var scienceDiv = '<div id="scienceLegend" style="display:none">' + inner + '</div>';
            $('#scienceTree').prepend(scienceDiv);
            _BotUI.$scienceLegend = $('#scienceLegend');
            _BotUI.$scienceLegendTitle = $('#scienceLegendTitle');
            _BotUI.$scienceLegendDescr = $('#scienceLegendDescr');
            _BotUI.$scienceLegendPrice = $('#scienceLegendPrice');
            _BotUI.$scienceLegendUnlock = $('#scienceLegendUnlock');
        },
        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;
            if (typeof data !== 'undefined') {
                var techName = data.id;
                if (techName.indexOf('_') === -1) {
                    var tech = game.science.get(techName);
                    _BotUI.initScienceLabel(tech);
                    _BotUI.$scienceLegend.show();
                } else {
                    _BotUI.$scienceLegend.hide();
                }
            } else {
                _BotUI.$scienceLegend.hide();
            }
        },
        getTechUnlocksTableHtml: function(tech) {
            var br = '<br/>', hr = '<hr/>';
            var unlocksTable = '<table border="0" cellpadding="0" cellspacing="0"><tr>';
            var _addToUnlocksTable = function (title, techUnlocksUid, nameCallback) {
                var unlockArr = tech.unlocks[techUnlocksUid];
                if (typeof unlockArr === 'undefined') return;
                unlocksTable += '<td>' + title + ':' + hr;
                for (var iid in unlockArr) {
                    if (unlockArr.hasOwnProperty(iid)) {
                        var aName = nameCallback(unlockArr[iid]);
                        unlocksTable += aName + br;
                    }
                }
                unlocksTable += '</td>';
            };
            var _translate = function(key, defValue) {
                return i18nLang.messages[key] ? $I(key) : defValue;
            };

            _addToUnlocksTable( $I('tab.name.workshop'), 'crafts', function(name) {
                return $I('resources.' + name + '.title');
            });

            _addToUnlocksTable( $I('tab.name.science'), 'tech', function(name) {
                return game.science.get(name).label;
            });

            _addToUnlocksTable( $I('workshop.upgradePanel.label'), 'upgrades', function(name) {
                return _translate('workshop.' + name + '.label', name);
            });

            _addToUnlocksTable( 'Buildings', 'buildings', function(name) {
                return _translate('buildings.' + name + '.label', name);
            });

            _addToUnlocksTable( 'Jobs', 'jobs', function(name) {
                return $I('village.job.' + name);
            });

            _addToUnlocksTable( 'Tabs', 'tabs', function(name) {
                return $I('tab.name.' + name);
            });

            _addToUnlocksTable( 'Chronoforge', 'chronoforge', function(name) {
                return _translate('time.cfu.' + name + '.label', name);
            });

            _addToUnlocksTable( 'Void Space', 'voidSpace', function(name) {
                return _translate('time.vsu.' + name + '.label', name);
            });

            _addToUnlocksTable( 'Challenges', 'challenges', function(name) {
                return _translate('challendge.' + name + '.label', name);
            });

            _addToUnlocksTable( 'Stages', 'stages', function(name) {
                if (typeof name === 'object') {
                    if (name.bld && typeof name.stage !== 'undefined') {
                        var bld = game.bld.get(name.bld);
                        var newName = name.stage;
                        if (bld.stages && bld.stages[name.stage]) {
                            newName = bld.stages[name.stage].label;
                        }
                        return _translate('buildings.' + name.bld + '.label', name.bld) + ' (' + newName + ')';
                    } else {
                        return JSON.stringify(name);
                    }
                }
                return name;
            });

            unlocksTable += '</tr></table>';

            return unlocksTable;
        },
        getTechPriceTableHtml: function(tech) {
            var prices = game.science.getPrices(tech);
            var priceTable = '<table border="0" cellpadding="0" cellspacing="0">';
            for (var pid in prices) {
                if (prices.hasOwnProperty(pid)) {
                    var price = prices[pid];
                    var resName = $I('resources.' + price.name + '.title');
                    var value = game.getDisplayValueExt(parseInt(price.val));
                    priceTable += '<tr><td>' + resName + '</td><td>' + value + '</td></tr>';
                }
            }
            priceTable += '</table>';

            return priceTable;
        },
        initScienceLabel: function (tech) {
            _BotUI.$scienceLegendTitle.text(tech.label);
            _BotUI.$scienceLegendDescr.text(tech.description);

            var priceTable = _BotUI.getTechPriceTableHtml(tech);
            _BotUI.$scienceLegendPrice.html(priceTable);

            if (tech.unlocks) {
                var unlocksTable = _BotUI.getTechUnlocksTableHtml(tech);
                _BotUI.$scienceLegendUnlock.html(unlocksTable).show();
            } else {
                _BotUI.$scienceLegendUnlock.hide();
            }
        },
        onChangeSettingIdxClick: function (newIndex) {
            newIndex = parseInt(newIndex);
            if (_BotSettings.settingIdx !== newIndex) {
                _BotSettings.settingIdx = newIndex;
                _BotSettings.store('settingsIdx', _BotSettings.settingIdx);
                _BotUI.initSettingsPage();
            }
        }
    };

	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.isAuto('Buy')) return;

            var currentSettingBuys = _BotSettings.settings[_BotSettings.settingIdx].buys;
            for (var x in currentSettingBuys) {
                if (currentSettingBuys.hasOwnProperty(x)) {
                    if (currentSettingBuys[x] === true) {
                        _BotLogic.autoBuyItem(x);
                    }
                }
            }
        },
        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();
            _BotLogic.autoTrade();
        },
        autoTrade: function() {
            if (_BotSettings.isAuto('Trade')) {
                _BotActions.sendZebraCaravan();
                _BotActions.sendDragonsCaravan();
                _BotActions.sendNagasCaravan();
            }
        },
        promoteKittens: function () {
            if (_BotSettings.isAuto('PromoteKittens')) _BotActions.promoteKittens();
        },
        collectFaith: function () {
            if (_BotSettings.isAuto('CollectFaith')) _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.isAuto('CreateSteel')) _BotActions.ironToSteel();
        },
        ironToPlates: function () {
            if (_BotSettings.isAuto('CreatePlates')) _BotActions.ironToPlates();
        },
        sendAllHunters: function () {
            if (_BotSettings.isAuto('SendHunters')) _BotActions.sendAllHunters();
        },
        cultureToManuscript: function () {
            if (_BotSettings.isAuto('CreateManuscript')) _BotActions.cultureToManuscript();
        },
        catnipToWood: function () {
            if (_BotSettings.isAuto('CreateWood')) _BotActions.catnipToWood();
        },
        woodToBeams: function () {
            if (_BotSettings.isAuto('CreateBeams')) _BotActions.woodToBeams();
        },
        mineralsToSlabs: function () {
            if (_BotSettings.isAuto('CreateSlabs')) _BotActions.mineralsToSlabs();
        },
        scienceToCompendium: function () {
            if (_BotSettings.isAuto('CreateCompendium')) _BotActions.scienceToCompendium();
        }
	};

	var _BotInjections = {
	    game: {
	        _oldMsg: undefined,
	        msg: function (message, type, tag, noBullet) {
                if (tag === 'astronomicalEvent') {
                    _Helpers.log2(message);
                } else {
                    _BotInjections.game._oldMsg.call(game, message, type, tag, noBullet);
                }
            },
            tabs: {
	            _oldRenderActiveGroup: undefined,
                renderActiveGroup: function (groupContainer) {
                    _BotInjections.game.tabs._oldRenderActiveGroup.call(game.tabs[0], groupContainer);
                    _BotUI.addColorsToBuildingsButtons();
                }
            }
        },
        init: function () {
            if (game && game.msg) {
                _BotInjections.game._oldMsg = game.msg;
                game.msg = _BotInjections.game.msg;
            }
            if (game && game.tabs && game.tabs[0] && game.tabs[0].__proto__.renderActiveGroup) {
                _BotInjections.game.tabs._oldRenderActiveGroup = game.tabs[0].__proto__.renderActiveGroup;
                game.tabs[0].__proto__.renderActiveGroup = _BotInjections.game.tabs.renderActiveGroup;
            }
        }
    };

    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());
        _BotInjections.init();

        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();
})();