One commited on
Commit
d3c2f4c
·
verified ·
1 Parent(s): 1db91f4

Upload index.html

Browse files
Files changed (1) hide show
  1. index.html +137 -5
index.html CHANGED
@@ -84,6 +84,8 @@ function genKoiPatches(type) {
84
  return arr;
85
  }
86
  let fishes = [], bubbles = [], seaweeds = [], motes = [], rays = [], rocks = [], grass = [], fgGrass = [];
 
 
87
  let curNight = 0; // v7: shared night factor (dims the top-light + sheen)
88
  let plantPoints = [], _ppUsed = 0; // collision points — a pooled set, reused in place
89
  let sandHeights = [];
@@ -452,9 +454,34 @@ class Fish {
452
  if (this.roll > 1.15) { this.roll = 1.15; this.rollVel *= -0.25; }
453
  else if (this.roll < -1.15) { this.roll = -1.15; this.rollVel *= -0.25; }
454
 
455
- const sp = this.speed * this.cruiseSpeed * this.speedFactor * 60 * dt;
456
- this.x += Math.cos(this.angle) * sp;
457
- this.y += Math.sin(this.angle) * sp;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
458
 
459
  // NOTE: fish do NOT collide with plants — they plough straight through.
460
  // The seaweed bends out of the way instead (see updatePlants).
@@ -788,6 +815,20 @@ class Fish {
788
  c.restore();
789
  }
790
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
791
  }
792
  }
793
 
@@ -1269,8 +1310,9 @@ function drawRays(t, light) {
1269
  const [rr, rg, rb] = light.ray;
1270
  const yTop0 = -10, ySpan = H * 0.95 + 10;
1271
 
1272
- // PERF: only rebuild tinted sprites when the light color shifts significantly (delta > 14)
1273
- if (!rayTintRGB || Math.abs(rayTintRGB[0] - rr) + Math.abs(rayTintRGB[1] - rg) + Math.abs(rayTintRGB[2] - rb) > 14) {
 
1274
  rayTintRGB = [rr, rg, rb];
1275
  buildRaySprites(); // rebuild with the new tint
1276
  }
@@ -1810,10 +1852,100 @@ function doResize() {
1810
  buildStaticLayer();
1811
  if (!grainPattern) buildGrainTile(); // v8: grain tile is size-independent, build once
1812
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1813
  window.addEventListener("resize", doResize);
1814
  doResize();
1815
  requestAnimationFrame(frame);
1816
  })();
1817
  </script>
 
 
 
 
 
 
1818
  </body>
1819
  </html>
 
84
  return arr;
85
  }
86
  let fishes = [], bubbles = [], seaweeds = [], motes = [], rays = [], rocks = [], grass = [], fgGrass = [];
87
+ let playerFish = null; // the fish controlled by gamepad (spawned with F key)
88
+ const GAMEPAD_DEADZONE = 0.15; // ignore small stick movements
89
  let curNight = 0; // v7: shared night factor (dims the top-light + sheen)
90
  let plantPoints = [], _ppUsed = 0; // collision points — a pooled set, reused in place
91
  let sandHeights = [];
 
454
  if (this.roll > 1.15) { this.roll = 1.15; this.rollVel *= -0.25; }
455
  else if (this.roll < -1.15) { this.roll = -1.15; this.rollVel *= -0.25; }
456
 
