@joaobarrosfranca · 8/12/2026, 3:36:18 PM
Versão inicial — todas as linhas são novas.
+<!doctype html>+<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><title>Rooftop Rumble 3D</title>+<style>+*{box-sizing:border-box}html,body{margin:0;height:100%;overflow:hidden;background:#241a4e;font-family:system-ui,-apple-system,sans-serif;touch-action:none}canvas{display:block}.barbox{position:fixed;z-index:5;top:19px;width:43%;color:#fff;text-shadow:0 2px 5px #000}.barbox.left{left:16px}.barbox.right{right:16px;text-align:right}.name{font-size:11px;font-weight:950;letter-spacing:1.8px;margin-bottom:5px}.bar{height:13px;background:#1b1236;border:2px solid #fff9;transform:skewX(-12deg);overflow:hidden}.right .bar{transform:skewX(12deg)}.fill{height:100%;width:100%;background:linear-gradient(90deg,#53ffe2,#c7ff72);transition:width .2s}.right .fill{margin-left:auto;background:linear-gradient(90deg,#ffd76a,#ff5b87)}#round{position:fixed;z-index:5;top:20px;left:50%;transform:translateX(-50%);color:#fff5be;text-shadow:0 2px 7px #000;font-weight:1000;font-size:15px;letter-spacing:1.2px}.controls{position:fixed;z-index:6;left:0;right:0;bottom:max(17px,env(safe-area-inset-bottom));padding:0 14px;display:flex;justify-content:space-between;align-items:end}.move,.attacks{display:flex;gap:9px}.controls button{border:2px solid #fff8;color:#fff;font-weight:950;cursor:pointer;box-shadow:0 4px 10px #1c113d88}.move button{width:53px;height:53px;border-radius:16px;background:#5247a5bb;font-size:24px}.attacks button{width:64px;height:64px;border-radius:50%;font-size:11px;letter-spacing:.4px}.punch{background:#ec5c95}.kick{background:#f5a93d}.controls button:active{transform:scale(.94);filter:brightness(1.2)}#cover{position:fixed;z-index:10;inset:0;display:grid;place-items:center;text-align:center;padding:26px;color:white;background:radial-gradient(circle at 50% 15%,#f88a7666,transparent 32%),linear-gradient(150deg,#261549e8,#601f64e5 57%,#fa744aab)}#card{max-width:320px}.tag{font-size:11px;letter-spacing:4px;font-weight:950;color:#ffc45d}.title{font-size:51px;font-weight:1000;line-height:.84;letter-spacing:-3px;margin:17px 0 20px;text-transform:uppercase;text-shadow:4px 4px 0 #29164a}.title span{color:#65f3e1}.copy{font-size:16px;line-height:1.5;color:#f7eeff;margin-bottom:26px}#start{border:3px solid #fff1c6;border-radius:6px;background:#faad3e;color:#391b50;padding:15px 28px;font-size:14px;font-weight:1000;letter-spacing:1px;cursor:pointer;box-shadow:0 5px 0 #99394d}#start:active{transform:translateY(3px);box-shadow:0 2px 0 #99394d}.hint{font-size:12px;color:#fff2d0;margin-top:18px}+</style></head><body>+<div class="barbox left"><div class="name">KITE</div><div class="bar"><div id="playerHp" class="fill"></div></div></div><div id="round">ROUND 1</div><div class="barbox right"><div class="name">VOLT</div><div class="bar"><div id="enemyHp" class="fill"></div></div></div>+<div class="controls"><div class="move"><button id="left" aria-label="Move left">←</button><button id="right" aria-label="Move right">→</button></div><div class="attacks"><button id="punch" class="punch">PUNCH</button><button id="kick" class="kick">KICK</button></div></div>+<div id="cover"><div id="card"><div class="tag">ORIGINAL 3D FIGHTING</div><div class="title">ROOFTOP<br><span>RUMBLE</span></div><div id="copy" class="copy">Kite versus Volt. Close the distance, strike first, and own the skyline.</div><button id="start" type="button">FIGHT!</button><div class="hint">Move with arrows · Punch or kick with the buttons</div></div></div>+<script type="module">+import * as THREE from "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js";+const scene=new THREE.Scene();scene.background=new THREE.Color(0x2a1a59);scene.fog=new THREE.Fog(0x2a1a59,13,35);+const camera=new THREE.PerspectiveCamera(58,innerWidth/innerHeight,.1,100);camera.position.set(0,4.2,10.3);camera.lookAt(0,1.5,0);+const renderer=new THREE.WebGLRenderer({antialias:true});renderer.setPixelRatio(Math.min(devicePixelRatio,2));renderer.setSize(innerWidth,innerHeight);renderer.outputColorSpace=THREE.SRGBColorSpace;renderer.shadowMap.enabled=true;document.body.appendChild(renderer.domElement);+scene.add(new THREE.HemisphereLight(0xc4b9ff,0x1b153b,2.3));const moon=new THREE.DirectionalLight(0xffc36a,2.2);moon.position.set(-4,10,5);moon.castShadow=true;scene.add(moon);const pink=new THREE.PointLight(0xff4b99,18,16);pink.position.set(4,3,1);scene.add(pink);+const roof=new THREE.Mesh(new THREE.BoxGeometry(13,.6,7),new THREE.MeshStandardMaterial({color:0x313058,roughness:.82}));roof.position.y=-.3;roof.receiveShadow=true;scene.add(roof);+const edge=new THREE.Mesh(new THREE.BoxGeometry(13,.25,.18),new THREE.MeshStandardMaterial({color:0xf4a03e,emissive:0xa84919,emissiveIntensity:1.4}));edge.position.set(0,.05,-3.4);scene.add(edge);+for(let i=0;i<22;i++){const h=2+Math.random()*8,w=.8+Math.random()*1.4;const b=new THREE.Mesh(new THREE.BoxGeometry(w,h,w),new THREE.MeshStandardMaterial({color:0x201949,emissive:0x120d36,roughness:.65}));b.position.set((Math.random()-.5)*24,-.1+h/2,-8-Math.random()*15);scene.add(b);for(let y=.5;y<h;y+=.8){const win=new THREE.Mesh(new THREE.BoxGeometry(w*.58,.08,.03),new THREE.MeshBasicMaterial({color:Math.random()<.45?0xffa94d:0x5eece0}));win.position.set(b.position.x,y,b.position.z+w/2+.02);scene.add(win)}}+function makeFighter(bodyColor,gloveColor){const g=new THREE.Group(),mat=new THREE.MeshStandardMaterial({color:bodyColor,roughness:.48,metalness:.1}),skin=new THREE.MeshStandardMaterial({color:0xf0a27f,roughness:.65}),glove=new THREE.MeshStandardMaterial({color:gloveColor,roughness:.34,metalness:.15});const torso=new THREE.Mesh(new THREE.BoxGeometry(.78,1.05,.43),mat);torso.position.y=1.55;torso.castShadow=true;g.add(torso);const head=new THREE.Mesh(new THREE.SphereGeometry(.38,15,12),skin);head.position.y=2.36;head.castShadow=true;g.add(head);const hair=new THREE.Mesh(new THREE.SphereGeometry(.41,10,7),new THREE.MeshStandardMaterial({color:0x1b1530,roughness:.8}));hair.scale.y=.42;hair.position.y=2.63;g.add(hair);const arms=[];[-1,1].forEach(side=>{const arm=new THREE.Group(),upper=new THREE.Mesh(new THREE.BoxGeometry(.24,.74,.24),mat),hand=new THREE.Mesh(new THREE.SphereGeometry(.22,10,8),glove);upper.position.y=-.29;hand.position.y=-.72;arm.add(upper,hand);arm.position.set(side*.54,1.91,0);arm.rotation.z=side*-.22;g.add(arm);arms.push(arm)});[-1,1].forEach(side=>{const leg=new THREE.Mesh(new THREE.BoxGeometry(.28,.94,.3),mat);leg.position.set(side*.22,.57,0);leg.castShadow=true;g.add(leg)});return {g,arms}}+const player=makeFighter(0x48d6cf,0xffcf55),enemy=makeFighter(0xf16b91,0xb468ff);player.g.position.x=-2.4;enemy.g.position.x=2.4;enemy.g.rotation.y=Math.PI;scene.add(player.g,enemy.g);+const hpP=document.querySelector("#playerHp"),hpE=document.querySelector("#enemyHp"),cover=document.querySelector("#cover"),copy=document.querySelector("#copy"),start=document.querySelector("#start");+let playerHealth=100,enemyHealth=100,running=false,moveDir=0,last=0,pAttack=0,eAttack=0,pCooldown=0,eCooldown=0,bob=0;+function clamp(n,a,b){return Math.max(a,Math.min(b,n))}function updateBars(){hpP.style.width=playerHealth+"%";hpE.style.width=enemyHealth+"%"}+function end(won){running=false;copy.innerHTML=won?"<b>Kite wins!</b><br>The rooftop belongs to you.":"<b>Volt wins!</b><br>Step back into the ring and fight again.";start.textContent="REMATCH";cover.style.display="grid"}+function strike(type){if(!running||pCooldown>0)return;const kick=type==="kick";pAttack=kick?330:230;pCooldown=kick?520:360;if(Math.abs(player.g.position.x-enemy.g.position.x)<(kick?1.95:1.52)){enemyHealth=clamp(enemyHealth-(kick?17:10),0,100);updateBars();if(enemyHealth===0)end(true)}}+function botStrike(){if(eCooldown>0)return;eAttack=260;eCooldown=520;if(Math.abs(player.g.position.x-enemy.g.position.x)<1.62){playerHealth=clamp(playerHealth-12,0,100);updateBars();if(playerHealth===0)end(false)}}+function hold(el,v){el.addEventListener("pointerdown",e=>{e.preventDefault();moveDir=v});["pointerup","pointerleave","pointercancel"].forEach(t=>el.addEventListener(t,()=>moveDir=0))}+hold(document.querySelector("#left"),-1);hold(document.querySelector("#right"),1);document.querySelector("#punch").addEventListener("click",()=>strike("punch"));document.querySelector("#kick").addEventListener("click",()=>strike("kick"));start.addEventListener("click",()=>{playerHealth=100;enemyHealth=100;player.g.position.x=-2.4;enemy.g.position.x=2.4;updateBars();running=true;cover.style.display="none";last=0});+addEventListener("keydown",e=>{const k=e.key.toLowerCase();if(k==="arrowleft"||k==="a"){e.preventDefault();moveDir=-1}if(k==="arrowright"||k==="d"){e.preventDefault();moveDir=1}if(k==="j")strike("punch");if(k==="k")strike("kick");if((k===" "||k==="enter")&&!running)start.click()});addEventListener("keyup",e=>{const k=e.key.toLowerCase();if(k==="arrowleft"||k==="a"||k==="arrowright"||k==="d")moveDir=0});+function animate(t){requestAnimationFrame(animate);const dt=Math.min(35,last?t-last:16);last=t;bob+=dt*.006;if(running){player.g.position.x=clamp(player.g.position.x+moveDir*dt*.0047,-5.1,4.3);const gap=player.g.position.x-enemy.g.position.x;if(Math.abs(gap)>1.35)enemy.g.position.x+=Math.sign(gap)*dt*.0022;else if(Math.random()<.013)botStrike();enemy.g.position.x=clamp(enemy.g.position.x,-4.3,5.1);pAttack=Math.max(0,pAttack-dt);eAttack=Math.max(0,eAttack-dt);pCooldown=Math.max(0,pCooldown-dt);eCooldown=Math.max(0,eCooldown-dt);player.g.position.y=Math.abs(Math.sin(bob))*-.02;enemy.g.position.y=Math.abs(Math.sin(bob+1))*-.02;player.arms[1].rotation.x=pAttack?-.95*Math.sin(pAttack/330*Math.PI):0;player.arms[0].rotation.x=pAttack?-.3:0;enemy.arms[0].rotation.x=eAttack?-.9*Math.sin(eAttack/260*Math.PI):0;enemy.arms[1].rotation.x=eAttack?-.28:0}renderer.render(scene,camera)}+addEventListener("resize",()=>{camera.aspect=innerWidth/innerHeight;camera.updateProjectionMatrix();renderer.setSize(innerWidth,innerHeight)});animate(0);+</script></body></html>