Mercurial
changeset 212:84826b3c655b
[MrJuneJune] Forgot to add assets.
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Sun, 15 Feb 2026 21:38:36 -0800 |
| parents | a6d8d32a0261 |
| children | 60918f88070e |
| files | mrjunejune/src/public/dog-game.js mrjunejune/src/public/dog-treat.png mrjunejune/src/public/shiba_open_sprites.zip mrjunejune/src/public/sprite_shiba0.png mrjunejune/src/public/sprite_shiba1.png mrjunejune/src/public/sprite_shiba2.png mrjunejune/src/public/sprite_shiba3.png mrjunejune/src/public/start-large.png mrjunejune/src/public/start-small-1.png mrjunejune/src/public/start-small-2.png mrjunejune/src/public/start-small-3.png |
| diffstat | 11 files changed, 295 insertions(+), 0 deletions(-) [+] |
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 +}; +