457
+ // Player fish: input-driven movement (keyboard WASD + gamepad sticks),
458
+ // overrides the AI cruising so the player is fully in control.
459
+ if (this === playerFish) {
460
+ const inp = getPlayerInput();
461
+ // Turn toward the combined input direction (WASD or left stick).
462
+ if (Math.hypot(inp.dx, inp.dy) > GAMEPAD_DEADZONE) {
463
+ const targetAngle = Math.atan2(inp.dy, inp.dx);
464
+ let d = targetAngle - this.angle;
465
+ while (d > Math.PI) d -= 2 * Math.PI;
466
+ while (d < -Math.PI) d += 2 * Math.PI;
467
+ const turnSpeed = 4.0; // rad/s — responsive turning
468
+ this.angle += Math.max(-turnSpeed * dt, Math.min(turnSpeed * dt, d));
469
+ }
470
+ // Speed: cruise baseline, boosted by Shift / R-stick down,
471
+ // slowed by Space / R-stick up.
472
+ let speedMult = 1;
473
+ if (inp.boost > 0) speedMult += inp.boost * 2.0; // speed up
474
+ if (inp.slow > 0) speedMult -= inp.slow * 0.85; // slow down
475
+ speedMult = Math.max(0.15, Math.min(3.0, speedMult));
476
+ const playerSpeed = this.speed * this.cruiseSpeed * speedMult * 60 * dt;
477
+ this.x += Math.cos(this.angle) * playerSpeed;
478
+ this.y += Math.sin(this.angle) * playerSpeed;
479
+ } else {
480
+ // Normal AI fish movement.
481
+ const sp = this.speed * this.cruiseSpeed * this.speedFactor * 60 * dt;
482
+ this.x += Math.cos(this.angle) * sp;
483
+ this.y += Math.sin(this.angle) * sp;
484
+ }
485
 
486
  // NOTE: fish do NOT collide with plants — they plough straight through.
487
  // The seaweed bends out of the way instead (see updatePlants).
 
815
  c.restore();
816
  }
817
 
818
+ // Player fish indicator: glowing outline to make it easy to spot
819
+ if (this === playerFish) {
820
+ c.save();
821
+ c.globalAlpha = 0.6 + Math.sin(performance.now() * 0.005) * 0.3; // pulsing glow
822
+ c.strokeStyle = '#FFD700'; // gold color
823
+ c.lineWidth = 2;
824
+ c.shadowColor = '#FFD700';
825
+ c.shadowBlur = 8;
826
+ c.beginPath();
827
+ c.ellipse(0, 0, s * 0.75, s * 0.35, 0, 0, Math.PI * 2);
828
+ c.stroke();
829
+ c.restore();
830
+ }
831
+
832
  }
833
  }
834
 
 
1310
  const [rr, rg, rb] = light.ray;
1311
  const yTop0 = -10, ySpan = H * 0.95 + 10;
1312
 
1313
+ // PERF: only rebuild tinted sprites when the light color shifts significantly (delta > 35)
1314
+ // Higher threshold = fewer expensive canvas rebuilds during slow dusk/dawn transitions
1315
+ if (!rayTintRGB || Math.abs(rayTintRGB[0] - rr) + Math.abs(rayTintRGB[1] - rg) + Math.abs(rayTintRGB[2] - rb) > 35) {
1316
  rayTintRGB = [rr, rg, rb];
1317
  buildRaySprites(); // rebuild with the new tint
1318
  }
 
1852
  buildStaticLayer();
1853
  if (!grainPattern) buildGrainTile(); // v8: grain tile is size-independent, build once
1854
  }
