Tetrikitty

Kitten Scientists, minus font changes

Jul 17th, 2019
163
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name        Kitten Scientists
  3. // @namespace   http://www.reddit.com/r/kittensgame/comments/34gb2u/kitten_scientists_automation_script/
  4. // @description Launch Kitten Scientists
  5. // @include     *bloodrizer.ru/games/kittens/*
  6. // @include     file:///*kitten-game*
  7. // @version     1.3.3
  8. // @grant       none
  9. // @copyright   2015, cameroncondry
  10. // ==/UserScript==
  11.  
  12. // ==========================================
  13. // Begin Kitten Scientist's Automation Engine
  14. // ==========================================
  15.  
  16. var version = 'Kitten Scientists version 1.3.3';
  17. var address = '19ZBVyaXQhikcuUszY2MVRb1MGL2YqicDX';
  18.  
  19. // Game will be referenced in loadTest function
  20. var game = null;
  21.  
  22. var run = function() {
  23.  
  24.     var options = {
  25.         // When debug is enabled, messages that go to the game log are also logged using window.console.
  26.         debug: false,
  27.  
  28.         // The interval at which the internal processing loop is run, in milliseconds.
  29.         interval: 2000,
  30.  
  31.         // The default color for KS messages in the game log (like enabling and disabling items).
  32.         msgcolor: '#aa50fe', // dark purple
  33.         // The color for activity summaries.
  34.         summarycolor: '#009933', // light green
  35.         // The color for log messages that are about activities (like festivals and star observations).
  36.         activitycolor: '#E65C00', // orange
  37.         // The color for resources with stock counts higher than current resource max
  38.         stockwarncolor: '#DD1E00',
  39.  
  40.         // Should activity be logged to the game log?
  41.         showactivity: true,
  42.  
  43.         // The default consume rate.
  44.         consume: 0.6,
  45.  
  46.         // How many messages to keep in the game log.
  47.         logMessages:   100,
  48.  
  49.         // The default settings for game automation.
  50.         auto: {
  51.             // Settings related to KS itself.
  52.             engine: {
  53.                 // Should any automation run at all?
  54.                 enabled: false
  55.             },
  56.             crypto: {
  57.                 // Should crypto exchange be automated?
  58.                 enabled: true,
  59.                 // At what percentage of the relic storage capacity should KS exchange?
  60.                 trigger: 10000
  61.             },
  62.             explore: {
  63.                 // Should exploring be automated?
  64.                 enabled: false,
  65.             },
  66.             autofeed: {
  67.                 // Should feeding elders be automated?
  68.                 enabled: true,
  69.             },
  70.             faith: {
  71.                 // Should praising be automated?
  72.                 enabled: true,
  73.                 // At what percentage of the faith storage capacity should KS praise the sun?
  74.                 trigger: 0.99,
  75.                 // Which religious upgrades should be researched?
  76.                 items: {
  77.                     // Variant denotes which category the building or upgrade falls within in the Religion tab.
  78.                     // Ziggurats are variant z.
  79.                     unicornTomb:        {require: false,         enabled: false, variant: 'z'},
  80.                     ivoryTower:         {require: false,         enabled: false, variant: 'z'},
  81.                     ivoryCitadel:       {require: false,         enabled: false, variant: 'z'},
  82.                     skyPalace:          {require: false,         enabled: false, variant: 'z'},
  83.                     unicornUtopia:      {require: 'gold',        enabled: false, variant: 'z'},
  84.                     sunspire:           {require: 'gold',        enabled: false, variant: 'z'},
  85.                     marker:             {require: 'unobtainium', enabled: false, variant: 'z'},
  86.                     unicornGraveyard:   {require: false,         enabled: false, variant: 'z'},
  87.                     unicornNecropolis:  {require: false,         enabled: false, variant: 'z'},
  88.                     blackPyramid:       {require: 'unobtainium', enabled: false, variant: 'z'},
  89.                     // Order of the Sun is variant s.
  90.                     solarchant:         {require: 'faith',       enabled: true,  variant: 's'},
  91.                     scholasticism:      {require: 'faith',       enabled: true,  variant: 's'},
  92.                     goldenSpire:        {require: 'faith',       enabled: true,  variant: 's'},
  93.                     sunAltar:           {require: 'faith',       enabled: true,  variant: 's'},
  94.                     stainedGlass:       {require: 'faith',       enabled: true,  variant: 's'},
  95.                     solarRevolution:    {require: 'faith',       enabled: true,  variant: 's'},
  96.                     basilica:           {require: 'faith',       enabled: true,  variant: 's'},
  97.                     templars:           {require: 'faith',       enabled: true,  variant: 's'},
  98.                     apocripha:          {require: 'faith',       enabled: false, variant: 's'},
  99.                     transcendence:      {require: 'faith',       enabled: true,  variant: 's'},
  100.                     // Cryptotheology is variant c.
  101.                     blackObelisk:       {require: false,         enabled: false, variant: 'c'},
  102.                     blackNexus:         {require: false,         enabled: false, variant: 'c'},
  103.                     blackCore:          {require: false,         enabled: false, variant: 'c'},
  104.                     singularity:        {require: false,         enabled: false, variant: 'c'},
  105.                     blackLibrary:       {require: false,         enabled: false, variant: 'c'},
  106.                     blackRadiance:      {require: false,         enabled: false, variant: 'c'},
  107.                     blazar:             {require: false,         enabled: false, variant: 'c'},
  108.                     darkNova:           {require: false,         enabled: false, variant: 'c'},
  109.                     holyGenocide:       {require: false,         enabled: false, variant: 'c'},
  110.                 }
  111.             },
  112.             festival: {
  113.                 // Should festivals be held automatically?
  114.                 enabled: true
  115.             },
  116.             hunt: {
  117.                 // Should hunters be sent on hunts automatically?
  118.                 enabled: true,
  119.                 // At what percentage of the catpower storage capacity should KS send hunters on hunts?
  120.                 trigger: 0.6
  121.             },
  122.             build: {
  123.                 // Should buildings be built automatically?
  124.                 enabled: true,
  125.                 // When a building requires a certain resource (this is what their *require* property refers to), then
  126.                 // this is the percentage of the storage capacity of that resource, that has to be met for the building
  127.                 // to be built.
  128.                 trigger: 0.75,
  129.                 // The items that be automatically built.
  130.                 // Every item can define a required resource. This resource has to be available at a certain capacity for
  131.                 // the building to be built. The capacity requirement is defined by the trigger value set for the section.
  132.                 //
  133.                 // Additionally, for upgradeable buildings, the item can define which upgrade stage it refers to.
  134.                 // For upgraded buildings, the ID (or internal name) of the building can be controlled through the *name*
  135.                 // property. For other buildings, the key of the item itself is used.
  136.                 items: {
  137.                     // housing
  138.                     hut:            {require: 'wood',        enabled: false},
  139.                     logHouse:       {require: 'minerals',    enabled: false},
  140.                     mansion:        {require: 'titanium',    enabled: false},
  141.  
  142.                     // craft bonuses
  143.                     workshop:       {require: 'minerals',    enabled: true},
  144.                     factory:        {require: 'titanium',    enabled: true},
  145.  
  146.                     // production
  147.                     field:          {require: 'catnip',      enabled: true},
  148.                     pasture:        {require: 'catnip',      enabled: true, stage: 0},
  149.                     solarFarm:      {require: 'titanium',    enabled: true, stage: 1, name: 'pasture'},
  150.                     mine:           {require: 'wood',        enabled: true},
  151.                     lumberMill:     {require: 'minerals',    enabled: true},
  152.                     aqueduct:       {require: 'minerals',    enabled: true, stage: 0},
  153.                     hydroPlant:     {require: 'titanium',    enabled: true, stage: 1, name: 'aqueduct'},
  154.                     oilWell:        {require: 'coal',        enabled: true},
  155.                     quarry:         {require: 'coal',        enabled: true},
  156.  
  157.                     // conversion
  158.                     smelter:        {require: 'minerals',    enabled: true},
  159.                     biolab:         {require: 'science',     enabled: false},
  160.                     calciner:       {require: 'titanium',    enabled: false},
  161.                     reactor:        {require: 'titanium',    enabled: false},
  162.                     accelerator:    {require: 'titanium',    enabled: false},
  163.                     steamworks:     {require: false,         enabled: false},
  164.                     magneto:        {require: false,         enabled: false},
  165.  
  166.                     // science
  167.                     library:        {require: 'wood',        enabled: true, stage: 0},
  168.                     dataCenter:     {require: false,         enabled: true, stage: 1, name: 'library'},
  169.                     academy:        {require: 'wood',        enabled: true},
  170.                     observatory:    {require: 'iron',        enabled: true},
  171.  
  172.                     // other
  173.                     amphitheatre:   {require: 'minerals',    enabled: true, stage: 0},
  174.                     broadcastTower: {require: 'titanium',    enabled: true, stage: 1, name: 'amphitheatre'},
  175.                     tradepost:      {require: 'gold',        enabled: true},
  176.                     chapel:         {require: 'minerals',    enabled: true},
  177.                     temple:         {require: 'gold',        enabled: true},
  178.                     mint:           {require: false,         enabled: false},
  179.                     unicornPasture: {require: false,         enabled: true},
  180.                     ziggurat:       {require: false,         enabled: true},
  181.                     chronosphere:   {require: 'unobtainium', enabled: true},
  182.                     aiCore:         {require: false,         enabled: false},
  183.  
  184.                     // storage
  185.                     barn:           {require: 'wood',        enabled: true},
  186.                     harbor:         {require: false,         enabled: false},
  187.                     warehouse:      {require: false,         enabled: false}
  188.                 }
  189.             },
  190.             space: {
  191.                 // Should space buildings be built automatically?
  192.                 enabled: false,
  193.                 // The functionality of the space section is identical to the build section. It just needs to be treated
  194.                 // seperately, because the game internals are slightly different.
  195.                 trigger: 0.95,
  196.                 items: {
  197.                     // Cath
  198.                     spaceElevator:  {require: 'unobtainium', enabled: false},
  199.                     sattelite:      {require: 'titanium',    enabled: false},
  200.                     spaceStation:   {require: 'oil',         enabled: false},
  201.  
  202.                     // Moon
  203.                     moonOutpost:    {require: 'uranium',     enabled: false},
  204.                     moonBase:       {require: 'unobtainium', enabled: false},
  205.  
  206.                     // Dune
  207.                     planetCracker:  {require: 'science',     enabled: false},
  208.                     hydrofracturer: {require: 'science',     enabled: false},
  209.                     spiceRefinery:  {require: 'science',     enabled: false},
  210.  
  211.                     // Piscine
  212.                     researchVessel: {require: 'titanium',    enabled: false},
  213.                     orbitalArray:   {require: 'eludium',     enabled: false},
  214.  
  215.                     // Helios
  216.                     sunlifter:          {require: 'eludium', enabled: false},
  217.                     containmentChamber: {require: 'science', enabled: false},
  218.                     heatsink:           {require: 'thorium', enabled: false},
  219.                     sunforge:           {require: false,     enabled: false},
  220.  
  221.                     // T-Minus
  222.                     cryostation:    {require: 'eludium',     enabled: false},
  223.  
  224.                     // Kairo
  225.                     spaceBeacon:    {require: 'antimatter',  enabled: false},
  226.  
  227.                     // Yarn
  228.                     terraformingStation: {require: 'antimatter',  enabled: false},
  229.                     hydroponics:         {require: 'kerosene',    enabled: false},
  230.  
  231.                     // Umbra
  232.                     hrHarvester:    {require: 'antimatter',  enabled: false},
  233.  
  234.                     // Charon
  235.                     entangler:    {require: 'antimatter',  enabled: false},
  236.  
  237.                     // Centaurus
  238.                     tectonic:   {require: 'antimatter', enabled: false},
  239.                     moltenCore: {require: 'uranium',    enabled: false}
  240.                 }
  241.             },
  242.             time: {
  243.                 // Should time upgrades be built automatically?
  244.                 enabled: false,
  245.                 trigger: 0.95,
  246.                 items: {
  247.                     // Variants denote whether these buildings fall within the Chronoforge or Void categories.
  248.                     // Chronoforge has variant chrono.
  249.                     temporalBattery:     {require: false,          enabled: false, variant: 'chrono'},
  250.                     blastFurnace:        {require: false,          enabled: false, variant: 'chrono'},
  251.                     temporalAccelerator: {require: false,          enabled: false, variant: 'chrono'},
  252.                     temporalImpedance:   {require: false,          enabled: false, variant: 'chrono'},
  253.                     ressourceRetrieval:  {require: false,          enabled: false, variant: 'chrono'},
  254.                    
  255.                     // Void Space has variant void.
  256.                     cryochambers:        {require: false,          enabled: false, variant: 'void'},
  257.                     voidHoover:          {require: 'antimatter',   enabled: false, variant: 'void'},
  258.                     voidRift:            {require: false,          enabled: false, variant: 'void'},
  259.                     chronocontrol:       {require: 'temporalFlux', enabled: false, variant: 'void'},
  260.                     voidResonator:       {require: false,          enabled: false, variant: 'void'}
  261.                 }
  262.             },
  263.             craft: {
  264.                 // Should resources be crafted automatically?
  265.                 enabled: true,
  266.                 // Every item can define a required resource with the *require* property.
  267.                 // At what percentage of the storage capacity of that required resource should the listed resource be crafted?
  268.                 trigger: 0.95,
  269.                 // The items that can be crafted.
  270.                 // In addition to the *require* property, which is explained above, items can also define a *max*. If they
  271.                 // do, no more than that resource will be automatically produced. This feature can not be controlled through
  272.                 // the UI and is not used for any resource by default.
  273.                 // The *limited* property tells KS to craft resources whenever the ratio of the component cost of the stored resources
  274.                 // to the number of stored components is greater than the limit ratio "limRat".
  275.                 // This means that if limRat is 0.5, then if you have 1000 beams and 500 beams worth of scaffolds, 250 of the beams
  276.                 // will be crafted into scaffolds. If instead limRat is 0.75, 625 of the beams will be crafted into scaffolds for a final result
  277.                 // of 1125 beams-worth of scaffolds and 375 remaining beams.
  278.                 // Currently, limRat is not modifiable through the UI, though if there is demand, perhaps this will be added in the future.
  279.                 items: {
  280.                     wood:       {require: 'catnip',      max: 0, limited: false, limRat: 0.5, enabled: true},
  281.                     beam:       {require: 'wood',        max: 0, limited: false, limRat: 0.5, enabled: true},
  282.                     slab:       {require: 'minerals',    max: 0, limited: false, limRat: 0.5, enabled: true},
  283.                     steel:      {require: 'coal',        max: 0, limited: false, limRat: 0.5, enabled: true},
  284.                     plate:      {require: 'iron',        max: 0, limited: false, limRat: 0.5, enabled: true},
  285.                     alloy:      {require: 'titanium',    max: 0, limited: true,  limRat: 0.5, enabled: false},
  286.                     concrete:   {require: false,         max: 0, limited: true,  limRat: 0.5, enabled: false},
  287.                     gear:       {require: false,         max: 0, limited: true,  limRat: 0.5, enabled: false},
  288.                     scaffold:   {require: false,         max: 0, limited: true,  limRat: 0.5, enabled: false},
  289.                     ship:       {require: false,         max: 0, limited: true,  limRat: 0.5, enabled: false},
  290.                     tanker:     {require: false,         max: 0, limited: true,  limRat: 0.5, enabled: false},
  291.                     parchment:  {require: false,         max: 0, limited: false, limRat: 0.5, enabled: true},
  292.                     manuscript: {require: 'culture',     max: 0, limited: true,  limRat: 0.5, enabled: true},
  293.                     compendium: {require: 'science',     max: 0, limited: true,  limRat: 0.5, enabled: true},
  294.                     blueprint:  {require: 'science',     max: 0, limited: true,  limRat: 0.5, enabled: false},
  295.                     kerosene:   {require: 'oil',         max: 0, limited: false, limRat: 0.5, enabled: false},
  296.                     megalith:   {require: false,         max: 0, limited: true,  limRat: 0.5, enabled: false},
  297.                     eludium:    {require: 'unobtainium', max: 0, limited: false, limRat: 0.5, enabled: false},
  298.                     thorium:    {require: 'uranium',     max: 0, limited: false, limRat: 0.5, enabled: false}
  299.                 }
  300.             },
  301.             trade: {
  302.                 // Should KS automatically trade?
  303.                 enabled: true,
  304.                 // Every trade can define a required resource with the *require* property.
  305.                 // At what percentage of the storage capacity of that required resource should the trade happen?
  306.                 trigger: 0.95,
  307.                 // Trades can be limited to only happen during specific seasons. This is because trades with certain races
  308.                 // are more effective during specific seasons.
  309.                 // The *allowcapped* property allows us to trade even if the sold resources are at their cap.
  310.                 items: {
  311.                     dragons:    {enabled: false,  require: 'titanium',    allowcapped: false,
  312.                         summer:  true,  autumn:  true,  winter:  true,          spring:      true},
  313.  
  314.                     zebras:     {enabled: true,  require: false,         allowcapped: false,
  315.                         summer:  true,  autumn:  true,  winter:  true,          spring:      true},
  316.  
  317.                     lizards:    {enabled: false,  require: 'minerals',    allowcapped: false,
  318.                         summer:  true,  autumn:  false, winter:  false,         spring:      false},
  319.  
  320.                     sharks:     {enabled: false,  require: 'iron',        allowcapped: false,
  321.                         summer:  false, autumn:  false, winter:  true,          spring:      false},
  322.  
  323.                     griffins:   {enabled: false,  require: 'wood',        allowcapped: false,
  324.                         summer:  false, autumn:  true,  winter:  false,         spring:      false},
  325.  
  326.                     nagas:      {enabled: false,  require: false,         allowcapped: false,
  327.                         summer:  false, autumn:  false, winter:  false,         spring:      true},
  328.  
  329.                     spiders:    {enabled: false,  require: false,         allowcapped: false,
  330.                         summer:  false, autumn:  true,  winter:  false,         spring:      false},
  331.  
  332.                     leviathans: {enabled: false,  require: 'unobtainium', allowcapped: true,
  333.                         summer:  true,  autumn:  true,  winter:  true,          spring:      true}
  334.                 }
  335.             },
  336.             upgrade: {
  337.                 //Should KS automatically upgrade?
  338.                 enabled: false,
  339.                 items: {
  340.                     upgrades:  {enabled: false},
  341.                     techs:     {enabled: false},
  342.                     buildings: {enabled: false}
  343.                 }
  344.             },
  345.             resources: {
  346.                 furs:        {stock: 1000},
  347.                 unobtainium: {consume: 1.0}
  348.             }
  349.         }
  350.     };
  351.  
  352.     // GameLog Modification
  353.     // ====================
  354.  
  355.     // Increase messages displayed in log
  356.     game.console.maxMessages = 1000;
  357.  
  358.     var printoutput = function (args) {
  359.         var color = args.pop();
  360.         args[1] = args[1] || 'ks-default';
  361.  
  362.         // update the color of the message immediately after adding
  363.         var msg = game.msg.apply(game, args);
  364.         $(msg.span).css('color', color);
  365.  
  366.         if (options.debug && console) console.log(args);
  367.     };
  368.  
  369.     // Used for option change messages and other special notifications
  370.     var message = function () {
  371.         var args = Array.prototype.slice.call(arguments);
  372.         args.push('ks-default');
  373.         args.push(options.msgcolor);
  374.         printoutput(args);
  375.     };
  376.  
  377.     var activity = function () {
  378.         if (options.showactivity) {
  379.             var args = Array.prototype.slice.call(arguments);
  380.             var activityClass = args.length > 1 ? ' type_' + args.pop() : '';
  381.             args.push('ks-activity' + activityClass);
  382.             args.push(options.activitycolor);
  383.             printoutput(args);
  384.         }
  385.     };
  386.  
  387.     var summary = function () {
  388.         var args = Array.prototype.slice.call(arguments);
  389.         args.push('ks-summary');
  390.         args.push(options.summarycolor);
  391.         printoutput(args);
  392.     };
  393.  
  394.     var warning = function () {
  395.         var args = Array.prototype.slice.call(arguments);
  396.         args.unshift('Warning!');
  397.  
  398.         if (console) console.log(args);
  399.     };
  400.  
  401.     // Core Engine for Kitten Scientists
  402.     // =================================
  403.  
  404.     var Engine = function () {
  405.         this.upgradeManager = new UpgradeManager();
  406.         this.buildManager = new BuildManager();
  407.         this.spaceManager = new SpaceManager();
  408.         this.craftManager = new CraftManager();
  409.         this.tradeManager = new TradeManager();
  410.         this.religionManager = new ReligionManager();
  411.         this.timeManager = new TimeManager();
  412.         this.explorationManager = new ExplorationManager();
  413.         // this.autofeedManager = new AutofeedManager();
  414.         this.villageManager = new TabManager('Village');
  415.     };
  416.  
  417.     Engine.prototype = {
  418.         upgradeManager: undefined,
  419.         buildManager: undefined,
  420.         spaceManager: undefined,
  421.         craftManager: undefined,
  422.         tradeManager: undefined,
  423.         religionManager: undefined,
  424.         timeManager: undefined,
  425.         explorationManager: undefined,
  426.         // autofeedManager: undefined,
  427.         villageManager: undefined,
  428.         loop: undefined,
  429.         start: function () {
  430.             if (this.loop) return;
  431.  
  432.             this.loop = setInterval(this.iterate.bind(this), options.interval);
  433.             message('Enabling the kitten scientists!');
  434.         },
  435.         stop: function () {
  436.             if (!this.loop) return;
  437.  
  438.             clearInterval(this.loop);
  439.             this.loop = undefined;
  440.             message('Disabling the kitten scientists!');
  441.         },
  442.         iterate: function () {
  443.             this.observeStars();
  444.             if (options.auto.upgrade.enabled) this.upgrade();
  445.             if (options.auto.festival.enabled) this.holdFestival();
  446.             if (options.auto.build.enabled) this.build();
  447.             if (options.auto.space.enabled) this.space();
  448.             if (options.auto.craft.enabled) this.craft();
  449.             if (options.auto.trade.enabled) this.trade();
  450.             if (options.auto.hunt.enabled) this.hunt();
  451.             if (options.auto.faith.enabled) this.worship();
  452.             if (options.auto.time.enabled) this.chrono();
  453.             if (options.auto.crypto.enabled) this.crypto();
  454.             if (options.auto.explore.enabled) this.explore();
  455.             if (options.auto.autofeed.enabled) this.autofeed();
  456.         },
  457.         autofeed: function(){
  458.         // var manager = this.autofeedManager;
  459.         // Only feed if it's enabled
  460.         if (!options.auto.autofeed.enabled) return;
  461.         if(game.diplomacy.get("leviathans").unlocked && game.resPool.get("necrocorn").value>=1) {
  462.         if(game.diplomacy.get("leviathans").energy<game.religion.getZU("marker").val * 5 + 5) {
  463.         game.diplomacy.feedElders();
  464.         activity('Kittens fed the Elders. The elders are pleased');
  465.         }}
  466.     },
  467.  
  468.         crypto: function () {
  469.             var coinPrice = game.calendar.cryptoPrice;
  470.             var previousRelic = game.resPool.get('relic').value;
  471.             var previousCoin = game.resPool.get('blackcoin').value;
  472.             var exchangedCoin = 0.0;
  473.             var exchangedRelic = 0.0;
  474.             var waitForBestPrice = false;
  475.  
  476.             // Only exchange if it's enabled
  477.             if (!options.auto.crypto.enabled) return;
  478.  
  479.             // Waits for coin price to drop below a certain treshold before starting the exchange process
  480.             if (waitForBestPrice == true && coinPrice < 860.0) { waitForBestPrice = false; }
  481.  
  482.             // Exchanges up to a certain threshold, in order to keep a good exchange rate, then waits for a higher treshold before exchanging for relics.
  483.             if (waitForBestPrice == false && coinPrice < 950.0 && previousRelic > options.auto.crypto.trigger) {
  484.                 var currentCoin;
  485.  
  486.                 game.diplomacy.buyEcoin();
  487.  
  488.                 currentCoin = game.resPool.get('blackcoin').value;
  489.                 exchangedCoin = Math.round(currentCoin - previousCoin);
  490.                 activity('Kittens sold your Relics and bought '+ exchangedCoin +' Blackcoins');
  491.             }
  492.             else if (coinPrice > 1050.0 && game.resPool.get('blackcoin').value > 0) {
  493.                 var currentRelic;
  494.  
  495.                 waitForBestPrice = true;
  496.  
  497.                 game.diplomacy.sellEcoin();
  498.  
  499.                 currentRelic = game.resPool.get('blackcoin').value;
  500.                 exchangedRelic = Math.round(currentRelic - previousRelic);
  501.  
  502.                 activity('Kittens sold your Blackcoins and bought '+ exchangedRelic +' Relics');
  503.             }
  504.         },
  505.         explore: function () {
  506.             var manager = this.explorationManager;
  507.             var expeditionNode = game.village.map.expeditionNode;
  508.  
  509.             // Only exchange if it's enabled
  510.             if (!options.auto.explore.enabled) return;
  511.  
  512.             if( expeditionNode == null) {
  513.                 manager.getCheapestNode();
  514.  
  515.                 manager.explore(manager.cheapestNodeX, manager.cheapestNodeY);
  516.  
  517.                 activity('Your kittens started exploring node '+ manager.cheapestNodeX +'-'+ manager.cheapestNodeY +' of the map.');
  518.             }
  519.         },
  520.         worship: function () {
  521.             var builds = options.auto.faith.items;
  522.             var buildManager = this.religionManager;
  523.             var craftManager = this.craftManager;
  524.             var trigger = options.auto.faith.trigger;
  525.  
  526.             // Render the tab to make sure that the buttons actually exist in the DOM. Otherwise we can't click them.
  527.             buildManager.manager.render();
  528.  
  529.             for (var name in builds) {
  530.                 if (!builds[name].enabled) continue;
  531.  
  532.                 var build = builds[name];
  533.                 var require = !build.require ? false : craftManager.getResource(build.require);
  534.  
  535.                 if (!require || trigger <= require.value / require.maxValue) {
  536.                     buildManager.build(name, build.variant);
  537.                 }
  538.             }
  539.  
  540.             // Praise the sun with any faith left over
  541.             var faith = craftManager.getResource('faith');
  542.  
  543.             if (options.auto.faith.trigger <= faith.value / faith.maxValue) {
  544.                 storeForSummary('faith', faith.value * (1 + game.religion.getFaithBonus()));
  545.                 activity('Praised the sun!', 'ks-praise');
  546.                 game.religion.praise();
  547.             }
  548.         },
  549.         chrono: function () {
  550.             var builds = options.auto.time.items;
  551.             var buildManager = this.timeManager;
  552.             var craftManager = this.craftManager;
  553.             var trigger = options.auto.time.trigger;
  554.            
  555.             // Render the tab to make sure that the buttons actually exist in the DOM. Otherwise we can't click them.
  556.             buildManager.manager.render();
  557.            
  558.             for (var name in builds) {
  559.                 if (!builds[name].enabled) continue;
  560.  
  561.                 var build = builds[name];
  562.                 var require = !build.require ? false : craftManager.getResource(build.require);
  563.  
  564.                 if (!require || trigger <= require.value / require.maxValue) {
  565.                     buildManager.build(name, build.variant);
  566.                 }
  567.             }
  568.         },
  569.         upgrade: function () {
  570.             var upgrades = options.auto.upgrade.items;
  571.             var upgradeManager = this.upgradeManager;
  572.             var craftManager = this.craftManager;
  573.            
  574.             upgradeManager.workManager.render();
  575.             upgradeManager.sciManager.render();
  576.            
  577.             if (upgrades.upgrades.enabled && gamePage.tabs[3].visible) {
  578.                 var work = game.workshop.upgrades;
  579.                 workLoop:
  580.                 for (var upg in work) {
  581.                     if (work[upg].researched || !work[upg].unlocked) {continue;}
  582.                    
  583.                     var prices = work[upg].prices;
  584.                     for (var resource in prices) {
  585.                         if (craftManager.getValueAvailable(prices[resource].name, true) < prices[resource].val) {continue workLoop;}
  586.                     }
  587.                     upgradeManager.build(work[upg], 'workshop');
  588.                 }
  589.             }
  590.            
  591.             if(upgrades.techs.enabled && gamePage.tabs[2].visible) {
  592.                 var tech = game.science.techs;
  593.                 techLoop:
  594.                 for (var upg in tech) {
  595.                     if (tech[upg].researched || !tech[upg].unlocked) {continue;}
  596.                    
  597.                     var prices = tech[upg].prices;
  598.                     for (var resource in prices) {
  599.                         if (craftManager.getValueAvailable(prices[resource].name, true) < prices[resource].val) {continue techLoop;}
  600.                     }
  601.                     upgradeManager.build(tech[upg], 'science');
  602.                 }
  603.             }
  604.            
  605.             if (upgrades.buildings.enabled) {
  606.                 var pastureMeta = game.bld.getBuildingExt('pasture').meta;
  607.                 if (pastureMeta.stage === 0) {
  608.                     if (pastureMeta.stages[1].stageUnlocked) {
  609.                         pastureMeta.on = 0;
  610.                         pastureMeta.val = 0;
  611.                         pastureMeta.stage = 1;
  612.                         game.render();
  613.                         activity('Upgraded pastures to solar farms!', 'ks-upgrade');
  614.                     }
  615.                 }
  616.                
  617.                 var aqueductMeta = game.bld.getBuildingExt('aqueduct').meta;
  618.                 if (aqueductMeta.stage === 0) {
  619.                     if (aqueductMeta.stages[1].stageUnlocked) {
  620.                         aqueductMeta.on = 0
  621.                         aqueductMeta.val = 0
  622.                         aqueductMeta.stage = 1
  623.                         aqueductMeta.calculateEffects(aqueductMeta, game)
  624.                         game.render()
  625.                         activity('Upgraded aqueducts to hydro plants!', 'ks-upgrade');
  626.                     }
  627.                 }
  628.                
  629.                 var libraryMeta = game.bld.getBuildingExt('library').meta;
  630.                 if (libraryMeta.stage === 0) {
  631.                     if (libraryMeta.stages[1].stageUnlocked) {
  632.                         libraryMeta.on = 0
  633.                         libraryMeta.val = 0
  634.                         libraryMeta.stage = 1
  635.                         libraryMeta.calculateEffects(libraryMeta, game)
  636.                         game.render()
  637.                         activity('Upgraded libraries to data centers!', 'ks-upgrade');
  638.                     }
  639.                    
  640.                 }
  641.                
  642.                 var amphitheatreMeta = game.bld.getBuildingExt('amphitheatre').meta;
  643.                 if (amphitheatreMeta.stage === 0) {
  644.                     if (amphitheatreMeta.stages[1].stageUnlocked) {
  645.                         amphitheatreMeta.on = 0
  646.                         amphitheatreMeta.val = 0
  647.                         amphitheatreMeta.stage = 1
  648.                         game.render()
  649.                         activity('Upgraded amphitheatres to broadcast towers!', 'ks-upgrade');
  650.                     }
  651.                 }
  652.             }
  653.         },
  654.         build: function () {
  655.             var builds = options.auto.build.items;
  656.             var buildManager = this.buildManager;
  657.             var craftManager = this.craftManager;
  658.             var trigger = options.auto.build.trigger;
  659.  
  660.             // Render the tab to make sure that the buttons actually exist in the DOM. Otherwise we can't click them.
  661.             buildManager.manager.render();
  662.  
  663.             // Using labeled for loop to break out of a nested loop
  664.             // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/label
  665.             buildLoop:
  666.             for (var name in builds) {
  667.                 if (!builds[name].enabled) continue;
  668.  
  669.                 var build = builds[name];
  670.                 var require = !build.require ? false : craftManager.getResource(build.require);
  671.  
  672.                 if (!require || trigger <= require.value / require.maxValue) {
  673.                     // verify that the building prices is within the current stock settings
  674.                     var prices = game.bld.getPrices(build.name || name);
  675.                     for (var p = 0; p < prices.length; p++) {
  676.                         if (craftManager.getValueAvailable(prices[p].name, true) < prices[p].val) continue buildLoop;
  677.                     }
  678.  
  679.                     // If the build overrides the name, use that name instead.
  680.                     // This is usually true for buildings that can be upgraded.
  681.                     buildManager.build(build.name || name, build.stage);
  682.                 }
  683.             }
  684.         },
  685.         space: function () {
  686.             var builds = options.auto.space.items;
  687.             var buildManager = this.spaceManager;
  688.             var craftManager = this.craftManager;
  689.             var trigger = options.auto.space.trigger;
  690.  
  691.             // Render the tab to make sure that the buttons actually exist in the DOM. Otherwise we can't click them.
  692.             buildManager.manager.render();
  693.  
  694.             for (var name in builds) {
  695.                 var build = builds[name];
  696.                 var require = !build.require ? false : craftManager.getResource(build.require);
  697.  
  698.                 if (!require || trigger <= require.value / require.maxValue) {
  699.                     buildManager.build(name);
  700.                 }
  701.             }
  702.         },
  703.         craft: function () {
  704.             var crafts = options.auto.craft.items;
  705.             var manager = this.craftManager;
  706.             var trigger = options.auto.craft.trigger;
  707.  
  708.             for (var name in crafts) {
  709.                 var craft = crafts[name];
  710.                 var current = !craft.max ? false : manager.getResource(name);
  711.                 var require = !craft.require ? false : manager.getResource(craft.require);
  712.                 var season = game.calendar.season;
  713.  
  714.                 // Ensure that we have reached our cap
  715.                 if (current && current.value > craft.max) continue;
  716.  
  717.                 // Craft the resource if we meet the trigger requirement
  718.                 if (!require || trigger <= require.value / require.maxValue) {
  719.                     var amount = manager.getLowestCraftAmount(name, craft.limited, craft.limRat);
  720.  
  721.                     if (amount > 0) {
  722.                         manager.craft(name, amount);
  723.                     }
  724.                 }
  725.             }
  726.         },
  727.         holdFestival: function () {
  728.             // Render the tab to make sure that the buttons actually exist in the DOM. Otherwise we can't click them.
  729.             this.villageManager.render();
  730.  
  731.             if (game.science.get('drama').researched && game.calendar.festivalDays === 0 && game.villageTab.festivalBtn.model.enabled) {
  732.                 game.villageTab.festivalBtn.onClick();
  733.                 if (game.calendar.festivalDays !== 0) {
  734.                     storeForSummary('festival');
  735.                     activity('Kittens begin holding a festival', 'ks-festival');
  736.                 }
  737.             }
  738.         },
  739.         observeStars: function () {
  740.             if (game.calendar.observeBtn != null){
  741.                 game.calendar.observeHandler();
  742.                 activity('Kitten Scientists have observed a star', 'ks-star');
  743.                 storeForSummary('stars', 1);
  744.             }
  745.         },
  746.         hunt: function () {
  747.             var catpower = this.craftManager.getResource('catpower');
  748.  
  749.             if (options.auto.hunt.trigger <= catpower.value / catpower.maxValue && catpower.value >= 100) {
  750.                 // No way to send only some hunters. Thus, we hunt with everything
  751.                 var hunters = game.village.getJob('hunter').value;
  752.                 storeForSummary('hunt', hunters);
  753.                 activity('Sent ' + game.getDisplayValueExt(hunters) + ' kitten' + (hunters == 1 ? '' : 's') + ' on the hunt', 'ks-hunt');
  754.                 game.village.huntAll();
  755.             }
  756.         },
  757.         trade: function () {
  758.             var craftManager = this.craftManager;
  759.             var tradeManager = this.tradeManager;
  760.             var gold = craftManager.getResource('gold');
  761.             var trades = [];
  762.            
  763.             tradeManager.manager.render();
  764.            
  765.             // Only trade if it's enabled
  766.             if (!options.auto.trade.enabled) return;
  767.  
  768.             // Trade when we have enough gold. Don't worry about catpower.
  769.             if (options.auto.trade.trigger >= gold.value / gold.maxValue) return;
  770.  
  771.             // Determine how many races we will trade this cycle
  772.             for (var name in options.auto.trade.items) {
  773.                 var trade = options.auto.trade.items[name];
  774.                 var season = game.calendar.getCurSeason().name;
  775.  
  776.                 // Only check if we are in season and enabled
  777.                 if (!trade.enabled) continue;
  778.                 if (!trade[season]) continue;
  779.  
  780.                 var require = !trade.require ? false : craftManager.getResource(trade.require);
  781.                 var requireTrigger = options.auto.trade.trigger;
  782.  
  783.                 // If we have enough to trigger the check, then attempt to trade
  784.                 if (!require || requireTrigger <= require.value / require.maxValue) {
  785.                     trades.push(name);
  786.                 }
  787.             }
  788.  
  789.             // Figure out how much we can currently trade
  790.             var maxTrades = tradeManager.getLowestTradeAmount(undefined);
  791.  
  792.             // Try our best not to starve any single race
  793.             maxTrades = (trades.length > 0) ? Math.floor(maxTrades / trades.length) : 0;
  794.  
  795.             if (maxTrades < 1) return;
  796.  
  797.             for (var i in trades) {
  798.                 var name = trades[i];
  799.                 tradeManager.trade(name, Math.min(tradeManager.getLowestTradeAmount(name), maxTrades));
  800.             }
  801.         }
  802.     };
  803.  
  804.     // Tab Manager
  805.     // ===========
  806.  
  807.     var TabManager = function (name) {
  808.         this.setTab(name);
  809.     };
  810.  
  811.     TabManager.prototype = {
  812.         tab: undefined,
  813.         render: function () {
  814.             if (this.tab && game.ui.activeTabId !== this.tab.tabId) this.tab.render();
  815.  
  816.             return this;
  817.         },
  818.         setTab: function (name) {
  819.             for (var tab in game.tabs) {
  820.                 if (game.tabs[tab].tabId === name) {
  821.                     this.tab = game.tabs[tab];
  822.                     break;
  823.                 }
  824.             }
  825.  
  826.             this.tab ? this.render() : warning('unable to find tab ' + name);
  827.         }
  828.     };
  829.  
  830.     // Exploration Manager
  831.     // ===================
  832.  
  833.     var ExplorationManager = function () {
  834.         this.manager = new TabManager('Village');
  835.     };
  836.  
  837.     ExplorationManager.prototype = {
  838.         manager: undefined,
  839.         currentCheapestNode: null,
  840.         currentCheapestNodeValue: null,
  841.         cheapestNodeX: null,
  842.         cheapestNodeY: null,
  843.         explore: function(x, y) {
  844.             game.village.map.expeditionNode = {x, y};
  845.             game.village.map.explore(x, y);
  846.         },
  847.         getCheapestNode: function () {
  848.             var tileArray = game.village.map.villageData;
  849.             var tileKey = "";
  850.  
  851.             this.currentCheapestNode = null;
  852.  
  853.             for (var i in tileArray) {
  854.                 tileKey = i;
  855.  
  856.                 // Discards locked nodes
  857.                 if (i.unlocked == false) { break; }
  858.  
  859.                 // Discards junk nodes
  860.                 if(tileKey.includes('-')) { break; }
  861.  
  862.                 // Acquire node coordinates
  863.                 var regex = /(\d).(\d*)/g;
  864.                 var keyMatch = regex.exec(tileKey);
  865.                 var xCoord = parseInt(keyMatch[1]);
  866.                 var yCoord = parseInt(keyMatch[2]);
  867.  
  868.                 if(this.currentCheapestNode == null) {
  869.                     this.currentCheapestNodeValue = this.getNodeValue(xCoord, yCoord)
  870.                     this.currentCheapestNode = i;
  871.                     this.cheapestNodeX = xCoord;
  872.                     this.cheapestNodeY = yCoord;
  873.                 }
  874.  
  875.                 if (this.currentCheapestNode != null && this.getNodeValue(xCoord, yCoord) < this.currentCheapestNodeValue) {
  876.                     this.currentCheapestNodeValue = this.getNodeValue(xCoord, yCoord)
  877.                     this.currentCheapestNode = i;
  878.                     this.cheapestNodeX = xCoord;
  879.                     this.cheapestNodeY = yCoord;
  880.                 }
  881.             }
  882.         },
  883.         getNodeValue: function (x, y){
  884.             var nodePrice = game.village.map.toLevel(x, y);
  885.             var exploreCost = game.village.map.getExplorationPrice(x,y);
  886.  
  887.             var tileValue = nodePrice / exploreCost;
  888.  
  889.             return tileValue;
  890.         }
  891.     };
  892.  
  893.     // Religion manager
  894.     // ================
  895.  
  896.     var ReligionManager = function () {
  897.         this.manager = new TabManager('Religion');
  898.         this.crafts = new CraftManager();
  899.     };
  900.  
  901.     ReligionManager.prototype = {
  902.         manager: undefined,
  903.         crafts: undefined,
  904.         build: function (name, variant) {
  905.             var build = this.getBuild(name, variant);
  906.             var button = this.getBuildButton(name, variant);
  907.  
  908.             if (!button || !button.model.enabled) return;
  909.  
  910.             //need to simulate a click so the game updates everything properly
  911.             button.domNode.click(build);
  912.             storeForSummary(name, 1, 'faith');
  913.             if (variant === "s") {
  914.                 activity('Kittens have discovered ' + build.label, 'ks-faith');
  915.             } else {
  916.                 activity('Kittens have built a new ' + build.label, 'ks-build');
  917.             }
  918.         },
  919.         getBuild: function (name, variant) {
  920.             switch (variant) {
  921.                 case 'z':
  922.                     return game.religion.getZU(name);
  923.                 case 's':
  924.                     return game.religion.getRU(name);
  925.                 case 'c':
  926.                     return game.religion.getTU(name);
  927.             }
  928.         },
  929.         getBuildButton: function (name, variant) {
  930.             switch (variant) {
  931.                 case 'z':
  932.                     var buttons = this.manager.tab.zgUpgradeButtons;
  933.                     break;
  934.                 case 's':
  935.                     var buttons = this.manager.tab.rUpgradeButtons;
  936.                     break;
  937.                 case 'c':
  938.                     var buttons = this.manager.tab.children[0].children[0].children;
  939.             }
  940.             var build = this.getBuild(name, variant);
  941.             for (var i in buttons) {
  942.                 var haystack = buttons[i].model.name;
  943.                 if (haystack.indexOf(build.label) !== -1) {
  944.                     return buttons[i];
  945.                 }
  946.             }
  947.         }
  948.     };
  949.  
  950.     // Time manager
  951.     // ============
  952.    
  953.     var TimeManager = function () {
  954.         this.manager = new TabManager('Time');
  955.         this.crafts = new CraftManager();
  956.     };
  957.    
  958.     TimeManager.prototype = {
  959.         manager: undefined,
  960.         crafts: undefined,
  961.         build: function (name, variant) {
  962.             var build = this.getBuild(name, variant);
  963.             var button = this.getBuildButton(name, variant);
  964.  
  965.             if (!button || !button.model.enabled) return;
  966.  
  967.             //need to simulate a click so the game updates everything properly
  968.             button.domNode.click(build);
  969.             storeForSummary(name, 1, 'build');
  970.  
  971.             var label = build.label;
  972.             activity('Kittens have built a new ' + label, 'ks-build');
  973.         },
  974.         getBuild: function (name, variant) {
  975.             if (variant === 'chrono') {
  976.                 return game.time.getCFU(name);
  977.             } else {
  978.                 return game.time.getVSU(name);
  979.             }
  980.         },
  981.         getBuildButton: function (name, variant) {
  982.             if (variant === 'chrono') {
  983.                 var buttons = this.manager.tab.children[2].children[0].children;
  984.             } else {
  985.                 var buttons = this.manager.tab.children[3].children[0].children;
  986.             }
  987.             var build = this.getBuild(name, variant);
  988.             for (var i in buttons) {
  989.                 var haystack = buttons[i].model.name;
  990.                 if (haystack.indexOf(build.label) !== -1) {
  991.                     return buttons[i];
  992.                 }
  993.             }
  994.         }
  995.     };
  996.    
  997.     // Upgrade manager
  998.     // ============
  999.    
  1000.     var UpgradeManager = function () {
  1001.         this.workManager = new TabManager('Workshop');
  1002.         this.sciManager = new TabManager('Science');
  1003.         this.crafts = new CraftManager();
  1004.     };
  1005.    
  1006.     UpgradeManager.prototype = {
  1007.         manager: undefined,
  1008.         crafts: undefined,
  1009.         build: function (upgrade, variant) {
  1010.             var button = this.getBuildButton(upgrade, variant);
  1011.  
  1012.             if (!button || !button.model.enabled) return;
  1013.  
  1014.             //need to simulate a click so the game updates everything properly
  1015.             button.domNode.click(upgrade);
  1016.             storeForSummary(build.name, 1, 'upgrade');
  1017.  
  1018.             var label = upgrade.label;
  1019.             activity('Kittens have bought the upgrade ' + label, 'ks-upgrade');
  1020.         },
  1021.         getBuildButton: function (upgrade, variant) {
  1022.             if (variant === 'workshop') {
  1023.                 var buttons = this.workManager.tab.buttons;
  1024.             } else if (variant === 'science') {
  1025.                 var buttons = this.sciManager.tab.buttons;
  1026.             }
  1027.             for (var i in buttons) {
  1028.                 var haystack = buttons[i].model.name;
  1029.                 if (haystack === upgrade.label) {
  1030.                     return buttons[i];
  1031.                 }
  1032.             }
  1033.         }
  1034.     };
  1035.    
  1036.     // Building manager
  1037.     // ================
  1038.  
  1039.     var BuildManager = function () {
  1040.         this.manager = new TabManager('Bonfire');
  1041.         this.crafts = new CraftManager();
  1042.     };
  1043.  
  1044.     BuildManager.prototype = {
  1045.         manager: undefined,
  1046.         crafts: undefined,
  1047.         build: function (name, stage) {
  1048.             var build = this.getBuild(name);
  1049.             var button = this.getBuildButton(name, stage);
  1050.  
  1051.             if (!button || !button.model.enabled) return;
  1052.  
  1053.             //need to simulate a click so the game updates everything properly
  1054.             button.domNode.click(build);
  1055.             storeForSummary(name, 1, 'build');
  1056.  
  1057.             var label = build.meta.label ? build.meta.label : build.meta.stages[0].label;
  1058.             activity('Kittens have built a new ' + label, 'ks-build');
  1059.         },
  1060.         getBuild: function (name) {
  1061.             return game.bld.getBuildingExt(name);
  1062.         },
  1063.         getBuildButton: function (name, stage) {
  1064.             var buttons = this.manager.tab.buttons;
  1065.             var build = this.getBuild(name);
  1066.             var label = typeof stage !== 'undefined' ? build.meta.stages[stage].label : build.meta.label;
  1067.  
  1068.             for (var i in buttons) {
  1069.                 var haystack = buttons[i].model.name;
  1070.                 if (haystack.indexOf(label) !== -1){
  1071.                     return buttons[i];
  1072.                 }
  1073.             }
  1074.         }
  1075.     };
  1076.  
  1077.     // Space manager
  1078.     // ================
  1079.  
  1080.     var SpaceManager = function () {
  1081.         this.manager = new TabManager('Space');
  1082.         this.crafts = new CraftManager();
  1083.     };
  1084.  
  1085.     SpaceManager.prototype = {
  1086.         manager: undefined,
  1087.         crafts: undefined,
  1088.         build: function (name) {
  1089.             var build = this.getBuild(name);
  1090.             var button = this.getBuildButton(name);
  1091.  
  1092.             if (!build.unlocked || !button || !button.model.enabled || !options.auto.space.items[name].enabled) return;
  1093.  
  1094.             //need to simulate a click so the game updates everything properly
  1095.             button.domNode.click(build);
  1096.             storeForSummary(name, 1, 'build');
  1097.  
  1098.             var label = build.label;
  1099.             activity('Kittens have built a new ' + label, 'ks-build');
  1100.         },
  1101.         getBuild: function (name) {
  1102.             return game.space.getProgram(name);
  1103.         },
  1104.         getBuildButton: function (name) {
  1105.             var panels = this.manager.tab.planetPanels;
  1106.  
  1107.             for (var panel in panels) {
  1108.                 for (var child in panels[panel].children) {
  1109.                     if (panels[panel].children[child].id === name) return panels[panel].children[child];
  1110.                 }
  1111.             }
  1112.         }
  1113.     };
  1114.  
  1115.     // Crafting Manager
  1116.     // ================
  1117.  
  1118.     var CraftManager = function () {};
  1119.  
  1120.     CraftManager.prototype = {
  1121.         craft: function (name, amount) {
  1122.             amount = Math.floor(amount);
  1123.  
  1124.             if (!name || 1 > amount) return;
  1125.             if (!this.canCraft(name, amount)) return;
  1126.  
  1127.             var craft = this.getCraft(name);
  1128.             var ratio = game.getResCraftRatio(craft);
  1129.  
  1130.             game.craft(craft.name, amount);
  1131.  
  1132.             // determine actual amount after crafting upgrades
  1133.             amount = (amount * (1 + ratio)).toFixed(2);
  1134.  
  1135.             storeForSummary(name, amount, 'craft');
  1136.             activity('Kittens have crafted ' + game.getDisplayValueExt(amount) + ' ' + ucfirst(name), 'ks-craft');
  1137.         },
  1138.         canCraft: function (name, amount) {
  1139.             var craft = this.getCraft(name);
  1140.             var enabled = options.auto.craft.items[name].enabled;
  1141.             var result = false;
  1142.  
  1143.             if (craft.unlocked && enabled) {
  1144.                 result = true;
  1145.  
  1146.                 for (var i in craft.prices) {
  1147.                     var price = craft.prices[i];
  1148.                     var value = this.getValueAvailable(price.name);
  1149.  
  1150.                     if (value < price.val * amount) {
  1151.                         result = false;
  1152.                     }
  1153.                 }
  1154.             }
  1155.  
  1156.             return result;
  1157.         },
  1158.         getCraft: function (name) {
  1159.             return game.workshop.getCraft(this.getName(name));
  1160.         },
  1161.         getLowestCraftAmount: function (name, limited, limRat) {
  1162.             var amount = Number.MAX_VALUE;
  1163.             var materials = this.getMaterials(name);
  1164.            
  1165.             var craft = this.getCraft(name);
  1166.             var ratio = game.getResCraftRatio(craft);
  1167.            
  1168.             // Safeguard if materials for craft cannot be determined.
  1169.             if (!materials) return 0;
  1170.  
  1171.             var res = this.getResource(name);
  1172.  
  1173.             for (var i in materials) {
  1174.                 var delta = undefined;
  1175.                 if(this.getResource(i).maxValue > 0 || ! limited) {
  1176.                     // If there is a storage limit, we can just use everything returned by getValueAvailable, since the regulation happens there
  1177.                     delta = this.getValueAvailable(i) / materials[i];
  1178.                 } else {
  1179.                     // Take the currently present amount of material to craft into account
  1180.                     // Currently this determines the amount of resources that can be crafted such that the produced resources and their components
  1181.                     // in storage both are "worth" the same number of base materials (as determined by price and craft ratio).
  1182.                     // This base material distribution is governed by limRat "limited ratio" which defaults to 0.5, corresponding to half of the possible components being further crafted.
  1183.                     // If this were another value, such as 0.75, then if you had 10000 beams and 0 scaffolds, 7500 of the beams would be crafted into scaffolds.
  1184.                     delta = limRat * ((this.getValueAvailable(i) + (materials[i] / (1 + ratio)) * this.getValueAvailable(res.name)) / materials[i]) - (this.getValueAvailable(res.name) / (1 + ratio));
  1185.                 }
  1186.  
  1187.                 amount = Math.min(delta,amount);
  1188.             }
  1189.  
  1190.             // If we have a maximum value, ensure that we don't produce more than
  1191.             // this value. This should currently only impact wood crafting, but is
  1192.             // written generically to ensure it works for any craft that produces a
  1193.             // good with a maximum value.
  1194.             if (res.maxValue > 0 && amount > (res.maxValue - res.value))
  1195.                 amount = res.maxValue - res.value;
  1196.  
  1197.             return Math.floor(amount);
  1198.         },
  1199.         getMaterials: function (name) {
  1200.             var materials = {};
  1201.             var craft = this.getCraft(name);
  1202.  
  1203.             // Safeguard against craft items that aren't actually available yet.
  1204.             if (!craft) return;
  1205.  
  1206.             var prices = game.workshop.getCraftPrice(craft);
  1207.  
  1208.             for (var i in prices) {
  1209.                 var price = prices[i];
  1210.  
  1211.                 materials[price.name] = price.val;
  1212.             }
  1213.  
  1214.             return materials;
  1215.         },
  1216.         getName: function (name) {
  1217.             // adjust for spelling discrepancies in core game logic
  1218.             if ('catpower' === name) name = 'manpower';
  1219.             if ('compendium' === name) name = 'compedium';
  1220.             if ('concrete' === name) name = 'concrate';
  1221.  
  1222.             return name;
  1223.         },
  1224.         getResource: function (name) {
  1225.             for (var i in game.resPool.resources) {
  1226.                 var res = game.resPool.resources[i];
  1227.                 if (res.name === this.getName(name)) return res;
  1228.             }
  1229.             warning('unable to find resource ' + name);
  1230.             return null;
  1231.         },
  1232.         getValue: function (name) {
  1233.             return this.getResource(name).value;
  1234.         },
  1235.         getStock: function (name) {
  1236.             var res = options.auto.resources[this.getName(name)];
  1237.             var stock = res ? res.stock : 0;
  1238.  
  1239.             return !stock ? 0 : stock;
  1240.         },
  1241.         getValueAvailable: function (name, all) {
  1242.             var value = this.getValue(name);
  1243.             var stock = this.getStock(name);
  1244.  
  1245.             if ('catnip' === name) {
  1246.                 var resPerTick = game.getResourcePerTick(name, false, {
  1247.                     modifiers: {
  1248.                         'catnip': 0.10 - game.calendar.getWeatherMod()
  1249.                     }});
  1250.  
  1251.                 if (resPerTick < 0) stock -= resPerTick * 202 * 5;
  1252.             }
  1253.  
  1254.             value = Math.max(value - stock, 0);
  1255.  
  1256.             // If we have a maxValue, and user hasn't requested all, check
  1257.             // consumption rate
  1258.             if (!all && this.getResource(name).maxValue > 0) {
  1259.                 var res = options.auto.resources[name];
  1260.                 var consume = res && (res.consume != undefined) ? res.consume : options.consume;
  1261.  
  1262.                 value *= consume;
  1263.             }
  1264.  
  1265.             return value;
  1266.         }
  1267.     };
  1268.  
  1269.     // Trading Manager
  1270.     // ===============
  1271.  
  1272.     var TradeManager = function () {
  1273.         this.craftManager = new CraftManager();
  1274.         this.manager = new TabManager('Trade');
  1275.     };
  1276.  
  1277.     TradeManager.prototype = {
  1278.         craftManager: undefined,
  1279.         manager: undefined,
  1280.         trade: function (name, amount) {
  1281.  
  1282.             if (!name || 1 > amount) return;
  1283.  
  1284.             var race = this.getRace(name);
  1285.  
  1286.             if (!race.unlocked) return;
  1287.  
  1288.             var button = this.getTradeButton(race.name);
  1289.  
  1290.             if (!button.model.enabled || !options.auto.trade.items[name].enabled) return;
  1291.  
  1292.             game.diplomacy.tradeMultiple(race, amount);
  1293.             storeForSummary(name, amount, 'trade');
  1294.             activity('Kittens have traded ' + amount + 'x with ' + ucfirst(name), 'ks-trade');
  1295.         },
  1296.         getLowestTradeAmount: function (name) {
  1297.             var amount = undefined;
  1298.             var highestCapacity = undefined;
  1299.             var materials = this.getMaterials(name);
  1300.             var race = this.getRace(name);
  1301.  
  1302.             for (var i in materials) {
  1303.                 var total = this.craftManager.getValueAvailable(i) / materials[i];
  1304.  
  1305.                 amount = (amount === undefined || total < amount) ? total : amount;
  1306.             }
  1307.  
  1308.             if (race === null || options.auto.trade.items[name].allowcapped) return Math.floor(amount);
  1309.  
  1310.             // Loop through the items obtained by the race, and determine
  1311.             // which good has the most space left. Once we've determined this,
  1312.             // reduce the amount by this capacity. This ensures that we continue to trade
  1313.             // as long as at least one resource has capacity, and we never over-trade.
  1314.             for (var s in race.sells) {
  1315.                 var item = race.sells[s];
  1316.                 var resource = this.craftManager.getResource(item.name);
  1317.                 var max = 0;
  1318.  
  1319.                 // No need to process resources that don't cap
  1320.                 if (!resource.maxValue) continue;
  1321.  
  1322.                 // Zebras special cased titanium taken directly from game code
  1323.                 if (race.name == "zebras" && item.name == "titanium") {
  1324.                     var val = 1.5 + (1.5 * game.resPool.get("ship").value / 100 * 2);
  1325.                     max = Math.ceil(val);
  1326.                 } else {
  1327.                     var sratio = item.seasons[game.calendar.getCurSeason().name];
  1328.                     var tratio = game.getEffect("tradeRatio");
  1329.                     var val = item.value + item.value * tratio;
  1330.  
  1331.                     max = val * sratio * (1 + item.delta/2);
  1332.                 }
  1333.  
  1334.                 capacity = (resource.maxValue - resource.value) / max;
  1335.  
  1336.                 highestCapacity = (capacity < highestCapacity) ? highestCapacity : capacity;
  1337.             }
  1338.  
  1339.             // We must take the ceiling of capacity so that we will trade as long
  1340.             // as there is any room, even if it doesn't have exact space. Otherwise
  1341.             // we seem to starve trading altogether.
  1342.             highestCapacity = Math.ceil(highestCapacity);
  1343.  
  1344.             // Now that we know the most we *should* trade for, check to ensure that
  1345.             // we trade for our max cost, or our max capacity, whichever is lower.
  1346.             // This helps us prevent trading for resources we can't store. Note that we
  1347.             // essentially ignore blueprints here. In addition, if highestCapacity was never set,
  1348.             // then we just
  1349.             amount = (highestCapacity < amount) ? highestCapacity : amount;
  1350.  
  1351.             return Math.floor(amount);
  1352.         },
  1353.         getMaterials: function (name) {
  1354.             var materials = {catpower: 50, gold: 15};
  1355.  
  1356.             if (name === undefined)
  1357.                 return materials;
  1358.  
  1359.             var prices = this.getRace(name).buys;
  1360.  
  1361.             for (var i in prices) {
  1362.                 var price = prices[i];
  1363.  
  1364.                 materials[price.name] = price.val;
  1365.             }
  1366.  
  1367.             return materials;
  1368.         },
  1369.         getRace: function (name) {
  1370.             if (name === undefined)
  1371.                 return null;
  1372.             else
  1373.                 return game.diplomacy.get(name);
  1374.         },
  1375.         getTradeButton: function (race) {
  1376.             for (var i in this.manager.tab.racePanels) {
  1377.                 var panel = this.manager.tab.racePanels[i];
  1378.  
  1379.                 if (panel.race.name === race) return panel.tradeBtn;
  1380.             }
  1381.  
  1382.             warning('unable to find trade button for ' + race);
  1383.         }
  1384.     };
  1385.  
  1386.     // ==============================
  1387.     // Configure overall page display
  1388.     // ==============================
  1389.  
  1390.     var right = $('#rightColumn');
  1391.  
  1392.     var addRule = function (rule) {
  1393.         var sheets = document.styleSheets;
  1394.         sheets[0].insertRule(rule, 0);
  1395.     };
  1396.  
  1397.     var defaultSelector = 'body[data-ks-style]:not(.scheme_sleek)';
  1398. /*
  1399.     addRule(defaultSelector + ' #game {'
  1400.         + 'font-family: monospace;'
  1401.         + 'font-size: 12px;'
  1402.         + 'min-width: 1300px;'
  1403.         + 'top: 32px;'
  1404.         + '}');
  1405.  
  1406.     addRule(defaultSelector + ' {'
  1407.         + 'font-family: monospace;'
  1408.         + 'font-size: 12px;'
  1409.         + '}');
  1410. */
  1411.     addRule(defaultSelector + ' .column {'
  1412.         + 'min-height: inherit;'
  1413.         + 'max-width: inherit !important;'
  1414.         + 'padding: 1%;'
  1415.         + 'margin: 0;'
  1416.         + 'overflow-y: auto;'
  1417.         + '}');
  1418.  
  1419.     addRule(defaultSelector + ' #leftColumn {'
  1420.         + 'height: 92%;'
  1421.         + 'width: 26%;'
  1422.         + '}');
  1423.  
  1424.     addRule(defaultSelector + ' #midColumn {'
  1425.         + 'margin-top: 1% !important;'
  1426.         + 'height: 90%;'
  1427.         + 'width: 48%;'
  1428.         + '}');
  1429.  
  1430.     addRule(defaultSelector + ' #rightColumn {'
  1431.         + 'overflow-y: scroll;'
  1432.         + 'height: 92%;'
  1433.         + 'width: 19%;'
  1434.         + '}');
  1435.  
  1436.     addRule(defaultSelector + ' #gameLog .msg {'
  1437.         + 'display: block;'
  1438.         + '}');
  1439.  
  1440.     addRule(defaultSelector + ' #gameLog {'
  1441.         + 'overflow-y: hidden !important;'
  1442.         + 'width: 100% !important;'
  1443.         + 'padding-top: 5px !important;'
  1444.         + '}');
  1445.  
  1446.     addRule(defaultSelector + ' #resContainer .maxRes {'
  1447.         + 'color: #676766;'
  1448.         + '}');
  1449. /*
  1450.     addRule(defaultSelector + ' #game .btn {'
  1451.         + 'border-radius: 0px;'
  1452.         + 'font-family: monospace;'
  1453.         + 'font-size: 12px !important;'
  1454.         + 'margin: 0 5px 7px 0;'
  1455.         + 'width: 290px;'
  1456.         + '}');
  1457. */
  1458.     addRule(defaultSelector + ' #game .map-viewport {'
  1459.         + 'height: 340px;'
  1460.         + 'max-width: 500px;'
  1461.         + 'overflow: visible;'
  1462.         + '}');
  1463.  
  1464.     addRule(' #game .map-dashboard {'
  1465.         + 'height: 120px;'
  1466.         + 'width: 292px;'
  1467.         + '}');
  1468.  
  1469.     addRule('#ks-options ul {'
  1470.         + 'list-style: none;'
  1471.         + 'margin: 0 0 5px;'
  1472.         + 'padding: 0;'
  1473.         + '}');
  1474.  
  1475.     addRule('#ks-options ul:after {'
  1476.         + 'clear: both;'
  1477.         + 'content: " ";'
  1478.         + 'display: block;'
  1479.         + 'height: 0;'
  1480.         + '}');
  1481.  
  1482.     addRule('#ks-options ul li {'
  1483.         + 'display: block;'
  1484.         + 'float: left;'
  1485.         + 'width: 100%;'
  1486.         + '}');
  1487.  
  1488.     addRule('#ks-options #toggle-list-resources .stockWarn {'
  1489.         + 'color: ' + options.stockwarncolor + ';'
  1490.         + '}');
  1491.  
  1492.     document.body.toggleAttribute("data-ks-style", true);
  1493.  
  1494.     // Local Storage
  1495.     // =============
  1496.  
  1497.     var kittenStorageVersion = 1;
  1498.  
  1499.     var kittenStorage = {
  1500.         version: kittenStorageVersion,
  1501.         toggles: {},
  1502.         items: {},
  1503.         resources: {},
  1504.         triggers: {}
  1505.     };
  1506.  
  1507.     var initializeKittenStorage = function () {
  1508.         $("#items-list-build, #items-list-craft, #items-list-trade").find("input[id^='toggle-']").each(function () {
  1509.             kittenStorage.items[$(this).attr("id")] = $(this).prop("checked");
  1510.         });
  1511.  
  1512.         saveToKittenStorage();
  1513.     };
  1514.  
  1515.     var saveToKittenStorage = function () {
  1516.         kittenStorage.toggles = {
  1517.             build: options.auto.build.enabled,
  1518.             space: options.auto.space.enabled,
  1519.             craft: options.auto.craft.enabled,
  1520.             upgrade: options.auto.upgrade.enabled,
  1521.             trade: options.auto.trade.enabled,
  1522.             hunt: options.auto.hunt.enabled,
  1523.             faith: options.auto.faith.enabled,
  1524.             time: options.auto.time.enabled,
  1525.             festival: options.auto.festival.enabled,
  1526.             crypto: options.auto.crypto.enabled,
  1527.             autofeed: options.auto.autofeed.enabled,
  1528.             explore: options.auto.explore.enabled
  1529.         };
  1530.         kittenStorage.resources = options.auto.resources;
  1531.         kittenStorage.triggers = {
  1532.             faith: options.auto.faith.trigger,
  1533.             time: options.auto.time.trigger,
  1534.             hunt: options.auto.hunt.trigger,
  1535.             build: options.auto.build.trigger,
  1536.             space: options.auto.space.trigger,
  1537.             craft: options.auto.craft.trigger,
  1538.             crypto: options.auto.crypto.trigger,
  1539.             explore: options.auto.explore.trigger,
  1540.             trade: options.auto.trade.trigger
  1541.         };
  1542.         localStorage['cbc.kitten-scientists'] = JSON.stringify(kittenStorage);
  1543.     };
  1544.  
  1545.     var loadFromKittenStorage = function () {
  1546.         var saved = JSON.parse(localStorage['cbc.kitten-scientists'] || 'null');
  1547.         if (saved && saved.version == kittenStorageVersion) {
  1548.             kittenStorage = saved;
  1549.  
  1550.             if (saved.toggles) {
  1551.                 for (var toggle in saved.toggles) {
  1552.                     if (toggle !== 'engine' && options.auto[toggle]) {
  1553.                         options.auto[toggle].enabled = !!saved.toggles[toggle];
  1554.                         var el = $('#toggle-' + toggle);
  1555.                         el.prop('checked', options.auto[toggle].enabled);
  1556.                     }
  1557.                 }
  1558.             }
  1559.  
  1560.             for (var item in kittenStorage.items) {
  1561.                 var value = kittenStorage.items[item];
  1562.                 var el = $('#' + item);
  1563.                 var option = el.data('option');
  1564.                 var name = item.split('-');
  1565.  
  1566.                 el.prop('checked', value);
  1567.  
  1568.                 if (name.length == 2) {
  1569.                     option.enabled = value;
  1570.                 } else {
  1571.                     if (name[1] == 'limited') {
  1572.                         option.limited = value;
  1573.                     } else {
  1574.                         option[name[2]] = value;
  1575.                     }
  1576.                 }
  1577.             }
  1578.  
  1579.             var list = $("#toggle-list-resources");
  1580.             for (var resource in kittenStorage.resources) {
  1581.                 var res = kittenStorage.resources[resource];
  1582.  
  1583.                 if ($('#resource-' + resource).length === 0) {
  1584.                     list.append(addNewResourceOption(resource));
  1585.                 }
  1586.                 if ('stock' in res) {
  1587.                     setStockValue(resource, res.stock);
  1588.                 }
  1589.                 if ('consume' in res) {
  1590.                     setConsumeRate(resource, res.consume);
  1591.                 }
  1592.             }
  1593.  
  1594.             if (saved.triggers) {
  1595.                 options.auto.faith.trigger = saved.triggers.faith;
  1596.                 options.auto.time.trigger = saved.triggers.time;
  1597.                 options.auto.hunt.trigger = saved.triggers.hunt;
  1598.                 options.auto.build.trigger = saved.triggers.build;
  1599.                 options.auto.space.trigger = saved.triggers.space;
  1600.                 options.auto.craft.trigger = saved.triggers.craft;
  1601.                 options.auto.trade.trigger = saved.triggers.trade;
  1602.                 options.auto.crypto.trigger = saved.triggers.crypto;
  1603.                 options.auto.explore.trigger = saved.triggers.explore;
  1604.  
  1605.                 $('#trigger-faith')[0].title = options.auto.faith.trigger;
  1606.                 $('#trigger-time')[0].title = options.auto.time.trigger;
  1607.                 $('#trigger-hunt')[0].title = options.auto.hunt.trigger;
  1608.                 $('#trigger-build')[0].title = options.auto.build.trigger;
  1609.                 $('#trigger-space')[0].title = options.auto.space.trigger;
  1610.                 $('#trigger-craft')[0].title = options.auto.craft.trigger;
  1611.                 $('#trigger-trade')[0].title = options.auto.trade.trigger;
  1612.                 $('#trigger-crypto')[0].title = options.auto.crypto.trigger;
  1613.             }
  1614.  
  1615.         } else {
  1616.             initializeKittenStorage();
  1617.         }
  1618.     };
  1619.  
  1620.     // Add options element
  1621.     // ===================
  1622.  
  1623.     var ucfirst = function (string) {
  1624.         return string.charAt(0).toUpperCase() + string.slice(1);
  1625.     };
  1626.  
  1627.     var roundToTwo = function (n) {
  1628.         return +(Math.round(n + "e+2") + "e-2")
  1629.     };
  1630.  
  1631.     var setStockWarning = function(name, value) {
  1632.         // simplest way to ensure it doesn't stick around too often; always do
  1633.         // a remove first then re-add only if needed
  1634.         $("#resource-" + name).removeClass("stockWarn");
  1635.  
  1636.         var maxValue = game.resPool.resources.filter(i => i.name == name)[0].maxValue;
  1637.         if (value > maxValue && !(maxValue === 0)) $("#resource-" + name).addClass("stockWarn");
  1638.     }
  1639.  
  1640.     var setStockValue = function (name, value) {
  1641.         var n = Number(value);
  1642.  
  1643.         if (isNaN(n) || n < 0) {
  1644.             warning('ignoring non-numeric or invalid stock value ' + value);
  1645.             return;
  1646.         }
  1647.  
  1648.         if (!options.auto.resources[name]) options.auto.resources[name] = {};
  1649.         options.auto.resources[name].stock = n;
  1650.         $('#stock-value-' + name).text('Stock: ' + game.getDisplayValueExt(n));
  1651.  
  1652.         setStockWarning(name, n);
  1653.     };
  1654.  
  1655.     var setConsumeRate = function (name, value) {
  1656.         var n = parseFloat(value);
  1657.  
  1658.         if (isNaN(n) || n < 0.0 || n > 1.0) {
  1659.             warning('ignoring non-numeric or invalid consume rate ' + value);
  1660.             return;
  1661.         }
  1662.  
  1663.         if (!options.auto.resources[name]) options.auto.resources[name] = {};
  1664.         options.auto.resources[name].consume = n;
  1665.         $('#consume-rate-' + name).text('Consume: ' + n.toFixed(2));
  1666.     };
  1667.  
  1668.     var removeResourceControl = function (name) {
  1669.         delete options.auto.resources[name];
  1670.     };
  1671.  
  1672.     var addNewResourceOption = function (name, title) {
  1673.         var res = options.auto.resources[name];
  1674.         var stock = res && (res.stock != undefined) ? res.stock : 0;
  1675.         var consume = res && (res.consume != undefined) ? res.consume : options.consume;
  1676.  
  1677.         var container = $('<div/>', {
  1678.             id: 'resource-' + name,
  1679.             css: {display: 'inline-block', width: '100%'},
  1680.         });
  1681.  
  1682.         var label = $('<div/>', {
  1683.             id: 'resource-label-' + name,
  1684.             text: ucfirst(title ? title : name),
  1685.             css: {display: 'inline-block', width: '95px'},
  1686.         });
  1687.  
  1688.         var stock = $('<div/>', {
  1689.             id: 'stock-value-' + name,
  1690.             text: 'Stock: ' + game.getDisplayValueExt(stock),
  1691.             css: {cursor: 'pointer', display: 'inline-block', width: '80px'},
  1692.         });
  1693.  
  1694.         var consume = $('<div/>', {
  1695.             id: 'consume-rate-' + name,
  1696.             text: 'Consume: ' + consume.toFixed(2),
  1697.             css: {cursor: 'pointer', display: 'inline-block'},
  1698.         });
  1699.  
  1700.         var del = $('<div/>', {
  1701.             id: 'resource-delete-' + name,
  1702.             text: 'del',
  1703.             css: {cursor: 'pointer',
  1704.                 display: 'inline-block',
  1705.                 float: 'right',
  1706.                 paddingRight: '5px',
  1707.                 textShadow: '3px 3px 4px gray'},
  1708.         });
  1709.  
  1710.         container.append(label, stock, consume, del);
  1711.  
  1712.         // once created, set color if relevant
  1713.         if (res != undefined && res.stock != undefined) setStockWarning(name, res.stock);
  1714.  
  1715.         stock.on('click', function () {
  1716.             var value = window.prompt('Stock for ' + ucfirst(title ? title : name));
  1717.             if (value !== null) {
  1718.                 setStockValue(name, value);
  1719.                 saveToKittenStorage();
  1720.             }
  1721.         });
  1722.  
  1723.         consume.on('click', function () {
  1724.             var value = window.prompt('Consume rate for ' + ucfirst(title ? title : name));
  1725.             if (value !== null) {
  1726.                 setConsumeRate(name, value);
  1727.                 saveToKittenStorage();
  1728.             }
  1729.         });
  1730.  
  1731.         del.on('click', function () {
  1732.             if (window.confirm('Delete resource controls for ' + ucfirst(title ? title : name) + '?')) {
  1733.                 container.remove();
  1734.                 removeResourceControl(name);
  1735.                 saveToKittenStorage();
  1736.             }
  1737.         });
  1738.  
  1739.         return container;
  1740.     };
  1741.  
  1742.     var getAvailableResourceOptions = function () {
  1743.         var items = [];
  1744.  
  1745.         for (var i in game.resPool.resources) {
  1746.             var res = game.resPool.resources[i];
  1747.  
  1748.             // Show only new resources that we don't have in the list and that are
  1749.             // visible. This helps cut down on total size.
  1750.             if (res.name && $('#resource-' + res.name).length === 0) {
  1751.                 var item = $('<div/>', {
  1752.                     id: 'resource-add-' + res.name,
  1753.                     text: ucfirst(res.title ? res.title : res.name),
  1754.                     css: {cursor: 'pointer',
  1755.                         textShadow: '3px 3px 4px gray'},
  1756.                 });
  1757.  
  1758.                 // Wrapper function needed to make closure work
  1759.                 (function (res, item) {
  1760.                     item.on('click', function () {
  1761.                         item.remove();
  1762.                         if (!options.auto.resources[res.name]) options.auto.resources[res.name] = {};
  1763.                         options.auto.resources[res.name].stock = 0;
  1764.                         options.auto.resources[res.name].consume = options.consume;
  1765.                         $('#toggle-list-resources').append(addNewResourceOption(res.name, res.title));
  1766.                     });
  1767.                 })(res, item);
  1768.  
  1769.                 items.push(item);
  1770.             }
  1771.         }
  1772.  
  1773.         return items;
  1774.     };
  1775.  
  1776.     var getResourceOptions = function () {
  1777.         var list = $('<ul/>', {
  1778.             id: 'toggle-list-resources',
  1779.             css: {display: 'none', paddingLeft: '20px'}
  1780.         });
  1781.  
  1782.         var add = $('<div/>', {
  1783.             id: 'resources-add',
  1784.             text: 'add resources',
  1785.             css: {cursor: 'pointer',
  1786.                 display: 'inline-block',
  1787.                 textShadow: '3px 3px 4px gray',
  1788.                 borderBottom: '1px solid rgba(185, 185, 185, 0.7)' },
  1789.         });
  1790.  
  1791.         var clearunused = $('<div/>', {
  1792.             id: 'resources-clear-unused',
  1793.             text: 'clear unused',
  1794.             css: {cursor: 'pointer',
  1795.                 display: 'inline-block',
  1796.                 float: 'right',
  1797.                 paddingRight: '5px',
  1798.                 textShadow: '3px 3px 4px gray' },
  1799.         });
  1800.  
  1801.         clearunused.on('click', function () {
  1802.             for (var name in options.auto.resources) {
  1803.                 // Only delete resources with unmodified values. Require manual
  1804.                 // removal of resources with non-standard values.
  1805.                 if (!options.auto.resources[name].stock &&
  1806.                     options.auto.resources[name].consume == options.consume ||
  1807.                     options.auto.resources[name].consume == undefined) {
  1808.                     $('#resource-' + name).remove();
  1809.                 }
  1810.             }
  1811.         });
  1812.  
  1813.         allresources = $('<ul/>', {
  1814.             id: 'available-resources-list',
  1815.             css: {display: 'none', paddingLeft: '20px'}
  1816.         });
  1817.  
  1818.         add.on('click', function () {
  1819.             allresources.toggle();
  1820.             allresources.empty();
  1821.             allresources.append(getAvailableResourceOptions());
  1822.         });
  1823.  
  1824.         list.append(add, clearunused, allresources);
  1825.  
  1826.         // Add all the current resources
  1827.         for (var name in options.auto.resources) {
  1828.             list.append(addNewResourceOption(name));
  1829.         }
  1830.  
  1831.         return list;
  1832.     };
  1833.  
  1834.     var getToggle = function (toggleName, text) {
  1835.         var auto = options.auto[toggleName];
  1836.         var element = $('<li/>', {id: 'ks-' + toggleName});
  1837.  
  1838.         var label = $('<label/>', {
  1839.             'for': 'toggle-' + toggleName,
  1840.             text: text
  1841.         });
  1842.  
  1843.         var input = $('<input/>', {
  1844.             id: 'toggle-' + toggleName,
  1845.             type: 'checkbox'
  1846.         });
  1847.  
  1848.         if (auto.enabled) {
  1849.             input.prop('checked', true);
  1850.         }
  1851.  
  1852.         // engine needs a custom toggle
  1853.         if (toggleName !== 'engine') {
  1854.             input.on('change', function () {
  1855.                 if (input.is(':checked') && auto.enabled == false) {
  1856.                     auto.enabled = true;
  1857.                     message('Enabled Auto ' + ucfirst(text));
  1858.                     saveToKittenStorage();
  1859.                 } else if ((!input.is(':checked')) && auto.enabled == true) {
  1860.                     auto.enabled = false;
  1861.                     message('Disabled Auto ' + ucfirst(text));
  1862.                     saveToKittenStorage();
  1863.                 }
  1864.             });
  1865.         }
  1866.  
  1867.         element.append(input, label);
  1868.  
  1869.         if (auto.items) {
  1870.             // Add a border on the element
  1871.             element.css('borderBottom', '1px  solid rgba(185, 185, 185, 0.7)');
  1872.  
  1873.             var toggle = $('<div/>', {
  1874.                 css: {display: 'inline-block', float: 'right'}
  1875.             });
  1876.  
  1877.             var button = $('<div/>', {
  1878.                 id: 'toggle-items-' + toggleName,
  1879.                 text: 'items',
  1880.                 css: {cursor: 'pointer',
  1881.                     display: 'inline-block',
  1882.                     paddingRight: '5px',
  1883.                     textShadow: '3px 3px 4px gray'}
  1884.             });
  1885.  
  1886.             toggle.append(button);
  1887.  
  1888.             var list = $('<ul/>', {
  1889.                 id: 'items-list-' + toggleName,
  1890.                 css: {display: 'none', paddingLeft: '20px'}
  1891.             });
  1892.  
  1893.             var disableall = $('<div/>', {
  1894.                 id: 'toggle-all-items-' + toggleName,
  1895.                 text: 'disable all',
  1896.                 css: {cursor: 'pointer',
  1897.                     display: 'inline-block',
  1898.                     textShadow: '3px 3px 4px gray',
  1899.                     marginRight: '8px'}
  1900.             });
  1901.  
  1902.             disableall.on('click', function () {
  1903.                 // can't use find as we only want one layer of checkboxes
  1904.                 var items = list.children().children(':checkbox');
  1905.                 items.prop('checked', false);
  1906.                 items.change();
  1907.                 list.children().children(':checkbox').change();
  1908.             });
  1909.  
  1910.             list.append(disableall);
  1911.  
  1912.             var enableall = $('<div/>', {
  1913.                 id: 'toggle-all-items-' + toggleName,
  1914.                 text: 'enable all',
  1915.                 css: {cursor: 'pointer',
  1916.                     display: 'inline-block',
  1917.                     textShadow: '3px 3px 4px gray'}
  1918.             });
  1919.  
  1920.             enableall.on('click', function () {
  1921.                 // can't use find as we only want one layer of checkboxes
  1922.                 var items = list.children().children(':checkbox');
  1923.                 items.prop('checked', true);
  1924.                 items.change();
  1925.                 list.children().children(':checkbox').change();
  1926.             });
  1927.  
  1928.             list.append(enableall);
  1929.  
  1930.             // fill out list with toggle items
  1931.             for (var itemName in auto.items) {
  1932.                 if (toggleName === 'trade')
  1933.                     list.append(getTradeOption(itemName, auto.items[itemName]));
  1934.                 else if (toggleName === 'craft')
  1935.                     list.append(getCraftOption(itemName, auto.items[itemName]));
  1936.                 else
  1937.                     list.append(getOption(itemName, auto.items[itemName]));
  1938.             }
  1939.  
  1940.             button.on('click', function () {
  1941.                 list.toggle();
  1942.             });
  1943.  
  1944.             element.append(toggle, list);
  1945.  
  1946.             // Add resource controls for crafting, sort of a hack
  1947.             if (toggleName === 'craft') {
  1948.                 var resources = $('<div/>', {
  1949.                     id: 'toggle-resource-controls',
  1950.                     text: 'resources',
  1951.                     css: {cursor: 'pointer',
  1952.                         display: 'inline-block',
  1953.                         paddingRight: '5px',
  1954.                         textShadow: '3px 3px 4px gray'},
  1955.                 });
  1956.  
  1957.                 var resourcesList = getResourceOptions();
  1958.  
  1959.                 // When we click the items button, make sure we clear resources
  1960.                 button.on('click', function () {
  1961.                     resourcesList.toggle(false);
  1962.                 });
  1963.  
  1964.                 resources.on('click', function () {
  1965.                     list.toggle(false);
  1966.                     resourcesList.toggle();
  1967.                 });
  1968.  
  1969.                 toggle.prepend(resources);
  1970.  
  1971.                 element.append(resourcesList);
  1972.             }
  1973.  
  1974.         }
  1975.  
  1976.         if (auto.trigger) {
  1977.             var triggerButton = $('<div/>', {
  1978.                 id: 'trigger-' + toggleName,
  1979.                 text: 'trigger',
  1980.                 title: auto.trigger,
  1981.                 css: {cursor: 'pointer',
  1982.                     display: 'inline-block',
  1983.                     float: 'right',
  1984.                     paddingRight: '5px',
  1985.                     textShadow: '3px 3px 4px gray'}
  1986.             });
  1987.  
  1988.             triggerButton.on('click', function () {
  1989.                 var value;
  1990.                 if (text == 'Crypto'){value = window.prompt('Enter a new trigger value for ' + text + '. Corresponds to the amount of Relics needed before the exchange is made.', auto.trigger);}
  1991.                 else{value = window.prompt('Enter a new trigger value for ' + text + '. Should be in the range of 0 to 1.', auto.trigger);}
  1992.  
  1993.                 if (value !== null) {
  1994.                     auto.trigger = parseFloat(value);
  1995.                     saveToKittenStorage();
  1996.                     triggerButton[0].title = auto.trigger;
  1997.                 }
  1998.             });
  1999.  
  2000.             element.append(triggerButton);
  2001.         }
  2002.  
  2003.         return element;
  2004.     };
  2005.  
  2006.     var getTradeOption = function (name, option) {
  2007.         var element = getOption(name, option);
  2008.         element.css('borderBottom', '1px solid rgba(185, 185, 185, 0.7)');
  2009.  
  2010.         var button = $('<div/>', {
  2011.             id: 'toggle-seasons-' + name,
  2012.             text: 'seasons',
  2013.             css: {cursor: 'pointer',
  2014.                 display: 'inline-block',
  2015.                 float: 'right',
  2016.                 paddingRight: '5px',
  2017.                 textShadow: '3px 3px 4px gray'},
  2018.         });
  2019.  
  2020.         var list = $('<ul/>', {
  2021.             id: 'seasons-list-' + name,
  2022.             css: {display: 'none', paddingLeft: '20px'}
  2023.         });
  2024.  
  2025.         // fill out the list with seasons
  2026.         list.append(getSeason(name, 'spring', option));
  2027.         list.append(getSeason(name, 'summer', option));
  2028.         list.append(getSeason(name, 'autumn', option));
  2029.         list.append(getSeason(name, 'winter', option));
  2030.  
  2031.         button.on('click', function () {
  2032.             list.toggle();
  2033.         });
  2034.  
  2035.         element.append(button, list);
  2036.  
  2037.         return element;
  2038.     };
  2039.  
  2040.     var getSeason = function (name, season, option) {
  2041.         var element = $('<li/>');
  2042.  
  2043.         var label = $('<label/>', {
  2044.             'for': 'toggle-' + name + '-' + season,
  2045.             text: ucfirst(season)
  2046.         });
  2047.  
  2048.         var input = $('<input/>', {
  2049.             id: 'toggle-' + name + '-' + season,
  2050.             type: 'checkbox'
  2051.         }).data('option', option);
  2052.  
  2053.         if (option[season]) {
  2054.             input.prop('checked', true);
  2055.         }
  2056.  
  2057.         input.on('change', function () {
  2058.             if (input.is(':checked') && option[season] == false) {
  2059.                 option[season] = true;
  2060.                 message('Enabled trading with ' + ucfirst(name) + ' in the ' + ucfirst(season));
  2061.             } else if ((!input.is(':checked')) && option[season] == true) {
  2062.                 option[season] = false;
  2063.                 message('Disabled trading ' + ucfirst(name) + ' in the ' + ucfirst(season));
  2064.             }
  2065.             kittenStorage.items[input.attr('id')] = option[season];
  2066.             saveToKittenStorage();
  2067.         });
  2068.  
  2069.         element.append(input, label);
  2070.  
  2071.         return element;
  2072.     };
  2073.  
  2074.     var getOption = function (name, option) {
  2075.         var element = $('<li/>');
  2076.         var elementLabel = option.label || ucfirst(name);
  2077.  
  2078.         var label = $('<label/>', {
  2079.             'for': 'toggle-' + name,
  2080.             text: elementLabel,
  2081.             css: {display: 'inline-block', minWidth: '80px'}
  2082.         });
  2083.  
  2084.         var input = $('<input/>', {
  2085.             id: 'toggle-' + name,
  2086.             type: 'checkbox'
  2087.         }).data('option', option);
  2088.  
  2089.         if (option.enabled) {
  2090.             input.prop('checked', true);
  2091.         }
  2092.  
  2093.         input.on('change', function () {
  2094.             if (input.is(':checked') && option.enabled == false) {
  2095.                 option.enabled = true;
  2096.                 message('Enabled Auto ' + elementLabel);
  2097.             } else if ((!input.is(':checked')) && option.enabled == true) {
  2098.                 option.enabled = false;
  2099.                 message('Disabled Auto ' + elementLabel);
  2100.             }
  2101.             kittenStorage.items[input.attr('id')] = option.enabled;
  2102.             saveToKittenStorage();
  2103.         });
  2104.  
  2105.         element.append(input, label);
  2106.  
  2107.         return element;
  2108.     };
  2109.  
  2110.     var getCraftOption = function (name, option) {
  2111.         var element = getOption(name, option);
  2112.  
  2113.         var label = $('<label/>', {
  2114.             'for': 'toggle-limited-' + name,
  2115.             text: 'Limited'
  2116.         });
  2117.  
  2118.         var input = $('<input/>', {
  2119.             id: 'toggle-limited-' + name,
  2120.             type: 'checkbox'
  2121.         }).data('option', option);
  2122.  
  2123.         if (option.limited) {
  2124.             input.prop('checked', true);
  2125.         }
  2126.  
  2127.         input.on('change', function () {
  2128.             if (input.is(':checked') && option.limited == false) {
  2129.                 option.limited = true;
  2130.                 message('Crafting ' + ucfirst(name) + ': limited to be proportional to cost ratio');
  2131.             } else if ((!input.is(':checked')) && option.limited == true) {
  2132.                 option.limited = false;
  2133.                 message('Crafting ' + ucfirst(name) + ': unlimited');
  2134.             }
  2135.             kittenStorage.items[input.attr('id')] = option.limited;
  2136.             saveToKittenStorage();
  2137.         });
  2138.  
  2139.         element.append(input, label);
  2140.  
  2141.         return element;
  2142.     };
  2143.  
  2144.     // Grab button labels for religion options
  2145.     var religionManager = new ReligionManager();
  2146.     for (var buildOption in options.auto.faith.items) {
  2147.         var buildItem = options.auto.faith.items[buildOption];
  2148.         var build = religionManager.getBuild(buildItem.name || buildOption, buildItem.variant);
  2149.         if (build) {
  2150.             options.auto.faith.items[buildOption].label = build.label;
  2151.         }
  2152.     }
  2153.  
  2154.     // Grab button labels for time options
  2155.     var timeManager = new TimeManager();
  2156.     for (var buildOption in options.auto.time.items) {
  2157.         var buildItem = options.auto.time.items[buildOption];
  2158.         var build = timeManager.getBuild(buildItem.name || buildOption, buildItem.variant);
  2159.         if (build) {
  2160.             options.auto.time.items[buildOption].label = build.label;
  2161.         }
  2162.     }
  2163.  
  2164.     // Grab button labels for build options
  2165.     var buildManager = new BuildManager();
  2166.     for (var buildOption in options.auto.build.items) {
  2167.         var buildItem = options.auto.build.items[buildOption];
  2168.         var build = buildManager.getBuild(buildItem.name || buildOption);
  2169.         if (build) {
  2170.             if ("stage" in buildItem) {
  2171.                 options.auto.build.items[buildOption].label = build.meta.stages[buildItem.stage].label;
  2172.             } else {
  2173.                 options.auto.build.items[buildOption].label = build.meta.label;
  2174.             }
  2175.         }
  2176.     }
  2177.  
  2178.     // Grab button labels for space options
  2179.     var spaceManager = new SpaceManager();
  2180.     for (var spaceOption in options.auto.space.items) {
  2181.         var build = spaceManager.getBuild(spaceOption);
  2182.         if (build) {
  2183.             // It's changed to label in 1.3.0.0
  2184.             var title = build.title ? build.title : build.label;
  2185.             options.auto.space.items[spaceOption].label = title;
  2186.         }
  2187.     }
  2188.  
  2189.     var optionsElement = $('<div/>', {id: 'ks-options', css: {marginBottom: '10px'}});
  2190.     var optionsListElement = $('<ul/>');
  2191.     var optionsTitleElement = $('<div/>', {
  2192.         css: { bottomBorder: '1px solid gray', marginBottom: '5px' },
  2193.         text: version
  2194.     });
  2195.  
  2196.     optionsElement.append(optionsTitleElement);
  2197.  
  2198.     optionsListElement.append(getToggle('engine',   'Enable Scientists'));
  2199.     optionsListElement.append(getToggle('build',    'Building'));
  2200.     optionsListElement.append(getToggle('space',    'Space'));
  2201.     optionsListElement.append(getToggle('craft',    'Crafting'));
  2202.     optionsListElement.append(getToggle('upgrade',  'Upgrading'));
  2203.     optionsListElement.append(getToggle('trade',    'Trading'));
  2204.     optionsListElement.append(getToggle('hunt',     'Hunting'));
  2205.     optionsListElement.append(getToggle('faith',    'Religion'));
  2206.     optionsListElement.append(getToggle('time',     'Time'));
  2207.     optionsListElement.append(getToggle('festival', 'Festival'));
  2208.     optionsListElement.append(getToggle('crypto',   'Crypto'));
  2209.     optionsListElement.append(getToggle('autofeed',   'Autofeed'));
  2210.     optionsListElement.append(getToggle('explore',  'Explore'));
  2211.  
  2212.     // add activity button
  2213.     // ===================
  2214.  
  2215.     activitySummary = {};
  2216.     var resetActivitySummary = function () {
  2217.         activitySummary = {
  2218.             lastyear: game.calendar.year,
  2219.             lastday:  game.calendar.day,
  2220.             craft:    {},
  2221.             trade:    {},
  2222.             build:    {},
  2223.             other:    {}
  2224.         };
  2225.     };
  2226.  
  2227.     var storeForSummary = function (name, amount, section) {
  2228.         if (amount === undefined) amount = 1;
  2229.         if (section === undefined) section = 'other';
  2230.  
  2231.         if (activitySummary[section] === undefined)
  2232.             activitySummary[section] = {};
  2233.  
  2234.         if (activitySummary[section][name] === undefined) {
  2235.             activitySummary[section][name] = parseInt(amount, 10);
  2236.         } else {
  2237.             activitySummary[section][name] += parseInt(amount, 10);
  2238.         }
  2239.     };
  2240.  
  2241.     var displayActivitySummary = function () {
  2242.         // Festivals
  2243.         if (activitySummary.other.festival) {
  2244.             summary('Held ' + game.getDisplayValueExt(activitySummary.other.festival) + ' festivals');
  2245.         }
  2246.  
  2247.         // Observe stars
  2248.         if (activitySummary.other.stars) {
  2249.             summary('Observed ' + game.getDisplayValueExt(activitySummary.other.stars) + ' stars');
  2250.         }
  2251.  
  2252.         // Praise the Sun
  2253.         if (activitySummary.other.faith) {
  2254.             summary('Accumulated ' + game.getDisplayValueExt(activitySummary.other.faith) + ' by praising the sun');
  2255.         }
  2256.  
  2257.         // Hunters
  2258.         if (activitySummary.other.hunt) {
  2259.             summary('Sent ' + game.getDisplayValueExt(activitySummary.other.hunt) + ' adorable kitten hunter' + (activitySummary.other.hunt == 1 ? '' : 's'));
  2260.         }
  2261.  
  2262.         // Buildings
  2263.         for (var name in activitySummary.build) {
  2264.             summary('Built: +' + game.getDisplayValueExt(activitySummary.build[name]) + ' ' + ucfirst(name));
  2265.         }
  2266.  
  2267.         // Crafts
  2268.         for (var name in activitySummary.craft) {
  2269.             summary('Crafted: +' + game.getDisplayValueExt(activitySummary.craft[name]) + ' ' + ucfirst(name));
  2270.         }
  2271.  
  2272.         // Trading
  2273.         for (var name in activitySummary.trade) {
  2274.             summary('Traded: ' + game.getDisplayValueExt(activitySummary.trade[name]) + 'x ' + ucfirst(name));
  2275.         }
  2276.  
  2277.         // Show time since last run. Assumes that the day and year are always higher.
  2278.         if (activitySummary.lastyear && activitySummary.lastday) {
  2279.             var years = game.calendar.year - activitySummary.lastyear;
  2280.             var days = game.calendar.day - activitySummary.lastday;
  2281.  
  2282.             if (days < 0) {
  2283.                 years -= 1;
  2284.                 days += 400;
  2285.             }
  2286.  
  2287.             var duration = '';
  2288.             if (years > 0) {
  2289.                 duration += years + ' ';
  2290.                 duration += (years == 1) ? 'year' : 'years';
  2291.             }
  2292.  
  2293.             if (days >= 0) {
  2294.                 if (years > 0) duration += ' and ';
  2295.                 duration += roundToTwo(days) + ' ';
  2296.                 duration += (days == 1) ? 'day' : 'days';
  2297.             }
  2298.  
  2299.             summary('Summary of the last ' + duration);
  2300.         }
  2301.  
  2302.         // Clear out the old activity
  2303.         resetActivitySummary()
  2304.     };
  2305.  
  2306.     resetActivitySummary();
  2307.  
  2308.     var activityBox = $('<div/>', {
  2309.         id: 'activity-box',
  2310.         css: {
  2311.             display: 'inline-block',
  2312.             float: 'right',
  2313.             verticalAlign: 'top'
  2314.         }
  2315.     });
  2316.  
  2317.     var showActivity = $('<a/>', {
  2318.         id: 'showActivityHref',
  2319.         text: 'Show activity',
  2320.         href: '#',
  2321.         css: {
  2322.             verticalAlign: 'top'
  2323.         }
  2324.     });
  2325.  
  2326.     var activityCheckbox = $('<input/>', {
  2327.         id: 'enable-activity',
  2328.         type: 'checkbox',
  2329.         css: {
  2330.             verticalAlign: 'top'
  2331.         }
  2332.     });
  2333.  
  2334.     var activityLabel = $('<label/>', {
  2335.         for: 'enable-activity'
  2336.     });
  2337.  
  2338.     if (options.showactivity)
  2339.         activityCheckbox.prop('checked', true);
  2340.  
  2341.     activityCheckbox.on('change', function () {
  2342.         if (activityCheckbox.is(':checked') && options.showactivity == false) {
  2343.             options.showactivity = true;
  2344.             message('Showing Kitten Scientists activity live');
  2345.         } else if (activityCheckbox.not(':checked') && options.showactivity == true) {
  2346.             options.showactivity = false;
  2347.             message('Hiding updates of Kitten Scientists activity');
  2348.         }
  2349.     });
  2350.  
  2351.     showActivity.on('click', displayActivitySummary);
  2352.  
  2353.     activityBox.append(activityCheckbox, activityLabel, showActivity);
  2354.  
  2355.     $('#clearLog').append(activityBox);
  2356.  
  2357.     // Donation Button
  2358.     // ===============
  2359.  
  2360.     var donate = $('<li/>', {id: "ks-donate"}).append($('<a/>', {
  2361.         href: 'bitcoin:' + address + '?amount=0.005&label=Kittens Donation',
  2362.         target: '_blank',
  2363.         text: address
  2364.     })).prepend($('<img/>', {
  2365.         css: {
  2366.             height: '15px',
  2367.             width: '15px',
  2368.             padding: '3px 4px 0 4px',
  2369.             verticalAlign: 'bottom'
  2370.         },
  2371.         src: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgdmVyc2lvbj0iMS4xIgogICB3aWR0aD0iNTEycHgiCiAgIGhlaWdodD0iNTEycHgiCiAgIHZpZXdCb3g9IjAgMCAxIDEiCiAgIHByZXNlcnZlQXNwZWN0UmF0aW89InhNaWRZTWlkIgogICBpZD0ic3ZnMiIKICAgaW5rc2NhcGU6dmVyc2lvbj0iMC40OC4yIHI5ODE5IgogICBzb2RpcG9kaTpkb2NuYW1lPSJiaXRjb2luLWxvZ28tbm9zaGFkb3cuc3ZnIj4KICA8bWV0YWRhdGEKICAgICBpZD0ibWV0YWRhdGEyMiI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgICA8ZGM6dGl0bGU+PC9kYzp0aXRsZT4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxIgogICAgIG9iamVjdHRvbGVyYW5jZT0iMTAiCiAgICAgZ3JpZHRvbGVyYW5jZT0iMTAiCiAgICAgZ3VpZGV0b2xlcmFuY2U9IjEwIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwIgogICAgIGlua3NjYXBlOnBhZ2VzaGFkb3c9IjIiCiAgICAgaW5rc2NhcGU6d2luZG93LXdpZHRoPSIxNDQ3IgogICAgIGlua3NjYXBlOndpbmRvdy1oZWlnaHQ9Ijg2MSIKICAgICBpZD0ibmFtZWR2aWV3MjAiCiAgICAgc2hvd2dyaWQ9ImZhbHNlIgogICAgIGlua3NjYXBlOnpvb209IjAuOTIxODc1IgogICAgIGlua3NjYXBlOmN4PSIyMTIuNTE0MzciCiAgICAgaW5rc2NhcGU6Y3k9IjIzMy4yNDYxNyIKICAgICBpbmtzY2FwZTp3aW5kb3cteD0iMCIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iMCIKICAgICBpbmtzY2FwZTp3aW5kb3ctbWF4aW1pemVkPSIwIgogICAgIGlua3NjYXBlOmN1cnJlbnQtbGF5ZXI9InN2ZzIiIC8+CiAgPCEtLSBBbmRyb2lkIGxhdW5jaGVyIGljb25zOiB2aWV3Qm94PSItMC4wNDUgLTAuMDQ1IDEuMDkgMS4wOSIgLS0+CiAgPGRlZnMKICAgICBpZD0iZGVmczQiPgogICAgPGZpbHRlcgogICAgICAgaWQ9Il9kcm9wLXNoYWRvdyIKICAgICAgIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiI+CiAgICAgIDxmZUdhdXNzaWFuQmx1cgogICAgICAgICBpbj0iU291cmNlQWxwaGEiCiAgICAgICAgIHJlc3VsdD0iYmx1ci1vdXQiCiAgICAgICAgIHN0ZERldmlhdGlvbj0iMSIKICAgICAgICAgaWQ9ImZlR2F1c3NpYW5CbHVyNyIgLz4KICAgICAgPGZlQmxlbmQKICAgICAgICAgaW49IlNvdXJjZUdyYXBoaWMiCiAgICAgICAgIGluMj0iYmx1ci1vdXQiCiAgICAgICAgIG1vZGU9Im5vcm1hbCIKICAgICAgICAgaWQ9ImZlQmxlbmQ5IiAvPgogICAgPC9maWx0ZXI+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJjb2luLWdyYWRpZW50IgogICAgICAgeDE9IjAlIgogICAgICAgeTE9IjAlIgogICAgICAgeDI9IjAlIgogICAgICAgeTI9IjEwMCUiPgogICAgICA8c3RvcAogICAgICAgICBvZmZzZXQ9IjAlIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZjlhYTRiIgogICAgICAgICBpZD0ic3RvcDEyIiAvPgogICAgICA8c3RvcAogICAgICAgICBvZmZzZXQ9IjEwMCUiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNmNzkzMWEiCiAgICAgICAgIGlkPSJzdG9wMTQiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogIDwvZGVmcz4KICA8ZwogICAgIHRyYW5zZm9ybT0ic2NhbGUoMC4wMTU2MjUpIgogICAgIGlkPSJnMTYiPgogICAgPHBhdGgKICAgICAgIGlkPSJjb2luIgogICAgICAgZD0ibSA2My4wMzU5LDM5Ljc0MSBjIC00LjI3NCwxNy4xNDMgLTIxLjYzNywyNy41NzYgLTM4Ljc4MiwyMy4zMDEgLTE3LjEzOCwtNC4yNzQgLTI3LjU3MSwtMjEuNjM4IC0yMy4yOTUsLTM4Ljc4IDQuMjcyLC0xNy4xNDUgMjEuNjM1LC0yNy41NzkgMzguNzc1LC0yMy4zMDUgMTcuMTQ0LDQuMjc0IDI3LjU3NiwyMS42NCAyMy4zMDIsMzguNzg0IHoiCiAgICAgICBzdHlsZT0iZmlsbDp1cmwoI2NvaW4tZ3JhZGllbnQpIiAvPgogICAgPHBhdGgKICAgICAgIGlkPSJzeW1ib2wiCiAgICAgICBkPSJtIDQ2LjEwMDksMjcuNDQxIGMgMC42MzcsLTQuMjU4IC0yLjYwNSwtNi41NDcgLTcuMDM4LC04LjA3NCBsIDEuNDM4LC01Ljc2OCAtMy41MTEsLTAuODc1IC0xLjQsNS42MTYgYyAtMC45MjMsLTAuMjMgLTEuODcxLC0wLjQ0NyAtMi44MTMsLTAuNjYyIGwgMS40MSwtNS42NTMgLTMuNTA5LC0wLjg3NSAtMS40MzksNS43NjYgYyAtMC43NjQsLTAuMTc0IC0xLjUxNCwtMC4zNDYgLTIuMjQyLC0wLjUyNyBsIDAuMDA0LC0wLjAxOCAtNC44NDIsLTEuMjA5IC0wLjkzNCwzLjc1IGMgMCwwIDIuNjA1LDAuNTk3IDIuNTUsMC42MzQgMS40MjIsMC4zNTUgMS42NzksMS4yOTYgMS42MzYsMi4wNDIgbCAtMS42MzgsNi41NzEgYyAwLjA5OCwwLjAyNSAwLjIyNSwwLjA2MSAwLjM2NSwwLjExNyAtMC4xMTcsLTAuMDI5IC0wLjI0MiwtMC4wNjEgLTAuMzcxLC0wLjA5MiBsIC0yLjI5Niw5LjIwNSBjIC0wLjE3NCwwLjQzMiAtMC42MTUsMS4wOCAtMS42MDksMC44MzQgMC4wMzUsMC4wNTEgLTIuNTUyLC0wLjYzNyAtMi41NTIsLTAuNjM3IGwgLTEuNzQzLDQuMDE5IDQuNTY5LDEuMTM5IGMgMC44NSwwLjIxMyAxLjY4MywwLjQzNiAyLjUwMywwLjY0NiBsIC0xLjQ1Myw1LjgzNCAzLjUwNywwLjg3NSAxLjQzOSwtNS43NzIgYyAwLjk1OCwwLjI2IDEuODg4LDAuNSAyLjc5OCwwLjcyNiBsIC0xLjQzNCw1Ljc0NSAzLjUxMSwwLjg3NSAxLjQ1MywtNS44MjMgYyA1Ljk4NywxLjEzMyAxMC40ODksMC42NzYgMTIuMzg0LC00LjczOSAxLjUyNywtNC4zNiAtMC4wNzYsLTYuODc1IC0zLjIyNiwtOC41MTUgMi4yOTQsLTAuNTI5IDQuMDIyLC0yLjAzOCA0LjQ4MywtNS4xNTUgeiBtIC04LjAyMiwxMS4yNDkgYyAtMS4wODUsNC4zNiAtOC40MjYsMi4wMDMgLTEwLjgwNiwxLjQxMiBsIDEuOTI4LC03LjcyOSBjIDIuMzgsMC41OTQgMTAuMDEyLDEuNzcgOC44NzgsNi4zMTcgeiBtIDEuMDg2LC0xMS4zMTIgYyAtMC45OSwzLjk2NiAtNy4xLDEuOTUxIC05LjA4MiwxLjQ1NyBsIDEuNzQ4LC03LjAxIGMgMS45ODIsMC40OTQgOC4zNjUsMS40MTYgNy4zMzQsNS41NTMgeiIKICAgICAgIHN0eWxlPSJmaWxsOiNmZmZmZmYiIC8+CiAgPC9nPgo8L3N2Zz4='
  2372.     }));
  2373.  
  2374.     // Add some padding above the donation item
  2375.     donate.css('padding', '5px');
  2376.  
  2377.     optionsListElement.append(donate);
  2378.  
  2379.     // add the options above the game log
  2380.     right.prepend(optionsElement.append(optionsListElement));
  2381.  
  2382.     // Initialize and set toggles for Engine
  2383.     // =====================================
  2384.  
  2385.     var engine = new Engine();
  2386.     var toggleEngine = $('#toggle-engine');
  2387.  
  2388.     toggleEngine.on('change', function () {
  2389.         if (toggleEngine.is(':checked')) {
  2390.             engine.start();
  2391.         } else {
  2392.             engine.stop();
  2393.         }
  2394.     });
  2395.  
  2396.     loadFromKittenStorage();
  2397.  
  2398.     if (console && console.log) console.log(version + " loaded");
  2399.     game._publish("kitten_scientists/ready", version);
  2400.  
  2401. }
  2402.  
  2403. var loadTest = function() {
  2404.     if (typeof gamePage === 'undefined') {
  2405.         // Test if kittens game is already loaded or wait 2s and try again
  2406.         setTimeout(function(){
  2407.             loadTest();
  2408.         }, 2000);
  2409.     } else {
  2410.         // Kittens loaded, run Kitten Scientist's Automation Engine
  2411.         game = gamePage;
  2412.         run();
  2413.     }
  2414. }
  2415.  
  2416. loadTest();
Add Comment
Please, Sign In to add comment