Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ export const GAMES = [
{id:'sheep',title:'Sheep Escape',tag:'BRIDGE BUILDER',description:'Hold to grow a bridge upward, then release to lower it and lead your sheep across.',controls:'Hold mouse / touch / Space: grow · Release: lower bridge',color:'#a1d9a1'},
{id:'gravity',title:'Gravity Flip',tag:'ENDLESS RUNNER',description:'Swap floor and ceiling to dodge the neon barriers.',controls:'Space / tap: flip gravity',color:'#d5a2ff'},
{id:'bowling',title:'Pocket Bowling',tag:'FIVE FRAMES',description:'Aim carefully and knock down all ten pins. Two rolls per frame.',controls:'Left / right: aim · Up / down: power · Q / E: spin · Space / Roll: bowl',color:'#7ed9ee'},
{id:'rally',title:'Neon Rally',tag:'3D · TEN TRACKS',description:'Race four fast GT opponents on longer circuits. Climb mountain switchbacks, brake for hairpins and pass on the straights.',controls:'Up: accelerate · Down: brake / reverse · Left / right: steer · R: cockpit / chase camera',color:'#ff97b3',three:true},
{id:'rally',title:'Neon Rally',tag:'3D · TWELVE TRACKS',description:'Race four fast GT opponents on longer circuits. Climb mountain switchbacks, brake for hairpins and pass on the straights.',controls:'Up: accelerate · Down: brake / reverse · Left / right: steer · R: cockpit / chase camera',color:'#ff97b3',three:true},
{id:'dash',title:'Neon Dash',tag:'FIVE LEVELS',description:'Five long portal courses: jump as a cube, fly a ship, flip a ball and steer a wave.',controls:'Hold / tap: jump, fly or flip · Blue platforms are safe',color:'#a8b6ff',local:true},
{id:'marble',title:'Marble Run 3D',tag:'3D · THREE COURSES',description:'Race against the clock through boosts, jump gaps, narrow turns and moving sweepers.',controls:'Arrow keys / WASD: roll · Space: jump · Q: brake',color:'#9de6da',local:true,three:true},
{id:'space',title:'Space Survival 3D',tag:'3D · ARCADE',description:'Battle formations of alien ships, dodge diving fighters and collect shield energy.',controls:'Arrow keys / WASD: fly · Space: fire',color:'#e7bbff',local:true,three:true},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,12 +123,14 @@ export const RALLY_COURSES=[
{name:'Canyon Switchback',width:18,scale:2.8,heights:[0,8,18,12,4,0,7,20,13,5,2,10,5],laps:2,theme:'canyon',points:[[30,0],[32,24],[9,29],[4,10],[-13,9],[-14,32],[-36,23],[-34,-20],[-14,-30],[-2,-10],[13,-15],[17,-33],[34,-25]]},
{name:'Alpine Hairpins',width:18,scale:1,heights:[0,8,20,36,46,60,65,58,53,47,40,36,28,18,8,4,2,0,0],laps:2,theme:'alpine',mountains:[[-35,155,110,90],[155,115,145,80],[-170,95,135,85],[0,-180,90,80]],points:[[145,-100],[170,-35],[140,35],[80,60],[70,120],[0,150],[-65,110],[-35,70],[25,80],[30,40],[-40,25],[-115,55],[-150,0],[-135,-65],[-75,-90],[-45,-40],[15,-55],[55,-100],[80,-145]]},
{name:'Midnight Metro',width:20,scale:2.8,heights:[0,2,6,4,0,0,10,15,8,2,0,0,3,0,5],laps:2,theme:'city',points:[[35,0],[34,28],[10,29],[8,12],[-10,12],[-12,32],[-34,28],[-36,4],[-20,2],[-20,-18],[-35,-20],[-25,-36],[3,-34],[7,-17],[32,-24]]},
{name:'Coastal Sprint',width:24,scale:1,heights:[1,3,6,8,7,4,1,0,0,1],laps:2,theme:'harbor',points:[[150,0],[145,65],[80,100],[-35,105],[-145,65],[-170,-10],[-120,-90],[-20,-105],[90,-90],[155,-55]]},
{name:'Red Mesa Run',width:20,scale:1,heights:[0,5,14,25,31,27,20,12,6,2,0,0],laps:2,theme:'canyon',points:[[160,0],[150,75],[75,120],[0,95],[-70,130],[-155,85],[-175,0],[-130,-75],[-55,-110],[10,-75],[80,-110],[150,-65]]},
{name:'Pine Ridge',width:20,scale:1,heights:[0,6,14,22,30,35,30,24,17,10,4,0],laps:2,theme:'alpine',mountains:[[-220,160,65,90],[190,205,78,95],[-205,-155,60,85],[50,-220,68,90]],points:[[140,-10],[130,65],[60,105],[-10,70],[-75,115],[-145,75],[-165,0],[-120,-80],[-40,-110],[25,-60],[95,-100],[150,-65]]},
{name:'Old Town Loop',width:20,scale:1,heights:[0,1,4,8,7,4,1,0,0,2,5,6,3,0],laps:2,theme:'city',points:[[150,0],[145,85],[65,110],[5,65],[-55,110],[-145,85],[-165,10],[-120,-25],[-145,-105],[-55,-120],[5,-70],[75,-115],[155,-95],[185,-40]]},
{name:'Summit Grand Prix',width:20,scale:1,heights:[0,8,20,35,50,63,70,62,52,42,32,22,12,4,0,0],laps:2,theme:'alpine',mountains:[[-240,160,140,100],[205,210,155,95],[-210,-180,125,100],[40,-245,130,90]],points:[[185,-60],[190,30],[145,105],[70,150],[-15,115],[-85,165],[-170,120],[-200,40],[-150,-20],[-200,-95],[-155,-170],[-65,-190],[0,-125],[70,-175],[150,-150],[205,-115]]},
{name:'Sunset Docks',width:22,scale:1,heights:[0,2,5,9,12,10,5,1,0,2,6,9,5,0],laps:2,theme:'harbor',points:[[160,-10],[150,70],[80,120],[0,90],[-80,125],[-160,70],[-175,-10],[-105,-50],[-155,-125],[-65,-150],[10,-105],[85,-150],[165,-110],[200,-65]]},
{name:'Coastal Sprint',scenery:'coast',width:20,scale:1,heights:[1,3,6,8,7,4,1,0,0,1],laps:2,theme:'harbor',points:[[150,0],[145,65],[80,100],[-35,105],[-145,65],[-170,-10],[-120,-90],[-20,-105],[90,-90],[155,-55]]},
{name:'Red Mesa Run',scenery:'mesa',width:18,scale:1,heights:[0,5,14,25,31,27,20,12,6,2,0,0],laps:2,theme:'canyon',points:[[160,0],[150,75],[75,120],[0,95],[-70,130],[-155,85],[-175,0],[-130,-75],[-55,-110],[10,-75],[80,-110],[150,-65]]},
{name:'Pine Ridge',scenery:'ridge',width:18,scale:1,heights:[0,6,14,22,30,35,30,24,17,10,4,0],laps:2,theme:'alpine',mountains:[[-220,160,65,90],[190,205,78,95],[-205,-155,60,85],[50,-220,68,90]],points:[[140,-10],[130,65],[60,105],[-10,70],[-75,115],[-145,75],[-165,0],[-120,-80],[-40,-110],[25,-60],[95,-100],[150,-65]]},
{name:'Old Town Loop',scenery:'town',width:18,scale:1,heights:[0,1,4,8,7,4,1,0,0,2,5,6,3,0],laps:2,theme:'city',points:[[150,0],[145,85],[65,110],[5,65],[-55,110],[-145,85],[-165,10],[-120,-25],[-145,-105],[-55,-120],[5,-70],[75,-115],[155,-95],[185,-40]]},
{name:'Summit Grand Prix',scenery:'summit',width:18,scale:1,heights:[0,8,20,35,50,63,70,62,52,42,32,22,12,4,0,0],laps:2,theme:'alpine',mountains:[[-240,160,140,100],[205,210,155,95],[-210,-180,125,100],[40,-245,130,90]],points:[[185,-60],[190,30],[145,105],[70,150],[-15,115],[-85,165],[-170,120],[-200,40],[-150,-20],[-200,-95],[-155,-170],[-65,-190],[0,-125],[70,-175],[150,-150],[205,-115]]},
{name:'Sunset Docks',scenery:'docks',width:20,scale:1,heights:[0,2,5,9,12,10,5,1,0,2,6,9,5,0],laps:2,theme:'harbor',points:[[160,-10],[150,70],[80,120],[0,90],[-80,125],[-160,70],[-175,-10],[-105,-50],[-155,-125],[-65,-150],[10,-105],[85,-150],[165,-110],[200,-65]]},
{name:'Aurora Fjord',scenery:'aurora',width:18,scale:1,heights:[0,4,11,20,28,34,30,23,15,8,3,0],laps:2,theme:'aurora',mountains:[[-210,170,90,85],[200,170,110,95],[-220,-150,80,90]],points:[[150,-15],[150,65],[80,120],[0,85],[-70,130],[-150,85],[-180,0],[-125,-75],[-50,-120],[25,-75],[100,-115],[165,-75]]},
{name:'Polar Night Pass',scenery:'polar',width:18,scale:1,heights:[0,6,15,26,39,44,38,30,21,12,5,0,0,0],laps:2,theme:'polar',mountains:[[-215,175,110,95],[210,190,125,85],[-220,-180,95,90],[40,-220,80,80]],points:[[165,-35],[175,50],[110,120],[30,155],[-40,110],[-120,155],[-185,85],[-195,-5],[-125,-45],[-165,-125],[-75,-165],[5,-110],[95,-165],[180,-110]]},
];
export function roadDistance(points,x,z){let best=Infinity;for(let i=0;i<points.length;i++){const a=points[i],b=points[(i+1)%points.length],dx=b.x-a.x,dz=b.z-a.z,u=clamp(((x-a.x)*dx+(z-a.z)*dz)/(dx*dx+dz*dz),0,1);best=Math.min(best,Math.hypot(x-a.x-dx*u,z-a.z-dz*u));}return best;}
export function bridgeLanding(start,next,length){const tip=start+length;return tip>=next.x&&tip<=next.x+next.w;}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ test('every full Dash course is completable across all four modes without losing
}
});
test('Rally tracks differ and road proximity follows their curves, not an ellipse',()=>{
assert.equal(RALLY_COURSES.length,10);assert.equal(new Set(RALLY_COURSES.map(c=>c.theme)).size,4);
assert.equal(RALLY_COURSES.length,12);assert.equal(new Set(RALLY_COURSES.map(c=>c.theme)).size,6);
assert.equal(new Set(RALLY_COURSES.map(c=>c.name)).size,RALLY_COURSES.length);
assert.equal(new Set(RALLY_COURSES.map(c=>JSON.stringify(c.points))).size,RALLY_COURSES.length);
for(const c of RALLY_COURSES){const curve=new THREE.CatmullRomCurve3(c.points.map(([x,z])=>new THREE.Vector3(x,0,z)),true,'centripetal'),p=curve.getSpacedPoints(128).slice(0,-1);assert.ok(roadDistance(p,p[20].x,p[20].z)<.01);assert.ok(roadDistance(p,10000,10000)>c.width);assert.ok(curve.getLength()>150);}
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading