comparison 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
comparison
equal deleted inserted replaced
211:a6d8d32a0261 212:84826b3c655b
1 // Dog frames
2 const imagesSrc = [
3 "/public/sprite_shiba0.png",
4 "/public/sprite_shiba1.png",
5 "/public/sprite_shiba2.png",
6 "/public/sprite_shiba3.png",
7 ];
8 const images =imagesSrc.map((src, index) => {
9 const img = new Image();
10 img.src = src;
11 img.onload = () => {
12 if (index == imagesSrc.length-1)
13 startAnimation();
14 };
15 return img
16 });
17
18 // Load treat image
19 const treatImage = new Image();
20 treatImage.src = "/public/dog-treat.png";
21
22 // Load star images for background
23 let starImages = [
24 { src: "/public/start-large.png", img: new Image() },
25 { src: "/public/start-small-1.png", img: new Image() },
26 { src: "/public/start-small-2.png", img: new Image() },
27 { src: "/public/start-small-3.png", img: new Image() }
28 ];
29 starImages.forEach(star => star.img.src = star.src);
30
31 const background = document.getElementById('background');
32 const bgCtx = background.getContext("2d");
33 const ctx = game.getContext("2d");
34 const uiCtx = gameUI.getContext("2d");
35
36
37 // Dog initial position and movement
38 const initialDogVx = 2;
39 let x = Math.random() * (game.width);
40 let y = Math.random() * (game.height);
41 let vx = initialDogVx;
42 let vy = 0;
43 const gravity = 0.5;
44 const jumpForce = -12;
45 let isFlipped = false;
46
47 // Random jump interval
48 // TODO: let's do it by frame?
49 let nextJump = Math.random() * 2000 + 1000;
50
51 // Treat system
52 const originalTreatSize = 30;
53 let treatSize = originalTreatSize;
54 const treatMargin = 20;
55 const reachDistance = 50;
56 let treatRainActive = false;
57 let treats = [];
58 let lastTreatSpawn = 0;
59 const treatFallSpeed = 0.25;
60 const treatGravity = 0.13;
61
62 // Background stars
63 let stars = [];
64 function createStars() {
65 stars = [];
66 const starCount = window.innerWidth * 0.05;
67
68 for (let i = 0; i < starCount; i++) {
69 const starType = starImages[Math.floor(Math.random() * starImages.length)];
70 const scale = 0.5 + Math.random() * 0.8; // Random scale
71
72 stars.push({
73 x: Math.random() * background.width,
74 y: Math.random() * background.height,
75 vx: (Math.random() - 0.5) * 0.5,
76 vy: (Math.random() - 0.5) * 0.5,
77 rotation: Math.random() * Math.PI * 2,
78 rotationSpeed: (Math.random() - 0.5) * 0.02,
79 image: starType.img,
80 size: starType.img.width,
81 scale: scale,
82 opacity: 0.3 + Math.random() * 0.4 // Random opacity
83 });
84 }
85 }
86
87 function animateBackground() {
88 bgCtx.clearRect(0, 0, background.width, background.height);
89
90 stars.forEach(star => {
91 // Update position
92 star.x += star.vx;
93 star.y += star.vy;
94 star.rotation += star.rotationSpeed;
95
96 // Wrap around edges
97 if (star.x < -100) star.x = background.width + 100;
98 if (star.x > background.width + 100) star.x = -100;
99 if (star.y < -100) star.y = background.height + 100;
100 if (star.y > background.height + 100) star.y = -100;
101
102 // Draw star
103 bgCtx.save();
104 bgCtx.globalAlpha = star.opacity;
105 bgCtx.translate(star.x, star.y);
106 bgCtx.rotate(star.rotation);
107
108 const size = star.size * star.scale;
109 bgCtx.drawImage(star.image, -size / 2, -size / 2, size, size);
110
111 bgCtx.restore();
112 });
113
114 requestAnimationFrame(animateBackground);
115 }
116
117 function getMousePosition(event) {
118 const rect = game.getBoundingClientRect();
119 const x = event.clientX - rect.left;
120 const y = event.clientY - rect.top;
121 return { x: x, y: y };
122 }
123
124 // UI canvas click - toggle treat rain
125 gameUI.addEventListener('click', (e) => {
126 treatRainActive = !treatRainActive;
127 if (treatRainActive)
128 game.classList.add('active');
129 else
130 game.classList.remove('active');
131 });
132
133 game.addEventListener('click', (e) => {
134 if (treatRainActive) {
135 const mousePos = getMousePosition(e);
136 treats.push({ x: mousePos.x, y: mousePos.y, vy: treatFallSpeed });
137 }
138 });
139
140 function startAnimation() {
141 requestAnimationFrame(animate);
142 }
143
144
145 let index = 0;
146 let last = 0;
147 const frameDuration = 120; // ms per frame
148 function animate(ts) {
149 if (ts - last > frameDuration) {
150 index = (index + 1) % images.length;
151 last = ts;
152 }
153
154 // Get image dimensions
155 const imgWidth = images[0].width;
156 const imgHeight = images[0].height;
157
158 // Update falling treats
159 treats.forEach(treat => {
160 treat.vy += treatGravity;
161 treat.y += treat.vy;
162 });
163 treats = treats.filter(treat => treat.y < game.height + treatSize);
164
165 // Find closest treat for dog to chase
166 let targetTreat = null;
167 let closestDist = Infinity;
168 treats.forEach(treat => {
169 const dx = treat.x + treatSize / 2 - (x + imgWidth / 2);
170 const dy = treat.y + treatSize / 2 - (y + imgHeight / 2);
171 const dist = Math.sqrt(dx * dx + dy * dy);
172
173 if (dist < closestDist) {
174 closestDist = dist;
175 targetTreat = treat;
176 }
177 });
178
179 // Dog behavior
180 if (targetTreat) {
181 const dx = targetTreat.x + treatSize / 2 - (x + imgWidth / 2);
182 const dy = targetTreat.y + treatSize / 2 - (y + imgHeight / 2);
183 const distance = Math.sqrt(dx * dx + dy * dy);
184
185 // Dog eating
186 if (distance < reachDistance)
187 {
188 treats = treats.filter(t => t !== targetTreat);
189 vx = initialDogVx;
190 }
191 // Looking for it
192 else
193 {
194 const speed = initialDogVx * 1.5;
195 vx = dx > 0 ? speed : -speed;
196
197 // Update flip direction
198 if ((dx > 0 && isFlipped) || (dx < 0 && !isFlipped))
199 {
200 isFlipped = !isFlipped;
201 }
202
203 // Jump if treat is above and dog is on ground
204 const onGround = y + imgHeight >= game.height - 5;
205 if (dy < -50 && onGround && Math.abs(vy) < 1)
206 {
207 vy = jumpForce;
208 }
209 }
210 }
211
212 // --- Entity updates --- //
213 vy += gravity;
214 x += vx;
215 y += vy;
216
217 // Bounce off walls
218 if (x <= 0 || x + imgWidth >= game.width)
219 {
220 if (!targetTreat)
221 {
222 vx = -vx;
223 isFlipped = !isFlipped;
224 }
225 x = x <= 0 ? 0 : game.width - imgWidth;
226 }
227
228 // Bounce off floor
229 if (y + imgHeight >= game.height)
230 {
231 y = game.height - imgHeight;
232 vy = -vy * 0.7;
233 if (Math.abs(vy) < 2) vy = 0;
234 }
235
236 // Random jumps (only when not chasing treat)
237 if (!targetTreat && Date.now() > nextJump && Math.abs(vy) < 1)
238 {
239 vy = jumpForce;
240 nextJump = Date.now() + 1000 + Math.random() * 3000;
241 }
242
243 // --- Draw entity --- //
244 ctx.clearRect(0, 0, game.width, game.height);
245 ctx.save();
246
247 if (isFlipped) {
248 ctx.translate(x + imgWidth, y);
249 ctx.scale(-1, 1);
250 ctx.drawImage(images[index], 0, 0);
251 } else {
252 ctx.drawImage(images[index], x, y);
253 }
254
255 ctx.restore();
256 treats.forEach(treat => {
257 ctx.drawImage(treatImage, treat.x, treat.y, treatSize, treatSize);
258 });
259
260 drawUI();
261 // repeat
262 requestAnimationFrame(animate);
263 }
264
265 // Draw UI elements (treat toggle button)
266 function drawUI() {
267 uiCtx.drawImage(treatImage, 0, 0, treatSize, treatSize);
268 }
269
270 // --- Window functions --- //
271 function resizeCanvas() {
272 background.width = window.innerWidth;
273 background.height = window.innerHeight;
274 game.width = window.innerWidth;
275 game.height = window.innerHeight;
276
277 // resize treats
278 treatSize = window.innerWidth < 700 ? originalTreatSize * 0.6 : originalTreatSize;
279
280 // Size UI canvas to fit treat button + text
281 gameUI.width = treatSize + 10;
282 gameUI.height = treatSize + 25;
283
284 createStars(); // Recreate stars on resize
285 drawUI(); // Redraw UI after resize
286 }
287 resizeCanvas();
288 window.addEventListener('resize', resizeCanvas);
289
290 window.onload = () => {
291 resizeCanvas(); // ensure canvas has real size
292 createStars(); // stars need correct canvas size
293 animateBackground(); // safe to start background animation
294 };
295