Futur Verbe
by BlazeGecko8191 lines5.2 KB
from pathlib import Path
import zipfile, textwrap
root = Path("/mnt/data/Futur_Verbe")
root.mkdir(exist_ok=True)
html = r'''<!doctype html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Futur Verbe</title>
<style>
*{box-sizing:border-box}html,body{margin:0;min-height:100%;background:#0b1020;color:white;font-family:system-ui,Arial,sans-serif}
body{display:flex;justify-content:center;align-items:center;padding:12px}
.wrap{width:min(100%,760px);text-align:center}.game{position:relative;overflow:hidden;border-radius:18px;background:#111b38;border:2px solid #33416b}
canvas{display:block;width:100%;height:auto;aspect-ratio:16/9;touch-action:none}
.hud{position:absolute;top:10px;left:12px;right:12px;display:flex;justify-content:space-between;font-weight:900;text-shadow:0 2px 4px #000;pointer-events:none}
button{margin:10px 4px 0;padding:12px 18px;border:0;border-radius:12px;background:#38bdf8;color:#062033;font-weight:900;font-size:16px;cursor:pointer}
button+button{background:#263454;color:white}.help{opacity:.75;font-size:13px;margin-top:8px}
</style>
</head>
<body>
<div class="wrap">
<div class="game">
<canvas id="game" width="960" height="540" aria-label="Futur Verbe"></canvas>
<div class="hud"><span id="score">SCORE 0</span><span id="best">MEILLEUR 0</span></div>
</div>
<button id="jump">👽 SAUTER</button><button id="restart">↻ RECOMMENCER</button>
<div class="help">Tape/clic sur le jeu ou ESPACE pour sauter. Attention : le jeu est très difficile !</div>
</div>
<script>
(()=> {
const c=document.getElementById("game"),x=c.getContext("2d"),W=c.width,H=c.height;
let p,pots,cam,score,best=Number(localStorage.getItem("fv_best")||0),dead=false,started=false,last=0;
const scoreEl=document.getElementById("score"),bestEl=document.getElementById("best");
function hud(){scoreEl.textContent="SCORE "+score;bestEl.textContent="MEILLEUR "+best}
function addPot(first=false){let lastp=pots[pots.length-1];let px=first?90:lastp.x+105+Math.random()*100;let py=first?405:350-Math.random()*170;pots.push({x:px,y:py,w:55,h:24})}
function reset(){p={x:120,y:350,r:18,vy:0,on:false};pots=[];cam=0;score=0;dead=false;started=false;for(let i=0;i<10;i++)addPot(i===0);hud()}
function action(){if(dead){reset();return}started=true;if(p.on){p.vy=-760;p.on=false}}
function fail(){dead=true;if(score>best){best=score;localStorage.setItem("fv_best",best)}hud()}
function draw(){
x.clearRect(0,0,W,H);
let g=x.createLinearGradient(0,0,0,H);g.addColorStop(0,"#0b1533");g.addColorStop(1,"#18284a");x.fillStyle=g;x.fillRect(0,0,W,H);
x.fillStyle="rgba(255,255,255,.65)";for(let i=0;i<45;i++){let sx=(i*137-cam*.12)%W;if(sx<0)sx+=W;x.fillRect(sx,(i*71)%270,2,2)}
x.save();x.translate(-cam,0);
for(const q of pots){x.fillStyle="#475569";x.beginPath();x.roundRect(q.x,q.y,q.w,q.h,7);x.fill();x.fillStyle="#65a30d";x.fillRect(q.x+6,q.y-6,q.w-12,7);x.fillStyle="#22c55e";x.fillRect(q.x+12,q.y-13,5,9);x.fillRect(q.x+q.w-17,q.y-10,5,7)}
x.restore();
let px=p.x-cam,py=p.y;x.fillStyle="#67e8f9";x.beginPath();x.arc(px,py,p.r,0,Math.PI*2);x.fill();
x.fillStyle="#e0f2fe";x.beginPath();x.ellipse(px-7,py-3,5,8,0,0,7);x.ellipse(px+7,py-3,5,8,0,0,7);x.fill();
x.fillStyle="#0f172a";x.beginPath();x.arc(px-7,py-2,2.5,0,7);x.arc(px+7,py-2,2.5,0,7);x.fill();
x.strokeStyle="#67e8f9";x.lineWidth=3;x.beginPath();x.moveTo(px-11,py-15);x.lineTo(px-18,py-27);x.moveTo(px+11,py-15);x.lineTo(px+18,py-27);x.stroke();
if(dead){x.fillStyle="rgba(0,0,0,.65)";x.fillRect(0,0,W,H);x.textAlign="center";x.fillStyle="#fff";x.font="900 42px system-ui";x.fillText("GAME OVER",W/2,250);x.font="700 22px system-ui";x.fillText("Score : "+score,W/2,290);x.fillText("Tape pour recommencer",W/2,330);x.textAlign="left"}
}
function loop(t){let dt=Math.min((t-last)/1000,.03);last=t;
if(started&&!dead){p.vy+=1900*dt;p.y+=p.vy*dt;p.x+=185*dt;p.on=false;
for(const q of pots)if(p.vy>=0&&p.x>q.x-10&&p.x<q.x+q.w+10&&p.y+p.r>=q.y&&p.y+p.r<=q.y+20){p.y=q.y-p.r;p.vy=0;p.on=true}
if(p.x-cam>W*.55)cam=p.x-W*.55;
while(pots[pots.length-1].x<W+cam+200)addPot();
while(pots[0].x+pots[0].w<cam-100)pots.shift();
score=Math.max(score,Math.floor(p.x/100));hud();if(p.y>H+100)fail()}
draw();requestAnimationFrame(loop)}
c.addEventListener("pointerdown",e=>{e.preventDefault();action()});document.getElementById("jump").onclick=action;document.getElementById("restart").onclick=reset;
window.addEventListener("keydown",e=>{if(e.code==="Space"){e.preventDefault();action()}});reset();requestAnimationFrame(loop);
})();
</script>
</body>
</html>'''
(root/"index.html").write_text(html, encoding="utf-8")
(root/"README.txt").write_text(
"""FUTUR VERBE
Mini-jeu 2D de plateforme très difficile.
POUR JOUER :
- Ouvre index.html dans un navigateur.
- Tape/clic sur le jeu ou appuie sur ESPACE pour faire sauter l'alien.
- Le bouton RECOMMENCER relance une partie.
PUBLICATION :
Le fichier index.html est autonome et peut être envoyé sur un hébergeur de jeux
qui accepte les fichiers HTML/CSS/JavaScript statiques.
""", encoding="utf-8")
zip_path=Path("/mnt/data/Futur_Verbe.zip")
with zipfile.ZipFile(zip_path,"w",zipfile.ZIP_DEFLATED) as z:
for f in root.rglob("*"):
if f.is_file():
z.write(f, f.relative_to(root.parent))
print(zip_path)
Game Source: Futur Verbe
Creator: BlazeGecko81
Libraries: none
Complexity: moderate (91 lines, 5.2 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: futur-verbe-blazegecko81" to link back to the original. Then publish at arcadelab.ai/publish.