1855
+ // ============ Player controls: keyboard + gamepad ============
1856
+ const keys = {}; // currently-held keyboard keys (lowercased)
1857
+ let prevStartPressed = false; // edge-detect the gamepad Start button
1858
+
1859
+ // Spawn a fresh player-controlled fish at the centre of the tank.
1860
+ function spawnPlayerFish() {
1861
+ if (playerFish) { // replace any existing one
1862
+ const idx = fishes.indexOf(playerFish);
1863
+ if (idx !== -1) fishes.splice(idx, 1);
1864
+ }
1865
+ playerFish = new Fish();
1866
+ playerFish.x = W / 2;
1867
+ playerFish.y = H / 2;
1868
+ playerFish.size = rand(20, 35); // medium size for visibility
1869
+ playerFish.speed *= 1.5; // a touch faster than average for fun
1870
+ playerFish.hue = (Math.random() * 360) | 0; // unique colour so it stands out
1871
+ playerFish.saturation = 90;
1872
+ playerFish.lightness = 55;
1873
+ fishes.push(playerFish);
1874
+ console.log('Player fish spawned! Steer with WASD or the left stick.');
1875
+ }
1876
+
1877
+ // Keyboard: WASD steer, Shift speed up, Space slow down, F spawn.
1878
+ document.addEventListener('keydown', (e) => {
1879
+ const k = e.key.toLowerCase();
1880
+ keys[k] = true;
1881
+ if (k === ' ') e.preventDefault(); // stop Space from scrolling the page
1882
+ if (k === 'f' && !e.repeat) spawnPlayerFish();
1883
+ });
1884
+ document.addEventListener('keyup', (e) => {
1885
+ keys[e.key.toLowerCase()] = false;
1886
+ });
1887
+
1888
+ // Poll gamepad state each frame (Gamepad API requires polling).
1889
+ function pollGamepad() {
1890
+ const pads = navigator.getGamepads ? navigator.getGamepads() : null;
1891
+ let startNow = false;
1892
+ if (pads) {
1893
+ for (const pad of pads) {
1894
+ if (!pad || !pad.connected) continue;
1895
+ // Xbox 360 layout: Start button is index [7].
1896
+ if (pad.buttons[7] && pad.buttons[7].pressed) startNow = true;
1897
+ break; // only the first connected pad
1898
+ }
1899
+ }
1900
+ if (startNow && !prevStartPressed) spawnPlayerFish(); // on-press only
1901
+ prevStartPressed = startNow;
1902
+ }
1903
+ setInterval(pollGamepad, 10);
1904
+
1905
+ // Combine keyboard + gamepad into one input vector for the player fish.
1906
+ function getPlayerInput() {
1907
+ let dx = 0, dy = 0, boost = 0, slow = 0;
1908
+ // Keyboard: WASD steer, Shift speed up, Space slow down.
1909
+ if (keys['a']) dx -= 1;
1910
+ if (keys['d']) dx += 1;
1911
+ if (keys['w']) dy -= 1;
1912
+ if (keys['s']) dy += 1;
1913
+ if (keys['shift']) boost = 1;
1914
+ if (keys[' ']) slow = 1;
1915
+
1916
+ // Gamepad: left stick steers, right stick down speeds up / up slows down.
1917
+ const pads = navigator.getGamepads ? navigator.getGamepads() : null;
1918
+ if (pads) {
1919
+ for (const pad of pads) {
1920
+ if (!pad || !pad.connected) continue;
1921
+ const lx = Math.abs(pad.axes[0]) > GAMEPAD_DEADZONE ? pad.axes[0] : 0;
1922
+ const ly = Math.abs(pad.axes[1]) > GAMEPAD_DEADZONE ? pad.axes[1] : 0;
1923
+ dx += lx;
1924
+ dy += ly;
1925
+ if (pad.axes[3] > GAMEPAD_DEADZONE) boost = Math.max(boost, pad.axes[3]); // down → faster
1926
+ if (pad.axes[3] < -GAMEPAD_DEADZONE) slow = Math.max(slow, -pad.axes[3]); // up → slower
1927
+ break; // only the first connected pad
1928
+ }
1929
+ }
1930
+ return { dx, dy, boost, slow };
1931
+ }
1932
+
1933
+ // Fade out the controls help text 10s after load/refresh.
1934
+ setTimeout(() => {
1935
+ const el = document.getElementById('controls-help');
1936
+ if (el) el.style.opacity = '0';
1937
+ }, 10000);
1938
+
1939
  window.addEventListener("resize", doResize);
1940
  doResize();
1941
  requestAnimationFrame(frame);
1942
  })();
1943
  </script>
1944
+
1945
+ <div id="controls-help" style="position: fixed; bottom: 15px; left: 15px; color: rgba(255,255,255,0.7); font-family: monospace; font-size: 12px; pointer-events: none; z-index: 1000; line-height: 1.7; transition: opacity 1.5s ease;">
1946
+ <strong>Keyboard:</strong> F spawn &nbsp;·&nbsp; WASD steer &nbsp;·&nbsp; Shift speed up &nbsp;·&nbsp; Space slow down<br>
1947
+ <strong>Controller:</strong> Start spawn &nbsp;·&nbsp; Left stick steer &nbsp;·&nbsp; Right stick ↓ speed up &nbsp;·&nbsp; Right stick ↑ slow down
1948
+ </div>
1949
+
1950
  </body>
1951
  </html>