YudaiNao

Incremental Zoo II Bot

Feb 28th, 2014
1,875
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name        Incremental Zoo II Bot
  3. // @namespace   YudaiNao-2aefd016eda566ee92d6911705ece4ca
  4. // @description Automates portions of the game; last updated at game version 0.44
  5. // @include     http://magicaweb.com/zooII/
  6. // @include     http://magicaweb.com/zooII/#
  7. // @version     2.0
  8. // @grant       none
  9. // ==/UserScript==
  10.  
  11. var Zookeeper = function ()
  12. {
  13.     var self = this;
  14.    
  15.     self.load = function ()
  16.     {
  17.         if (typeof unsafeWindow == 'undefined')
  18.         {
  19.             self.state = IZ.Zoo;
  20.             self.commands = IZ;
  21.         }
  22.         else
  23.         {
  24.             self.state = unsafeWindow.IZ.Zoo;
  25.             self.commands = unsafeWindow.IZ;
  26.         }
  27.        
  28.         createInterfacePanel();
  29.  
  30.         for (var rName in routines)
  31.         {
  32.             if (!routines.hasOwnProperty(rName))
  33.                 continue;
  34.            
  35.             if (routines[rName].obj.load)
  36.                 routines[rName].obj.load();
  37.         }
  38.  
  39.         self.tick();
  40.         intervalObj = setInterval(self.tick, options.updateInterval);
  41.     }
  42.     self.tick = function ()
  43.     {
  44.         for (var rName in routines)
  45.         {
  46.             if (!routines.hasOwnProperty(rName))
  47.                 continue;
  48.            
  49.             if (routines[rName].enabled)
  50.                 routines[rName].obj.tick();
  51.         }
  52.     }
  53.     self.register = function (routine)
  54.     {
  55.         var rName = routine.getName();
  56.         routines[rName] = {
  57.             obj : routine,
  58.             enabled : false
  59.         };
  60.     }
  61.     self.toggleHandler = function (event)
  62.     {
  63.         var regs = event.target.name.match(/routine_(.+)_toggle/);
  64.         if (!regs)
  65.             return;
  66.  
  67.         var routineName = regs[1];
  68.         var newState = (event.target.value == 'on' ? true : false);
  69.         routines[routineName].enabled = newState;
  70.     }
  71.     self.createElem = function (tagName, opts)
  72.     {
  73.         //(opts) = { properties : {...}, style : {...}, text : str, parent : elem }
  74.        
  75.         var elem = document.createElement(tagName);
  76.         if (opts)
  77.         {
  78.             if (opts.properties)
  79.             {
  80.                 for (var pKey in opts.properties)
  81.                 {
  82.                     if (opts.properties.hasOwnProperty(pKey))
  83.                         elem.setAttribute(pKey, opts.properties[pKey]);
  84.                 }
  85.             }
  86.             if (opts.style)
  87.             {
  88.                 for (var sKey in opts.style)
  89.                 {
  90.                     if (opts.style.hasOwnProperty(sKey))
  91.                         elem.style[sKey] = opts.style[sKey];
  92.                 }
  93.             }
  94.             if (opts.text !== undefined)
  95.             {
  96.                 var textNode = document.createTextNode(opts.text);
  97.                 elem.appendChild(textNode);
  98.             }
  99.             if (opts.parent)
  100.                 opts.parent.appendChild(elem);
  101.         }
  102.         return elem;
  103.     }
  104.     self.getRoutine = function (rName)
  105.     {
  106.         if (routines[rName])
  107.             return routines[rName].obj;
  108.     }
  109.     self.isRoutineEnabled = function (rName)
  110.     {
  111.         return routines[rName].enabled;
  112.     }
  113.     self.getAnimalName = function (animal)
  114.     {
  115.         if (typeof animal == 'number')
  116.             return self.commands.animals[animal].name;
  117.     }
  118.     self.alterValue = function (value, alteration)
  119.     {
  120.         if (typeof alteration == 'string')
  121.         {
  122.             var regs = alteration.match(/^([^0-9])([0-9.]+)$/);
  123.             if (!regs)
  124.                 return;
  125.            
  126.             var operator = regs[1];
  127.             var num = regs[2];
  128.             if (operator == '+' || operator == '-')
  129.                 return value + parseFloat(num);
  130.             else if (operator == '*')
  131.                 return value * parseFloat(num);
  132.             else if (operator == '/')
  133.                 return value / parseFloat(num);
  134.         }
  135.         else if (typeof alteration == 'number')
  136.             return alteration;
  137.     }
  138.     self.formatNumber = function (rawNum, decimalPad)
  139.     {
  140.         var magnitude = Math.log(Math.abs(rawNum)) / Math.LN10;
  141.         if (magnitude % 1 > 0.99)
  142.             magnitude = Math.round(magnitude);
  143.        
  144.         var unit = '';
  145.         var power = 0;
  146.         if (magnitude >= 3)
  147.         {
  148.             for (var _i = 3; _i < 100; _i += 3)
  149.             {
  150.                 if (!numericUnits[_i])
  151.                     break;
  152.                
  153.                 if (magnitude < _i + 3)
  154.                 {
  155.                     unit = numericUnits[_i];
  156.                     power = _i;
  157.                     break;
  158.                 }
  159.             }
  160.         }
  161.        
  162.         var formatNumber = Math.round(rawNum / Math.pow(10, power) * 100) / 100;
  163.         formatNumber = formatNumber.toString();
  164.         if (decimalPad)
  165.         {
  166.             if (formatNumber.indexOf('.') == -1)
  167.                 formatNumber += '.00';
  168.             else if (formatNumber.indexOf('.') == formatNumber.length - 2)
  169.                 formatNumber += '0';
  170.         }
  171.         return formatNumber + unit;
  172.     }
  173.     self.unformatNumber = function (formatNum)
  174.     {
  175.         var regs = formatNum.match(/^([0-9-,.]+)([A-Za-z]+)?$/)
  176.         if (!regs)
  177.             return;
  178.        
  179.         var num = parseFloat(regs[1].replace(/,/, ''));
  180.         var unit = regs[2];
  181.         if (!unit)
  182.             return num;
  183.        
  184.         var power = numericUnits[unit];
  185.         if (!power)
  186.             return;
  187.         return num * Math.pow(10, power);
  188.     }
  189.    
  190.     var createInterfacePanel = function ()
  191.     {
  192.         var panel = self.createElem(
  193.             'div',
  194.             {
  195.                 style : {
  196.                     position : 'absolute',
  197.                     top : '1em',
  198.                     right : '1em',
  199.                     zIndex : 1000,
  200.                     fontFamily : 'Trebuchet, Arial, sans-serif',
  201.                     lineHeight : '150%'
  202.                 },
  203.                 parent : document.querySelector('body > div.container')
  204.             }
  205.         );
  206.        
  207.         for (var rName in routines)
  208.         {
  209.             if (!routines.hasOwnProperty(rName))
  210.                 continue;
  211.            
  212.             var container = self.createElem(
  213.                 'div',
  214.                 {
  215.                     style : {
  216.                         marginBottom : '0.5em',
  217.                         border : '1px solid ' + options.borderColor,
  218.                         backgroundColor : 'white'
  219.                     },
  220.                     parent : panel
  221.                 }
  222.             );
  223.             self.createElem(
  224.                 'div',
  225.                 {
  226.                     text : rName,
  227.                     style : {
  228.                         textAlign : 'center',
  229.                         color : '#444488',
  230.                         backgroundColor : options.borderColor,
  231.                         fontFamily : 'Georgia, serif',
  232.                         fontWeight : 'bold'
  233.                     },
  234.                     parent : container
  235.                 }
  236.             );
  237.            
  238.             var onLabel = self.createElem(
  239.                 'label',
  240.                 {
  241.                     text : 'On',
  242.                     style : {
  243.                         backgroundColor : '#D0F0D0',
  244.                         padding : '0.25em 0.5em 0.25em 0',
  245.                         borderColor : options.borderColor,
  246.                         borderStyle : 'solid',
  247.                         borderWidth : '2px 0 2px 2px'
  248.                     }
  249.                 }
  250.             );
  251.             var onRadio = self.createElem(
  252.                 'input',
  253.                 {
  254.                     properties : {
  255.                         type : 'radio',
  256.                         name : 'routine_' + rName + '_toggle',
  257.                         value : 'on'
  258.                     }
  259.                 }
  260.             );
  261.             onRadio.addEventListener('change', self.toggleHandler);
  262.             onLabel.insertBefore(onRadio, onLabel.childNodes[0]);
  263.            
  264.             var offLabel = self.createElem(
  265.                 'label',
  266.                 {
  267.                     text : 'Off',
  268.                     style : {
  269.                         backgroundColor : '#F0D0D0',
  270.                         padding : '0.25em 0 0.25em 0.5em',
  271.                         borderColor : options.borderColor,
  272.                         borderStyle : 'solid',
  273.                         borderWidth : '2px 2px 2px 0'
  274.                     }
  275.                 }
  276.             );
  277.             var offRadio = self.createElem(
  278.                 'input',
  279.                 {
  280.                     properties : {
  281.                         type : 'radio',
  282.                         name : 'routine_' + rName + '_toggle',
  283.                         value : 'off',
  284.                         checked : 'checked'
  285.                     }
  286.                 }
  287.             );
  288.             offRadio.addEventListener('change', self.toggleHandler);
  289.             offLabel.appendChild(offRadio);
  290.            
  291.             var toggle = self.createElem(
  292.                 'div',
  293.                 {
  294.                     style : {
  295.                         textAlign : 'center',
  296.                         fontSize : '10pt'
  297.                     },
  298.                     parent : container
  299.                 }
  300.             );
  301.             toggle.appendChild(onLabel);
  302.             toggle.appendChild(offLabel);
  303.            
  304.             var opts = routines[rName].obj.createOptionsDOM();
  305.             if (opts)
  306.             {
  307.                 opts.style.fontSize = '10pt';
  308.                 opts.style.margin = '0.5em';
  309.                 container.appendChild(opts);
  310.             }
  311.         }
  312.     }
  313.    
  314.     var routines = {};
  315.     var options = {
  316.         updateInterval : 1000, //ms
  317.         borderColor : '#CCCCCC'
  318.     };
  319.     var intervalObj;
  320.     var numericUnits = {
  321.         'k' : 3, 3 : 'k',
  322.         'm' : 6, 6 : 'm',
  323.         'b' : 9, 9 : 'b',
  324.         't' : 12, 12 : 't',
  325.         'qa' : 15, 15 : 'qa',
  326.         'qi' : 18, 18 : 'qi'
  327.     };
  328. }
  329.  
  330. var SellerRoutine = function (controller)
  331. {
  332.     var self = this;
  333.    
  334.     self.load = function ()
  335.     {
  336.         updateDetails();
  337.     }
  338.     self.tick = function ()
  339.     {
  340.         checkAnimalPopulations();
  341.         checkTrade();
  342.         updateDetails();
  343.     }
  344.     self.getName = function ()
  345.     {
  346.         return routineName;
  347.     }
  348.     self.createOptionsDOM = function ()
  349.     {
  350.         var container = controller.createElem('div');
  351.        
  352.         var simpleRadios = {
  353.             hybrid : { label : 'Hybrid strategy', title : 'Minimal for newest ' + options.hybridThreshold + ' species, optimal for older ones', checked : true },
  354.             minimal : { label : 'Minimal population', title : '100 Meerkets, 200 Peacocks, 300 Chameleons, etc' },
  355.             optimal : { label : 'Optimal population', title : 'Maximizes growth rate' }
  356.         };
  357.        
  358.         for (var key in simpleRadios)
  359.         {
  360.             var radioDiv = controller.createElem('div', { parent : container });
  361.             var radioLabel = controller.createElem('label', { parent : radioDiv });
  362.             var radioInput = controller.createElem(
  363.                 'input',
  364.                 {
  365.                     properties : {
  366.                         type : 'radio',
  367.                         name : 'seller_strategy',
  368.                         value : key
  369.                     },
  370.                     parent: radioLabel
  371.                 }
  372.             );
  373.             var radioSpan = controller.createElem(
  374.                 'span',
  375.                 {
  376.                     text : simpleRadios[key].label,
  377.                     parent : radioLabel
  378.                 }
  379.             );
  380.             if (simpleRadios[key].title)
  381.             {
  382.                 radioSpan.setAttribute('title', simpleRadios[key].title);
  383.                 radioSpan.style.cursor = 'help';
  384.             }
  385.             if (simpleRadios[key].checked)
  386.                 radioInput.setAttribute('checked', 'checked');
  387.            
  388.             radioInput.addEventListener('change', self.optionsHandler);
  389.         }
  390.        
  391.         var fixedDiv = controller.createElem('div', { parent : container });
  392.         var fixedLabel = controller.createElem('label', { parent : fixedDiv });
  393.         var fixedRadio = controller.createElem(
  394.             'input',
  395.             {
  396.                 properties : {
  397.                     type : 'radio',
  398.                     name : 'seller_strategy',
  399.                     value : 'fixed'
  400.                 },
  401.                 parent : fixedLabel
  402.             }
  403.         );
  404.         fixedRadio.addEventListener('change', self.optionsHandler);
  405.         controller.createElem('span', { text : 'Keep ', parent : fixedLabel });
  406.         var fixedInput = controller.createElem(
  407.             'input',
  408.             {
  409.                 properties : {
  410.                     type : 'text',
  411.                     name : 'seller_fixedpop',
  412.                     size : 3,
  413.                     value : options.fixedPop,
  414.                     title : '1500, 10k, 1.2m, 5b, etc'
  415.                 },
  416.                 style : { cursor : 'help' },
  417.                 parent : fixedDiv
  418.             }
  419.         );
  420.         fixedInput.addEventListener('change', self.optionsHandler);
  421.         controller.createElem('span', { text : ' population', parent : fixedDiv });
  422.        
  423.         var tradeDiv = controller.createElem('div', { parent : container });
  424.         var tradeLabel = controller.createElem('label', { parent : tradeDiv });
  425.         var tradeInput = controller.createElem(
  426.             'input',
  427.             {
  428.                 properties : {
  429.                     type : 'checkbox',
  430.                     name : 'seller_trade',
  431.                     value : key,
  432.                     checked : 'checked'
  433.                 },
  434.                 parent: tradeLabel
  435.             }
  436.         );
  437.         var tradeSpan = controller.createElem(
  438.             'span',
  439.             {
  440.                 text : 'Accept profitable trades',
  441.                 parent : tradeLabel
  442.             }
  443.         );
  444.         tradeInput.addEventListener('change', self.optionsHandler);
  445.        
  446.         var detailsContainer = controller.createElem(
  447.             'div',
  448.             {
  449.                 style : {
  450.                     textAlign : 'center',
  451.                     marginTop : '0.5em'
  452.                 },
  453.                 parent : container
  454.             }
  455.         );
  456.         var detailsLink = controller.createElem(
  457.             'span',
  458.             {
  459.                 text : 'Details',
  460.                 style : {
  461.                     border : '1px dashed #CCCCCC',
  462.                     padding : '0.25em 0.5em',
  463.                     cursor : 'pointer'
  464.                 },
  465.                 parent : detailsContainer
  466.             }
  467.         );
  468.         detailsLink.addEventListener('click', self.toggleDetails);
  469.         detailsElems.container = controller.createElem(
  470.             'div',
  471.             {
  472.                 style : {
  473.                     border : '1px dashed #CCCCCC',
  474.                     display : 'none',
  475.                     textAlign : 'left',
  476.                     padding : '0.25em 0.5em'
  477.                 },
  478.                 parent : detailsContainer
  479.             }
  480.         );
  481.         detailsElems.income = controller.createElem('div', { parent : detailsElems.container });
  482.         detailsElems.trade = controller.createElem('div', { parent : detailsElems.container });
  483.         detailsElems.popFloor = controller.createElem('div', { parent : detailsElems.container });
  484.  
  485.         return container;
  486.     }
  487.     self.optionsHandler = function (event)
  488.     {
  489.         if (event.target.name == 'seller_strategy')
  490.         {
  491.             var newStrategy = event.target.value;
  492.             options.strategy = newStrategy;
  493.             updateDetails();
  494.         }
  495.         else if (event.target.name == 'seller_fixedpop')
  496.         {
  497.             var newVal = controller.unformatNumber(event.target.value);
  498.             options.fixedPop = newVal;
  499.             updateDetails();
  500.         }
  501.         else if (event.target.name == 'seller_trade')
  502.         {
  503.             options.acceptTrades = event.target.checked;
  504.         }
  505.     }
  506.     self.getPopulationFloor = function (animal, strategy)
  507.     {
  508.         if (!strategy)
  509.             strategy = options.strategy;
  510.        
  511.         if (strategy == 'minimal')
  512.         {
  513.             return (animal + 1) * 100;
  514.         }
  515.         else if (strategy == 'optimal')
  516.         {
  517.             var calc = new AnimalCalc(animal, controller);
  518.             var lastGrowth;
  519.             for (var pop = 50; pop < 100000; pop += 50)
  520.             {
  521.                 calc.alter({ own : pop });
  522.                 if (lastGrowth && (calc.getValue('growth') < lastGrowth || lastGrowth / calc.getValue('growth') > 0.99))
  523.                     return pop - 50;
  524.                 lastGrowth = calc.getValue('growth');
  525.             }
  526.             return self.getPopulationFloor(animal, 'minimal'); //failsafe
  527.         }
  528.         else if (strategy == 'hybrid')
  529.         {
  530.             var newestAnimal;
  531.             for (var _i = 0, _len_i = controller.state.animalsN; _i < _len_i; _i++)
  532.             {
  533.                 var owned = controller.state.animals[_i].own;
  534.                 if (owned == 0)
  535.                     break;
  536.                 else
  537.                     newestAnimal = _i;
  538.             }
  539.            
  540.             if (newestAnimal - animal < options.hybridThreshold)
  541.                 return self.getPopulationFloor(animal, 'minimal');
  542.             else
  543.                 return self.getPopulationFloor(animal, 'optimal');
  544.         }
  545.         else if (strategy == 'fixed')
  546.         {
  547.             return options.fixedPop;
  548.         }
  549.     }
  550.     self.toggleDetails = function (event)
  551.     {
  552.         detailsElems.container.style.display = (detailsElems.container.style.display == 'none' ? 'block' : 'none');
  553.     }
  554.    
  555.     var checkAnimalPopulations = function ()
  556.     {
  557.         for (var _i = 0, _len_i = controller.state.animalsN; _i < _len_i; _i++)
  558.         {
  559.             var owned = controller.state.animals[_i].own;
  560.             if (owned == 0)
  561.                 break;
  562.            
  563.             var populationFloor = self.getPopulationFloor(_i);
  564.             var populationCeiling = controller.state.animals[_i].maxPop;
  565.            
  566.             if (owned - 100 >= populationFloor)
  567.                 sellAnimals(_i, 100, owned);
  568.             else if (owned - 10 >= populationFloor)
  569.                 sellAnimals(_i, 10, owned);
  570.             else if (owned + 10 >= populationCeiling)
  571.                 sellAnimals(_i, 10, owned);
  572.         }
  573.     }
  574.     var sellAnimals = function (animal, amount, owned)
  575.     {
  576.         if (owned - amount < options.hardFloor)
  577.             return;
  578.        
  579.         var elem = document.getElementById('sell' + amount + animal);
  580.         if (elem)
  581.             controller.commands.sell(elem, amount);
  582.     }
  583.     var checkTrade = function ()
  584.     {
  585.         if (!options.acceptTrades || !controller.state.offerActive)
  586.             return;
  587.        
  588.         var lossAnimal = controller.state.offerGiveSpecies;
  589.         var lossAmount = controller.state.offerGiveNumber;
  590.         var lossValue = controller.state.animals[lossAnimal].buyPrice * lossAmount;
  591.        
  592.         var gainAnimal = controller.state.offerGetSpecies;
  593.         var gainAmount = controller.state.offerGetNumber;
  594.         var gainValue = controller.state.animals[gainAnimal].buyPrice * gainAmount;
  595.        
  596.         if (lossValue > gainValue)
  597.             controller.commands.offerRejected();
  598.         else
  599.         {
  600.             controller.commands.offerAccepted();
  601.             lastTrade = { lossAnimal : lossAnimal, lossAmount : lossAmount, gainAnimal : gainAnimal, gainAmount : gainAmount, profit : gainValue - lossValue };
  602.         }
  603.     }
  604.     var updateDetails = function ()
  605.     {
  606.         for (var _i = 0, _len_i = controller.state.animalsN; _i < _len_i; _i++)
  607.         {
  608.             if (controller.state.animals[_i].own == 0)
  609.                 break;
  610.             if (!detailsElems.popFloor.childNodes[_i])
  611.                 controller.createElem('div', { parent : detailsElems.popFloor });
  612.             detailsElems.popFloor.childNodes[_i].textContent = controller.getAnimalName(_i) + ' - ' + controller.formatNumber(self.getPopulationFloor(_i));
  613.         }
  614.        
  615.         var calc = new IncomeCalc(controller);
  616.         detailsElems.income.textContent = 'Est. income: $' + controller.formatNumber(calc.getIncome(), true) + '/s';
  617.        
  618.         if (lastTrade)
  619.         {
  620.             detailsElems.trade.textContent = 'Last trade: $' + controller.formatNumber(lastTrade.profit);
  621.             detailsElems.trade.title = controller.getAnimalName(lastTrade.lossAnimal) + ' (x' + lastTrade.lossAmount + ') -> ' + controller.getAnimalName(lastTrade.gainAnimal) + ' (x' + lastTrade.gainAmount + ')';
  622.             detailsElems.trade.style.cursor = 'help';
  623.         }
  624.         else
  625.         {
  626.             detailsElems.trade.textContent = '';
  627.             detailsElems.trade.title = '';
  628.             detailsElems.trade.style.cursor = 'auto';
  629.         }
  630.     }
  631.    
  632.     var controller;
  633.     var routineName = 'Seller';
  634.     var options = {
  635.         hardFloor : 10, //never sell animals to below this threshold (failsafe)
  636.         hybridThreshold : 3, //threshold before which minimal strategy is used, after which optimal strategy
  637.         fixedPop : 10000,
  638.         strategy : 'hybrid',
  639.         acceptTrades : true
  640.     };
  641.     var detailsElems = {};
  642.     var lastTrade;
  643. };
  644.  
  645. var SpenderRoutine = function (controller)
  646. {
  647.     var self = this;
  648.    
  649.     self.tick = function ()
  650.     {
  651.         //pays debt in full and terminates
  652.         //  or pays debt partially and continues
  653.         //buys an enclosure and terminates
  654.         //  or buys an economy upgrade preemptively and terminates
  655.         //    or continues
  656.         //seeds a new animal and terminates
  657.         //  or buys an economy upgrade preemptively and terminates
  658.         //    or continues
  659.         //buys an economy upgrade and terminates
  660.  
  661.         try
  662.         {
  663.             if (options.strategies.debt)
  664.             {
  665.                 if (controller.state.debt > 0)
  666.                 {
  667.                     var paidInFull = payDebt();
  668.                     if (paidInFull)
  669.                         throw { entryType : 'debt' };
  670.                 }
  671.             }
  672.             if (options.strategies.enclosure)
  673.             {
  674.                 var upgrade = checkEnclosures();
  675.                 if (upgrade)
  676.                 {
  677.                     var purchased = buyUpgrade(upgrade);
  678.                     if (typeof purchased == 'number')
  679.                     {
  680.                         logDetailsEntry({ entryType : 'saving', pType : 'enclosure', pAnimal : upgrade.animal, pCost : purchased });
  681.                         var preUpgrade = evaluatePreemptiveUpgrade(purchased);
  682.                         if (preUpgrade)
  683.                         {
  684.                             var prePurchase = buyUpgrade(preUpgrade);
  685.                             if (typeof prePurchase == 'number')
  686.                                 throw { entryType : 'saving', pType : preUpgrade.type, pAnimal : preUpgrade.animal, pCost : prePurchase };
  687.                             else if (prePurchase)
  688.                                 throw { entryType : 'bought', pType : preUpgrade.type, pAnimal : preUpgrade.animal };
  689.                         }
  690.                         throw '';
  691.                     }
  692.                     else if (purchased)
  693.                         throw { entryType : 'bought', pType : 'enclosure', pAnimal : upgrade.animal };
  694.                 }
  695.             }
  696.             if (options.strategies.seed)
  697.             {
  698.                 var purchase = checkNewAnimals();
  699.                 if (purchase)
  700.                 {
  701.                     if (purchase.type == 'seed')
  702.                         var purchased = seedNewAnimal(purchase.animal);
  703.                     else if (purchase.type == 'own')
  704.                         var purchased = buyAnimals(purchase.animal, 1);
  705.                     else
  706.                         var purchased = buyUpgrade(purchase);
  707.                    
  708.                     if (typeof purchased == 'number')
  709.                     {
  710.                         logDetailsEntry({ entryType : 'saving', pType : purchase.type, pAnimal : purchase.animal, pCost : purchased });
  711.                         var preUpgrade = evaluatePreemptiveUpgrade(purchased);
  712.                         if (preUpgrade)
  713.                         {
  714.                             var prePurchase = buyUpgrade(preUpgrade);
  715.                             if (typeof prePurchase == 'number')
  716.                                 throw { entryType : 'saving', pType : preUpgrade.type, pAnimal : preUpgrade.animal, pCost : prePurchase };
  717.                             else if (prePurchase)
  718.                                 throw { entryType : 'bought', pType : preUpgrade.type, pAnimal : preUpgrade.animal };
  719.                         }
  720.                         throw '';
  721.                     }
  722.                     else if (purchased)
  723.                         throw { entryType : 'bought', pType : purchase.type, pAnimal : purchase.animal };
  724.                 }
  725.             }
  726.             if (options.strategies.growth || options.strategies.price || options.strategies.visitor)
  727.             {
  728.                 var types = [];
  729.                 if (options.strategies.growth)
  730.                     types.push('birthRate', 'deathRate');
  731.                 if (options.strategies.price)
  732.                     types.push('buyPrice');
  733.                 if (options.strategies.visitor)
  734.                     types.push('visitorsBuy', 'visitorsTime');
  735.                
  736.                 var upgrade = findBestPurchase(types);
  737.                 if (upgrade !== false)
  738.                 {
  739.                     var purchase = buyUpgrade(upgrade);
  740.                     if (typeof purchase == 'number')
  741.                         throw { entryType : 'saving', pType : upgrade.type, pAnimal : upgrade.animal, pCost : purchase };
  742.                     else if (purchase)
  743.                         throw { entryType : 'bought', pType : upgrade.type, pAnimal : upgrade.animal };
  744.                 }
  745.             }
  746.             logDetailsEntry({ entryType : 'saving' });
  747.         }
  748.         catch (logMessage)
  749.         {
  750.             if (logMessage)
  751.                 logDetailsEntry(logMessage);
  752.         }
  753.         updateDetails();
  754.     }
  755.     self.getName = function ()
  756.     {
  757.         return routineName;
  758.     }
  759.     self.createOptionsDOM = function ()
  760.     {
  761.         var container = controller.createElem('div');
  762.        
  763.         var simpleChecks = {
  764.             growth : { label : 'Buy growth upgrades' },
  765.             price : { label : 'Buy price upgrades', title : "Only bought when population exceeds seller's threshold" },
  766.             visitor : { label : 'Buy visitor upgrades' },
  767.             enclosure : { label : 'Buy enclosure upgrades', title : "Only bought when population nears limit" },
  768.             debt : { label : 'Pay off debt', title : "Pays " + options.debtPaymentRate + "% per tick if debt can't be paid in full" },
  769.             seed : { label : 'Seed new animal types', title : "Buys animals and upgrades for new species to get growth rate above " + options.seedMinGrowth + "%" }
  770.         }
  771.        
  772.         for (var key in simpleChecks)
  773.         {
  774.             var checkDiv = controller.createElem('div', { parent : container });
  775.             var checkLabel = controller.createElem('label', { parent : checkDiv });
  776.             var checkInput = controller.createElem(
  777.                 'input',
  778.                 {
  779.                     properties : {
  780.                         type : 'checkbox',
  781.                         name : 'spender_' + key,
  782.                         checked : 'checked'
  783.                     },
  784.                     parent: checkLabel
  785.                 }
  786.             );
  787.             var checkSpan = controller.createElem(
  788.                 'span',
  789.                 {
  790.                     text : simpleChecks[key].label,
  791.                     parent : checkLabel
  792.                 }
  793.             );
  794.             if (simpleChecks[key].title)
  795.             {
  796.                 checkSpan.setAttribute('title', simpleChecks[key].title);
  797.                 checkSpan.style.cursor = 'help';
  798.             }
  799.            
  800.             checkInput.addEventListener('change', self.optionsHandler);
  801.         }
  802.        
  803.         controller.createElem('span', { text : 'Reserve $', parent : container });
  804.         var reserveInput = controller.createElem(
  805.             'input',
  806.             {
  807.                 properties : {
  808.                     type : 'text',
  809.                     name : 'spender_reserve',
  810.                     size : 5,
  811.                     value : options.reserve,
  812.                     title : '1500, 10k, 1.2m, 5b, etc'
  813.                 },
  814.                 style : { cursor : 'help' },
  815.                 parent : container
  816.             }
  817.         );
  818.         reserveInput.addEventListener('change', self.optionsHandler);
  819.        
  820.         var detailsContainer = controller.createElem(
  821.             'div',
  822.             {
  823.                 style : {
  824.                     textAlign : 'center',
  825.                     marginTop : '0.5em'
  826.                 },
  827.                 parent : container
  828.             }
  829.         );
  830.         var detailsLink = controller.createElem(
  831.             'span',
  832.             {
  833.                 text : 'Details',
  834.                 style : {
  835.                     border : '1px dashed #CCCCCC',
  836.                     padding : '0.25em 0.5em',
  837.                     cursor : 'pointer'
  838.                 },
  839.                 parent : detailsContainer
  840.             }
  841.         );
  842.         detailsLink.addEventListener('click', self.toggleDetails);
  843.         detailsElems.container = controller.createElem(
  844.             'div',
  845.             {
  846.                 style : {
  847.                     border : '1px dashed #CCCCCC',
  848.                     display : 'none',
  849.                     textAlign : 'left',
  850.                     padding : '0.25em 0.5em'
  851.                 },
  852.                 parent : detailsContainer
  853.             }
  854.         );
  855.         detailsElems.saving = controller.createElem('div', { parent : detailsElems.container });
  856.         detailsElems.spent = controller.createElem('div', { parent : detailsElems.container });
  857.        
  858.         return container;
  859.     }
  860.     self.optionsHandler = function (event)
  861.     {
  862.         if (!event.target.name.match(/^spender_[a-z]+$/))
  863.             return;
  864.        
  865.         var optName = event.target.name.match(/^spender_([a-z]+)$/)[1];
  866.         if (optName == 'reserve')
  867.         {
  868.             var newVal = controller.unformatNumber(event.target.value);
  869.             if (newVal !== undefined)
  870.             options.reserve = newVal;
  871.         }
  872.         else if (options.strategies[optName] !== undefined)
  873.         {
  874.             options.strategies[optName] = event.target.checked;
  875.         }
  876.     }
  877.     self.toggleDetails = function (event)
  878.     {
  879.         detailsElems.container.style.display = (detailsElems.container.style.display == 'none' ? 'block' : 'none');
  880.     }
  881.    
  882.     var payDebt = function ()
  883.     {
  884.         var money = availableMoney();
  885.         var debt = controller.state.debt;
  886.         if (debt == 0 || money < 10)
  887.             return;
  888.        
  889.         if (debt < money)
  890.         {
  891.             controller.commands.debtClick('max');
  892.             return true;
  893.         }
  894.         else
  895.         {
  896.             var paymentAmount = money * options.debtPaymentRate / 100;
  897.             var paymentIncrement = (paymentAmount < 1000 ? 10 : 1000);
  898.             var paymentCount = Math.max(Math.floor(paymentAmount / paymentIncrement), 1);
  899.  
  900.             for (var _i = 0; _i < paymentCount; _i++)
  901.                 controller.commands.debtClick(paymentIncrement);
  902.            
  903.             return false;
  904.         }
  905.     }
  906.     var checkEnclosures = function ()
  907.     {
  908.         for (var _i = 0, _len_i = controller.state.animalsN; _i < _len_i; _i++)
  909.         {
  910.             var popLimit = controller.state.animals[_i].maxPop;
  911.             var popCurrent = controller.state.animals[_i].own;
  912.             if (popCurrent >= popLimit - options.enclosureBuyMargin)
  913.                 return { animal : _i, type : 'enclosure' };
  914.         }
  915.         return false;
  916.     }
  917.     var checkNewAnimals = function ()
  918.     {
  919.         for (var _i = 0, _len_i = controller.state.animalsN; _i < _len_i; _i++)
  920.         {
  921.             if (_i == controller.state.speciesShown)
  922.                 break;
  923.             if (!animalPrereqMet(_i))
  924.                 continue;
  925.            
  926.             var calc = new AnimalCalc(_i, controller);
  927.             if (calc.getValue('own') == 0)
  928.                 return { animal : _i, type : 'seed' };
  929.             else if (calc.getValue('growth') < options.seedMinGrowth / 100)
  930.                 return findBestPurchase(['birthRate', 'deathRate', 'own'], _i);
  931.         }
  932.         return false;
  933.     }
  934.     var seedNewAnimal = function (animal)
  935.     {
  936.         var startPop = options.seedStartPop(animal) - controller.state.animals[animal].own;
  937.         var startBirth = options.seedStartBirth(animal) - controller.state.animals[animal].brCount;
  938.         var startDeath = options.seedStartDeath(animal) - controller.state.animals[animal].drCount;
  939.        
  940.         var singlePop = controller.state.animals[animal].buyPrice;
  941.         var firstBirth = controller.state.animals[animal].brPrice;
  942.         var firstDeath = controller.state.animals[animal].drPrice;
  943.        
  944.         var costPop = singlePop * startPop;
  945.         var costBirth = 0;
  946.         for (var _i = 0; _i < startBirth; _i++)
  947.             costBirth += firstBirth * Math.pow(1.5, _i);
  948.         var costDeath = 0;
  949.         for (var _i = 0; _i < startDeath; _i++)
  950.             costDeath += firstDeath * Math.pow(1.5, _i);
  951.         var costTotal = costPop + costBirth + costDeath;
  952.        
  953.         if (availableMoney() < costTotal)
  954.             return costTotal;
  955.        
  956.         var result = buyAnimals(animal, startPop);
  957.         for (var _i = 0; _i < startBirth; _i++)
  958.             result *= buyUpgrade({ animal : animal, type : 'birthRate' });
  959.         for (var _i = 0; _i < startDeath; _i++)
  960.             result *= buyUpgrade({ animal : animal, type : 'deathRate' });
  961.         return (result == 1 ? true : false);
  962.     }
  963.     var findBestPurchase = function (pTypes, pAnimal)
  964.     {
  965.         //pTypes [ 'birthRate'|'deathRate'|'buyPrice'|'own'|'visitorsBuy'|'visitorsTime' ]
  966.        
  967.         if (pTypes.length == 0)
  968.             return false;
  969.        
  970.         var startIndex = (pAnimal !== undefined ? pAnimal : 0);
  971.         var endIndex = (pAnimal !== undefined ? pAnimal + 1 : controller.state.animalsN);
  972.         var bestPurchase = { efficiency : 0 };
  973.         for (var _i = startIndex, _len_i = endIndex; _i < _len_i; _i++)
  974.         {
  975.             var calc = new AnimalCalc(_i, controller);
  976.             var owned = calc.getValue('own');
  977.             if (owned == 0)
  978.                 break;
  979.            
  980.             for (var _j = 0, _len_j = pTypes.length; _j < _len_j; _j++)
  981.             {
  982.                 var pType = pTypes[_j];
  983.                 if (pType == 'visitorsBuy' || pType == 'visitorsTime')
  984.                     continue;
  985.                 else if (pType == 'own')
  986.                 {
  987.                     if (owned == controller.state.animals[_i].maxPop)
  988.                         continue;
  989.                    
  990.                     var pCost = calc.getValue('buyPrice');
  991.                     var pEfficiency = calc.measureChange({ own : '+1' }, 'value') / pCost;
  992.                 }
  993.                 else
  994.                 {
  995.                     var upPrefix = upgradePrefixes[pType];
  996.                     if (controller.state.animals[_i][upPrefix + 'Count'] == options.upgradeLimit)
  997.                         continue;
  998.                     if (pType == 'buyPrice')
  999.                     {
  1000.                         var saleThreshold = controller.getRoutine('Seller').getPopulationFloor(_i);
  1001.                         if (owned < saleThreshold)
  1002.                             continue;
  1003.                     }
  1004.                    
  1005.                     var changeArg = {};
  1006.                     changeArg[pType] = '*' + controller.state[upPrefix + 'BoostEffect'];
  1007.                    
  1008.                     var pCost = calc.getValue(upPrefix + 'Price');
  1009.                     var pEfficiency = calc.measureChange(changeArg, 'value') / pCost;
  1010.                 }
  1011.                 if (bestPurchase.efficiency < pEfficiency)
  1012.                     bestPurchase = { animal : _i, type : pType, efficiency : pEfficiency, cost : pCost };
  1013.             }
  1014.         }
  1015.         if (pTypes.indexOf('visitorsBuy') > -1)
  1016.         {
  1017.             var pCost = controller.state.visitorsBuyCost;
  1018.             var pValue = controller.state.visitorsBuy / controller.state.visitorsTime;
  1019.             var pEfficiency = pValue / pCost;
  1020.             if (bestPurchase.efficiency < pEfficiency)
  1021.                 bestPurchase = { animal : false, type : 'visitorsBuy', efficiency : pEfficiency, cost : pCost };
  1022.         }
  1023.         if (pTypes.indexOf('visitorsTime') > -1 && controller.state.visitorsTime > 1)
  1024.         {
  1025.             var pCost = controller.state.visitorsTimeCost;
  1026.             var pValue = controller.state.visitorsTotal / (controller.state.visitorsTime - 1) - controller.state.visitorsTotal / controller.state.visitorsTime;
  1027.             var pEfficiency = pValue / pCost;
  1028.             if (bestPurchase.efficiency < pEfficiency)
  1029.                 bestPurchase = { animal : false, type : 'visitorsTime', efficiency : pEfficiency, cost : pCost };
  1030.         }
  1031.         if (bestPurchase.animal === undefined)
  1032.             return false;
  1033.         return bestPurchase;
  1034.     }
  1035.     var buyUpgrade = function (upgrade)
  1036.     {
  1037.         //{ animal : int|false, type : 'birthRate'|'deathRate'|'buyPrice'|'enclosure'|'visitorsBuy'|'visitorsTime',( cost : float) }
  1038.        
  1039.         if (upgrade.type == 'visitorsBuy' || upgrade.type == 'visitorsTime')
  1040.             return buyVisitorUpgrade(upgrade);
  1041.        
  1042.         var upPrefix = upgradePrefixes[upgrade.type];
  1043.         if (!upgrade.cost)
  1044.             upgrade.cost = controller.state.animals[upgrade.animal][upPrefix + 'Price']
  1045.         if (availableMoney() < upgrade.cost)
  1046.             return upgrade.cost;
  1047.        
  1048.         var elem = document.getElementById(upPrefix + upgrade.animal);
  1049.         if (elem)
  1050.         {
  1051.             if (upgrade.type == 'birthRate')
  1052.                 controller.commands.buyBRBoost(elem);
  1053.             else if (upgrade.type == 'deathRate')
  1054.                 controller.commands.buyDRBoost(elem);
  1055.             else if (upgrade.type == 'buyPrice')
  1056.                 controller.commands.buyBSBoost(elem);
  1057.             else if (upgrade.type == 'enclosure')
  1058.                 controller.commands.buyENBoost(elem);
  1059.             else
  1060.                 return false;
  1061.             return true;
  1062.         }
  1063.         return false;
  1064.     }
  1065.     var buyAnimals = function (animal, amount)
  1066.     {
  1067.         if (animal >= controller.state.speciesShown)
  1068.             return false;
  1069.         if (!animalPrereqMet(animal))
  1070.             return false;
  1071.        
  1072.         var cost = controller.state.animals[animal].buyPrice;
  1073.         if (cost * amount > availableMoney())
  1074.             return cost * amount;
  1075.        
  1076.         var countOne = amount % 10;
  1077.         var countTen = Math.floor(amount / 10);
  1078.         var elemOne = document.getElementById('buy' + animal);
  1079.         var elemTen = document.getElementById('buy10' + animal);
  1080.         if (elemOne && elemTen)
  1081.         {
  1082.             for (var _i = 0; _i < countOne; _i++)
  1083.                 controller.commands.buy(elemOne, 1);
  1084.             for (var _i = 0; _i < countTen; _i++)
  1085.                 controller.commands.buy(elemTen, 10);
  1086.             return true;
  1087.         }
  1088.         return false;
  1089.     }
  1090.     var evaluatePreemptiveUpgrade = function (goal)
  1091.     {
  1092.         if (!options.strategies.growth && !options.strategies.price && !options.strategies.visitor)
  1093.             return false;
  1094.        
  1095.         var types = [];
  1096.         if (options.strategies.growth)
  1097.             types.push('birthRate', 'deathRate');
  1098.         if (options.strategies.price)
  1099.             types.push('buyPrice');
  1100.         if (options.strategies.visitor)
  1101.             types.push('visitorsBuy', 'visitorsTime');
  1102.         var bestUpgrade = findBestPurchase(types);
  1103.  
  1104.         var calc = new IncomeCalc(controller);
  1105.         var currentIncome = calc.getIncome();
  1106.         if (currentIncome == 0)
  1107.             return;
  1108.        
  1109.         var alterArg = {};
  1110.         if (bestUpgrade.type == 'visitorsBuy')
  1111.             alterArg.visitorsTotal = '+' + controller.state.visitorsBuy;
  1112.         else if (bestUpgrade.type == 'visitorsTime')
  1113.             alterArg.visitorsTime = '-1';
  1114.         else
  1115.             alterArg[bestUpgrade.type] = '*' + controller.state[upgradePrefixes[bestUpgrade.type] + 'BoostEffect'];
  1116.         calc.alter(alterArg);
  1117.         var upgradeIncome = calc.getIncome();
  1118.        
  1119.        
  1120.         var currentGoal = goal - availableMoney();
  1121.         var upgradeGoal = currentGoal + bestUpgrade.cost;
  1122.        
  1123.         var currentTime = currentGoal / currentIncome;
  1124.         var upgradeTime = upgradeGoal / upgradeIncome;
  1125.         if (upgradeTime < currentTime)
  1126.             return bestUpgrade;
  1127.         return false;
  1128.     }
  1129.     var buyVisitorUpgrade = function (upgrade)
  1130.     {
  1131.         if (!upgrade.cost)
  1132.         {
  1133.             if (upgrade.type == 'visitorsBuy')
  1134.                 upgrade.cost = controller.state.visitorsBuyCost;
  1135.             else if (upgrade.type == 'visitorsTime')
  1136.                 upgrade.cost = controller.state.visitorsTimeCost;
  1137.         }
  1138.         if (availableMoney() < upgrade.cost)
  1139.             return upgrade.cost;
  1140.  
  1141.         if (upgrade.type == 'visitorsBuy')
  1142.             controller.commands.buyVisitors();
  1143.         else if (upgrade.type == 'visitorsTime')
  1144.             controller.commands.buyVisitorsTime();
  1145.         else
  1146.             return false;
  1147.         return true;
  1148.     }
  1149.     var logDetailsEntry = function (entry)
  1150.     {
  1151.         //{ entryType : 'bought'|'debt'|'saving'(, pType : str, pAnimal : int, pCost : float) }
  1152.        
  1153.         if (logDisplayNames[entry.pType])
  1154.             entry.pType = logDisplayNames[entry.pType];
  1155.        
  1156.         if (entry.entryType == 'saving' || entry.entryType == 'bought')
  1157.         {
  1158.             var purchaseStr = (entry.pAnimal !== false ? controller.getAnimalName(entry.pAnimal) + ' ' : '') + entry.pType;
  1159.            
  1160.             if (entry.entryType == 'saving')
  1161.             {
  1162.                 if (!entry.pType)
  1163.                     detailsLog.saving = undefined;
  1164.                 else
  1165.                     detailsLog.saving = purchaseStr + ' ($' + controller.formatNumber(entry.pCost) + ')';
  1166.             }
  1167.             else if (entry.entryType == 'bought')
  1168.             {
  1169.                 if (detailsLog.saving && detailsLog.saving.indexOf(purchaseStr) === 0)
  1170.                     detailsLog.saving = undefined;
  1171.                
  1172.                 if (detailsLog.spent[0] && detailsLog.spent[0].indexOf(purchaseStr) === 0)
  1173.                 {
  1174.                     var regs = detailsLog.spent[0].match(/ \(x([0-9]+)\)$/);
  1175.                     var messageCount = (regs ? parseInt(regs[1]) + 1 : 2);
  1176.                     detailsLog.spent[0] = purchaseStr + ' (x' + messageCount + ')';
  1177.                 }
  1178.                 else
  1179.                     detailsLog.spent.unshift(purchaseStr);
  1180.             }
  1181.         }
  1182.         else if (entry.entryType == 'debt')
  1183.             detailsLog.spent.unshift('paid off debt');
  1184.     }
  1185.     var updateDetails = function ()
  1186.     {
  1187.         if (detailsLog.spent.length > 10)
  1188.             detailsLog.spent.splice(10);
  1189.        
  1190.         for (var _i = 0, _len_i = detailsLog.spent.length; _i < _len_i; _i++)
  1191.         {
  1192.             if (!detailsElems.spent.childNodes[_i])
  1193.                 controller.createElem('div', { parent : detailsElems.spent });
  1194.             detailsElems.spent.childNodes[_i].textContent = (_i + 1) + '. ' + detailsLog.spent[_i];
  1195.         }
  1196.        
  1197.         detailsElems.saving.textContent = (detailsLog.saving ? 'Next: ' + detailsLog.saving : '\u00A0');
  1198.     }
  1199.     var availableMoney = function ()
  1200.     {
  1201.         var money = controller.state.money - options.reserve;
  1202.         return (money < 0 ? 0 : money);
  1203.     }
  1204.     var animalPrereqMet = function (animal)
  1205.     {
  1206.         if (animal < 2)
  1207.             return true;
  1208.        
  1209.         var prereqAnimal = animal - 2;
  1210.         var prereqAmount = (animal - 1) * 100;
  1211.         return (controller.state.animals[prereqAnimal].own >= prereqAmount);
  1212.     }
  1213.    
  1214.     var controller;
  1215.     var routineName = 'Spender';
  1216.     var options = {
  1217.         strategies : {
  1218.             growth : true,
  1219.             price : true,
  1220.             visitor : true,
  1221.             enclosure : true,
  1222.             debt : true,
  1223.             seed : true
  1224.         },
  1225.         upgradeLimit : 20,
  1226.         debtPaymentRate : 1, //percentage of funds to spend on debt payment if debt can't be paid off entirely
  1227.         enclosureBuyMargin : 20, //number below limit that population must reach before an enclosure is purchased
  1228.         seedStartPop : function (animal) { return (animal + 3) * 2; }, //number of animals to buy at once when first starting up
  1229.         seedStartBirth : function (animal) { return Math.round((animal + 2) / 3); },
  1230.         seedStartDeath : function (animal) { return Math.round((animal + 2) / 6); },
  1231.         seedMinGrowth : 8, //growth rate to establish before new species is no longer prioritized
  1232.         reserve : 0
  1233.     };
  1234.     var upgradePrefixes = {
  1235.         birthRate : 'br',
  1236.         deathRate : 'dr',
  1237.         buyPrice : 'bs',
  1238.         enclosure : 'en'
  1239.     };
  1240.     var logDisplayNames = {
  1241.         own : 'animal',
  1242.         visitorsBuy : 'visitorCount',
  1243.         visitorsTime : 'visitorFrequency',
  1244.         buyPrice : 'price'
  1245.     };
  1246.     var detailsLog = { spent : [] };
  1247.     var detailsElems = {};
  1248. };
  1249.  
  1250. var AnimalCalc = function (animalData, controller)
  1251. {
  1252.     var self = this;
  1253.    
  1254.     self.clone = function ()
  1255.     {
  1256.         var copyData = JSON.parse(JSON.stringify(animalData));
  1257.         return new AnimalCalc(copyData, controller);
  1258.     }
  1259.     self.alter = function (alterations)
  1260.     {
  1261.         for (var key in alterations)
  1262.         {
  1263.             if (!alterations.hasOwnProperty(key) || !animalData.hasOwnProperty(key))
  1264.                 continue;
  1265.            
  1266.             var newValue = controller.alterValue(animalData[key], alterations[key]);
  1267.             if (newValue === undefined)
  1268.                 continue;
  1269.             animalData[key] = newValue;
  1270.         }
  1271.     }
  1272.     self.getValue = function (key)
  1273.     {
  1274.         if (key == 'birthRate')
  1275.         {
  1276.             var br = animalData.birthRate;
  1277.             var pop = animalData.own;
  1278.             return pop / (br + pop);
  1279.         }
  1280.         else if (key == 'deathRate')
  1281.         {
  1282.             var dr = animalData.deathRate;
  1283.             var pop = animalData.own;
  1284.             return pop / (dr + pop * 2);
  1285.         }
  1286.         else if (key == 'growth' || key == 'growthRate')
  1287.         {
  1288.             return self.getValue('birthRate') - self.getValue('deathRate');
  1289.         }
  1290.         else if (key == 'value' || key == 'productionValue')
  1291.         {
  1292.             var sellPrice = animalData.buyPrice * controller.state.sellPrice;
  1293.             var growthRate = self.getValue('growth');
  1294.             return sellPrice * growthRate;
  1295.         }
  1296.         else if (animalData.hasOwnProperty(key))
  1297.             return animalData[key];
  1298.     }
  1299.     self.measureChange = function (alterations, values)
  1300.     {
  1301.         var altered = self.clone();
  1302.         altered.alter(alterations);
  1303.        
  1304.         if (typeof values != 'object')
  1305.             values = [values];
  1306.        
  1307.         var difference = {};
  1308.         for (var _i = 0, _len_i = values.length; _i < _len_i; _i++)
  1309.         {
  1310.             var value = values[_i];
  1311.             difference[value] = altered.getValue(value) - self.getValue(value);
  1312.         }
  1313.        
  1314.         if (values.length == 1)
  1315.             return difference[values[0]];
  1316.         return difference;
  1317.     }
  1318.    
  1319.     var construct = function ()
  1320.     {
  1321.         if (typeof animalData == 'number')
  1322.         {
  1323.             var copyData = JSON.parse(JSON.stringify(controller.state.animals[animalData]));
  1324.             animalData = copyData;
  1325.         }
  1326.     }
  1327.    
  1328.     var animalData;
  1329.     var controller;
  1330.    
  1331.     construct();
  1332. };
  1333.  
  1334. var IncomeCalc = function (controller, incomeData)
  1335. {
  1336.     var self = this;
  1337.    
  1338.     self.clone = function ()
  1339.     {
  1340.         var incomeData = { animalCalcs : [] };
  1341.        
  1342.         for (var _i = 0, _len_i = animalCalcs.length; _i < _len_i; _i++)
  1343.             incomeData.animalCalcs.push(animalCalcs[_i].clone());
  1344.        
  1345.         var copyData = JSON.parse(JSON.stringify(visitorData));
  1346.         incomeData.visitorData = copyData;
  1347.        
  1348.         return new IncomeCalc(controller, incomeData);
  1349.     }
  1350.     self.alter = function (alterations, animal)
  1351.     {
  1352.         if (alterations.visitorsTotal !== undefined || alterations.visitorsTime !== undefined)
  1353.         {
  1354.             for (var type in alterations)
  1355.             {
  1356.                 if (!alterations.hasOwnProperty(type) || !visitorData.hasOwnProperty(type))
  1357.                     continue;
  1358.                
  1359.                 var newValue = controller.alterValue(visitorData[type], alterations[type]);
  1360.                 if (newValue === undefined)
  1361.                     continue;
  1362.                 visitorData[type] = newValue;
  1363.             }
  1364.         }
  1365.         if (animal)
  1366.         {
  1367.             if (typeof animal != 'object')
  1368.                 animal = [animal];
  1369.             for (var _i = 0, _len_i = animal.length; _i < _len_i; _i++)
  1370.                 animalCalcs[animal[_i]].alter(alterations);
  1371.         }
  1372.         else
  1373.         {
  1374.             for (var _i = 0, _len_i = animalCalcs.length; _i < _len_i; _i++)
  1375.                 animalCalcs[_i].alter(alterations);
  1376.         }
  1377.     }
  1378.     self.getIncome = function ()
  1379.     {
  1380.         var income = 0;
  1381.         if (controller.isRoutineEnabled('Seller'))
  1382.         {
  1383.             for (var _i = 0, _len_i = animalCalcs.length; _i < _len_i; _i++)
  1384.             {
  1385.                 var owned = animalCalcs[_i].getValue('own');
  1386.                 if (owned < controller.getRoutine('Seller').getPopulationFloor(_i))
  1387.                     continue;
  1388.                 income += animalCalcs[_i].getValue('value');
  1389.             }
  1390.         }
  1391.         income += visitorData.visitorsTotal / visitorData.visitorsTime;
  1392.         return income;
  1393.     }
  1394.     self.measureChange = function (alterations)
  1395.     {
  1396.         var altered = self.clone();
  1397.         altered.alter(alterations);
  1398.        
  1399.         var difference = altered.getIncome() - self.getIncome();
  1400.         return difference;
  1401.     }
  1402.    
  1403.     var construct = function ()
  1404.     {
  1405.         for (var _i = 0, _len_i = controller.state.animalsN; _i < _len_i; _i++)
  1406.         {
  1407.             var owned = controller.state.animals[_i].own;
  1408.             if (owned == 0)
  1409.                 break;
  1410.            
  1411.             animalCalcs.push(new AnimalCalc(_i, controller));
  1412.         }
  1413.        
  1414.         visitorData.visitorsTotal = controller.state.visitorsTotal;
  1415.         visitorData.visitorsTime = controller.state.visitorsTime;
  1416.     }
  1417.    
  1418.     var controller;
  1419.     var animalCalcs = [];
  1420.     var visitorData = {};
  1421.    
  1422.     construct();
  1423. };
  1424.  
  1425. var keeper = new Zookeeper();
  1426. keeper.register(new SellerRoutine(keeper));
  1427. keeper.register(new SpenderRoutine(keeper));
  1428. setTimeout(keeper.load, 2000); //delayed start to allow propagation of game state object (IZ & IZ.Zoo)
Advertisement
Add Comment
Please, Sign In to add comment