diff mrjunejune/src/public/dog-game.js @ 212:84826b3c655b

[MrJuneJune] Forgot to add assets.
author MrJuneJune <me@mrjunejune.com>
date Sun, 15 Feb 2026 21:38:36 -0800
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mrjunejune/src/public/dog-game.js	Sun Feb 15 21:38:36 2026 -0800
@@ -0,0 +1,295 @@
+// Dog frames
+const imagesSrc = [
+  "/public/sprite_shiba0.png",
+  "/public/sprite_shiba1.png",
+  "/public/sprite_shiba2.png",
+  "/public/sprite_shiba3.png",
+];
+const images =imagesSrc.map((src, index) => {
+  const img = new Image();
+  img.src = src;
+  img.onload = () => {
+    if (index == imagesSrc.length-1)
+      startAnimation();
+  };
+  return img
+});
+
+// Load treat image
+const treatImage = new Image();
+treatImage.src = "/public/dog-treat.png";
+
+// Load star images for background
+let starImages = [
+  { src: "/public/start-large.png", img: new Image() },
+  { src: "/public/start-small-1.png", img: new Image() },
+  { src: "/public/start-small-2.png", img: new Image() },
+  { src: "/public/start-small-3.png", img: new Image() }
+];
+starImages.forEach(star => star.img.src = star.src);
+
+const background = document.getElementById('background');
+const bgCtx = background.getContext("2d");
+const ctx = game.getContext("2d");
+const uiCtx = gameUI.getContext("2d");
+
+
+// Dog initial position and movement
+const initialDogVx = 2;
+let x = Math.random() * (game.width);
+let y = Math.random() * (game.height);
+let vx = initialDogVx; 
+let vy = 0; 
+const gravity = 0.5;
+const jumpForce = -12;
+let isFlipped = false;
+
+// Random jump interval
+// TODO: let's do it by frame?
+let nextJump = Math.random() * 2000 + 1000;
+
+// Treat system
+const originalTreatSize = 30;
+let treatSize = originalTreatSize;
+const treatMargin = 20;
+const reachDistance = 50;
+let treatRainActive = false;
+let treats = []; 
+let lastTreatSpawn = 0;
+const treatFallSpeed = 0.25;
+const treatGravity = 0.13;
+
+// Background stars
+let stars = [];
+function createStars() {
+  stars = [];
+  const starCount = window.innerWidth * 0.05;
+
+  for (let i = 0; i < starCount; i++) {
+    const starType = starImages[Math.floor(Math.random() * starImages.length)];
+    const scale = 0.5 + Math.random() * 0.8; // Random scale
+
+    stars.push({
+      x: Math.random() * background.width,
+      y: Math.random() * background.height,
+      vx: (Math.random() - 0.5) * 0.5,
+      vy: (Math.random() - 0.5) * 0.5,
+      rotation: Math.random() * Math.PI * 2,
+      rotationSpeed: (Math.random() - 0.5) * 0.02,
+      image: starType.img,
+      size: starType.img.width,
+      scale: scale,
+      opacity: 0.3 + Math.random() * 0.4 // Random opacity
+    });
+  }
+}
+
+function animateBackground() {
+  bgCtx.clearRect(0, 0, background.width, background.height);
+
+  stars.forEach(star => {
+    // Update position
+    star.x += star.vx;
+    star.y += star.vy;
+    star.rotation += star.rotationSpeed;
+
+    // Wrap around edges
+    if (star.x < -100) star.x = background.width + 100;
+    if (star.x > background.width + 100) star.x = -100;
+    if (star.y < -100) star.y = background.height + 100;
+    if (star.y > background.height + 100) star.y = -100;
+
+    // Draw star
+    bgCtx.save();
+    bgCtx.globalAlpha = star.opacity;
+    bgCtx.translate(star.x, star.y);
+    bgCtx.rotate(star.rotation);
+
+    const size = star.size * star.scale;
+    bgCtx.drawImage(star.image, -size / 2, -size / 2, size, size);
+
+    bgCtx.restore();
+  });
+
+  requestAnimationFrame(animateBackground);
+}
+
+function getMousePosition(event) {
+  const rect = game.getBoundingClientRect();
+  const x = event.clientX - rect.left;
+  const y = event.clientY - rect.top;
+  return { x: x, y: y };
+}
+
+// UI canvas click - toggle treat rain
+gameUI.addEventListener('click', (e) => {
+  treatRainActive = !treatRainActive;
+  if (treatRainActive)
+    game.classList.add('active');
+  else
+    game.classList.remove('active');
+});
+
+game.addEventListener('click', (e) => {
+  if (treatRainActive) {
+    const mousePos = getMousePosition(e);
+    treats.push({ x: mousePos.x, y: mousePos.y, vy: treatFallSpeed });
+  }
+});
+
+function startAnimation() {
+  requestAnimationFrame(animate);
+}
+
+
+let index = 0;
+let last = 0;
+const frameDuration = 120; // ms per frame
+function animate(ts) {
+  if (ts - last > frameDuration) {
+    index = (index + 1) % images.length;
+    last = ts;
+  }
+
+  // Get image dimensions
+  const imgWidth = images[0].width;
+  const imgHeight = images[0].height;
+
+  // Update falling treats
+  treats.forEach(treat => {
+    treat.vy += treatGravity;
+    treat.y += treat.vy;
+  });
+  treats = treats.filter(treat => treat.y < game.height + treatSize);
+
+  // Find closest treat for dog to chase
+  let targetTreat = null;
+  let closestDist = Infinity;
+  treats.forEach(treat => {
+    const dx = treat.x + treatSize / 2 - (x + imgWidth / 2);
+    const dy = treat.y + treatSize / 2 - (y + imgHeight / 2);
+    const dist = Math.sqrt(dx * dx + dy * dy);
+
+    if (dist < closestDist) {
+      closestDist = dist;
+      targetTreat = treat;
+    }
+  });
+
+  // Dog behavior
+  if (targetTreat) {
+    const dx = targetTreat.x + treatSize / 2 - (x + imgWidth / 2);
+    const dy = targetTreat.y + treatSize / 2 - (y + imgHeight / 2);
+    const distance = Math.sqrt(dx * dx + dy * dy);
+
+    // Dog eating
+    if (distance < reachDistance)
+    {
+      treats = treats.filter(t => t !== targetTreat);
+      vx = initialDogVx;
+    }
+    // Looking for it
+    else
+    {
+      const speed = initialDogVx * 1.5;
+      vx = dx > 0 ? speed : -speed;
+
+      // Update flip direction
+      if ((dx > 0 && isFlipped) || (dx < 0 && !isFlipped))
+      {
+        isFlipped = !isFlipped;
+      }
+
+      // Jump if treat is above and dog is on ground
+      const onGround = y + imgHeight >= game.height - 5;
+      if (dy < -50 && onGround && Math.abs(vy) < 1)
+      {
+        vy = jumpForce;
+      }
+    }
+  }
+
+  // --- Entity updates --- // 
+  vy += gravity;
+  x += vx;
+  y += vy;
+
+  // Bounce off walls
+  if (x <= 0 || x + imgWidth >= game.width)
+  {
+    if (!targetTreat)
+    {
+      vx = -vx;
+      isFlipped = !isFlipped;
+    }
+    x = x <= 0 ? 0 : game.width - imgWidth;
+  }
+
+  // Bounce off floor
+  if (y + imgHeight >= game.height)
+  {
+    y = game.height - imgHeight;
+    vy = -vy * 0.7; 
+    if (Math.abs(vy) < 2) vy = 0;
+  }
+
+  // Random jumps (only when not chasing treat)
+  if (!targetTreat && Date.now() > nextJump && Math.abs(vy) < 1)
+  {
+    vy = jumpForce;
+    nextJump = Date.now() + 1000 + Math.random() * 3000;
+  }
+
+  // --- Draw entity --- // 
+  ctx.clearRect(0, 0, game.width, game.height);
+  ctx.save();
+
+  if (isFlipped) {
+    ctx.translate(x + imgWidth, y);
+    ctx.scale(-1, 1);
+    ctx.drawImage(images[index], 0, 0);
+  } else {
+    ctx.drawImage(images[index], x, y);
+  }
+
+  ctx.restore();
+  treats.forEach(treat => {
+    ctx.drawImage(treatImage, treat.x, treat.y, treatSize, treatSize);
+  });
+
+  drawUI();
+  // repeat
+  requestAnimationFrame(animate);
+}
+
+// Draw UI elements (treat toggle button)
+function drawUI() {
+  uiCtx.drawImage(treatImage, 0, 0, treatSize, treatSize);
+}
+
+// --- Window functions --- //
+function resizeCanvas() {
+  background.width = window.innerWidth;
+  background.height = window.innerHeight;
+  game.width = window.innerWidth;
+  game.height = window.innerHeight;
+
+  // resize treats
+  treatSize = window.innerWidth < 700 ? originalTreatSize * 0.6 : originalTreatSize;
+
+  // Size UI canvas to fit treat button + text
+  gameUI.width = treatSize + 10;
+  gameUI.height = treatSize + 25;
+
+  createStars(); // Recreate stars on resize
+  drawUI(); // Redraw UI after resize
+}
+resizeCanvas();
+window.addEventListener('resize', resizeCanvas);
+
+window.onload = () => {
+  resizeCanvas();     // ensure canvas has real size
+  createStars();      // stars need correct canvas size
+  animateBackground(); // safe to start background animation
+};
+