Widget:Calculator/Combat/Level: Difference between revisions

Lol (talk | contribs)
No edit summary
Refactored
 
(12 intermediate revisions by 2 users not shown)
Line 1: Line 1:
<widget>
<style>
  <html><![CDATA[
#combat-calc-widget { max-width: 360px; font-size: 14px; }
    <div id="combat-calculator" style="border: 1px solid #ccc; padding: 20px; max-width: 400px; background: #f9f9f9; border-radius: 10px;">
.combat-calc__lookup { display: flex; gap: 8px; margin-bottom: 15px; }
      <h3>Combat Level Calculator</h3>
.combat-calc__table { border-collapse: collapse; width: 100%; }
.combat-calc__table td { padding: 6px 4px; }
.combat-calc__stat-label { display: flex; align-items: center; gap: 8px; }
.combat-calc__stat-label a { color: var(--theme-color-link, #5c97e6); text-decoration: none; }
.combat-calc__stat-label a:hover { text-decoration: underline; }
.combat-calc__stat-label img { vertical-align: middle; }
.combat-calc__results { margin-top: 15px; padding: 12px; background-color: var(--bg-secondary, #1e1e1e); color: var(--text-main, #ccc); border: 1px solid var(--border-color-base, #333); border-radius: 5px; }
.combat-calc__results b { color: var(--text-bright, #fff); }
.combat-calc__hints ul { list-style: none; padding-left: 0; margin: 6px 0 0; }
.combat-calc__hints li { display: flex; align-items: center; gap: 6px; margin-top: 4px; }
#combat-calc-widget input[type="text"],
#combat-calc-widget input[type="number"] { width: 100%; background-color: var(--bg-main, #1e1f22); color: var(--text-main, #b6b4b2); border: 1px solid var(--border-color-base, #2d2d2d); border-radius: 4px; padding: 6px 8px; box-sizing: border-box; }
#combat-calc-widget button { background-color: var(--theme-color-accent-base, #2ecc71); color: white; border: none; padding: 6px 12px; cursor: pointer; border-radius: 4px; white-space: nowrap; }
#combat-calc-widget button:hover { background-color: var(--theme-color-accent-hover, #27ae60); }
#combat-calc-widget button:disabled { background-color: #555; cursor: not-allowed; }
</style>


      <label>Hitpoints: <input id="stat-hp" type="number" value="10" min="1" /></label><br>
<div id="combat-calc-widget">
      <label>Accuracy: <input id="stat-accuracy" type="number" value="1" min="1" /></label><br>
  <div class="combat-calc__lookup">
      <label>Strength: <input id="stat-strength" type="number" value="1" min="1" /></label><br>
    <input type="text" id="username-input" placeholder="Enter HighSpell Username...">
      <label>Defense: <input id="stat-defense" type="number" value="1" min="1" /></label><br>
    <button id="lookup-btn">Lookup</button>
      <label>Magic: <input id="stat-magic" type="number" value="1" min="1" /></label><br>
  </div>
      <label>Range: <input id="stat-range" type="number" value="1" min="1" /></label><br><br>
  <table class="combat-calc__table">
    <tbody id="stats-tbody"></tbody>
  </table>
  <div class="combat-calc__results">
    <b>Combat Level:</b> <span id="combat-result"></span>
    <div class="combat-calc__hints" id="level-hints" style="font-style: italic;"></div>
  </div>
</div>


      <button onclick="calculateCombat()">Calculate</button>
<script>
      <p><b>Combat Level:</b> <span id="combat-result">--</span></p>
(function() {
     </div>
  const STAT_CONFIG = [
   ]]></html>
    { id: 'hp', name: 'Hitpoints', icon: '/w/images/thumb/9/96/Hitpoints_icon.png/20px-Hitpoints_icon.png' },
    { id: 'accuracy', name: 'Accuracy', icon: '/w/images/thumb/d/df/Accuracy_icon.png/20px-Accuracy_icon.png' },
    { id: 'strength', name: 'Strength', icon: '/w/images/thumb/1/1b/Strength_icon.png/20px-Strength_icon.png' },
    { id: 'defense', name: 'Defense', icon: '/w/images/thumb/e/e5/Defense_icon.png/20px-Defense_icon.png' },
    { id: 'magic', name: 'Magic', icon: '/w/images/thumb/5/5c/Magic_icon.png/20px-Magic_icon.png' },
     { id: 'range', name: 'Range', icon: '/w/images/thumb/1/13/Range_icon.png/20px-Range_icon.png' },
   ];
  const tbody = document.getElementById('stats-tbody');
  const combatResultEl = document.getElementById('combat-result');
  const levelHintsEl = document.getElementById('level-hints');
  const lookupBtn = document.getElementById('lookup-btn');
  const usernameInput = document.getElementById('username-input');


   <script><![CDATA[
   function generateStatRows() {
    function calculateCombat() {
    tbody.innerHTML = STAT_CONFIG.map(stat => `
      var hp = parseFloat(document.getElementById("stat-hp").value) || 0;
      <tr>
      var acc = parseFloat(document.getElementById("stat-accuracy").value) || 0;
        <td>
      var str = parseFloat(document.getElementById("stat-strength").value) || 0;
          <span class="combat-calc__stat-label">
      var def = parseFloat(document.getElementById("stat-defense").value) || 0;
            <img src="${stat.icon}" alt="${stat.name} icon" width="20" height="20">
      var mag = parseFloat(document.getElementById("stat-magic").value) || 0;
            <a href="/w/${stat.name}" title="${stat.name}">${stat.name}</a>
      var rng = parseFloat(document.getElementById("stat-range").value) || 0;
          </span>
 
        </td>
       var combat = (hp + acc + str + def + (mag / 4) + (rng / 4)) / 3.75;
        <td style="width: 80px;">
       document.getElementById("combat-result").innerText = combat.toFixed(2);
          <input type="number" id="stat-${stat.id}" value="${stat.id === 'hp' ? 10 : 1}" min="1" max="100" data-stat-id="${stat.id}">
        </td>
      </tr>
    `).join('');
  }
  function getStatValue(id) {
    const input = document.getElementById(`stat-${id}`);
    let value = parseInt(input.value, 10) || 1;
    value = Math.max(1, Math.min(100, value));
    input.value = value;
    return value;
  }
  function renderHints(combatLvl, base) {
    const nextLevel = Math.floor(combatLvl) + 1;
    const needed = (nextLevel * 3.75) - base;
    if (needed <= 0) {
      levelHintsEl.innerHTML = "You are at the threshold for the next level!";
      return;
    }
    const neededPrimary = Math.ceil(needed);
    const neededSecondary = Math.ceil(needed * 4);
    const icon = (name) => `<img src="${STAT_CONFIG.find(s=>s.name===name).icon}" alt="${name}" width="20" height="20">`;
    levelHintsEl.innerHTML = `
      <div>To reach level ${nextLevel}, gain one of:</div>
      <ul>
        <li><b>+${neededPrimary}</b> ${icon('Hitpoints')} Hitpoints</li>
        <li><b>+${neededPrimary}</b> ${icon('Accuracy')} Accuracy, ${icon('Strength')} Strength, or ${icon('Defense')} Defense</li>
        <li><b>+${neededSecondary}</b> ${icon('Magic')} Magic or ${icon('Range')} Range</li>
      </ul>`;
  }
  function updateCombatLevel() {
    const stats = {
       hp: getStatValue('hp'), accuracy: getStatValue('accuracy'), strength: getStatValue('strength'),
      defense: getStatValue('defense'), magic: getStatValue('magic'), range: getStatValue('range')
    };
    const base = stats.hp + stats.accuracy + stats.strength + stats.defense + (stats.magic / 4) + (stats.range / 4);
    const combat = base / 3.75;
    combatResultEl.textContent = combat.toFixed(2);
    levelHintsEl.style.fontStyle = 'normal';
    renderHints(combat, base);
  }
  async function lookupStats() {
    const username = usernameInput.value.trim();
    if (!username) { levelHintsEl.innerHTML = 'Please enter a username.'; return; }
    lookupBtn.disabled = true;
    lookupBtn.textContent = '...';
    levelHintsEl.textContent = `Searching for ${username}...`;
    try {
      const response = await fetch(`https://highspell.com/hiscores/player/${encodeURIComponent(username)}`);
      if (!response.ok) throw new Error(`Player not found or server error.`);
       const html = await response.text();
      const doc = new DOMParser().parseFromString(html, "text/html");
      let found = false;
      doc.querySelectorAll('.hs_data__row').forEach(row => {
        const skillName = row.querySelector('.hs_data__row__name')?.textContent.trim();
        const config = STAT_CONFIG.find(s => s.name === skillName);
        if (config) {
          const level = row.querySelector('.hs_data__row__level')?.textContent.trim();
          document.getElementById(`stat-${config.id}`).value = parseInt(level, 10) || 1;
          found = true;
        }
      });
      if (found) updateCombatLevel();
      else throw new Error(`Could not parse stats for ${username}.`);
    } catch (err) {
      levelHintsEl.style.fontStyle = 'italic';
      levelHintsEl.innerHTML = err.message;
      combatResultEl.textContent = 'Error';
    } finally {
      lookupBtn.disabled = false;
      lookupBtn.textContent = 'Lookup';
     }
     }
   ]]></script>
   }
</widget>
  document.addEventListener('DOMContentLoaded', () => {
    generateStatRows();
    updateCombatLevel();
    tbody.addEventListener('input', updateCombatLevel);
    lookupBtn.addEventListener('click', lookupStats);
    usernameInput.addEventListener('keydown', e => { if (e.key === 'Enter') { e.preventDefault(); lookupStats(); } });
  });
})();
</script>