Widget:GWHonorCalculator

From Granblue Fantasy Wiki
Jump to navigation Jump to search
<style>
.gw-calc {
  border-spacing: 0px;
  border: 0;
}

.gw-calc .gw-calc-ratio {
  text-align: center;
}

.gw-calc .gw-calc-name {
  text-align: center;
}

.gw-calc td, .gw-calc input, .gw-calc select {
  text-align: right;
}

.gw-calc caption {
  /*background-color: #9BC2E6;*/
}

.gw-calc > tr > th, .gw-calc > tr > td {
  padding: 2px 5px;
}

.gw-calc td {
  border: 0 !important;
  border-bottom: 1px solid #ddd !important;
}

.gw-calc th {
  /*background-color: #BDD7EE;*/
}

.gw-calc-total td {
  /*background-color: #BDD7EE;*/
}
</style><table class="wikitable gw-calc gw-calc-options">
  <tr>
    <th colspan="8">Options</th>
  </tr>
  <tr>
    <td>Current meat:</td>
    <td><input id="meat_current" type="number" min="0" max="100000" step="1" value="0" /></td>
    <td>Current tokens:</td>
    <td><input id="token_current" type="number" min="0" max="9999999" step="1" value="0" /></td>
    <td>Current box:</td>
    <td><input id="box_current" type="number" min="1" max="250" step="1" value="1" /></td>
    <td>Current honor:</td>
    <td><input id="honor_current" type="number" min="0" max="99999999999" step="1" value="0" /></td>
  </tr>
  <tr>
    <td>&nbsp;</td>
    <td>&nbsp;</td>
    <td>&nbsp;</td>
    <td>&nbsp;</td>
    <td><span class="tooltip">Target box:<span class="tooltiptext">Your minimum desired number of boxes, or eternals weapons.</span></span></td>
    <td><input id="box_target" type="number" min="1" max="250" step="1" value="45" /></td>
    <td><span class="tooltip">Target honor:<span class="tooltiptext">Your minimum desired honor.</span></span></td>
    <td><input id="honor_target" type="number" min="1" max="99999999999" step="1" value="1000000000" /></td>
  </tr>
