JustPaste.it

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <title>Pokémon Battle Simulator</title>

  <!-- Basic Reset & Apple-Inspired Minimal Styling -->
  <style>
    * {
      0; 
      padding: 0; 
      box-sizing: border-box; 
      -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, 
                   Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
    }

    body {
      background: linear-gradient(to bottom right, #f2f2f2, #dcdcdc);
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: start;
      height: 100vh;
      padding-top: 1rem;
    }

    h1 {
      1rem;
      font-weight: 600;
      color: #444;
    }

    /* Battle Arena Container */
    .battle-arena {
      position: relative;
      width: 80%;
      height: 400px;
      border-radius: 12px;
      background: linear-gradient(to bottom right, #fdfdfd, #ffffff);
      box-shadow: 0 4px 10px rgba(0,0,0,0.15);
      overflow: hidden;
      display: flex;
      align-items: center;
      justify-content: space-between;
      padding: 0 2rem;
    }

    /* Pokémon Avatars and Info */
    .pokemon-container {
      position: relative;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: flex-start;
      width: 150px;
    }

    .pokemon-sprite {
      width: 80px; 
      height: 80px; 
      border-radius: 40px;
      background-color: #eee;
      color: #555;
      display: flex; 
      align-items: center; 
      justify-content: center;
      0.5rem;
      font-weight: 500;
      position: relative;
    }

    .hp-bar-container {
      width: 100%;
      height: 10px;
      border: 1px solid #bbb;
      border-radius: 5px;
      overflow: hidden;
      0.5rem;
    }

    .hp-bar {
      height: 100%;
      background-color: #66bb6a;
      transition: width 0.4s ease-in-out;
    }

    /* Active Pokémon Bouncing Animation */
    .active {
      animation: bounce 1.2s infinite alternate ease-in-out;
    }

    @keyframes bounce {
      0% {
        transform: translateY(0);
      }
      100% {
        transform: translateY(-10px);
      }
    }

    /* Attack Icon for Fire, Grass, Water, etc. */
    .attack-icon {
      position: absolute;
      font-size: 1.5rem;
      opacity: 0;
      transform: translate(0, 0);
      transition: transform 0.8s ease, opacity 0.8s ease;
    }

    /* Example color icons: Feel free to add more if you like */
    .attack-icon.fire {
      color: #ff5722;
    }
    .attack-icon.grass {
      color: #4caf50;
    }
    .attack-icon.water {
      color: #2196f3;
    }

  </style>
</head>
<body>
  <h1>Pokémon Battle Simulator</h1>
  <div class="battle-arena">
    <!-- Left Side (Attacker 1) -->
    <div class="pokemon-container">
      <div id="pokemon1Sprite" class="pokemon-sprite">
        <!-- Name goes here dynamically -->
      </div>
      <div class="hp-bar-container">
        <div id="pokemon1HpBar" class="hp-bar"></div>
      </div>
      <div id="pokemon1HpText"></div>
    </div>

    <!-- Right Side (Attacker 2) -->
    <div class="pokemon-container">
      <div id="pokemon2Sprite" class="pokemon-sprite">
        <!-- Name goes here dynamically -->
      </div>
      <div class="hp-bar-container">
        <div id="pokemon2HpBar" class="hp-bar"></div>
      </div>
      <div id="pokemon2HpText"></div>
    </div>
  </div>

  <script>
    /*********************************************
     * 1. Pokémon Representation
     *********************************************/
    const charmander = {
      name: "Charmander",
      hp: 39,
      maxHp: 39,
      types: ["Fire"],
      moves: [
        { name: "Ember", type: "Fire", power: 40, accuracy: 1.0 },
        { name: "Scratch", type: "Normal", power: 40, accuracy: 1.0 }
      ],
      resonance: 0 // Start resonance at 0
    };

    const bulbasaur = {
      name: "Bulbasaur",
      hp: 45,
      maxHp: 45,
      types: ["Grass"],
      moves: [
        { name: "Vine Whip", type: "Grass", power: 45, accuracy: 1.0 },
        { name: "Tackle", type: "Normal", power: 40, accuracy: 1.0 }
      ],
      resonance: 0
    };

    /*********************************************
     * 2. Type Effectiveness Representation
     * 
     * Fire > Grass (2x)
     * Grass > Water (2x)
     * Water > Fire (2x)
     * Fire < Water (0.5x)
     * Grass < Fire (0.5x)
     * Water < Grass (0.5x)
     * All else 1x
     *********************************************/
    const typeEffectivenessChart = {
      Fire:   { Grass: 2, Water: 0.5, Fire: 1 },
      Grass:  { Water: 2, Fire: 0.5, Grass: 1 },
      Water:  { Fire: 2, Grass: 0.5, Water: 1 }
      // For simplicity, we only map these interactions.
      // Any type not listed defaults to 1.0 as needed in the function below.
    };

    /**
     * calculateTypeEffectiveness(moveType, targetTypes)
     * Returns the total effectiveness multiplier 
     * based on the target's dual-types (or single type).
     */
    function calculateTypeEffectiveness(moveType, targetTypes) {
      let multiplier = 1;
      targetTypes.forEach(targetType => {
        const effectivenessMap = typeEffectivenessChart[moveType] || {};
        const typeMultiplier = effectivenessMap[targetType] || 1;
        multiplier *= typeMultiplier;
      });
      return multiplier;
    }

    /*********************************************
     * 3. Damage Calculation w/ Synergy Resonance
     *********************************************/
    function calculateDamage(move, attacker, defender) {
      // Check resonance: If attacker has 3 or more, 
      // and the move's type matches one of attacker’s own types => +10 to power
      let movePower = move.power;
      if (attacker.resonance >= 3 && attacker.types.includes(move.type)) {
        console.log(`${attacker.name}'s Synergy Resonance activates! +10 Power to ${move.name}.`);
        movePower += 10;
        attacker.resonance = 0; // Reset resonance
      }

      const effectiveness = calculateTypeEffectiveness(move.type, defender.types);
      const damage = movePower * effectiveness;

      // Adjust resonance if super/not very effective
      if (effectiveness > 1) {
        attacker.resonance++;
        console.log(`${attacker.name}'s resonance increased to ${attacker.resonance} (super effective)!`);
      } else if (effectiveness < 1) {
        defender.resonance++;
        console.log(`${defender.name}'s resonance increased to ${defender.resonance} (resisted attack)!`);
      }

      return damage;
    }

    /**
     * Executes a single attack turn
     * @param {Object} attacker 
     * @param {Object} defender 
     * @param {Object} move 
     */
    function executeTurn(attacker, defender, move) {
      // Announce the move
      console.log(`${attacker.name} used ${move.name}!`);

      // Roll accuracy
      if (Math.random() <= move.accuracy) {
        const dmg = calculateDamage(move, attacker, defender);
        const finalDamage = Math.min(dmg, defender.hp); // can't go below 0
        defender.hp -= finalDamage;
        
        // Check effectiveness text
        const effectiveness = calculateTypeEffectiveness(move.type, defender.types);
        if (effectiveness > 1) {
          console.log("It's super effective!");
        } else if (effectiveness < 1) {
          console.log("It's not very effective...");
        }
        
        console.log(`${defender.name} took ${finalDamage.toFixed(2)} damage. (HP left: ${defender.hp})`);
        if (defender.hp <= 0) {
          console.log(`${defender.name} fainted!`);
        }

        // Trigger a little attack animation
        animateAttack(move.type, attacker, defender);

      } else {
        console.log(`${attacker.name}'s attack missed!`);
      }

      // Update UI after turn
      updateHpBarsUI();
    }

    /*********************************************
     * 4. Animate Attack
     * For a "Fire" type move, add a fire icon that
     * travels from attacker to defender.
     *********************************************/
    function animateAttack(moveType, attacker, defender) {
      const attackerSprite = 
        (attacker === pokemon1) ? document.getElementById("pokemon1Sprite") 
                                : document.getElementById("pokemon2Sprite");
      const defenderSprite = 
        (defender === pokemon1) ? document.getElementById("pokemon1Sprite") 
                                : document.getElementById("pokemon2Sprite");

      // Create an icon (🔥, 💧, or something else)
      let iconSymbol = "💥";
      if (moveType.toLowerCase() === "fire")  iconSymbol = "🔥";
      if (moveType.toLowerCase() === "water") iconSymbol = "💧";
      if (moveType.toLowerCase() === "grass") iconSymbol = "🌱";

      const icon = document.createElement("div");
      icon.classList.add("attack-icon", moveType.toLowerCase());
      icon.textContent = iconSymbol;
      document.body.appendChild(icon);

      // Position it at the center of the attacker sprite
      const attackerRect = attackerSprite.getBoundingClientRect();
      const defenderRect = defenderSprite.getBoundingClientRect();

      icon.style.left = attackerRect.left + attackerRect.width/2 + "px";
      icon.style.top = attackerRect.top + attackerRect.height/2 + "px";
      icon.style.opacity = 1;

      // Force reflow so transition will trigger
      void icon.offsetWidth;

      // Animate it to the defender
      icon.style.transform = `translate(${defenderRect.left - attackerRect.left}px, 
                                        ${defenderRect.top - attackerRect.top}px)`;

      // Fade out after traveling
      setTimeout(() => {
        icon.style.opacity = 0;
      }, 500);

      // Remove from DOM after completed
      setTimeout(() => {
        if (icon.parentNode) {
          icon.parentNode.removeChild(icon);
        }
      }, 1300);
    }

    /*********************************************
     * 5. UI Updaters
     *********************************************/
    let pokemon1 = null;
    let pokemon2 = null;

    function updateHpBarsUI() {
      const p1HpBar = document.getElementById("pokemon1HpBar");
      const p2HpBar = document.getElementById("pokemon2HpBar");
      const p1HpText = document.getElementById("pokemon1HpText");
      const p2HpText = document.getElementById("pokemon2HpText");

      p1HpBar.style.width = `${(pokemon1.hp / pokemon1.maxHp) * 100}%`;
      p2HpBar.style.width = `${(pokemon2.hp / pokemon2.maxHp) * 100}%`;

      p1HpText.textContent = `${pokemon1.hp}/${pokemon1.maxHp}`;
      p2HpText.textContent = `${pokemon2.hp}/${pokemon2.maxHp}`;
    }

    function setActivePokemonUI(activeIndex) {
      const p1Sprite = document.getElementById("pokemon1Sprite");
      const p2Sprite = document.getElementById("pokemon2Sprite");

      p1Sprite.classList.remove("active");
      p2Sprite.classList.remove("active");

      if (activeIndex === 1) {
        p1Sprite.classList.add("active");
      } else {
        p2Sprite.classList.add("active");
      }
    }

    /*********************************************
     * 6. Start Battle - Turn Loop
     *********************************************/
    function startBattle(p1, p2) {
      pokemon1 = p1;
      pokemon2 = p2;

      // Initialize UI
      document.getElementById("pokemon1Sprite").textContent = pokemon1.name;
      document.getElementById("pokemon2Sprite").textContent = pokemon2.name;
      updateHpBarsUI();

      // Simple alternating turn-based battle
      console.log(`A wild battle begins between ${pokemon1.name} and ${pokemon2.name}!`);

      let active = 1; // 1 => pokemon1's turn, 2 => pokemon2's turn
      let turnCount = 0;

      const turnInterval = setInterval(() => {
        if (pokemon1.hp <= 0 || pokemon2.hp <= 0) {
          // Battle ends
          clearInterval(turnInterval);
          console.log("Battle concluded.");
          return;
        }

        turnCount++;
        console.log(`\n--- TURN ${turnCount} ---`);

        // Set active UI
        setActivePokemonUI(active);

        // Choose a random move
        if (active === 1) {
          const moveIndex = Math.floor(Math.random() * pokemon1.moves.length);
          executeTurn(pokemon1, pokemon2, pokemon1.moves[moveIndex]);
          active = 2;
        } else {
          const moveIndex = Math.floor(Math.random() * pokemon2.moves.length);
          executeTurn(pokemon2, pokemon1, pokemon2.moves[moveIndex]);
          active = 1;
        }
      }, 2000); // 2 seconds per turn for demonstration
    }

    // Example scenario: Charmander (Fire) vs. Bulbasaur (Grass)
    // The synergy resonance will naturally play out if you watch the console logs.
    startBattle(charmander, bulbasaur);
  </script>
</body>
</html>