view birthday_page/pages/index.html @ 71:75de5903355c

Giagantic changes that update Dowa library to be more align with stb style array and hashmap. Updated Seobeo to be caching on server side instead of file level caching. Deleted bunch of things I don't really use.
author June Park <parkjune1995@gmail.com>
date Sun, 28 Dec 2025 20:34:22 -0800
parents 6639f5389f47
children
line wrap: on
line source

<!DOCTYPE html>
<html lang="en">
  <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <link rel="preload" href="/punpun.png" as="image">
      <link rel="preload" href="/random_1.jpeg" as="image">
      <link rel="preload" href="/random_2.png" as="image">
      <link rel="preload" href="/random_3.jpeg" as="image">
      <title>Amu Birthday Game</title>
      <style>
          * {
              margin: 0;
              padding: 0;
              box-sizing: border-box;
          }
          
          body {
              font-family: Arial, sans-serif;
              display: flex;
              justify-content: center;
              align-items: center;
              min-height: 100vh;
              background: linear-gradient(to bottom, #87CEEB 0%, #E0F6FF 100%);
              touch-action: manipulation;
          }
          
          #gameContainer {
              position: relative;
              width: 100%;
              max-width: 800px;
              height: 400px;
              background-image: url('/background.avif'); /* Replace with your actual image path */
              background-size: cover; /* Ensures the image covers the entire element */
              background-repeat: no-repeat; /* Prevents tiling */
              overflow: hidden;
              border: 3px solid #333;
              box-shadow: 0 10px 30px rgba(0,0,0,0.3);
          }
          
          #ground {
              position: absolute;
              bottom: 0;
              width: 100%;
              height: 50px;
              background: #c2b280;
              border-top: 3px solid #8b7355;
          }

          #dino {
              position: absolute;
              bottom: 50px;
              left: 50px;
              width: 40px;
              height: 50px;
              background-image: url('/punpun.png'); /* Replace with your actual image path */
              background-size: cover; /* Ensures the image covers the entire element */
              background-repeat: no-repeat; /* Prevents tiling */
              border-radius: 5px;
              transition: none;
          }
        
          .obstacle {
              position: absolute;
              bottom: 50px;
              width: 100px;
              height: 50px;
              background: #8b4513;
              border-radius: 3px;
          }

          .random_1 {
              background-image: url('/random_1.jpeg'); /* Replace with your actual image path */
              background-size: cover; /* Ensures the image covers the entire element */
          }

          .random_2 {
              background-image: url('/random_2.png'); /* Replace with your actual image path */
              background-size: cover; /* Ensures the image covers the entire element */
          }

          .random_3 {
              background-image: url('/random_3.jpeg'); /* Replace with your actual image path */
              background-size: cover; /* Ensures the image covers the entire element */
          }


          
          #goal {
              position: absolute;
              bottom: 50px;
              width: 50px;
              height: 60px;
              background: linear-gradient(45deg, #ff69b4, #ff1493);
              border-radius: 10px;
              display: none;
          }
          
          #score {
              position: absolute;
              top: 20px;
              right: 20px;
              font-size: 24px;
              font-weight: bold;
              color: #333;
          }
          
          #message {
              position: absolute;
              top: 50%;
              left: 50%;
              transform: translate(-50%, -50%);
              font-size: 32px;
              font-weight: bold;
              color: #ff1493;
              text-align: center;
              display: none;
              background: rgba(255, 255, 255, 0.9);
              padding: 30px;
              border-radius: 15px;
              box-shadow: 0 5px 20px rgba(0,0,0,0.3);
          }
          
          #startBtn {
              position: absolute;
              top: 50%;
              left: 50%;
              transform: translate(-50%, -50%);
              padding: 15px 40px;
              font-size: 24px;
              background: #4CAF50;
              color: white;
              border: none;
              border-radius: 10px;
              cursor: pointer;
              font-weight: bold;
              z-index: 1000;
              box-shadow: 0 5px 15px rgba(0,0,0,0.3);
          }
          
          #startBtn:hover {
              background: #45a049;
          }
          
          .confetti {
              position: absolute;
              width: 10px;
              height: 10px;
              background: #ff0;
              animation: fall 3s linear;
          }
          
          @keyframes fall {
              to {
                  transform: translateY(400px) rotate(360deg);
                  opacity: 0;
              }
          }
          
          @media (max-width: 600px) {
              #gameContainer {
                  height: 300px;
              }
              
              #score {
                  font-size: 18px;
              }
              
              #message {
                  font-size: 24px;
                  padding: 20px;
              }
          }
      </style>
  </head>
  <body>
      <button id="startBtn">START GAME</button>
      <div id="gameContainer">
          <div id="ground"></div>
          <div id="dino"></div>
          <div id="goal"></div>
          <div id="score">Distance: 0m</div>
          <div id="message"></div>
      </div>
  
      <script>
          const dino = document.getElementById('dino');
          const gameContainer = document.getElementById('gameContainer');
          const scoreEl = document.getElementById('score');
          const messageEl = document.getElementById('message');
          const startBtn = document.getElementById('startBtn');
          const goal = document.getElementById('goal');
          
          let isJumping = false;
          let gameRunning = false;
          let obstacles = [];
          let distance = 0;
          let gameSpeed = 5;
          const goalDistance = 500;
          
          function jump() {
              if (isJumping || !gameRunning) return;
              
              isJumping = true;
              let jumpHeight = 0;
              let jumpUp = setInterval(() => {
                  if (jumpHeight >= 120) {
                      clearInterval(jumpUp);
                      let jumpDown = setInterval(() => {
                          if (jumpHeight <= 0) {
                              clearInterval(jumpDown);
                              isJumping = false;
                              jumpHeight = 0;
                          }
                          jumpHeight -= 5;
                          dino.style.bottom = (50 + jumpHeight) + 'px';
                      }, 20);
                  }
                  jumpHeight += 5;
                  dino.style.bottom = (50 + jumpHeight) + 'px';
              }, 20);
          }
          
          document.addEventListener('keydown', (e) => {
              if (e.code === 'Space' || e.code === 'ArrowUp') {
                  e.preventDefault();
                  jump();
              }
          });
          
          gameContainer.addEventListener('click', jump);
          gameContainer.addEventListener('touchstart', (e) => {
              e.preventDefault();
              jump();
          });
          
          function createObstacle() {
              const randomVal = Math.floor(Math.random() * 3) + 1;
              const obstacle = document.createElement('div');
              obstacle.classList.add(`obstacle`);
              obstacle.classList.add(`random_${randomVal}`);
              obstacle.style.right = '-80px';
              gameContainer.appendChild(obstacle);
              obstacles.push(obstacle);
          }
          
          function createConfetti() {
              for (let i = 0; i < 50; i++) {
                  setTimeout(() => {
                      const confetti = document.createElement('div');
                      confetti.classList.add('confetti');
                      confetti.style.left = Math.random() * 100 + '%';
                      confetti.style.background = `hsl(${Math.random() * 360}, 100%, 50%)`;
                      gameContainer.appendChild(confetti);
                      setTimeout(() => confetti.remove(), 3000);
                  }, i * 50);
              }
          }
          
          function checkCollision(obstacle) {
              const dinoRect = dino.getBoundingClientRect();
              const obstacleRect = obstacle.getBoundingClientRect();
              
              return !(dinoRect.right < obstacleRect.left ||
                      dinoRect.left > obstacleRect.right ||
                      dinoRect.bottom < obstacleRect.top ||
                      dinoRect.top > obstacleRect.bottom);
          }
          
          function checkGoalReached() {
              const dinoRect = dino.getBoundingClientRect();
              const goalRect = goal.getBoundingClientRect();
              
              return !(dinoRect.right < goalRect.left ||
                      dinoRect.left > goalRect.right);
          }
          
          function gameOver() {
              gameRunning = false;
              messageEl.textContent = 'Game Over! YOU SUCK';
              messageEl.style.display = 'block';
              obstacles.forEach(obs => obs.remove());
              obstacles = [];
              goal.style.display = 'none';
          }
          
          function winGame() {
              gameRunning = false;
              messageEl.innerHTML = '🎉 HAPPY BIRTHDAY! 🎉<br>You made it to the end!<br> Also I didnt buy any present ^_^';
              messageEl.style.display = 'block';
              createConfetti();
              obstacles.forEach(obs => obs.remove());
              obstacles = [];
          }
          
          function gameLoop() {
              if (!gameRunning) return;
              
              distance += 0.1;
              scoreEl.textContent = `Distance: ${Math.floor(distance)}m`;
              
              if (distance >= goalDistance && goal.style.display === 'none') {
                  goal.style.display = 'block';
                  goal.style.right = '-50px';
                  goal.style.color = 'dodgerblue';
              }
              
              obstacles.forEach((obstacle, index) => {
                  let right = parseInt(window.getComputedStyle(obstacle).right);
                  obstacle.style.right = (right + gameSpeed) + 'px';
                  
                  if (right > gameContainer.offsetWidth) {
                      obstacle.remove();
                      obstacles.splice(index, 1);
                  }
                  
                  if (checkCollision(obstacle)) {
                      gameOver();
                  }
              });
              
              if (goal.style.display === 'block') {
                  let goalRight = parseInt(window.getComputedStyle(goal).right);
                  goal.style.right = (goalRight + gameSpeed) + 'px';
                  
                  if (checkGoalReached()) {
                      winGame();
                      return;
                  }
              }
              
              requestAnimationFrame(gameLoop);
          }
          
          function startGame() {
              gameRunning = true;
              distance = 0;
              gameSpeed = 5;
              gracePeriod = 15;
              startBtn.style.display = 'none';
              messageEl.style.display = 'none';
              goal.style.display = 'none';
              
              let obstacleInterval = setInterval(() => {
                if (!gameRunning) {
                  clearInterval(obstacleInterval);
                  return;
                }
                if (distance < gracePeriod) return; // just skip early

                if (Math.random() > 0.3) {
                    createObstacle();
                }
              }, 1500);
              
              gameLoop();
          }
          
          startBtn.addEventListener('click', startGame);
      </script>
  </body>
</html>