</table>
<table class="wikitable gw-calc gw-calc-bosses">
  <tr>
    <th colspan="15">Bosses</th>
  </tr>
  <tr>
    <th rowspan="2"><span class="tooltip">Ratio<span class="tooltiptext">Enter which EX you're doing and an estimate how much meat you'll use for each type of NM.</span></span></th>
    <th rowspan="2">Name</th>
    <th colspan="2">Per battle</th>
    <th colspan="5">Total</th>
    <th colspan="2"><span class="tooltip">Pace (m)<span class="tooltiptext">Your average battle to battle pace, in minutes.</span></span></th>
    <th colspan="2"><span class="tooltip">Time (h)<span class="tooltiptext">Expected time to finish all battles, in hours.</span></span></th>
  </tr>
  <tr>
    <th>Honor</th>
    <th><span class="tooltip">Meat<span class="tooltiptext">Average number of meat obtained per battle.</span></span></th>
    <th>Battles</th>
    <th>Meat</th>
    <th>Tokens</th>
    <th>Honor</th>
    <th>Pots</th>
    <th>Slow</th>
    <th>Fast</th>
    <th>Slow</th>
    <th>Fast</th>
  </tr>
  <tr id="now_row">
    <td>&nbsp;</td>
    <td class="gw-calc-name">Have</td>
    <td>&nbsp;</td>
    <td>&nbsp;</td>
    <td id="now_battles">&nbsp;</td>
    <td id="now_meat_sum">0</td>
    <td><span class="tooltip"><span id="now_tokens">0</span><span class="tooltiptext">Includes tokens that will be earned from the Final Rally rewards based on your current Honor.</span></td>
    <td id="now_honor_sum">0</td>
    <td>&nbsp;</td>
    <td>&nbsp;</td>
    <td>&nbsp;</td>
    <td>&nbsp;</td>
    <td>&nbsp;</td>
  </tr>
  <tr id="ex0_row">
    <td class="gw-calc-ratio"><input type="radio" id="ex_ratio" name="ex_ratio" value="ex0"></td>
    <td class="gw-calc-name">EX</td>
    <td><input id="ex0_honor" type="number" min="1000" max="90000000" step="1000" value="51000" /></td>
    <td><input id="ex0_meat" type="number" min="0.1" max="99.9" step="0.1" value="5" /></td>
    <td id="ex0_battles">0</td>
    <td id="ex0_meat_sum">0</td>
    <td id="ex0_tokens">0</td>
    <td id="ex0_honor_sum">0</td>
    <td id="ex0_pots">0</td>
    <td><input id="ex0_pace_slow" type="number" min="0.1" max="9" step="0.05" value="1" /></td>
    <td><input id="ex0_pace_fast" type="number" min="0.1" max="9" step="0.05" value="0.3" /></td>
    <td id="ex0_time_slow">0m</td>
    <td id="ex0_time_fast">0m</td>
  </tr>
  <tr id="exx_row">
    <td class="gw-calc-ratio"><input type="radio" id="ex_ratio" name="ex_ratio" value="exx" checked></td>
    <td class="gw-calc-name">EX+</td>
    <td><input id="exx_honor" type="number" min="1000" max="90000000" step="1000" value="88000" /></td>
    <td><input id="exx_meat" type="number" min="0.1" max="99.9" step="0.1" value="6.5" /></td>
    <td id="exx_battles">0</td>
    <td id="exx_meat_sum">0</td>
    <td id="exx_tokens">0</td>
    <td id="exx_honor_sum">0</td>
    <td id="exx_pots">0</td>
    <td><input id="exx_pace_slow" type="number" min="0.1" max="9" step="0.05" value="1" /></td>
    <td><input id="exx_pace_fast" type="number" min="0.1" max="9" step="0.05" value="0.3" /></td>
    <td id="exx_time_slow">0m</td>
    <td id="exx_time_fast">0m</td>
  </tr>
  <tr id="n90_row">
    <td><select id="n90_ratio">
      <option value="0">0%</option>
      <option value="10">10%</option>
      <option value="20">20%</option>
      <option value="30">30%</option>
      <option value="40">40%</option>
      <option value="50">50%</option>
      <option value="60">60%</option>
      <option value="70">70%</option>
      <option value="80">80%</option>
      <option value="90">90%</option>
      <option value="100">100%</option>
    </select></td>
    <td class="gw-calc-name">NM 90</td>
    <td><input id="n90_honor" type="number" min="1000" max="90000000" step="1000" value="250000" /></td>
    <td id="n90_meat">-5</td>
    <td id="n90_battles">0</td>
    <td id="n90_meat_sum">0</td>
    <td id="n90_tokens">0</td>
    <td id="n90_honor_sum">0</td>
    <td id="n90_pots">0</td>
    <td><input id="n90_pace_slow" type="number" min="0.1" max="30" step="0.1" value="1" /></td>
    <td><input id="n90_pace_fast" type="number" min="0.1" max="30" step="0.1" value="0.3" /></td>
    <td id="n90_time_slow">0m</td>
    <td id="n90_time_fast">0m</td>
  </tr>
  <tr id="n95_row">
    <td><select id="n95_ratio">
      <option value="0">0%</option>
      <option value="10">10%</option>
      <option value="20">20%</option>
      <option value="30" selected>30%</option>
      <option value="40">40%</option>
      <option value="50">50%</option>
      <option value="60">60%</option>
      <option value="70">70%</option>
      <option value="80">80%</option>
      <option value="90">90%</option>
      <option value="100">100%</option>
    </select></td>
    <td class="gw-calc-name">NM 95</td>
    <td><input id="n95_honor" type="number" min="1000" max="90000000" step="1000" value="910000" /></td>
    <td id="n95_meat">-10</td>
    <td id="n95_battles">0</td>
    <td id="n95_meat_sum">0</td>
    <td id="n95_tokens">0</td>
    <td id="n95_honor_sum">0</td>
    <td id="n95_pots">0</td>
    <td><input id="n95_pace_slow" type="number" min="0.1" max="30" step="0.1" value="4" /></td>
    <td><input id="n95_pace_fast" type="number" min="0.1" max="30" step="0.1" value="2" /></td>
    <td id="n95_time_slow">0m</td>
    <td id="n95_time_fast">0m</td>
  </tr>
  <tr id="n10_row">
    <td><select id="n10_ratio">
      <option value="0">0%</option>
      <option value="10">10%</option>
      <option value="20">20%</option>
      <option value="30">30%</option>
      <option value="40">40%</option>
      <option value="50">50%</option>
      <option value="60">60%</option>
      <option value="70">70%</option>
      <option value="80">80%</option>
      <option value="90">90%</option>
      <option value="100">100%</option>
    </select></td>
    <td class="gw-calc-name">NM 100</td>
    <td><input id="n10_honor" type="number" min="1000" max="90000000" step="1000" value="2650000" /></td>
    <td id="n10_meat">-20</td>
    <td id="n10_battles">0</td>
    <td id="n10_meat_sum">0</td>
    <td id="n10_tokens">0</td>
    <td id="n10_honor_sum">0</td>
    <td id="n10_pots">0</td>
    <td><input id="n10_pace_slow" type="number" min="0.1" max="30" step="0.1" value="10" /></td>
    <td><input id="n10_pace_fast" type="number" min="0.1" max="30" step="0.1" value="5" /></td>
    <td id="n10_time_slow">0m</td>
    <td id="n10_time_fast">0m</td>
  </tr>
  <tr id="n15_row">
    <td><select id="n15_ratio">
      <option value="0">0%</option>
      <option value="10">10%</option>
      <option value="20">20%</option>
      <option value="30" selected>30%</option>
      <option value="40">40%</option>
      <option value="50">50%</option>
      <option value="60">60%</option>
      <option value="70">70%</option>
      <option value="80">80%</option>
      <option value="90">90%</option>
      <option value="100">100%</option>
    </select></td>
    <td class="gw-calc-name">NM 150</td>
    <td><input id="n15_honor" type="number" min="1000" max="90000000" step="1000" value="4100000" /></td>
    <td id="n15_meat">-20</td>
    <td id="n15_battles">0</td>
    <td id="n15_meat_sum">0</td>
    <td id="n15_tokens">0</td>
    <td id="n15_honor_sum">0</td>
    <td id="n15_pots">0</td>
    <td><input id="n15_pace_slow" type="number" min="0.1" max="30" step="0.1" value="10" /></td>
    <td><input id="n15_pace_fast" type="number" min="0.1" max="30" step="0.1" value="5" /></td>
    <td id="n15_time_slow">0m</td>
    <td id="n15_time_fast">0m</td>
  </tr>
  <tr id="n20_row">
    <td><select id="n20_ratio">
      <option value="0">0%</option>
      <option value="10">10%</option>
      <option value="20">20%</option>
      <option value="30">30%</option>
      <option value="40" selected>40%</option>
      <option value="50">50%</option>
      <option value="60">60%</option>
      <option value="70">70%</option>
      <option value="80">80%</option>
      <option value="90">90%</option>
      <option value="100">100%</option>
    </select></td>
    <td class="gw-calc-name">NM 200</td>
    <td><input id="n20_honor" type="number" min="1000" max="90000000" step="1000" value="13350000" /></td>
    <td id="n20_meat">-20</td>
    <td id="n20_battles">0</td>
    <td id="n20_meat_sum">0</td>
    <td id="n20_tokens">0</td>
    <td id="n20_honor_sum">0</td>
    <td id="n20_pots">0</td>
    <td><input id="n20_pace_slow" type="number" min="0.1" max="30" step="0.1" value="15" /></td>
    <td><input id="n20_pace_fast" type="number" min="0.1" max="30" step="0.1" value="7" /></td>
    <td id="n20_time_slow">0m</td>
    <td id="n20_time_fast">0m</td>
  </tr>
  <tr id="total_row" class="gw-calc-total">
    <th colspan="4">Total:</th>
    <td id="total_battles">0</td>
    <td><span class="tooltip"><span id="total_meat">0</span><span class="tooltiptext">Excess meat thrown away.</span></span></td>
    <td id="total_tokens">0</td>
    <td id="total_honor">0</td>
    <td id="total_pots">0</td>
    <td colspan="2">&nbsp;</td>
    <td id="total_time_slow">0h</td>
    <td id="total_time_fast">0h</td>
  </tr>
