import"./modulepreload-polyfill-B5Qt9EMX.js";import{R as st,V as h,C as ot,S as it,a as nt,b as at,c as X,M as _,d as k,G as W,e as O,f as rt,g as U,B as lt,h as q,P as ct,A as ht,i as dt,j as pt,k as ut,l as mt}from"./world-KhDGgEOv.js";const f={red:"#FF3B30",orange:"#FF9500",yellow:"#FFD60A",green:"#34C759",blue:"#0A84FF",purple:"#AF52DE",pink:"#FF6EB4"},A="#F5F5F7",ft=()=>({speedMul:1,jumpMul:1,massMul:1,grip:0,size:1,waterproof:!1,glow:0}),v=["red","orange","yellow","green","blue","purple","pink"];function bt(a){return v.indexOf(a)+1}function gt(a,t){const s=new Array(t+1).fill(0);for(let e=0;e0?a[o+1]/t:0;return{total:t>0?(t-a[0])/t:0,byColor:s}}const C=.2,T=.7,K=.15,xt=a=>a<0?0:a>1?1:a;function E(a){if(a=T)return 1;const t=(a-C)/(T-C);return K+(1-K)*t}function Y(a){let t=null,s=T;for(const e of v)a.byColor[e]>=s&&(s=a.byColor[e],t=e);return t}function wt(a){let t=null,s=0;for(const e of v)a.byColor[e]>s&&(s=a.byColor[e],t=e);return t}function Mt(a){const t=a.byColor.red,s=a.byColor.green,e=a.byColor.blue,o=1+.6*E(t),i=E(s),n=e>=C,r=1+.8*xt(a.total),l=Y(a)?1:0;return{speedMul:o,jumpMul:1,massMul:r,grip:i,size:1,waterproof:n,glow:l}}class Ct{canvas;texture;boundingRadius;mesh;ctx;size;splatScale;mask;raycaster=new st;cache=null;cacheTime=-1e9;_center=new h;_dir=new h;_origin=new h;constructor(t,s={}){this.mesh=t,this.size=s.size??256,this.splatScale=s.splatScale??.16,t.geometry.computeBoundingSphere(),this.boundingRadius=t.geometry.boundingSphere?.radius??1;const e=document.createElement("canvas");e.width=e.height=this.size;const o=e.getContext("2d");if(!o)throw new Error("PaintSkin: 2D canvas context unavailable");o.fillStyle=A,o.fillRect(0,0,this.size,this.size),this.canvas=e,this.ctx=o,this.mask=new Uint8Array(this.size*this.size);const i=new ot(e);i.colorSpace=it,i.wrapS=nt,i.wrapT=at,i.anisotropy=4,this.texture=i;const n=t.material;n.map=i,n.color&&n.color.set("#ffffff"),n.needsUpdate=!0}applySplat(t,s,e){const o=t.x*this.size,i=(1-t.y)*this.size,n=this.pixelRadius(e),r=this.makeSplatCircles(o,i,n);this.stampMask(r,bt(s)),this.stampCanvas(r,f[s]),this.texture.needsUpdate=!0}splatAtPoint(t,s,e){const o=this.uvAtWorldPoint(t);o&&this.applySplat(o,s,e)}coverage(){const t=performance.now();if(this.cache&&t-this.cacheTime<250)return this.cache;const s=gt(this.mask,v.length);return this.cache=yt(s,this.mask.length),this.cacheTime=t,this.cache}cleanse(t){const s=Math.max(0,Math.min(1,t));if(s<=0)return;if(s>=1){this.mask.fill(0),this.ctx.fillStyle=A,this.ctx.fillRect(0,0,this.size,this.size),this.invalidate();return}let e=0;for(let n=0;n0&&i++<2e4;){const n=Math.random()*this.size,r=Math.random()*this.size,l=6+Math.random()*12;o-=this.eraseCircle(n,r,l)}this.invalidate()}uvAtWorldPoint(t){if(this.mesh.updateWorldMatrix(!0,!1),this.mesh.getWorldPosition(this._center),this._dir.copy(t).sub(this._center),this._dir.lengthSq()<1e-12)return null;this._dir.normalize();const s=this.worldBoundingRadius();this._origin.copy(this._center).addScaledVector(this._dir,s*2.2),this.raycaster.set(this._origin,this._dir.clone().negate()),this.raycaster.near=0,this.raycaster.far=s*4.4;const o=this.raycaster.intersectObject(this.mesh,!1).find(i=>i.uv);return o?.uv?o.uv.clone():null}worldBoundingRadius(){const t=this.mesh.getWorldScale(this._origin);return this.boundingRadius*Math.max(t.x,t.y,t.z)}pixelRadius(t){const s=t/this.boundingRadius*this.size*this.splatScale;return Math.max(4,Math.min(this.size*.4,s))}makeSplatCircles(t,s,e){const o=[{x:t,y:s,r:e*.95}],i=3+Math.floor(Math.random()*3);for(let r=0;re?[0,-e]:[0]}invalidate(){this.texture.needsUpdate=!0,this.cache=null,this.cacheTime=-1e9}}function vt(a){const t=a.replace("#",""),s=parseInt(t,16);return`${s>>16&255},${s>>8&255},${s&255}`}const zt=6;class J{color;cfg;scene;projectiles=[];projGeo;projMat;barrel;acc=0;unsub;_lastTarget=new h;_targetVel=new h;hasLast=!1;_tpos=new h;_aim=new h;_hit=new h;_vtmp=new h;constructor(t){this.color=t.color,this.cfg={interval:0,muzzleSpeed:16,projRadius:.12,splatRadius:.28,gravity:14,groundY:0,lead:!0,...t},this.scene=t.world.scene;const s=f[t.color];this.projGeo=new X(this.cfg.projRadius,12,10),this.projMat=new _({color:s,emissive:s,emissiveIntensity:.35,roughness:.4}),this.barrel=this.buildBarrel(s),this.barrel.position.copy(t.position),this.scene.add(this.barrel),this.unsub=t.world.events.on("machine:signal",e=>{this.cfg.triggerId&&e?.id===this.cfg.triggerId&&this.fire()})}fire(){this.target(this._tpos);const t=this.muzzleWorld();if(this._aim.copy(this._tpos),this.cfg.lead&&this.hasLast){const o=t.distanceTo(this._tpos)/this.cfg.muzzleSpeed;this._aim.addScaledVector(this._targetVel,o)}const s=St(t,this._aim,this.cfg.muzzleSpeed,this.cfg.gravity)??this._aim.clone().sub(t).normalize().multiplyScalar(this.cfg.muzzleSpeed);this.aimBarrel(s);const e=new k(this.projGeo,this.projMat);e.castShadow=!0,e.position.copy(t),this.scene.add(e),this.projectiles.push({mesh:e,vel:s.clone(),prev:t.clone(),life:0})}update(t){if(t>0){if(this.target(this._tpos),this.hasLast){const e=1/t;this._vtmp.copy(this._tpos).sub(this._lastTarget).multiplyScalar(e),this._targetVel.lerp(this._vtmp,.4)}this._lastTarget.copy(this._tpos),this.hasLast=!0}if(this.cfg.interval>0)for(this.acc+=t;this.acc>=this.cfg.interval;)this.acc-=this.cfg.interval,this.fire();if(this.projectiles.length===0)return;this.target(this._tpos);const s=this.cfg.targetRadius+this.cfg.projRadius;for(let e=this.projectiles.length-1;e>=0;e--){const o=this.projectiles[e];if(o.prev.copy(o.mesh.position),o.vel.y-=this.cfg.gravity*t,o.mesh.position.addScaledVector(o.vel,t),o.life+=t,Rt(this._tpos,o.prev,o.mesh.position,this._hit)<=s){this.cfg.paint.splatAtPoint(this._hit,this.color,this.cfg.splatRadius),this.cfg.world.events.emit("paint:splatted",{color:this.color,target:"blob"}),this.despawn(e);continue}(o.mesh.position.y<=this.cfg.groundY||o.life>zt)&&(this.cfg.world.events.emit("paint:splatted",{color:this.color,target:"world"}),this.despawn(e))}}dispose(){this.unsub();for(let t=this.projectiles.length-1;t>=0;t--)this.despawn(t);this.scene.remove(this.barrel),this.projGeo.dispose(),this.projMat.dispose()}target(t){return this.cfg.target.getWorldPosition(t)}muzzleWorld(){return this.cfg.position.clone().add(new h(0,.55,0))}despawn(t){const s=this.projectiles[t];this.scene.remove(s.mesh),this.projectiles.splice(t,1)}buildBarrel(t){const s=new W,e=new k(new O(.45,.55,.5,16),new _({color:"#3a3a44",roughness:.7}));e.castShadow=!0,s.add(e);const o=new k(new O(.22,.28,1.1,16),new _({color:t,roughness:.5}));o.castShadow=!0;const i=new W;return i.position.y=.45,o.position.y=.4,o.rotation.x=Math.PI/2,i.add(o),i.name="pivot",s.add(i),s}aimBarrel(t){const s=this.barrel.getObjectByName("pivot");if(!s)return;const e=t.clone().normalize(),o=new rt().lookAt(new h(0,0,0),e,new h(0,1,0));s.quaternion.setFromRotationMatrix(o)}}function St(a,t,s,e){const o=t.x-a.x,i=t.z-a.z,n=t.y-a.y,r=Math.hypot(o,i);if(r<.001)return new h(0,Math.sign(n||1)*s,0);const l=s*s,c=l*l-e*(e*r*r+2*n*l);if(c<0)return null;const d=Math.sqrt(c),p=(l-d)/(e*r),y=Math.atan(p),u=o/r,x=i/r,w=s*Math.cos(y),P=s*Math.sin(y);return new h(u*w,P,x*w)}function Rt(a,t,s,e){const o=s.x-t.x,i=s.y-t.y,n=s.z-t.z,r=o*o+i*i+n*n;let l=0;return r>1e-12&&(l=((a.x-t.x)*o+(a.y-t.y)*i+(a.z-t.z)*n)/r,l=l<0?0:l>1?1:l),e.set(t.x+o*l,t.y+i*l,t.z+n*l),e.distanceTo(a)}const z=90;class _t{scene;blob;embers;emberPos;emberCol;emberVel;emberLife;emberCursor=0;emberHex=new U;_spawnAt=new h;clock=0;constructor(t,s){this.scene=t.scene,this.blob=s,this.emberHex.set(f.red).lerp(new U(f.orange),.5),this.emberPos=new Float32Array(z*3),this.emberCol=new Float32Array(z*3),this.emberVel=new Float32Array(z*3),this.emberLife=new Float32Array(z);const e=new lt;e.setAttribute("position",new q(this.emberPos,3)),e.setAttribute("color",new q(this.emberCol,3));const o=new ct({size:.16,vertexColors:!0,transparent:!0,depthWrite:!1,blending:ht,sizeAttenuation:!0});this.embers=new dt(e,o),this.embers.frustumCulled=!1,this.scene.add(this.embers)}update(t){this.clock+=t;const s=this.blob.paint;if(!s)return;const e=s.coverage(),o=Mt(e),i=this.blob.modifiers;i.speedMul=o.speedMul,i.jumpMul=o.jumpMul,i.massMul=o.massMul,i.grip=o.grip,i.size=o.size,i.waterproof=o.waterproof,i.glow=o.glow,this.updateEmbers(t,e.byColor.red),this.updateGlow(o.glow,Y(e)??wt(e))}dispose(){this.scene.remove(this.embers),this.embers.geometry.dispose(),this.embers.material.dispose()}updateEmbers(t,s){const e=E(s);if(s>=C){const i=e*3;let n=Math.floor(i)+(Math.random()0;)this.spawnEmber(this._spawnAt)}const o=this.emberHex;for(let i=0;i0&&s){const o=.55+.45*Math.sin(this.clock*8);e.emissive.set(f[s]),e.emissiveIntensity=t*o*1.4}else e.emissiveIntensity=0}}class kt{root;bars=new Map;totalPct;buffLine;lastUpdate=-1e9;constructor(t=document.body){const s=document.createElement("div");s.style.cssText=["position:fixed","top:12px","left:12px","z-index:10","font:12px/1.4 ui-monospace,SFMono-Regular,Menlo,monospace","color:#fff","background:rgba(18,18,24,0.72)","padding:10px 12px","border-radius:10px","min-width:210px","user-select:none","box-shadow:0 4px 18px rgba(0,0,0,0.35)","backdrop-filter:blur(4px)"].join(";");const e=document.createElement("div");e.textContent="PAINT COVERAGE",e.style.cssText="font-weight:700;letter-spacing:0.08em;opacity:0.85;margin-bottom:8px",s.appendChild(e);for(const i of v){const n=document.createElement("div");n.style.cssText="display:flex;align-items:center;gap:6px;margin:3px 0";const r=document.createElement("div");r.style.cssText=`width:10px;height:10px;border-radius:3px;background:${f[i]};flex:0 0 auto`,n.appendChild(r);const l=document.createElement("div");l.style.cssText="flex:1;height:8px;background:rgba(255,255,255,0.12);border-radius:4px;overflow:hidden";const c=document.createElement("div");c.style.cssText=`height:100%;width:0%;background:${f[i]};transition:width 0.12s linear`,l.appendChild(c),n.appendChild(l);const d=document.createElement("span");d.textContent="0%",d.style.cssText="width:34px;text-align:right;opacity:0.9",n.appendChild(d),s.appendChild(n),this.bars.set(i,{fill:c,pct:d})}const o=document.createElement("div");o.style.cssText="margin-top:8px;border-top:1px solid rgba(255,255,255,0.15);padding-top:6px",o.innerHTML="total painted: ",this.totalPct=document.createElement("span"),this.totalPct.textContent="0%",this.totalPct.style.fontWeight="700",o.appendChild(this.totalPct),s.appendChild(o),this.buffLine=document.createElement("div"),this.buffLine.style.cssText="margin-top:6px;opacity:0.95;min-height:16px",s.appendChild(this.buffLine),t.appendChild(s),this.root=s}update(t,s){const e=performance.now();if(!(e-this.lastUpdate<100)){this.lastUpdate=e;for(const o of v){const i=this.bars.get(o),n=t.byColor[o];i.fill.style.width=`${Math.min(100,n*100).toFixed(0)}%`,i.pct.textContent=`${(n*100).toFixed(0)}%`;const r=n>=C;i.pct.style.opacity=r?"1":"0.5",i.fill.style.boxShadow=n>=T?`0 0 8px ${f[o]}`:"none"}this.totalPct.textContent=`${(t.total*100).toFixed(0)}%`,this.buffLine.innerHTML=this.buffText(t,s)}}buffText(t,s){const e=[];return t.byColor.red>=C&&e.push(`BURN ×${s.speedMul.toFixed(2)}`),t.byColor.green>=C&&e.push(`GRIP ${s.grip.toFixed(2)}`),t.byColor.blue>=C&&e.push(`SLICK waterproof`),e.push(`mass ×${s.massMul.toFixed(2)}`),s.glow>0&&e.push('★ SUPER'),e.length?e.join("  ·  "):'no buffs'}dispose(){this.root.remove()}}function Pt(a,t){const s=new h,e=new h,o=()=>(t.mesh.getWorldScale(e),t.paint.boundingRadius*Math.max(e.x,e.y,e.z)),i=a.events.on("paint:request-splat",r=>{r?.point&&(t.mesh.getWorldPosition(s),s.distanceTo(r.point)<=o()+r.radius&&(t.paint.splatAtPoint(r.point,r.color,r.radius),a.events.emit("paint:splatted",{color:r.color,target:"blob"})))}),n=a.events.on("paint:request-cleanse",r=>{t.paint.cleanse(r?.fraction??1)});return()=>{i(),n()}}const B="sig-red",j="sig-green",L=3.6,S=.6,g=await pt(document.getElementById("app")),{scene:Q,camera:Z,physics:V,rapier:G,events:R}=g;Z.position.set(0,7.5,12);const D=new k(new ut(40,1,40),new _({color:"#e8d9b8"}));D.position.y=-.5;D.receiveShadow=!0;Q.add(D);V.createCollider(G.ColliderDesc.cuboid(20,.5,20).setTranslation(0,-.5,0).setFriction(1.2));const b=new k(new X(S,48,32),new _({color:"#F5F5F7",roughness:.35,metalness:0}));b.castShadow=!0;Q.add(b);const M=V.createRigidBody(G.RigidBodyDesc.dynamic().setTranslation(L,S+.2,0).setAngularDamping(.4));V.createCollider(G.ColliderDesc.ball(S).setRestitution(.2).setFriction(1.4).setDensity(2),M);const Ft=M.mass(),m={mesh:b,body:M,modifiers:ft(),paint:new Ct(b,{size:256})},$=new kt,tt=new J({world:g,position:new h(-7,0,-1.5),color:"red",target:b,paint:m.paint,targetRadius:S,triggerId:B,interval:.9,muzzleSpeed:15,splatRadius:.3}),et=new J({world:g,position:new h(7,0,1.5),color:"green",target:b,paint:m.paint,targetRadius:S,triggerId:j,interval:1,muzzleSpeed:15,splatRadius:.3}),Tt=new _t(g,m);Pt(g,{mesh:b,paint:m.paint});g.addSystem(tt);g.addSystem(et);g.addSystem(Tt);let F=!1;g.addSystem({update(a){M.setAdditionalMass(Math.max(0,Ft*(m.modifiers.massMul-1)),!0);const t=M.translation();if(!F){const o=Math.atan2(t.z,t.x)+.6,i=Math.cos(o)*L,n=Math.sin(o)*L,r=i-t.x,l=n-t.z,c=Math.hypot(r,l)||1,d=9*m.modifiers.speedMul;M.applyImpulse({x:r/c*d*a,y:0,z:l/c*d*a},!0);const p=M.linvel(),y=Math.hypot(p.x,p.z),u=4.2*m.modifiers.speedMul;if(y>u){const x=u/y;M.setLinvel({x:p.x*x,y:p.y,z:p.z*x},!0)}}b.position.set(t.x,t.y,t.z);const s=M.rotation();b.quaternion.set(s.x,s.y,s.z,s.w)}});g.onFrame(()=>{Z.lookAt(b.position.x*.3,.5,b.position.z*.3)});function H(a,t=130){for(let s=0;s{switch(a.code){case"Digit1":tt.fire();break;case"Digit2":et.fire();break;case"KeyB":R.emit("machine:signal",{id:B}),console.log("[signal] machine:signal",B);break;case"KeyN":R.emit("machine:signal",{id:j}),console.log("[signal] machine:signal",j);break;case"Digit9":H("red");break;case"Digit0":H("green");break;case"KeyG":{const t=b.position.clone().add(new h(0,S,0));R.emit("paint:request-splat",{point:t,color:"blue",radius:.35}),console.log("[request-splat] blue on ball");break}case"KeyK":R.emit("paint:request-cleanse",{fraction:.5}),console.log("[request-cleanse] 0.5");break;case"KeyC":m.paint.cleanse(.5),console.log("[cleanse] 0.5 (scrub)");break;case"KeyX":m.paint.cleanse(1),console.log("[cleanse] full");break;case"KeyP":F=!F,console.log(`[orbit] ${F?"paused":"running"}`);break;case"KeyH":$.root.style.display=$.root.style.display==="none"?"":"none";break}});let I=0;R.on("paint:splatted",a=>{a.target==="blob"&&I++});let N=performance.now();g.onFrame(()=>{const a=m.paint.coverage();$.update(a,m.modifiers);const t=performance.now();if(t-N>=1e3){N=t;const s=v.filter(i=>a.byColor[i]>.005).map(i=>`${i}:${(a.byColor[i]*100).toFixed(0)}%`).join(" "),e=m.modifiers,o=[e.speedMul>1.001?`speed×${e.speedMul.toFixed(2)}`:"",e.grip>.001?`grip${e.grip.toFixed(2)}`:"",e.waterproof?"waterproof":"",`mass×${e.massMul.toFixed(2)}`,e.glow>0?"SUPER":""].filter(Boolean).join(" ");console.log(`[paint] total ${(a.total*100).toFixed(0)}% | ${s||"(clean)"} | ${o} | +${I} splats/s`),I=0}});console.log("%cBLOBBO Lane B — Paint demo",`color:${f.red};font-weight:700`);console.log("keys: 1/2 fire B/N signal 9/0 flood G request-splat K request-cleanse C/X cleanse P pause H hud");g.start();