[object Object]

← back to Scarlet Riverboat Masquerade

Keep animals/cubs grounded (lower hop/bounce); add pterodactyl fly-over (button + key p) soaring across the canyon

1043c035abfcc61dad453ae6d69fba45f038f5cc · 2026-09-01 18:35:33 -0700 · Steve Abrams

Files touched

Diff

commit 1043c035abfcc61dad453ae6d69fba45f038f5cc
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Tue Sep 1 18:35:33 2026 -0700

    Keep animals/cubs grounded (lower hop/bounce); add pterodactyl fly-over (button + key p) soaring across the canyon
---
 index.html | 44 ++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 42 insertions(+), 2 deletions(-)

diff --git a/index.html b/index.html
index ee50823..9773e2f 100644
--- a/index.html
+++ b/index.html
@@ -256,6 +256,7 @@
         <button class="btn" id="reduceBtn" aria-pressed="false">◐ Reduced Motion</button>
         <button class="btn" id="trailBtn" aria-pressed="true">✧ Motion Trails</button>
         <button class="btn" id="reactBtn" aria-pressed="false">♫ React to Music</button>
+        <button class="btn" id="pteroBtn">🦖 Fly Over</button>
       </div>
     </div>
     <div class="group">
@@ -816,7 +817,7 @@ function rabbit(x,y,s,t,accent){
   const cyc=(t*1.6)%(Math.PI*2);
   const air=Math.max(0,Math.sin(cyc));
   const stretch=1+air*0.4, squash=1-Math.max(0,-Math.sin(cyc))*0.35;
-  const hopY=-air*26;
+  const hopY=-air*9;   // low, grounded hop — stays on the shore
   ctx.save();
   ctx.translate(x + ((t*22)% (S.W*0.14)) - S.W*0.05, y+hopY);
   ctx.scale(s*(2-stretch)*0.9 + s*0.1, s*stretch*squash);
@@ -871,7 +872,7 @@ function bearAndFireflies(x,y,s,t,accent){
 // two bear cubs dancing beside the conductor — bob + rock, bouncing harder on the beat when reactive
 function bearCub(x,y,s,t,ph){
   const beat=(S.pulse||0), amp=(S.amp||0);
-  const bounce=Math.abs(Math.sin(t*2.4+ph))*(4+beat*11+amp*6);
+  const bounce=Math.abs(Math.sin(t*2.4+ph))*(2+beat*4+amp*2.5);   // a grounded bop — feet stay near the shore
   const rock=Math.sin(t*2.4+ph)*0.2;
   ctx.save(); ctx.translate(x,y-bounce); ctx.rotate(rock); ctx.scale(s,s);
   ctx.fillStyle='rgba(24,7,11,0.96)';
@@ -956,6 +957,40 @@ function drawFireworks(dt){
 }
 let _seenBeat=0;
 
+// ---------- Pterodactyl fly-over (button / key 'p') ----------
+let ptero=null;
+function launchPtero(){
+  ptero={ p:0, y:S.H*(0.12+Math.random()*0.14), dir:(Math.random()<0.5?1:-1), scale:1.3+Math.random()*0.7 };
+  announce('A pterodactyl glides across the canyon.');
+}
+function drawPtero(dt){
+  if(!ptero) return;
+  ptero.p += dt*0.16;                         // crosses in ~6s
+  if(ptero.p>=1){ ptero=null; return; }
+  const W=S.W, span=W+280;
+  const x = ptero.dir>0 ? -140+ptero.p*span : W+140-ptero.p*span;
+  const y = ptero.y + Math.sin(ptero.p*Math.PI*2.4)*22;   // soaring undulation
+  const flap = S.reduced?0.5:(Math.sin(S.t*7)*0.5+0.5);   // wingbeat
+  ctx.save(); ctx.translate(x,y); ctx.scale(ptero.dir*ptero.scale, ptero.scale);
+  ctx.fillStyle='rgba(14,4,8,0.9)';
+  // wings (flapping) + body
+  ctx.beginPath();
+  ctx.moveTo(0,0);
+  ctx.quadraticCurveTo(-16,-8-flap*22,-44,-3);
+  ctx.quadraticCurveTo(-18,6,-4,4);
+  ctx.lineTo(4,4);
+  ctx.quadraticCurveTo(18,6,44,-3);
+  ctx.quadraticCurveTo(16,-8-flap*22,0,0);
+  ctx.closePath(); ctx.fill();
+  // long beak forward
+  ctx.beginPath(); ctx.moveTo(3,0); ctx.lineTo(22,-3); ctx.lineTo(4,3); ctx.closePath(); ctx.fill();
+  // swept-back head crest
+  ctx.beginPath(); ctx.moveTo(2,-1); ctx.lineTo(-7,-10); ctx.lineTo(5,-2); ctx.closePath(); ctx.fill();
+  // trailing tail
+  ctx.strokeStyle='rgba(14,4,8,0.9)'; ctx.lineWidth=1.6; ctx.beginPath(); ctx.moveTo(-2,2); ctx.lineTo(-16,6); ctx.stroke();
+  ctx.restore();
+}
+
 // ---------- Main loop ----------
 let last=performance.now();
 function frame(now){
@@ -1006,6 +1041,7 @@ function frame(now){
   drawRiver();
   drawAnimals();
   drawBoat();
+  drawPtero(dt);
   drawFireworks(dt);
 
   requestAnimationFrame(frame);
@@ -1122,6 +1158,7 @@ window.addEventListener('keydown',e=>{
     case 'm': reduceBtn.click(); break;
     case 't': trailBtn.click(); break;
     case 'a': { const rb=document.getElementById('reactBtn'); if(rb) rb.click(); } break;
+    case 'p': launchPtero(); break;
     case '1': document.querySelector('[data-cam="wide"]').click(); break;
     case '2': document.querySelector('[data-cam="deck"]').click(); break;
     case '3': document.querySelector('[data-cam="over"]').click(); break;
@@ -1131,6 +1168,9 @@ window.addEventListener('keydown',e=>{
   }
 });
 
+// pterodactyl fly-over button
+document.getElementById('pteroBtn').addEventListener('click', launchPtero);
+
 // click / tap anywhere on the scene (not on a control) to launch a firework
 document.getElementById('stage').addEventListener('pointerdown', e=>{
   if(e.target.closest('button,input,select,a,.dock,.cams,.chapters,.musicroom,.museum,.label-toggle')) return;

← b8b147f Add Photoshop-composited canyon-backdrop.png asset + two dan  ·  back to Scarlet Riverboat Masquerade  ·  Cutout/South-Park art pass: flat-color fills + bold dark out 1842974 →