</table>
<script type="application/javascript">
(function() {
  // Make an array for token boxes
  let token_boxes = (function(){ 
    let boxes = [];
    boxes[0] = {
      box: 0,
      tokens: 0,
      total: 0
    };
    let prev = 0;
    let total = 0;
    for (let i = 1; i < 250; i++) {
      let tokens = ({
        1: 1600,
        2: 2400,
        3: 2400,
        4: 2400,
        5: 2000,
        46: 10000,
        81: 15000,
      })[i] || prev;
      prev = tokens;
      total += tokens;
      boxes[i] = {
        box: i,
        tokens: tokens,
        total: total
      };
    }
    return boxes;
  })();
  
  // Make an array with the available bosses and updates it with inputs from the bosses table
  let get_bosses = (function() {
    let bosses = [
      { ix: 'ex0', name: 'EX',     meat:   0, ap: 30, base_tokens:  54 },
      { ix: 'exx', name: 'EX+',    meat:   0, ap: 30, base_tokens:  66 },
      { ix: 'n90', name: 'NM 90',  meat:  -5, ap: 30, base_tokens:  83 },
      { ix: 'n95', name: 'NM 95',  meat: -10, ap: 40, base_tokens: 111 },
      { ix: 'n10', name: 'NM 100', meat: -20, ap: 50, base_tokens: 168 },
      { ix: 'n15', name: 'NM 150', meat: -20, ap: 50, base_tokens: 257 },
      { ix: 'n20', name: 'NM 200', meat: -20, ap: 50, base_tokens: 338 },
    ];
    
    let ratio_sum_meat_use = 0;
    for (let i = 0; i < bosses.length; i++) {
      let boss = bosses[i];
      if (boss.meat == 0) {
        let ratio_radio = document.querySelector('input[name="ex_ratio"]:checked');
        boss.ratio = (ratio_radio.value == boss.ix) ? 100 : 0;
      } else {
        let ratio_select = document.querySelector('select#'+boss.ix+'_ratio');
        boss.ratio = parseFloat(ratio_select.value);
        ratio_sum_meat_use += Math.abs(boss.ratio);
      }
    }

    for (let i = 0; i < bosses.length; i++) {
      let boss = bosses[i];
      let ratio_sum = boss.meat > 0 ? 100 : ratio_sum_meat_use;
      if (ratio_sum == 0) {
        boss.percent = 0;
      } else {
        boss.percent = boss.ratio / ratio_sum;
      }
      let honor_input = document.querySelector('input#'+boss.ix+'_honor');
      boss.honor = parseFloat(honor_input.value);
      boss.tokens = boss.base_tokens + (boss.honor / 16667);
      let meat_input = document.querySelector('input#'+boss.ix+'_meat');
      if (meat_input != null)
        boss.meat = parseFloat(meat_input.value);
      let slow_input = document.querySelector('input#'+boss.ix+'_pace_slow');
      boss.pace_slow = parseFloat(slow_input.value);
      let fast_input = document.querySelector('input#'+boss.ix+'_pace_fast');
      boss.pace_fast = parseFloat(fast_input.value);
    }
    return bosses;
  });
  
  // Make an object with the values in the user options table
  let get_options = (function() {
    return {
      meat_current: parseFloat(document.querySelector('input#meat_current').value),
      token_current: parseFloat(document.querySelector('input#token_current').value),
      box_current: parseFloat(document.querySelector('input#box_current').value),
      box_target: parseFloat(document.querySelector('input#box_target').value),
      honor_current: parseFloat(document.querySelector('input#honor_current').value),
      honor_target: parseFloat(document.querySelector('input#honor_target').value),
    };
  });
  
  // Calculate the amount of honor and tokens one EX battle plus resulting NM battles reward
  let ex_value = (function(bosses) {
    let gained_meat = 0;
    let gained_honor = 0;
    let gained_tokens = 0;
    for (let i = 0; i < bosses.length; i++) {
      let boss = bosses[i];
      if (boss.ratio == 0) continue;
      if (boss.meat <= 0) continue;
      gained_meat = boss.meat;
      gained_honor = boss.honor;
      gained_tokens = boss.tokens;
    }
    for (let i = 0; i < bosses.length; i++) {
      let boss = bosses[i];
      if (boss.meat >= 0) continue;
      let used_meat = gained_meat * boss.percent;
      let meat_ratio = used_meat / Math.abs(boss.meat);
      gained_tokens += boss.tokens * meat_ratio;
      gained_honor += boss.honor * meat_ratio;
    }
    return {
      meat: gained_meat,
      honor: gained_honor,
      tokens: gained_tokens,
    };
  });
  
  // Calculate static values
  let update_calc = (function() {
    console.time("gw token calc");
    let bosses = get_bosses();
    let options = get_options();
    let ex = ex_value(bosses);
    
    // How much is our current supply of meat worth?
    let stock_tokens = 0;
    let stock_honor = 0;
    for (let i = 0; i < bosses.length; i++) {
      let boss = bosses[i];
      if (boss.meat >= 0) continue;
      if (boss.ratio == 0) continue;
      let fights = Math.floor((boss.percent * options.meat_current) / Math.abs(boss.meat));
      //console.log(boss.ratio, options.meat_current, fights);
      stock_tokens += fights * boss.tokens;
      stock_honor += fights * boss.honor;
    }
    
    // How many ex to get enough tokens
    let current_box = token_boxes[Math.max(0, options.box_current)];
    let current_tokens = current_box.total - current_box.tokens + options.token_current + Math.floor(options.honor_current / 16667);
    let target_box = token_boxes[Math.min(250, options.box_target)];
    let target_tokens = target_box.total;
    let remaining_tokens = target_tokens - stock_tokens - current_tokens;
    let token_fights = Math.ceil(remaining_tokens / ex.tokens);
    
    // How many ex to get enough honor
    let remaining_honor = options.honor_target - stock_honor - options.honor_current;
    let honor_fights = Math.ceil(remaining_honor / ex.honor);
    let ex_fights = Math.max(token_fights, honor_fights);
    
    // The amount of meat we'll have from stock and ex fights
    let available_meat = Math.floor(ex_fights * ex.meat + options.meat_current);
    
    let remaining_meat = available_meat;
    let consumed_meat = 0;
    for (let i = bosses.length-1; i >= 0; i--) {
      let boss = bosses[i];
      boss.fights = 0;
      boss.tokens_gained = 0;
      boss.honor_gained = 0;
      boss.pots_spent = 0;
      if (boss.meat >= 0) continue;
      if (boss.ratio == 0) continue;
      boss.fights = Math.round(available_meat * boss.percent / Math.abs(boss.meat))
      consumed_meat += boss.fights * Math.abs(boss.meat);
    }
    for (let i = 0; i < bosses.length; i++) {
      let boss = bosses[i];
      if ((boss.meat <= 0) || (boss.ratio <= 0)) continue;
      boss.fights = Math.max(0, Math.ceil((consumed_meat - options.meat_current) / boss.meat));
    }
    for (let i = 0; i < bosses.length; i++) {
      let boss = bosses[i];
      boss.meat_flow = boss.fights * boss.meat;
    }
    
    let sum_fights = 0;
    let sum_meat = options.meat_current;
    let sum_tokens = options.token_current + Math.floor(options.honor_current / 16667);
    let sum_honor = options.honor_current;
    let sum_pots = 0;
    let sum_slow = 0;
    let sum_fast = 0;

    document.querySelector('td#now_meat_sum').innerHTML = sum_meat.toLocaleString();
    document.querySelector('span#now_tokens').innerHTML = sum_tokens.toLocaleString();
    document.querySelector('td#now_honor_sum').innerHTML = sum_honor.toLocaleString();
    
    for (let i = 0; i < bosses.length; i++) {
      let boss = bosses[i];

      boss.tokens_gained = Math.floor(boss.fights * boss.tokens);
      boss.honor_gained = Math.floor(boss.fights * boss.honor);
      boss.pots_spent = boss.fights * (boss.ap / 75);
      boss.time_fast = boss.fights * (boss.pace_fast / 60);
      boss.time_slow = boss.fights * (boss.pace_slow / 60);

      sum_fights += boss.fights;
      sum_meat += boss.meat_flow;
      sum_tokens += boss.tokens_gained;
      sum_honor += boss.honor_gained;
      sum_pots += boss.pots_spent;
      sum_slow += boss.time_slow;
      sum_fast += boss.time_fast;
      document.querySelector('td#'+boss.ix+'_battles').innerHTML = boss.fights.toLocaleString();
      document.querySelector('td#'+boss.ix+'_meat_sum').innerHTML = boss.meat_flow.toLocaleString();
      document.querySelector('td#'+boss.ix+'_tokens').innerHTML = boss.tokens_gained.toLocaleString();
      document.querySelector('td#'+boss.ix+'_honor_sum').innerHTML = boss.honor_gained.toLocaleString();
      document.querySelector('td#'+boss.ix+'_pots').innerHTML = boss.pots_spent.toFixed(1);
      document.querySelector('td#'+boss.ix+'_time_slow').innerHTML = boss.time_slow.toFixed(1) + 'h';
      document.querySelector('td#'+boss.ix+'_time_fast').innerHTML = boss.time_fast.toFixed(1) + 'h';
    }

    document.querySelector('td#total_battles').innerHTML = sum_fights.toLocaleString();
    document.querySelector('#total_meat').innerHTML = sum_meat.toLocaleString();
    document.querySelector('td#total_tokens').innerHTML = sum_tokens.toLocaleString();
    document.querySelector('td#total_honor').innerHTML = sum_honor.toLocaleString();
    document.querySelector('td#total_pots').innerHTML = sum_pots.toFixed(1);
    document.querySelector('td#total_time_slow').innerHTML = sum_slow.toFixed(1) + 'h';
    document.querySelector('td#total_time_fast').innerHTML = sum_fast.toFixed(1) + 'h';

    document.querySelector('#total_meat').style.color = (sum_meat > 1000  ? 'red' : (sum_meat > 50 ? 'orange' : 'green'));
    console.timeEnd("gw token calc");
  });
  
  let inputs = document.querySelectorAll('.gw-calc select, .gw-calc input');
  inputs.forEach(function(input) {
    input.addEventListener('change', function(event) { update_calc(); });
  });
  update_calc();
})();
</script>