cascading-reel 0.0.16 → 0.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs.js CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const Y=6,g=3,d=3,V=[.04,0,-.04],w={normal:{columnStaggerMs:76,fallMs:800,outroOverlapMs:88,outroRowGapMs:16,rowBaseSpacingRatio:.05,incomingAlphaRampMs:34,fixedStepMs:1e3/60,maxCatchUpStepsPerFrame:4,pixelSnapY:"half"},cinematic:{columnStaggerMs:170,fallMs:2200,outroOverlapMs:300,outroRowGapMs:28,rowBaseSpacingRatio:.05,incomingAlphaRampMs:90,fixedStepMs:1e3/120,maxCatchUpStepsPerFrame:6,pixelSnapY:"none"},turbo:{columnStaggerMs:120,fallMs:1200,outroOverlapMs:180,outroRowGapMs:20,rowBaseSpacingRatio:.04,incomingAlphaRampMs:50,fixedStepMs:1e3/120,maxCatchUpStepsPerFrame:4,pixelSnapY:"half"}},X="normal";function k(e){return w[e]}w.normal.outroRowGapMs;w.normal.rowBaseSpacingRatio;const z=5e3,H=1800,$=.1,L=720,G=34,K=[255,235,110];w.normal.columnStaggerMs;w.normal.fallMs;w.normal.outroOverlapMs;const Q=200;function T(e,t,s){return e<t?t:e>s?s:e}function j(e){return Math.floor(Math.random()*e)}function C(e,t){return(e%t+t)%t}function x(e){return T(Math.round(e),0,255)}function y(e){const t=[];for(let s=0;s<g;s+=1){const i=[];for(let n=0;n<d;n+=1)i.push(j(e));t.push(i)}return t}function U(e){const t=new Map;for(let r=0;r<g;r+=1)for(let o=0;o<d;o+=1){const l=e[r][o];t.set(l,(t.get(l)??0)+1)}let s=e[0][0],i=-1;for(const[r,o]of t.entries())o>i&&(i=o,s=r);const n=[];for(let r=0;r<g;r+=1)for(let o=0;o<d;o+=1)e[r][o]===s&&n.push({col:r,row:o});return n}function E(){return Array.from({length:g},()=>Array.from({length:d},()=>0))}function M(e,t){for(let s=0;s<g;s+=1)for(let i=0;i<d;i+=1)e[s][i]=t}class Z{rafId=null;step=null;start(t){this.rafId===null&&(this.step=t,this.rafId=requestAnimationFrame(this.tick))}stop(){this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null),this.step=null}isRunning(){return this.rafId!==null}tick=t=>{if(!this.step){this.stop();return}if(this.step(t)===!1){this.stop();return}this.rafId!==null&&(this.rafId=requestAnimationFrame(this.tick))}}function J(e){const t=T(e,0,1);return t*t*t*(t*(t*6-15)+10)}function N(e,t){if(e.endMs<=e.startMs)return{value:e.to,t:1,done:!0};const s=T((t-e.startMs)/(e.endMs-e.startMs),0,1),i=J(s);return{value:e.from+(e.to-e.from)*i,t:s,done:s>=1}}function tt(e,t,s,i){const n=[0,0,0];let r=0;for(let o=d-1;o>=0;o-=1){if(e[o]===0){n[o]=0;continue}n[o]=r;const l=Math.floor(t*i);r+=l+s}return n}function B(e){const s=e.height-e.boardY+e.cellH+2,i=[s,s,s];return{columnStaggerMs:e.motionProfile.columnStaggerMs,fallMs:e.motionProfile.fallMs,incomingAlphaRampMs:e.motionProfile.incomingAlphaRampMs,outgoingDistance:s,incomingFromOffsets:[-e.cellH,-e.cellH*2,-e.cellH*3],rowStartDelays:tt(i,e.motionProfile.fallMs,e.motionProfile.outroRowGapMs,e.motionProfile.rowBaseSpacingRatio),incomingStartShift:Math.max(0,e.motionProfile.fallMs-e.motionProfile.outroOverlapMs)}}function it(e){let t=!0,s=!0;for(let i=0;i<e.scriptedOutgoingOffsets.length;i+=1){const n=e.elapsedMs-i*e.motionPlan.columnStaggerMs;for(let r=0;r<d;r+=1){const o=n-e.motionPlan.rowStartDelays[r];if(o<=0){e.scriptedOutgoingOffsets[i][r]=0,e.scriptedIncomingOffsets[i][r]=e.motionPlan.incomingFromOffsets[r],e.scriptedIncomingAlpha[i][r]=0,e.scriptedIncomingVisibility[i][r]="hidden",t=!1,s=!1;continue}const l=N({startMs:0,endMs:e.motionPlan.fallMs,from:0,to:e.motionPlan.outgoingDistance},o);e.scriptedOutgoingOffsets[i][r]=l.value,e.scriptedIncomingVisibility[i][r]=l.done?"entering":"exiting",l.done||(t=!1);const h=o-e.motionPlan.incomingStartShift;if(h<=0){e.scriptedIncomingOffsets[i][r]=e.motionPlan.incomingFromOffsets[r],e.scriptedIncomingAlpha[i][r]=0,e.scriptedIncomingVisibility[i][r]="hidden",s=!1;continue}const c=N({startMs:0,endMs:e.motionPlan.fallMs,from:e.motionPlan.incomingFromOffsets[r],to:0},h);e.scriptedIncomingOffsets[i][r]=c.value,e.scriptedIncomingAlpha[i][r]=T(h/Math.max(1,e.motionPlan.incomingAlphaRampMs),0,1),e.scriptedIncomingVisibility[i][r]=c.done?"active":"entering",c.done||(s=!1)}}return{allOutgoingDone:t,allIncomingDone:s}}function et(e){return e?[x(e[0]),x(e[1]),x(e[2])]:K}function st(e){return e==="rainbow"?"rainbow":"solid"}function W(e){if(e.length!==d)throw new Error(`rows must contain ${d} rows`);for(let t=0;t<d;t+=1)if(!Array.isArray(e[t])||e[t].length!==g)throw new Error(`rows[${t}] must contain ${g} columns`);return[[e[0][0],e[1][0],e[2][0]],[e[0][1],e[1][1],e[2][1]],[e[0][2],e[1][2],e[2][2]]]}function F(e,t){if(e.length!==g)throw new Error(`stopGrid must contain ${g} columns`);const s=[];for(let i=0;i<g;i+=1){const n=e[i];if(!Array.isArray(n)||n.length!==d)throw new Error(`stopGrid[${i}] must contain ${d} rows`);s[i]=[C(n[0],t),C(n[1],t),C(n[2],t)]}return s}function nt(e,t){return F(W(e),t)}function rt(e){return{stopGrid:e.stopGrid?.map(t=>[...t]),stopRows:e.stopRows?.map(t=>[...t]),finaleSequence:e.finaleSequence?.map(t=>t.map(s=>[...s])),finaleSequenceRows:e.finaleSequenceRows?.map(t=>t.map(s=>[...s])),highlightWin:e.highlightWin,callback:e.callback}}class ot{queue;constructor(t){this.queue=(t??[]).map(s=>rt(s))}hasPending(){return this.queue.length>0}consume(){return this.queue.length===0?null:this.queue.shift()??null}}function lt(){return{isSpinning:!1,hasStartedFirstSpin:!1,queueFinished:!1,shouldHighlightCurrentSpin:!1,activeSpinState:null,phase:"idle",winFlashStartedAt:0,outroStartedAt:0,idleStartedAt:0,preSpinStartedAt:0,winEffectsEnvelope:1}}function ht(e,t){e.hasStartedFirstSpin=!0,e.isSpinning=!0,e.phase="outro",e.outroStartedAt=t.startedAt,e.activeSpinState=t.activeSpinState,e.shouldHighlightCurrentSpin=t.shouldHighlightCurrentSpin}function at(e,t,s){e.phase="idle",e.idleStartedAt=s,e.isSpinning=!1,e.shouldHighlightCurrentSpin=!1,e.queueFinished=!t,e.activeSpinState=null}function D(e,t){e.winFlashStartedAt=t,e.phase="winFlash"}function ct(e){e.isSpinning=!1,e.queueFinished=!0}const ut=`
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const V=6,g=3,d=3,X=[.04,0,-.04],w={normal:{columnStaggerMs:76,fallMs:800,outroOverlapMs:88,outroRowGapMs:14,rowBaseSpacingRatio:.05,incomingAlphaRampMs:34,fixedStepMs:1e3/120,maxCatchUpStepsPerFrame:6,pixelSnapY:"none"},cinematic:{columnStaggerMs:170,fallMs:2200,outroOverlapMs:300,outroRowGapMs:28,rowBaseSpacingRatio:.05,incomingAlphaRampMs:90,fixedStepMs:1e3/120,maxCatchUpStepsPerFrame:6,pixelSnapY:"none"},turbo:{columnStaggerMs:120,fallMs:1200,outroOverlapMs:180,outroRowGapMs:20,rowBaseSpacingRatio:.04,incomingAlphaRampMs:50,fixedStepMs:1e3/120,maxCatchUpStepsPerFrame:4,pixelSnapY:"half"}},k="normal";function z(e){return w[e]}w.normal.outroRowGapMs;w.normal.rowBaseSpacingRatio;const $=5e3,y=1800,K=.1,x=720,G=34,Q=[255,235,110];w.normal.columnStaggerMs;w.normal.fallMs;w.normal.outroOverlapMs;const j=200;function T(e,t,s){return e<t?t:e>s?s:e}function Z(e){return Math.floor(Math.random()*e)}function C(e,t){return(e%t+t)%t}function L(e){return T(Math.round(e),0,255)}function U(e){const t=[];for(let s=0;s<g;s+=1){const i=[];for(let n=0;n<d;n+=1)i.push(Z(e));t.push(i)}return t}function N(e){const t=new Map;for(let r=0;r<g;r+=1)for(let o=0;o<d;o+=1){const l=e[r][o];t.set(l,(t.get(l)??0)+1)}let s=e[0][0],i=-1;for(const[r,o]of t.entries())o>i&&(i=o,s=r);const n=[];for(let r=0;r<g;r+=1)for(let o=0;o<d;o+=1)e[r][o]===s&&n.push({col:r,row:o});return n}function A(){return Array.from({length:g},()=>Array.from({length:d},()=>0))}function M(e,t){for(let s=0;s<g;s+=1)for(let i=0;i<d;i+=1)e[s][i]=t}class J{rafId=null;step=null;start(t){this.rafId===null&&(this.step=t,this.rafId=requestAnimationFrame(this.tick))}stop(){this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null),this.step=null}isRunning(){return this.rafId!==null}tick=t=>{if(!this.step){this.stop();return}if(this.step(t)===!1){this.stop();return}this.rafId!==null&&(this.rafId=requestAnimationFrame(this.tick))}}function tt(e){const t=T(e,0,1);return t*t*t*(t*(t*6-15)+10)}function D(e,t){if(e.endMs<=e.startMs)return{value:e.to,t:1,done:!0};const s=T((t-e.startMs)/(e.endMs-e.startMs),0,1),i=tt(s);return{value:e.from+(e.to-e.from)*i,t:s,done:s>=1}}function it(e,t,s,i){const n=[0,0,0];let r=0;for(let o=d-1;o>=0;o-=1){if(e[o]===0){n[o]=0;continue}n[o]=r;const l=Math.floor(t*i);r+=l+s}return n}function W(e){const s=e.height-e.boardY+e.cellH+2,i=[s,s,s];return{columnStaggerMs:e.motionProfile.columnStaggerMs,fallMs:e.motionProfile.fallMs,incomingAlphaRampMs:e.motionProfile.incomingAlphaRampMs,outgoingDistance:s,incomingFromOffsets:[-e.cellH,-e.cellH*2,-e.cellH*3],rowStartDelays:it(i,e.motionProfile.fallMs,e.motionProfile.outroRowGapMs,e.motionProfile.rowBaseSpacingRatio),incomingStartShift:Math.max(0,e.motionProfile.fallMs-e.motionProfile.outroOverlapMs)}}function B(e){let t=!0,s=!0;for(let i=0;i<e.scriptedOutgoingOffsets.length;i+=1){const n=e.elapsedMs-i*e.motionPlan.columnStaggerMs;for(let r=0;r<d;r+=1){const o=n-e.motionPlan.rowStartDelays[r];if(o<=0){e.scriptedOutgoingOffsets[i][r]=0,e.scriptedIncomingOffsets[i][r]=e.motionPlan.incomingFromOffsets[r],e.scriptedIncomingAlpha[i][r]=0,e.scriptedIncomingVisibility[i][r]="hidden",t=!1,s=!1;continue}const l=D({startMs:0,endMs:e.motionPlan.fallMs,from:0,to:e.motionPlan.outgoingDistance},o);e.scriptedOutgoingOffsets[i][r]=l.value,e.scriptedIncomingVisibility[i][r]=l.done?"entering":"exiting",l.done||(t=!1);const h=o-e.motionPlan.incomingStartShift;if(h<=0){e.scriptedIncomingOffsets[i][r]=e.motionPlan.incomingFromOffsets[r],e.scriptedIncomingAlpha[i][r]=0,e.scriptedIncomingVisibility[i][r]="hidden",s=!1;continue}const c=D({startMs:0,endMs:e.motionPlan.fallMs,from:e.motionPlan.incomingFromOffsets[r],to:0},h);e.scriptedIncomingOffsets[i][r]=c.value,e.scriptedIncomingAlpha[i][r]=T(h/Math.max(1,e.motionPlan.incomingAlphaRampMs),0,1),e.scriptedIncomingVisibility[i][r]=c.done?"active":"entering",c.done||(s=!1)}}return{allOutgoingDone:t,allIncomingDone:s}}function et(e){return e?[L(e[0]),L(e[1]),L(e[2])]:Q}function st(e){return e==="rainbow"?"rainbow":"solid"}function F(e){if(e.length!==d)throw new Error(`rows must contain ${d} rows`);for(let t=0;t<d;t+=1)if(!Array.isArray(e[t])||e[t].length!==g)throw new Error(`rows[${t}] must contain ${g} columns`);return[[e[0][0],e[1][0],e[2][0]],[e[0][1],e[1][1],e[2][1]],[e[0][2],e[1][2],e[2][2]]]}function H(e,t){if(e.length!==g)throw new Error(`stopGrid must contain ${g} columns`);const s=[];for(let i=0;i<g;i+=1){const n=e[i];if(!Array.isArray(n)||n.length!==d)throw new Error(`stopGrid[${i}] must contain ${d} rows`);s[i]=[C(n[0],t),C(n[1],t),C(n[2],t)]}return s}function nt(e,t){return H(F(e),t)}function rt(e){return{stopGrid:e.stopGrid?.map(t=>[...t]),stopRows:e.stopRows?.map(t=>[...t]),finaleSequence:e.finaleSequence?.map(t=>t.map(s=>[...s])),finaleSequenceRows:e.finaleSequenceRows?.map(t=>t.map(s=>[...s])),highlightWin:e.highlightWin,callback:e.callback}}class ot{queue;constructor(t){this.queue=(t??[]).map(s=>rt(s))}hasPending(){return this.queue.length>0}consume(){return this.queue.length===0?null:this.queue.shift()??null}}function lt(){return{isSpinning:!1,hasStartedFirstSpin:!1,queueFinished:!1,shouldHighlightCurrentSpin:!1,activeSpinState:null,phase:"idle",winFlashStartedAt:0,outroStartedAt:0,idleStartedAt:0,preSpinStartedAt:0,winEffectsEnvelope:1}}function ht(e,t){e.hasStartedFirstSpin=!0,e.isSpinning=!0,e.phase="outro",e.outroStartedAt=t.startedAt,e.activeSpinState=t.activeSpinState,e.shouldHighlightCurrentSpin=t.shouldHighlightCurrentSpin}function at(e,t,s){e.phase="idle",e.idleStartedAt=s,e.isSpinning=!1,e.shouldHighlightCurrentSpin=!1,e.queueFinished=!t,e.activeSpinState=null}function q(e,t){e.winFlashStartedAt=t,e.phase="winFlash"}function ct(e){e.isSpinning=!1,e.queueFinished=!0}const ut=`
2
2
  attribute vec2 a_pos;
3
3
  uniform vec2 u_resolution;
4
4
  uniform vec4 u_destRect;
@@ -50,5 +50,5 @@ void main() {
50
50
  gl_FragColor = u_color;
51
51
  }
52
52
  }
53
- `;class gt{canvas;spriteImage;spriteElementsCount;gl;program;uniforms;quadBuffer;texture;viewportW=1;viewportH=1;spriteWidth;spriteHeight;spriteSegmentHeight;constructor(t){this.canvas=t.canvas,this.spriteImage=t.spriteImage,this.spriteElementsCount=Math.max(1,t.spriteElementsCount),this.spriteWidth=this.spriteImage.width,this.spriteHeight=this.spriteImage.height,this.spriteSegmentHeight=this.spriteHeight/this.spriteElementsCount;const s=this.canvas.getContext("webgl2",{alpha:!0,antialias:!1})??this.canvas.getContext("webgl",{alpha:!0,antialias:!1});if(!s)throw new Error("WebGL context is not available");this.gl=s;const i=this.createShader(this.gl.VERTEX_SHADER,ut),n=this.createShader(this.gl.FRAGMENT_SHADER,dt);this.program=this.createProgram(i,n),this.gl.deleteShader(i),this.gl.deleteShader(n);const r=this.gl.createBuffer();if(!r)throw new Error("Failed to create WebGL quad buffer");this.quadBuffer=r,this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.quadBuffer),this.gl.bufferData(this.gl.ARRAY_BUFFER,new Float32Array([0,0,1,0,0,1,0,1,1,0,1,1]),this.gl.STATIC_DRAW);const o=this.gl.createTexture();if(!o)throw new Error("Failed to create WebGL texture");this.texture=o,this.gl.bindTexture(this.gl.TEXTURE_2D,this.texture),this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL,0),this.gl.pixelStorei(this.gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL,1),this.gl.texImage2D(this.gl.TEXTURE_2D,0,this.gl.RGBA,this.gl.RGBA,this.gl.UNSIGNED_BYTE,this.spriteImage),this.gl.pixelStorei(this.gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL,0),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_MIN_FILTER,this.gl.LINEAR),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_MAG_FILTER,this.gl.LINEAR),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_S,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_T,this.gl.CLAMP_TO_EDGE),this.gl.useProgram(this.program);const l=this.gl.getAttribLocation(this.program,"a_pos");this.gl.enableVertexAttribArray(l),this.gl.vertexAttribPointer(l,2,this.gl.FLOAT,!1,8,0),this.uniforms={resolution:this.gl.getUniformLocation(this.program,"u_resolution"),destRect:this.gl.getUniformLocation(this.program,"u_destRect"),srcRect:this.gl.getUniformLocation(this.program,"u_srcRect"),color:this.gl.getUniformLocation(this.program,"u_color"),useTexture:this.gl.getUniformLocation(this.program,"u_useTexture"),shapeMode:this.gl.getUniformLocation(this.program,"u_shapeMode"),texture:this.gl.getUniformLocation(this.program,"u_texture")},this.gl.uniform1i(this.uniforms.texture,0),this.gl.clearColor(0,0,0,0),this.gl.enable(this.gl.BLEND),this.gl.blendFunc(this.gl.ONE,this.gl.ONE_MINUS_SRC_ALPHA)}resize(t,s){this.viewportW=Math.max(1,Math.floor(t)),this.viewportH=Math.max(1,Math.floor(s)),this.gl.viewport(0,0,this.viewportW,this.viewportH)}beginFrame(){this.gl.useProgram(this.program),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.quadBuffer),this.gl.activeTexture(this.gl.TEXTURE0),this.gl.bindTexture(this.gl.TEXTURE_2D,this.texture),this.gl.uniform2f(this.uniforms.resolution,this.viewportW,this.viewportH),this.gl.uniform1f(this.uniforms.shapeMode,0),this.gl.clear(this.gl.COLOR_BUFFER_BIT)}drawSprite(t,s,i,n,r,o=1){const h=C(t,this.spriteElementsCount)*this.spriteSegmentHeight,c=h+this.spriteSegmentHeight,a=.5,f=a/this.spriteWidth,p=1-a/this.spriteWidth,S=1-(c-a)/this.spriteHeight,A=1-(h+a)/this.spriteHeight;this.gl.uniform4f(this.uniforms.destRect,s,i,n,r),this.gl.uniform4f(this.uniforms.srcRect,f,S,p,A),this.gl.uniform4f(this.uniforms.color,1,1,1,o),this.gl.uniform1f(this.uniforms.shapeMode,0),this.gl.uniform1f(this.uniforms.useTexture,1),this.gl.drawArrays(this.gl.TRIANGLES,0,6)}drawSolidRect(t,s,i,n,r){this.gl.uniform4f(this.uniforms.destRect,t,s,i,n),this.gl.uniform4f(this.uniforms.srcRect,0,0,1,1),this.gl.uniform4f(this.uniforms.color,r[0],r[1],r[2],r[3]),this.gl.uniform1f(this.uniforms.shapeMode,0),this.gl.uniform1f(this.uniforms.useTexture,0),this.gl.drawArrays(this.gl.TRIANGLES,0,6)}drawSoftCircle(t,s,i,n){const r=i*2;this.gl.uniform4f(this.uniforms.destRect,t-i,s-i,r,r),this.gl.uniform4f(this.uniforms.srcRect,0,0,1,1),this.gl.uniform4f(this.uniforms.color,n[0],n[1],n[2],n[3]),this.gl.uniform1f(this.uniforms.useTexture,0),this.gl.uniform1f(this.uniforms.shapeMode,1),this.gl.drawArrays(this.gl.TRIANGLES,0,6),this.gl.uniform1f(this.uniforms.shapeMode,0)}beginAdditiveBlend(){this.gl.blendFunc(this.gl.SRC_ALPHA,this.gl.ONE)}endAdditiveBlend(){this.gl.blendFunc(this.gl.ONE,this.gl.ONE_MINUS_SRC_ALPHA)}dispose(){this.gl.deleteTexture(this.texture),this.gl.deleteBuffer(this.quadBuffer),this.gl.deleteProgram(this.program)}createShader(t,s){const i=this.gl.createShader(t);if(!i)throw new Error("Failed to create WebGL shader");if(this.gl.shaderSource(i,s),this.gl.compileShader(i),!this.gl.getShaderParameter(i,this.gl.COMPILE_STATUS)){const n=this.gl.getShaderInfoLog(i)??"unknown error";throw this.gl.deleteShader(i),new Error(`WebGL shader compile failed: ${n}`)}return i}createProgram(t,s){const i=this.gl.createProgram();if(!i)throw new Error("Failed to create WebGL program");if(this.gl.attachShader(i,t),this.gl.attachShader(i,s),this.gl.linkProgram(i),!this.gl.getProgramParameter(i,this.gl.LINK_STATUS)){const n=this.gl.getProgramInfoLog(i)??"unknown error";throw this.gl.deleteProgram(i),new Error(`WebGL program link failed: ${n}`)}return i}}class u{static RAINBOW_HUE_BUCKETS=24;static PARTICLE_GLOBAL_ALPHA=.9;static PARTICLE_MAX_DISTANCE=.72;static PARTICLE_BASE_RADIUS=.028;canvas;container;button;spinQueueController;spriteUrl;spriteElementsCount;highlightInitialWinningCells;particleColorRgb;particleColorMode;motionProfile;pixelSnapY;spriteImage=null;webglRenderer=null;rafLoop=new Z;runtime=lt();width=0;height=0;cellW=0;cellH=0;boardX=0;boardY=0;scriptedCascadeQueue=[];scriptedOutgoingGrid=null;scriptedPendingGrid=null;scriptedOutroStartedAt=0;scriptedOutroElapsedMs=0;outroMotionPlan=null;scriptedOutgoingOffsets=E();scriptedOutgoingOffsetsPrev=E();scriptedIncomingOffsets=E();scriptedIncomingOffsetsPrev=E();scriptedIncomingAlpha=E();scriptedIncomingAlphaPrev=E();scriptedIncomingVisibility=u.createVisibilityGrid("hidden");scriptedIncomingVisibilityPrev=u.createVisibilityGrid("hidden");winningCells=[];winningCellKeys=new Set;grid;particlesPerCell=G;lastRafTime=0;initialHighlightRequestedAt=0;simulationLastNow=0;simulationAccumulatorMs=0;outroInterpolationAlpha=1;static PRE_SPIN_MS=150;static WIN_EFFECTS_ENVELOPE_TAU_MS=120;static MAX_FRAME_DELTA_MS=250;static WIN_BORDER_ALPHA=.72;static WIN_BORDER_INSET_RATIO=.06;static particleSeedsCache=new Map;constructor(t){this.canvas=t.canvas,this.container=t.container,this.button=t.button,this.spriteUrl=t.sprite,this.spriteElementsCount=Math.max(1,t.spriteElementsCount??Y),this.highlightInitialWinningCells=t.highlightInitialWinningCells!==!1,this.spinQueueController=new ot(t.queuedSpinStates),this.particleColorRgb=et(t.particleColorRgb),this.particleColorMode=st(t.particleColorMode),this.motionProfile=k(t.motionProfile??X),this.pixelSnapY=t.pixelSnapY??this.motionProfile.pixelSnapY,this.grid=t.initialSegments?nt(t.initialSegments,this.spriteElementsCount):y(this.spriteElementsCount)}async init(){if(this.bindEvents(),this.resize(),await this.loadSpriteIfProvided(),!this.spriteImage)throw new Error("sprite is required for WebGL renderer");this.webglRenderer=new gt({canvas:this.canvas,spriteImage:this.spriteImage,spriteElementsCount:this.spriteElementsCount}),this.webglRenderer.resize(this.width,this.height),this.applyInitialHighlightIfNeeded(),requestAnimationFrame(t=>{this.render(t),this.startLoop()})}destroy(){this.unbindEvents(),this.rafLoop.stop(),this.simulationLastNow=0,this.simulationAccumulatorMs=0,ct(this.runtime),this.webglRenderer?.dispose(),this.webglRenderer=null,this.clearWinningCells()}spin(){if(this.runtime.isSpinning||this.runtime.queueFinished)return;if(!this.spinQueueController.hasPending()){this.runtime.queueFinished=!0,this.button&&(this.button.disabled=!0);return}const t=this.spinQueueController.consume(),s=t?.highlightWin===!0;this.runtime.isSpinning=!0,this.runtime.phase="preSpin",this.runtime.preSpinStartedAt=performance.now(),this.runtime.activeSpinState=t,this.runtime.shouldHighlightCurrentSpin=s,this.runtime.hasStartedFirstSpin=!0,this.simulationLastNow=0,this.simulationAccumulatorMs=0,this.button&&(this.button.disabled=!0),this.startLoop()}bindEvents(){window.addEventListener("resize",this.resize),this.button?.addEventListener("click",this.onSpinClick)}unbindEvents(){window.removeEventListener("resize",this.resize),this.button?.removeEventListener("click",this.onSpinClick)}onSpinClick=()=>{if(this.runtime.phase==="winFlash"&&(this.runtime.shouldHighlightCurrentSpin||!this.runtime.hasStartedFirstSpin)){this.finishSpinWithUi(!0),this.spinQueueController.hasPending()&&this.spin();return}this.runtime.isSpinning||this.spin()};getNextGrid(t){return t?F(t,this.spriteElementsCount):y(this.spriteElementsCount)}update(t){if(this.runtime.isSpinning){if(this.runtime.phase==="preSpin"){if(t-this.runtime.preSpinStartedAt<u.PRE_SPIN_MS)return;ht(this.runtime,{activeSpinState:this.runtime.activeSpinState,shouldHighlightCurrentSpin:this.runtime.shouldHighlightCurrentSpin,startedAt:t}),this.runtime.preSpinStartedAt=0;const s=this.runtime.activeSpinState?.finaleSequenceRows?this.runtime.activeSpinState.finaleSequenceRows.map(r=>W(r)):this.runtime.activeSpinState?.finaleSequence??[];this.scriptedCascadeQueue=s.map(r=>r.map(o=>[...o])),this.clearWinningCells();const i=this.runtime.activeSpinState?.stopRows?W(this.runtime.activeSpinState.stopRows):this.runtime.activeSpinState?.stopGrid,n=this.getNextGrid(i);this.startOutroTransition(n,t)}if(this.runtime.phase!=="outro"&&this.runtime.phase==="winFlash"){!this.runtime.shouldHighlightCurrentSpin&&t-this.runtime.winFlashStartedAt>=z&&this.finishSpinWithUi();return}}}stepScriptedOutro(t){if(!this.scriptedOutgoingGrid||!this.scriptedPendingGrid){this.finishSpinWithUi();return}this.outroMotionPlan||(this.outroMotionPlan=B({height:this.height,boardY:this.boardY,cellH:this.cellH,motionProfile:this.motionProfile})),this.scriptedOutroElapsedMs+=t;const{allOutgoingDone:s,allIncomingDone:i}=it({elapsedMs:this.scriptedOutroElapsedMs,scriptedOutgoingOffsets:this.scriptedOutgoingOffsets,scriptedIncomingOffsets:this.scriptedIncomingOffsets,scriptedIncomingAlpha:this.scriptedIncomingAlpha,scriptedIncomingVisibility:this.scriptedIncomingVisibility,motionPlan:this.outroMotionPlan});if(!(!s||!i)&&(this.grid=this.scriptedPendingGrid,this.scriptedOutgoingGrid=null,this.scriptedPendingGrid=null,this.outroMotionPlan=null,this.clearWinningCells(),this.resetOutroBuffers(1,"active"),!this.tryStartScriptedCascade(this.scriptedOutroStartedAt+this.scriptedOutroElapsedMs))){if(!this.runtime.shouldHighlightCurrentSpin){this.finishSpinWithUi();return}this.setWinningCells(U(this.grid)),D(this.runtime,this.scriptedOutroStartedAt+this.scriptedOutroElapsedMs),this.runtime.activeSpinState?.callback?.(),this.button&&this.runtime.shouldHighlightCurrentSpin&&this.spinQueueController.hasPending()&&(this.button.disabled=!1)}}tryStartScriptedCascade(t){if(this.scriptedCascadeQueue.length===0)return!1;const s=this.scriptedCascadeQueue.shift();return s?(this.startOutroTransition(F(s,this.spriteElementsCount),t),!0):!1}startOutroTransition(t,s){this.scriptedOutgoingGrid=this.grid.map(i=>[...i]),this.scriptedPendingGrid=t.map(i=>[...i]),this.outroMotionPlan=B({height:this.height,boardY:this.boardY,cellH:this.cellH,motionProfile:this.motionProfile}),this.resetOutroBuffers(0,"hidden"),this.clearWinningCells(),this.runtime.phase="outro",this.scriptedOutroStartedAt=s,this.scriptedOutroElapsedMs=0,this.outroInterpolationAlpha=1}finishSpinWithUi(t=!1){const s=this.runtime.activeSpinState,i=t?void 0:s?.callback,n=this.spinQueueController.hasPending();at(this.runtime,n,performance.now()),this.button&&(this.button.disabled=this.runtime.queueFinished),i?.()}applyInitialHighlightIfNeeded(){this.highlightInitialWinningCells&&(this.setWinningCells(U(this.grid)),this.initialHighlightRequestedAt=performance.now())}render(t){if(!this.webglRenderer)return;this.initialHighlightRequestedAt>0&&t-this.initialHighlightRequestedAt>=Q&&(D(this.runtime,this.initialHighlightRequestedAt),this.runtime.winEffectsEnvelope=1,this.initialHighlightRequestedAt=0,this.button&&(this.button.disabled=!1));const s=this.runtime.phase==="preSpin"?0:this.runtime.phase==="winFlash"?1:0;if(this.lastRafTime>0){const l=Math.min(t-this.lastRafTime,50),h=1-Math.exp(-l/u.WIN_EFFECTS_ENVELOPE_TAU_MS);this.runtime.winEffectsEnvelope+=(s-this.runtime.winEffectsEnvelope)*h}this.lastRafTime=t,this.webglRenderer.beginFrame();const i=this.runtime.phase==="winFlash"||this.runtime.phase==="preSpin"||this.initialHighlightRequestedAt>0&&this.winningCells.length>0;this.runtime.phase==="outro"&&this.scriptedOutgoingGrid&&this.scriptedPendingGrid?(this.drawGridInterpolated(this.scriptedOutgoingGrid,this.scriptedOutgoingOffsetsPrev,this.scriptedOutgoingOffsets,this.outroInterpolationAlpha,i),this.drawGridInterpolated(this.scriptedPendingGrid,this.scriptedIncomingOffsetsPrev,this.scriptedIncomingOffsets,this.outroInterpolationAlpha,i,this.scriptedIncomingAlphaPrev,this.scriptedIncomingAlpha,this.scriptedIncomingVisibility)):this.drawGrid(this.grid,null,i);const n=this.initialHighlightRequestedAt>0?"winFlash":this.runtime.phase,r=this.initialHighlightRequestedAt>0?this.initialHighlightRequestedAt:this.runtime.winFlashStartedAt,o=this.initialHighlightRequestedAt>0?1:this.runtime.winEffectsEnvelope;this.drawWinningEffects({now:t,phase:n,winFlashStartedAt:r,winEffectsEnvelope:o})}isWinningCell=(t,s)=>this.winningCellKeys.has(`${t}:${s}`);getRowCompactOffset(t){return(V[t]??0)*this.cellH}applyPixelSnapY(t){return this.pixelSnapY==="integer"?Math.round(t):this.pixelSnapY==="half"?Math.round(t*2)/2:t}drawGrid(t,s,i){this.drawGridWithSampler({grid:t,skipWinningCells:i,sampleOffsetY:(n,r)=>s?s[n][r]:0,sampleAlpha:()=>1,isVisible:()=>!0})}drawGridInterpolated(t,s,i,n,r,o,l,h){this.drawGridWithSampler({grid:t,skipWinningCells:r,sampleOffsetY:(c,a)=>s[c][a]+(i[c][a]-s[c][a])*n,sampleAlpha:(c,a)=>o&&l?o[c][a]+(l[c][a]-o[c][a])*n:1,isVisible:(c,a)=>!h||h[c][a]!=="hidden"})}drawGridWithSampler(t){const s=this.webglRenderer;if(s)for(let i=0;i<g;i+=1){const n=this.boardX+i*this.cellW;for(let r=0;r<d;r+=1){if(t.skipWinningCells&&this.isWinningCell(i,r)||!t.isVisible(i,r))continue;const o=t.sampleOffsetY(i,r),l=this.applyPixelSnapY(this.boardY+r*this.cellH+o+this.getRowCompactOffset(r));if(l>this.height||l+this.cellH<0)continue;const h=t.sampleAlpha(i,r);h<=0||s.drawSprite(t.grid[i][r],n,l,this.cellW,this.cellH,h)}}}drawWinningEffects(t){const s=this.webglRenderer;if(!s||this.winningCells.length===0||t.phase!=="winFlash"&&t.phase!=="preSpin")return;const i=Math.max(0,Math.min(1,t.winEffectsEnvelope)),n=Math.max(0,t.now-t.winFlashStartedAt),r=n%H/H,o=1+Math.sin(r*Math.PI*2)*$*i,l=Math.max(1,this.cellW*u.WIN_BORDER_INSET_RATIO),h=Math.max(1,this.cellW*.03),c=this.runtime.phase==="winFlash"&&this.runtime.shouldHighlightCurrentSpin&&this.runtime.hasStartedFirstSpin;for(const a of this.winningCells){const f=this.boardX+a.col*this.cellW,p=this.boardY+a.row*this.cellH+this.getRowCompactOffset(a.row),S=this.grid[a.col][a.row],A=u.WIN_BORDER_ALPHA*i,m=this.particleColorMode==="rainbow"?u.hslToRgb01((n*.2+a.col*36+a.row*22)%360,.96,.64):[this.particleColorRgb[0]/255,this.particleColorRgb[1]/255,this.particleColorRgb[2]/255],P=this.cellW*o,R=this.cellH*o,I=(this.cellW-P)*.5,q=(this.cellH-R)*.5;s.drawSprite(S,f+I,p+q,P,R,1);const _=f+l,b=p+l,O=this.cellW-l*2,v=this.cellH-l*2;O<=h*2||v<=h*2||(s.drawSolidRect(_,b,O,h,[m[0],m[1],m[2],A]),s.drawSolidRect(_,b+v-h,O,h,[m[0],m[1],m[2],A]),s.drawSolidRect(_,b,h,v,[m[0],m[1],m[2],A]),s.drawSolidRect(_+O-h,b,h,v,[m[0],m[1],m[2],A]),c&&this.drawCellParticleBurst({renderer:s,cell:a,centerX:f+this.cellW*.5,centerY:p+this.cellH*.5,elapsed:n,envelope:i}))}}drawCellParticleBurst(t){const s=Math.min(this.cellW,this.cellH)*u.PARTICLE_MAX_DISTANCE,i=Math.min(this.cellW,this.cellH)*u.PARTICLE_BASE_RADIUS,n=u.getParticleSeeds(t.cell.col,t.cell.row),r=[this.particleColorRgb[0]/255,this.particleColorRgb[1]/255,this.particleColorRgb[2]/255];t.renderer.beginAdditiveBlend();for(let o=0;o<this.particlesPerCell;o+=1){const l=n[o],h=l.phaseOffset*L,c=t.elapsed-h;if(c<0)continue;const a=c%L/L,f=l.seedA*Math.PI*2,p=s*a*(.35+l.seedB*.65),S=t.centerX+Math.cos(f)*p,A=t.centerY+Math.sin(f)*p,m=.7+.9*Math.max(0,Math.sin((t.elapsed*.012+l.twinkleSeed*2)*Math.PI*2)),P=Math.max(1,i*(.55+l.seedC*.6)*(1-a*.5)),R=Math.max(0,Math.min(1,(.9+m*.2)*u.PARTICLE_GLOBAL_ALPHA*t.envelope));if(R<=0)continue;const I=this.particleColorMode==="rainbow"?u.getRainbowParticleColor(t.elapsed,t.cell.col,t.cell.row,l.seedA):r;t.renderer.drawSoftCircle(S,A,P,[I[0],I[1],I[2],R])}t.renderer.endAdditiveBlend()}static hslToRgb01(t,s,i){const n=(t%360+360)%360,r=Math.max(0,Math.min(1,s)),o=Math.max(0,Math.min(1,i)),l=(1-Math.abs(2*o-1))*r,h=n/60,c=l*(1-Math.abs(h%2-1));let a=0,f=0,p=0;h>=0&&h<1?(a=l,f=c):h<2?(a=c,f=l):h<3?(f=l,p=c):h<4?(f=c,p=l):h<5?(a=c,p=l):(a=l,p=c);const S=o-l*.5;return[a+S,f+S,p+S]}static getRainbowParticleColor(t,s,i,n){const r=(n*360+t*.24+s*38+i*22)%360,o=360/u.RAINBOW_HUE_BUCKETS,l=Math.floor(r/o)*o;return u.hslToRgb01(l,.98,.64)}static hash01(t,s,i,n){const r=Math.sin(t*127.1+s*311.7+i*74.7+n*19.3)*43758.5453;return r-Math.floor(r)}static getParticleSeeds(t,s){const i=`${t},${s}`,n=u.particleSeedsCache.get(i);if(n)return n;const r=[];for(let o=0;o<G;o+=1)r.push({seedA:u.hash01(t,s,o,1),seedB:u.hash01(t,s,o,2),seedC:u.hash01(t,s,o,3),phaseOffset:u.hash01(t,s,o,4),twinkleSeed:u.hash01(t,s,o,5)});return u.particleSeedsCache.set(i,r),r}clearWinningCells(){this.winningCells=[],this.winningCellKeys.clear()}setWinningCells(t){this.winningCells=t,this.winningCellKeys.clear();for(const s of t)this.winningCellKeys.add(`${s.col}:${s.row}`)}resize=()=>{const t=this.container.getBoundingClientRect(),s=Math.max(1,Math.min(window.devicePixelRatio||1,2)),i=Math.max(300,Math.floor(t.width*s));this.width=i,this.height=i;const n=Math.floor(Math.min(this.width/g,this.height/d));this.cellW=n,this.cellH=n,this.boardX=Math.floor((this.width-this.cellW*g)/2),this.boardY=Math.floor((this.height-this.cellH*d)/2),this.canvas.width=this.width,this.canvas.height=this.height,this.webglRenderer?.resize(this.width,this.height)};async loadSpriteIfProvided(){if(!this.spriteUrl)return;const t=new Image;t.decoding="async",t.src=this.spriteUrl;try{await t.decode(),this.spriteImage=t}catch{this.spriteImage=null}}advanceSimulation(t){if(this.simulationLastNow<=0){this.simulationLastNow=t,this.update(t);return}const s=Math.max(0,Math.min(t-this.simulationLastNow,u.MAX_FRAME_DELTA_MS));if(this.simulationLastNow=t,this.update(t),this.runtime.phase!=="outro"){this.outroInterpolationAlpha=1;return}this.snapshotOutroState(),this.simulationAccumulatorMs+=s;const i=this.motionProfile.fixedStepMs;let n=0;for(;this.simulationAccumulatorMs>=i&&n<this.motionProfile.maxCatchUpStepsPerFrame&&this.runtime.phase==="outro";)this.stepScriptedOutro(i),this.simulationAccumulatorMs-=i,n+=1;if(this.runtime.phase!=="outro"){this.outroInterpolationAlpha=1;return}this.outroInterpolationAlpha=Math.max(0,Math.min(1,this.simulationAccumulatorMs/i))}snapshotOutroState(){this.copyOffsets(this.scriptedOutgoingOffsetsPrev,this.scriptedOutgoingOffsets),this.copyOffsets(this.scriptedIncomingOffsetsPrev,this.scriptedIncomingOffsets),this.copyOffsets(this.scriptedIncomingAlphaPrev,this.scriptedIncomingAlpha),this.copyVisibility(this.scriptedIncomingVisibilityPrev,this.scriptedIncomingVisibility)}copyOffsets(t,s){for(let i=0;i<g;i+=1)for(let n=0;n<d;n+=1)t[i][n]=s[i][n]}copyVisibility(t,s){for(let i=0;i<g;i+=1)for(let n=0;n<d;n+=1)t[i][n]=s[i][n]}resetOutroBuffers(t,s){M(this.scriptedOutgoingOffsets,0),M(this.scriptedIncomingOffsets,0),M(this.scriptedOutgoingOffsetsPrev,0),M(this.scriptedIncomingOffsetsPrev,0),M(this.scriptedIncomingAlpha,t),M(this.scriptedIncomingAlphaPrev,t),this.fillVisibilityGrid(this.scriptedIncomingVisibility,s),this.fillVisibilityGrid(this.scriptedIncomingVisibilityPrev,s)}static createVisibilityGrid(t){return Array.from({length:g},()=>Array.from({length:d},()=>t))}fillVisibilityGrid(t,s){for(let i=0;i<g;i+=1)for(let n=0;n<d;n+=1)t[i][n]=s}startLoop(){this.rafLoop.isRunning()||this.rafLoop.start(t=>(this.advanceSimulation(t),this.render(t),this.shouldKeepAnimating()))}shouldKeepAnimating(){return this.runtime.isSpinning||this.initialHighlightRequestedAt>0?!0:this.runtime.winEffectsEnvelope>.001}}exports.CascadingReel=u;
53
+ `;class gt{canvas;spriteImage;spriteElementsCount;gl;program;uniforms;quadBuffer;texture;viewportW=1;viewportH=1;spriteWidth;spriteHeight;spriteSegmentHeight;constructor(t){this.canvas=t.canvas,this.spriteImage=t.spriteImage,this.spriteElementsCount=Math.max(1,t.spriteElementsCount),this.spriteWidth=this.spriteImage.width,this.spriteHeight=this.spriteImage.height,this.spriteSegmentHeight=this.spriteHeight/this.spriteElementsCount;const s=this.canvas.getContext("webgl2",{alpha:!0,antialias:!1})??this.canvas.getContext("webgl",{alpha:!0,antialias:!1});if(!s)throw new Error("WebGL context is not available");this.gl=s;const i=this.createShader(this.gl.VERTEX_SHADER,ut),n=this.createShader(this.gl.FRAGMENT_SHADER,dt);this.program=this.createProgram(i,n),this.gl.deleteShader(i),this.gl.deleteShader(n);const r=this.gl.createBuffer();if(!r)throw new Error("Failed to create WebGL quad buffer");this.quadBuffer=r,this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.quadBuffer),this.gl.bufferData(this.gl.ARRAY_BUFFER,new Float32Array([0,0,1,0,0,1,0,1,1,0,1,1]),this.gl.STATIC_DRAW);const o=this.gl.createTexture();if(!o)throw new Error("Failed to create WebGL texture");this.texture=o,this.gl.bindTexture(this.gl.TEXTURE_2D,this.texture),this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL,0),this.gl.pixelStorei(this.gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL,1),this.gl.texImage2D(this.gl.TEXTURE_2D,0,this.gl.RGBA,this.gl.RGBA,this.gl.UNSIGNED_BYTE,this.spriteImage),this.gl.pixelStorei(this.gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL,0),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_MIN_FILTER,this.gl.LINEAR),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_MAG_FILTER,this.gl.LINEAR),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_S,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_T,this.gl.CLAMP_TO_EDGE),this.gl.useProgram(this.program);const l=this.gl.getAttribLocation(this.program,"a_pos");this.gl.enableVertexAttribArray(l),this.gl.vertexAttribPointer(l,2,this.gl.FLOAT,!1,8,0),this.uniforms={resolution:this.gl.getUniformLocation(this.program,"u_resolution"),destRect:this.gl.getUniformLocation(this.program,"u_destRect"),srcRect:this.gl.getUniformLocation(this.program,"u_srcRect"),color:this.gl.getUniformLocation(this.program,"u_color"),useTexture:this.gl.getUniformLocation(this.program,"u_useTexture"),shapeMode:this.gl.getUniformLocation(this.program,"u_shapeMode"),texture:this.gl.getUniformLocation(this.program,"u_texture")},this.gl.uniform1i(this.uniforms.texture,0),this.gl.clearColor(0,0,0,0),this.gl.enable(this.gl.BLEND),this.gl.blendFunc(this.gl.ONE,this.gl.ONE_MINUS_SRC_ALPHA)}resize(t,s){this.viewportW=Math.max(1,Math.floor(t)),this.viewportH=Math.max(1,Math.floor(s)),this.gl.viewport(0,0,this.viewportW,this.viewportH)}beginFrame(){this.gl.useProgram(this.program),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.quadBuffer),this.gl.activeTexture(this.gl.TEXTURE0),this.gl.bindTexture(this.gl.TEXTURE_2D,this.texture),this.gl.uniform2f(this.uniforms.resolution,this.viewportW,this.viewportH),this.gl.uniform1f(this.uniforms.shapeMode,0),this.gl.clear(this.gl.COLOR_BUFFER_BIT)}drawSprite(t,s,i,n,r,o=1){const h=C(t,this.spriteElementsCount)*this.spriteSegmentHeight,c=h+this.spriteSegmentHeight,a=.5,f=a/this.spriteWidth,p=1-a/this.spriteWidth,S=1-(c-a)/this.spriteHeight,E=1-(h+a)/this.spriteHeight;this.gl.uniform4f(this.uniforms.destRect,s,i,n,r),this.gl.uniform4f(this.uniforms.srcRect,f,S,p,E),this.gl.uniform4f(this.uniforms.color,1,1,1,o),this.gl.uniform1f(this.uniforms.shapeMode,0),this.gl.uniform1f(this.uniforms.useTexture,1),this.gl.drawArrays(this.gl.TRIANGLES,0,6)}drawSolidRect(t,s,i,n,r){this.gl.uniform4f(this.uniforms.destRect,t,s,i,n),this.gl.uniform4f(this.uniforms.srcRect,0,0,1,1),this.gl.uniform4f(this.uniforms.color,r[0],r[1],r[2],r[3]),this.gl.uniform1f(this.uniforms.shapeMode,0),this.gl.uniform1f(this.uniforms.useTexture,0),this.gl.drawArrays(this.gl.TRIANGLES,0,6)}drawSoftCircle(t,s,i,n){const r=i*2;this.gl.uniform4f(this.uniforms.destRect,t-i,s-i,r,r),this.gl.uniform4f(this.uniforms.srcRect,0,0,1,1),this.gl.uniform4f(this.uniforms.color,n[0],n[1],n[2],n[3]),this.gl.uniform1f(this.uniforms.useTexture,0),this.gl.uniform1f(this.uniforms.shapeMode,1),this.gl.drawArrays(this.gl.TRIANGLES,0,6),this.gl.uniform1f(this.uniforms.shapeMode,0)}beginAdditiveBlend(){this.gl.blendFunc(this.gl.SRC_ALPHA,this.gl.ONE)}endAdditiveBlend(){this.gl.blendFunc(this.gl.ONE,this.gl.ONE_MINUS_SRC_ALPHA)}dispose(){this.gl.deleteTexture(this.texture),this.gl.deleteBuffer(this.quadBuffer),this.gl.deleteProgram(this.program)}createShader(t,s){const i=this.gl.createShader(t);if(!i)throw new Error("Failed to create WebGL shader");if(this.gl.shaderSource(i,s),this.gl.compileShader(i),!this.gl.getShaderParameter(i,this.gl.COMPILE_STATUS)){const n=this.gl.getShaderInfoLog(i)??"unknown error";throw this.gl.deleteShader(i),new Error(`WebGL shader compile failed: ${n}`)}return i}createProgram(t,s){const i=this.gl.createProgram();if(!i)throw new Error("Failed to create WebGL program");if(this.gl.attachShader(i,t),this.gl.attachShader(i,s),this.gl.linkProgram(i),!this.gl.getProgramParameter(i,this.gl.LINK_STATUS)){const n=this.gl.getProgramInfoLog(i)??"unknown error";throw this.gl.deleteProgram(i),new Error(`WebGL program link failed: ${n}`)}return i}}class u{static RAINBOW_HUE_BUCKETS=24;static PARTICLE_GLOBAL_ALPHA=.9;static PARTICLE_MAX_DISTANCE=.72;static PARTICLE_BASE_RADIUS=.028;canvas;container;button;spinQueueController;spriteUrl;spriteElementsCount;highlightInitialWinningCells;particleColorRgb;particleColorMode;motionProfile;pixelSnapY;spriteImage=null;webglRenderer=null;rafLoop=new J;runtime=lt();width=0;height=0;cellW=0;cellH=0;boardX=0;boardY=0;scriptedCascadeQueue=[];scriptedOutgoingGrid=null;scriptedPendingGrid=null;scriptedOutroStartedAt=0;scriptedOutroElapsedMs=0;outroMotionPlan=null;scriptedOutgoingOffsets=A();scriptedOutgoingOffsetsPrev=A();scriptedIncomingOffsets=A();scriptedIncomingOffsetsPrev=A();scriptedIncomingAlpha=A();scriptedIncomingAlphaPrev=A();scriptedIncomingVisibility=u.createVisibilityGrid("hidden");scriptedIncomingVisibilityPrev=u.createVisibilityGrid("hidden");winningCells=[];winningCellKeys=new Set;grid;particlesPerCell=G;lastRafTime=0;initialHighlightRequestedAt=0;simulationLastNow=0;simulationAccumulatorMs=0;outroInterpolationAlpha=1;isOutroPipelineWarmedUp=!1;static PRE_SPIN_MS=150;static WIN_EFFECTS_ENVELOPE_TAU_MS=120;static MAX_FRAME_DELTA_MS=250;static WIN_BORDER_ALPHA=.72;static WIN_BORDER_INSET_RATIO=.06;static particleSeedsCache=new Map;constructor(t){this.canvas=t.canvas,this.container=t.container,this.button=t.button,this.spriteUrl=t.sprite,this.spriteElementsCount=Math.max(1,t.spriteElementsCount??V),this.highlightInitialWinningCells=t.highlightInitialWinningCells!==!1,this.spinQueueController=new ot(t.queuedSpinStates),this.particleColorRgb=et(t.particleColorRgb),this.particleColorMode=st(t.particleColorMode),this.motionProfile=z(t.motionProfile??k),this.pixelSnapY=t.pixelSnapY??this.motionProfile.pixelSnapY,this.grid=t.initialSegments?nt(t.initialSegments,this.spriteElementsCount):U(this.spriteElementsCount)}async init(){if(this.bindEvents(),this.resize(),await this.loadSpriteIfProvided(),!this.spriteImage)throw new Error("sprite is required for WebGL renderer");this.webglRenderer=new gt({canvas:this.canvas,spriteImage:this.spriteImage,spriteElementsCount:this.spriteElementsCount}),this.webglRenderer.resize(this.width,this.height),this.warmUpOutroPipeline(),this.applyInitialHighlightIfNeeded(),requestAnimationFrame(t=>{this.render(t),this.startLoop()})}destroy(){this.unbindEvents(),this.rafLoop.stop(),this.simulationLastNow=0,this.simulationAccumulatorMs=0,ct(this.runtime),this.webglRenderer?.dispose(),this.webglRenderer=null,this.clearWinningCells()}spin(){if(this.runtime.isSpinning||this.runtime.queueFinished)return;if(!this.spinQueueController.hasPending()){this.runtime.queueFinished=!0,this.button&&(this.button.disabled=!0);return}const t=this.spinQueueController.consume(),s=t?.highlightWin===!0;this.runtime.isSpinning=!0,this.runtime.phase="preSpin",this.runtime.preSpinStartedAt=performance.now(),this.runtime.activeSpinState=t,this.runtime.shouldHighlightCurrentSpin=s,this.runtime.hasStartedFirstSpin=!0,this.simulationLastNow=0,this.simulationAccumulatorMs=0,this.button&&(this.button.disabled=!0),this.startLoop()}bindEvents(){window.addEventListener("resize",this.resize),this.button?.addEventListener("click",this.onSpinClick)}unbindEvents(){window.removeEventListener("resize",this.resize),this.button?.removeEventListener("click",this.onSpinClick)}onSpinClick=()=>{if(this.runtime.phase==="winFlash"&&(this.runtime.shouldHighlightCurrentSpin||!this.runtime.hasStartedFirstSpin)){this.finishSpinWithUi(!0),this.spinQueueController.hasPending()&&this.spin();return}this.runtime.isSpinning||this.spin()};getNextGrid(t){return t?H(t,this.spriteElementsCount):U(this.spriteElementsCount)}update(t){if(this.runtime.isSpinning){if(this.runtime.phase==="preSpin"){if(t-this.runtime.preSpinStartedAt<u.PRE_SPIN_MS)return;ht(this.runtime,{activeSpinState:this.runtime.activeSpinState,shouldHighlightCurrentSpin:this.runtime.shouldHighlightCurrentSpin,startedAt:t}),this.runtime.preSpinStartedAt=0;const s=this.runtime.activeSpinState?.finaleSequenceRows?this.runtime.activeSpinState.finaleSequenceRows.map(r=>F(r)):this.runtime.activeSpinState?.finaleSequence??[];this.scriptedCascadeQueue=s.map(r=>r.map(o=>[...o])),this.clearWinningCells();const i=this.runtime.activeSpinState?.stopRows?F(this.runtime.activeSpinState.stopRows):this.runtime.activeSpinState?.stopGrid,n=this.getNextGrid(i);this.startOutroTransition(n,t)}if(this.runtime.phase!=="outro"&&this.runtime.phase==="winFlash"){!this.runtime.shouldHighlightCurrentSpin&&t-this.runtime.winFlashStartedAt>=$&&this.finishSpinWithUi();return}}}stepScriptedOutro(t){if(!this.scriptedOutgoingGrid||!this.scriptedPendingGrid){this.finishSpinWithUi();return}this.outroMotionPlan||(this.outroMotionPlan=W({height:this.height,boardY:this.boardY,cellH:this.cellH,motionProfile:this.motionProfile})),this.scriptedOutroElapsedMs+=t;const{allOutgoingDone:s,allIncomingDone:i}=B({elapsedMs:this.scriptedOutroElapsedMs,scriptedOutgoingOffsets:this.scriptedOutgoingOffsets,scriptedIncomingOffsets:this.scriptedIncomingOffsets,scriptedIncomingAlpha:this.scriptedIncomingAlpha,scriptedIncomingVisibility:this.scriptedIncomingVisibility,motionPlan:this.outroMotionPlan});if(!(!s||!i)&&(this.grid=this.scriptedPendingGrid,this.scriptedOutgoingGrid=null,this.scriptedPendingGrid=null,this.outroMotionPlan=null,this.clearWinningCells(),this.resetOutroBuffers(1,"active"),!this.tryStartScriptedCascade(this.scriptedOutroStartedAt+this.scriptedOutroElapsedMs))){if(!this.runtime.shouldHighlightCurrentSpin){this.finishSpinWithUi();return}this.setWinningCells(N(this.grid)),q(this.runtime,this.scriptedOutroStartedAt+this.scriptedOutroElapsedMs),this.runtime.activeSpinState?.callback?.(),this.button&&this.runtime.shouldHighlightCurrentSpin&&this.spinQueueController.hasPending()&&(this.button.disabled=!1)}}tryStartScriptedCascade(t){if(this.scriptedCascadeQueue.length===0)return!1;const s=this.scriptedCascadeQueue.shift();return s?(this.startOutroTransition(H(s,this.spriteElementsCount),t),!0):!1}startOutroTransition(t,s){this.scriptedOutgoingGrid=this.grid.map(i=>[...i]),this.scriptedPendingGrid=t.map(i=>[...i]),this.outroMotionPlan=W({height:this.height,boardY:this.boardY,cellH:this.cellH,motionProfile:this.motionProfile}),this.resetOutroBuffers(0,"hidden"),this.clearWinningCells(),this.runtime.phase="outro",this.scriptedOutroStartedAt=s,this.scriptedOutroElapsedMs=0,this.outroInterpolationAlpha=1}finishSpinWithUi(t=!1){const s=this.runtime.activeSpinState,i=t?void 0:s?.callback,n=this.spinQueueController.hasPending();at(this.runtime,n,performance.now()),this.button&&(this.button.disabled=this.runtime.queueFinished),i?.()}applyInitialHighlightIfNeeded(){this.highlightInitialWinningCells&&(this.setWinningCells(N(this.grid)),this.initialHighlightRequestedAt=performance.now())}warmUpOutroPipeline(){if(this.isOutroPipelineWarmedUp)return;const t=W({height:this.height,boardY:this.boardY,cellH:this.cellH,motionProfile:this.motionProfile}),s=A(),i=A(),n=A(),r=u.createVisibilityGrid("hidden"),o=Math.max(...t.rowStartDelays),l=(g-1)*t.columnStaggerMs,h=[0,this.motionProfile.fixedStepMs,t.incomingStartShift+this.motionProfile.fixedStepMs,t.fallMs+o+l+this.motionProfile.fixedStepMs];for(const c of h)B({elapsedMs:c,scriptedOutgoingOffsets:s,scriptedIncomingOffsets:i,scriptedIncomingAlpha:n,scriptedIncomingVisibility:r,motionPlan:t});this.isOutroPipelineWarmedUp=!0}render(t){if(!this.webglRenderer)return;this.initialHighlightRequestedAt>0&&t-this.initialHighlightRequestedAt>=j&&(q(this.runtime,this.initialHighlightRequestedAt),this.runtime.winEffectsEnvelope=1,this.initialHighlightRequestedAt=0,this.button&&(this.button.disabled=!1));const s=this.runtime.phase==="preSpin"?0:this.runtime.phase==="winFlash"?1:0;if(this.lastRafTime>0){const l=Math.min(t-this.lastRafTime,50),h=1-Math.exp(-l/u.WIN_EFFECTS_ENVELOPE_TAU_MS);this.runtime.winEffectsEnvelope+=(s-this.runtime.winEffectsEnvelope)*h}this.lastRafTime=t,this.webglRenderer.beginFrame();const i=this.runtime.phase==="winFlash"||this.runtime.phase==="preSpin"||this.initialHighlightRequestedAt>0&&this.winningCells.length>0;this.runtime.phase==="outro"&&this.scriptedOutgoingGrid&&this.scriptedPendingGrid?(this.drawGridInterpolated(this.scriptedOutgoingGrid,this.scriptedOutgoingOffsetsPrev,this.scriptedOutgoingOffsets,this.outroInterpolationAlpha,i),this.drawGridInterpolated(this.scriptedPendingGrid,this.scriptedIncomingOffsetsPrev,this.scriptedIncomingOffsets,this.outroInterpolationAlpha,i,this.scriptedIncomingAlphaPrev,this.scriptedIncomingAlpha,this.scriptedIncomingVisibility)):this.drawGrid(this.grid,null,i);const n=this.initialHighlightRequestedAt>0?"winFlash":this.runtime.phase,r=this.initialHighlightRequestedAt>0?this.initialHighlightRequestedAt:this.runtime.winFlashStartedAt,o=this.initialHighlightRequestedAt>0?1:this.runtime.winEffectsEnvelope;this.drawWinningEffects({now:t,phase:n,winFlashStartedAt:r,winEffectsEnvelope:o})}isWinningCell=(t,s)=>this.winningCellKeys.has(`${t}:${s}`);getRowCompactOffset(t){return(X[t]??0)*this.cellH}applyPixelSnapY(t){return this.pixelSnapY==="integer"?Math.round(t):this.pixelSnapY==="half"?Math.round(t*2)/2:t}drawGrid(t,s,i){this.drawGridWithSampler({grid:t,skipWinningCells:i,sampleOffsetY:(n,r)=>s?s[n][r]:0,sampleAlpha:()=>1,isVisible:()=>!0})}drawGridInterpolated(t,s,i,n,r,o,l,h){this.drawGridWithSampler({grid:t,skipWinningCells:r,sampleOffsetY:(c,a)=>s[c][a]+(i[c][a]-s[c][a])*n,sampleAlpha:(c,a)=>o&&l?o[c][a]+(l[c][a]-o[c][a])*n:1,isVisible:(c,a)=>!h||h[c][a]!=="hidden"})}drawGridWithSampler(t){const s=this.webglRenderer;if(s)for(let i=0;i<g;i+=1){const n=this.boardX+i*this.cellW;for(let r=0;r<d;r+=1){if(t.skipWinningCells&&this.isWinningCell(i,r)||!t.isVisible(i,r))continue;const o=t.sampleOffsetY(i,r),l=this.applyPixelSnapY(this.boardY+r*this.cellH+o+this.getRowCompactOffset(r));if(l>this.height||l+this.cellH<0)continue;const h=t.sampleAlpha(i,r);h<=0||s.drawSprite(t.grid[i][r],n,l,this.cellW,this.cellH,h)}}}drawWinningEffects(t){const s=this.webglRenderer;if(!s||this.winningCells.length===0||t.phase!=="winFlash"&&t.phase!=="preSpin")return;const i=Math.max(0,Math.min(1,t.winEffectsEnvelope)),n=Math.max(0,t.now-t.winFlashStartedAt),r=n%y/y,o=1+Math.sin(r*Math.PI*2)*K*i,l=Math.max(1,this.cellW*u.WIN_BORDER_INSET_RATIO),h=Math.max(1,this.cellW*.03),c=this.runtime.phase==="winFlash"&&this.runtime.shouldHighlightCurrentSpin&&this.runtime.hasStartedFirstSpin;for(const a of this.winningCells){const f=this.boardX+a.col*this.cellW,p=this.boardY+a.row*this.cellH+this.getRowCompactOffset(a.row),S=this.grid[a.col][a.row],E=u.WIN_BORDER_ALPHA*i,m=this.particleColorMode==="rainbow"?u.hslToRgb01((n*.2+a.col*36+a.row*22)%360,.96,.64):[this.particleColorRgb[0]/255,this.particleColorRgb[1]/255,this.particleColorRgb[2]/255],I=this.cellW*o,R=this.cellH*o,P=(this.cellW-I)*.5,Y=(this.cellH-R)*.5;s.drawSprite(S,f+P,p+Y,I,R,1);const O=f+l,_=p+l,b=this.cellW-l*2,v=this.cellH-l*2;b<=h*2||v<=h*2||(s.drawSolidRect(O,_,b,h,[m[0],m[1],m[2],E]),s.drawSolidRect(O,_+v-h,b,h,[m[0],m[1],m[2],E]),s.drawSolidRect(O,_,h,v,[m[0],m[1],m[2],E]),s.drawSolidRect(O+b-h,_,h,v,[m[0],m[1],m[2],E]),c&&this.drawCellParticleBurst({renderer:s,cell:a,centerX:f+this.cellW*.5,centerY:p+this.cellH*.5,elapsed:n,envelope:i}))}}drawCellParticleBurst(t){const s=Math.min(this.cellW,this.cellH)*u.PARTICLE_MAX_DISTANCE,i=Math.min(this.cellW,this.cellH)*u.PARTICLE_BASE_RADIUS,n=u.getParticleSeeds(t.cell.col,t.cell.row),r=[this.particleColorRgb[0]/255,this.particleColorRgb[1]/255,this.particleColorRgb[2]/255];t.renderer.beginAdditiveBlend();for(let o=0;o<this.particlesPerCell;o+=1){const l=n[o],h=l.phaseOffset*x,c=t.elapsed-h;if(c<0)continue;const a=c%x/x,f=l.seedA*Math.PI*2,p=s*a*(.35+l.seedB*.65),S=t.centerX+Math.cos(f)*p,E=t.centerY+Math.sin(f)*p,m=.7+.9*Math.max(0,Math.sin((t.elapsed*.012+l.twinkleSeed*2)*Math.PI*2)),I=Math.max(1,i*(.55+l.seedC*.6)*(1-a*.5)),R=Math.max(0,Math.min(1,(.9+m*.2)*u.PARTICLE_GLOBAL_ALPHA*t.envelope));if(R<=0)continue;const P=this.particleColorMode==="rainbow"?u.getRainbowParticleColor(t.elapsed,t.cell.col,t.cell.row,l.seedA):r;t.renderer.drawSoftCircle(S,E,I,[P[0],P[1],P[2],R])}t.renderer.endAdditiveBlend()}static hslToRgb01(t,s,i){const n=(t%360+360)%360,r=Math.max(0,Math.min(1,s)),o=Math.max(0,Math.min(1,i)),l=(1-Math.abs(2*o-1))*r,h=n/60,c=l*(1-Math.abs(h%2-1));let a=0,f=0,p=0;h>=0&&h<1?(a=l,f=c):h<2?(a=c,f=l):h<3?(f=l,p=c):h<4?(f=c,p=l):h<5?(a=c,p=l):(a=l,p=c);const S=o-l*.5;return[a+S,f+S,p+S]}static getRainbowParticleColor(t,s,i,n){const r=(n*360+t*.24+s*38+i*22)%360,o=360/u.RAINBOW_HUE_BUCKETS,l=Math.floor(r/o)*o;return u.hslToRgb01(l,.98,.64)}static hash01(t,s,i,n){const r=Math.sin(t*127.1+s*311.7+i*74.7+n*19.3)*43758.5453;return r-Math.floor(r)}static getParticleSeeds(t,s){const i=`${t},${s}`,n=u.particleSeedsCache.get(i);if(n)return n;const r=[];for(let o=0;o<G;o+=1)r.push({seedA:u.hash01(t,s,o,1),seedB:u.hash01(t,s,o,2),seedC:u.hash01(t,s,o,3),phaseOffset:u.hash01(t,s,o,4),twinkleSeed:u.hash01(t,s,o,5)});return u.particleSeedsCache.set(i,r),r}clearWinningCells(){this.winningCells=[],this.winningCellKeys.clear()}setWinningCells(t){this.winningCells=t,this.winningCellKeys.clear();for(const s of t)this.winningCellKeys.add(`${s.col}:${s.row}`)}resize=()=>{const t=this.container.getBoundingClientRect(),s=Math.max(1,Math.min(window.devicePixelRatio||1,2)),i=Math.max(300,Math.floor(t.width*s));this.width=i,this.height=i;const n=Math.floor(Math.min(this.width/g,this.height/d));this.cellW=n,this.cellH=n,this.boardX=Math.floor((this.width-this.cellW*g)/2),this.boardY=Math.floor((this.height-this.cellH*d)/2),this.canvas.width=this.width,this.canvas.height=this.height,this.webglRenderer?.resize(this.width,this.height)};async loadSpriteIfProvided(){if(!this.spriteUrl)return;const t=new Image;t.decoding="async",t.src=this.spriteUrl;try{await t.decode(),this.spriteImage=t}catch{this.spriteImage=null}}advanceSimulation(t){if(this.simulationLastNow<=0){this.simulationLastNow=t,this.update(t);return}const s=Math.max(0,Math.min(t-this.simulationLastNow,u.MAX_FRAME_DELTA_MS));if(this.simulationLastNow=t,this.update(t),this.runtime.phase!=="outro"){this.outroInterpolationAlpha=1;return}this.snapshotOutroState(),this.simulationAccumulatorMs+=s;const i=this.motionProfile.fixedStepMs;let n=0;for(;this.simulationAccumulatorMs>=i&&n<this.motionProfile.maxCatchUpStepsPerFrame&&this.runtime.phase==="outro";)this.stepScriptedOutro(i),this.simulationAccumulatorMs-=i,n+=1;if(this.runtime.phase!=="outro"){this.outroInterpolationAlpha=1;return}this.outroInterpolationAlpha=Math.max(0,Math.min(1,this.simulationAccumulatorMs/i))}snapshotOutroState(){this.copyOffsets(this.scriptedOutgoingOffsetsPrev,this.scriptedOutgoingOffsets),this.copyOffsets(this.scriptedIncomingOffsetsPrev,this.scriptedIncomingOffsets),this.copyOffsets(this.scriptedIncomingAlphaPrev,this.scriptedIncomingAlpha),this.copyVisibility(this.scriptedIncomingVisibilityPrev,this.scriptedIncomingVisibility)}copyOffsets(t,s){for(let i=0;i<g;i+=1)for(let n=0;n<d;n+=1)t[i][n]=s[i][n]}copyVisibility(t,s){for(let i=0;i<g;i+=1)for(let n=0;n<d;n+=1)t[i][n]=s[i][n]}resetOutroBuffers(t,s){M(this.scriptedOutgoingOffsets,0),M(this.scriptedIncomingOffsets,0),M(this.scriptedOutgoingOffsetsPrev,0),M(this.scriptedIncomingOffsetsPrev,0),M(this.scriptedIncomingAlpha,t),M(this.scriptedIncomingAlphaPrev,t),this.fillVisibilityGrid(this.scriptedIncomingVisibility,s),this.fillVisibilityGrid(this.scriptedIncomingVisibilityPrev,s)}static createVisibilityGrid(t){return Array.from({length:g},()=>Array.from({length:d},()=>t))}fillVisibilityGrid(t,s){for(let i=0;i<g;i+=1)for(let n=0;n<d;n+=1)t[i][n]=s}startLoop(){this.rafLoop.isRunning()||this.rafLoop.start(t=>(this.advanceSimulation(t),this.render(t),this.shouldKeepAnimating()))}shouldKeepAnimating(){return this.runtime.isSpinning||this.initialHighlightRequestedAt>0?!0:this.runtime.winEffectsEnvelope>.001}}exports.CascadingReel=u;
54
54
  //# sourceMappingURL=index.cjs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs.js","sources":["../src/constants.ts","../src/utils/math.ts","../src/core/grid.ts","../src/core/loop.ts","../src/core/motionTimeline.ts","../src/core/outro.ts","../src/normalize.ts","../src/core/spinQueue.ts","../src/core/state.ts","../src/render/webglRenderer.ts","../src/CascadingReel.ts"],"sourcesContent":["export const DEFAULT_SPRITE_ELEMENTS_COUNT = 6;\nexport const GRID_COLS = 3;\nexport const GRID_ROWS = 3;\nexport const ROW_COMPACT_OFFSETS_RATIO: [number, number, number] = [0.04, 0, -0.04];\nexport type PixelSnapYMode = 'none' | 'half' | 'integer';\nexport type MotionProfile = {\n columnStaggerMs: number;\n fallMs: number;\n outroOverlapMs: number;\n outroRowGapMs: number;\n rowBaseSpacingRatio: number;\n incomingAlphaRampMs: number;\n fixedStepMs: number;\n maxCatchUpStepsPerFrame: number;\n pixelSnapY: PixelSnapYMode;\n};\nexport const MOTION_PROFILES = {\n normal: {\n columnStaggerMs: 76,\n fallMs: 800,\n outroOverlapMs: 88,\n outroRowGapMs: 16,\n rowBaseSpacingRatio: 0.05,\n incomingAlphaRampMs: 34,\n fixedStepMs: 1000 / 60,\n maxCatchUpStepsPerFrame: 4,\n pixelSnapY: 'half',\n },\n cinematic: {\n columnStaggerMs: 170,\n fallMs: 2200,\n outroOverlapMs: 300,\n outroRowGapMs: 28,\n rowBaseSpacingRatio: 0.05,\n incomingAlphaRampMs: 90,\n fixedStepMs: 1000 / 120,\n maxCatchUpStepsPerFrame: 6,\n pixelSnapY: 'none',\n },\n turbo: {\n columnStaggerMs: 120,\n fallMs: 1200,\n outroOverlapMs: 180,\n outroRowGapMs: 20,\n rowBaseSpacingRatio: 0.04,\n incomingAlphaRampMs: 50,\n fixedStepMs: 1000 / 120,\n maxCatchUpStepsPerFrame: 4,\n pixelSnapY: 'half',\n },\n} as const satisfies Record<string, MotionProfile>;\nexport type MotionProfileName = keyof typeof MOTION_PROFILES;\nexport const DEFAULT_MOTION_PROFILE_NAME: MotionProfileName = 'normal';\nexport function getMotionProfile(name: MotionProfileName): MotionProfile {\n return MOTION_PROFILES[name];\n}\nexport const FLOW_OUTRO_ROW_GAP_MS = MOTION_PROFILES.normal.outroRowGapMs;\nexport const FLOW_ROW_BASE_SPACING_RATIO = MOTION_PROFILES.normal.rowBaseSpacingRatio;\n/** Длительность фазы подсветки выигрыша (мс). Подсветка отключается по таймауту. */\nexport const FLOW_WIN_FLASH_MS = 5000;\nexport const FLOW_WIN_PULSE_PERIOD_MS = 1800;\nexport const FLOW_WIN_PULSE_AMPLITUDE = 0.1;\n/** Duration for one particle to fly from center to edge (continuous spray). */\nexport const PARTICLE_FLY_DURATION_MS = 720;\nexport const FLOW_WIN_PARTICLES_PER_CELL_HIGH = 34;\nexport const DEFAULT_PARTICLE_COLOR_RGB: [number, number, number] = [255, 235, 110];\nexport const FLOW_COLUMN_STAGGER_MS = MOTION_PROFILES.normal.columnStaggerMs;\nexport const FLOW_FALL_MS = MOTION_PROFILES.normal.fallMs;\nexport const FLOW_OUTRO_OVERLAP_MS = MOTION_PROFILES.normal.outroOverlapMs;\n/** Delay before starting initial win-effect so canvas/JIT can warm up. */\nexport const INITIAL_WIN_FLASH_DELAY_MS = 200;\n","export function clamp(value: number, min: number, max: number): number {\n if (value < min) return min;\n if (value > max) return max;\n return value;\n}\n\nexport function easeOutCubic(t: number): number {\n return 1 - (1 - t) ** 3;\n}\n\nexport function easeOutQuad(t: number): number {\n return 1 - (1 - t) ** 2;\n}\n\nexport function randomInt(maxExclusive: number): number {\n return Math.floor(Math.random() * maxExclusive);\n}\n\nexport function normalizeSegment(segment: number, elementsCount: number): number {\n return ((segment % elementsCount) + elementsCount) % elementsCount;\n}\n\nexport function normalizeRgbChannel(value: number): number {\n return clamp(Math.round(value), 0, 255);\n}\n\nexport function normalizeAlphaChannel(value: number): number {\n return clamp(value, 0, 1);\n}\n","import { GRID_COLS, GRID_ROWS } from '../constants';\nimport type { CellPosition, SymbolId } from '../types';\nimport { randomInt } from '../utils/math';\n\nexport function createRandomGrid(spriteElementsCount: number): SymbolId[][] {\n const grid: SymbolId[][] = [];\n for (let col = 0; col < GRID_COLS; col += 1) {\n const column: SymbolId[] = [];\n for (let row = 0; row < GRID_ROWS; row += 1) {\n column.push(randomInt(spriteElementsCount));\n }\n grid.push(column);\n }\n return grid;\n}\n\nexport function findMostFrequentCells(grid: SymbolId[][]): CellPosition[] {\n const counts = new Map<SymbolId, number>();\n for (let col = 0; col < GRID_COLS; col += 1) {\n for (let row = 0; row < GRID_ROWS; row += 1) {\n const symbol = grid[col][row];\n counts.set(symbol, (counts.get(symbol) ?? 0) + 1);\n }\n }\n\n let selectedSymbol: SymbolId = grid[0][0];\n let maxCount = -1;\n for (const [symbol, count] of counts.entries()) {\n if (count > maxCount) {\n maxCount = count;\n selectedSymbol = symbol;\n }\n }\n\n const cells: CellPosition[] = [];\n for (let col = 0; col < GRID_COLS; col += 1) {\n for (let row = 0; row < GRID_ROWS; row += 1) {\n if (grid[col][row] === selectedSymbol) {\n cells.push({ col, row });\n }\n }\n }\n return cells;\n}\n\nexport function createZeroOffsets(): number[][] {\n return Array.from({ length: GRID_COLS }, () => Array.from({ length: GRID_ROWS }, () => 0));\n}\n\nexport function fillOffsets(offsets: number[][], value: number): void {\n for (let col = 0; col < GRID_COLS; col += 1) {\n for (let row = 0; row < GRID_ROWS; row += 1) {\n offsets[col][row] = value;\n }\n }\n}\n","export type RafStep = (now: number) => boolean;\n\nexport class RafLoop {\n private rafId: number | null = null;\n private step: RafStep | null = null;\n\n public start(step: RafStep): void {\n if (this.rafId !== null) return;\n this.step = step;\n this.rafId = requestAnimationFrame(this.tick);\n }\n\n public stop(): void {\n if (this.rafId !== null) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n this.step = null;\n }\n\n public isRunning(): boolean {\n return this.rafId !== null;\n }\n\n private readonly tick = (now: number): void => {\n if (!this.step) {\n this.stop();\n return;\n }\n\n const shouldContinue = this.step(now);\n if (shouldContinue === false) {\n this.stop();\n return;\n }\n\n if (this.rafId === null) return;\n this.rafId = requestAnimationFrame(this.tick);\n };\n}\n","import { clamp } from '../utils/math';\n\nexport type MotionSegment = {\n startMs: number;\n endMs: number;\n from: number;\n to: number;\n};\n\nexport function smootherStep(t: number): number {\n const x = clamp(t, 0, 1);\n return x * x * x * (x * (x * 6 - 15) + 10);\n}\n\nexport function sampleSegment(\n segment: MotionSegment,\n nowMs: number,\n): {\n value: number;\n t: number;\n done: boolean;\n} {\n if (segment.endMs <= segment.startMs) {\n return { value: segment.to, t: 1, done: true };\n }\n const t = clamp((nowMs - segment.startMs) / (segment.endMs - segment.startMs), 0, 1);\n const eased = smootherStep(t);\n return {\n value: segment.from + (segment.to - segment.from) * eased,\n t,\n done: t >= 1,\n };\n}\n","import type { MotionProfile } from '../constants';\nimport { GRID_ROWS } from '../constants';\nimport { clamp } from '../utils/math';\nimport { sampleSegment } from './motionTimeline';\n\nexport type CellVisibility = 'hidden' | 'entering' | 'active' | 'exiting';\n\nexport type OutroMotionPlan = {\n columnStaggerMs: number;\n fallMs: number;\n incomingAlphaRampMs: number;\n outgoingDistance: number;\n incomingFromOffsets: [number, number, number];\n rowStartDelays: [number, number, number];\n incomingStartShift: number;\n};\n\nexport function buildSequentialRowStartDelays(\n fromRowOffsets: [number, number, number],\n durationMs: number,\n gapMs: number,\n rowBaseSpacingRatio: number,\n): [number, number, number] {\n const delays: [number, number, number] = [0, 0, 0];\n let nextDelay = 0;\n for (let row = GRID_ROWS - 1; row >= 0; row -= 1) {\n if (fromRowOffsets[row] === 0) {\n delays[row] = 0;\n continue;\n }\n delays[row] = nextDelay;\n const baseSpacing = Math.floor(durationMs * rowBaseSpacingRatio);\n nextDelay += baseSpacing + gapMs;\n }\n return delays;\n}\n\nexport function buildOutroMotionPlan(params: {\n height: number;\n boardY: number;\n cellH: number;\n motionProfile: MotionProfile;\n}): OutroMotionPlan {\n const exitEpsilon = 2;\n const outgoingDistance = params.height - params.boardY + params.cellH + exitEpsilon;\n const outgoingOffsetsForOrder: [number, number, number] = [\n outgoingDistance,\n outgoingDistance,\n outgoingDistance,\n ];\n return {\n columnStaggerMs: params.motionProfile.columnStaggerMs,\n fallMs: params.motionProfile.fallMs,\n incomingAlphaRampMs: params.motionProfile.incomingAlphaRampMs,\n outgoingDistance,\n incomingFromOffsets: [-params.cellH, -params.cellH * 2, -params.cellH * 3],\n rowStartDelays: buildSequentialRowStartDelays(\n outgoingOffsetsForOrder,\n params.motionProfile.fallMs,\n params.motionProfile.outroRowGapMs,\n params.motionProfile.rowBaseSpacingRatio,\n ),\n // Делаем небольшое перекрытие фаз: новые символы начинают входить\n // немного раньше окончания исходящих, чтобы движение воспринималось\n // как непрерывный поток.\n incomingStartShift: Math.max(\n 0,\n params.motionProfile.fallMs - params.motionProfile.outroOverlapMs,\n ),\n };\n}\n\nexport function updateOutroOffsets(params: {\n elapsedMs: number;\n scriptedOutgoingOffsets: number[][];\n scriptedIncomingOffsets: number[][];\n scriptedIncomingAlpha: number[][];\n scriptedIncomingVisibility: CellVisibility[][];\n motionPlan: OutroMotionPlan;\n}): { allOutgoingDone: boolean; allIncomingDone: boolean } {\n let allOutgoingDone = true;\n let allIncomingDone = true;\n\n for (let col = 0; col < params.scriptedOutgoingOffsets.length; col += 1) {\n const columnElapsed = params.elapsedMs - col * params.motionPlan.columnStaggerMs;\n for (let row = 0; row < GRID_ROWS; row += 1) {\n const rowElapsed = columnElapsed - params.motionPlan.rowStartDelays[row];\n\n if (rowElapsed <= 0) {\n params.scriptedOutgoingOffsets[col][row] = 0;\n params.scriptedIncomingOffsets[col][row] = params.motionPlan.incomingFromOffsets[row];\n params.scriptedIncomingAlpha[col][row] = 0;\n params.scriptedIncomingVisibility[col][row] = 'hidden';\n allOutgoingDone = false;\n allIncomingDone = false;\n continue;\n }\n\n const outgoing = sampleSegment(\n {\n startMs: 0,\n endMs: params.motionPlan.fallMs,\n from: 0,\n to: params.motionPlan.outgoingDistance,\n },\n rowElapsed,\n );\n params.scriptedOutgoingOffsets[col][row] = outgoing.value;\n params.scriptedIncomingVisibility[col][row] = outgoing.done ? 'entering' : 'exiting';\n if (!outgoing.done) allOutgoingDone = false;\n\n const incomingElapsed = rowElapsed - params.motionPlan.incomingStartShift;\n if (incomingElapsed <= 0) {\n params.scriptedIncomingOffsets[col][row] = params.motionPlan.incomingFromOffsets[row];\n params.scriptedIncomingAlpha[col][row] = 0;\n params.scriptedIncomingVisibility[col][row] = 'hidden';\n allIncomingDone = false;\n continue;\n }\n\n const incoming = sampleSegment(\n {\n startMs: 0,\n endMs: params.motionPlan.fallMs,\n from: params.motionPlan.incomingFromOffsets[row],\n to: 0,\n },\n incomingElapsed,\n );\n params.scriptedIncomingOffsets[col][row] = incoming.value;\n params.scriptedIncomingAlpha[col][row] = clamp(\n incomingElapsed / Math.max(1, params.motionPlan.incomingAlphaRampMs),\n 0,\n 1,\n );\n params.scriptedIncomingVisibility[col][row] = incoming.done ? 'active' : 'entering';\n if (!incoming.done) allIncomingDone = false;\n }\n }\n\n return { allOutgoingDone, allIncomingDone };\n}\n","import { DEFAULT_PARTICLE_COLOR_RGB, GRID_COLS, GRID_ROWS } from './constants';\nimport type { SpinState, SymbolId } from './types';\nimport { normalizeRgbChannel, normalizeSegment } from './utils/math';\n\nexport function normalizeParticleColor(color?: [number, number, number]): [number, number, number] {\n if (!color) return DEFAULT_PARTICLE_COLOR_RGB;\n return [\n normalizeRgbChannel(color[0]),\n normalizeRgbChannel(color[1]),\n normalizeRgbChannel(color[2]),\n ];\n}\n\nexport function normalizeParticleColorMode(mode?: 'solid' | 'rainbow'): 'solid' | 'rainbow' {\n if (mode === 'rainbow') return 'rainbow';\n return 'solid';\n}\n\nexport function rowsToStopGrid(rows: number[][]): number[][] {\n if (rows.length !== GRID_ROWS) {\n throw new Error(`rows must contain ${GRID_ROWS} rows`);\n }\n for (let row = 0; row < GRID_ROWS; row += 1) {\n if (!Array.isArray(rows[row]) || rows[row].length !== GRID_COLS) {\n throw new Error(`rows[${row}] must contain ${GRID_COLS} columns`);\n }\n }\n\n return [\n [rows[0][0], rows[1][0], rows[2][0]],\n [rows[0][1], rows[1][1], rows[2][1]],\n [rows[0][2], rows[1][2], rows[2][2]],\n ];\n}\n\nexport function normalizeStopGrid(stopGrid: number[][], elementsCount: number): SymbolId[][] {\n if (stopGrid.length !== GRID_COLS) {\n throw new Error(`stopGrid must contain ${GRID_COLS} columns`);\n }\n\n const next: SymbolId[][] = [];\n for (let col = 0; col < GRID_COLS; col += 1) {\n const column = stopGrid[col];\n if (!Array.isArray(column) || column.length !== GRID_ROWS) {\n throw new Error(`stopGrid[${col}] must contain ${GRID_ROWS} rows`);\n }\n next[col] = [\n normalizeSegment(column[0], elementsCount),\n normalizeSegment(column[1], elementsCount),\n normalizeSegment(column[2], elementsCount),\n ];\n }\n return next;\n}\n\nexport function normalizeInitialSegments(\n initialSegments: number[][],\n elementsCount: number,\n): SymbolId[][] {\n return normalizeStopGrid(rowsToStopGrid(initialSegments), elementsCount);\n}\n\nexport function cloneSpinState(state: SpinState): SpinState {\n return {\n stopGrid: state.stopGrid?.map((column) => [...column]),\n stopRows: state.stopRows?.map((row) => [...row]),\n finaleSequence: state.finaleSequence?.map((grid) => grid.map((column) => [...column])),\n finaleSequenceRows: state.finaleSequenceRows?.map((grid) => grid.map((row) => [...row])),\n highlightWin: state.highlightWin,\n callback: state.callback,\n };\n}\n","import { cloneSpinState } from '../normalize';\nimport type { SpinState } from '../types';\n\nexport class SpinQueueController {\n private queue: SpinState[];\n\n public constructor(initialQueue?: SpinState[]) {\n this.queue = (initialQueue ?? []).map((entry) => cloneSpinState(entry));\n }\n\n public hasPending(): boolean {\n return this.queue.length > 0;\n }\n\n public consume(): SpinState | null {\n if (this.queue.length === 0) return null;\n return this.queue.shift() ?? null;\n }\n}\n","import type { SpinPhase, SpinState } from '../types';\n\nexport type RuntimeState = {\n isSpinning: boolean;\n hasStartedFirstSpin: boolean;\n queueFinished: boolean;\n shouldHighlightCurrentSpin: boolean;\n activeSpinState: SpinState | null;\n phase: SpinPhase;\n winFlashStartedAt: number;\n outroStartedAt: number;\n idleStartedAt: number;\n preSpinStartedAt: number;\n winEffectsEnvelope: number;\n};\n\nexport function createRuntimeState(): RuntimeState {\n return {\n isSpinning: false,\n hasStartedFirstSpin: false,\n queueFinished: false,\n shouldHighlightCurrentSpin: false,\n activeSpinState: null,\n phase: 'idle',\n winFlashStartedAt: 0,\n outroStartedAt: 0,\n idleStartedAt: 0,\n preSpinStartedAt: 0,\n winEffectsEnvelope: 1,\n };\n}\n\nexport function beginSpin(\n state: RuntimeState,\n params: {\n activeSpinState: SpinState | null;\n shouldHighlightCurrentSpin: boolean;\n startedAt: number;\n },\n): void {\n state.hasStartedFirstSpin = true;\n state.isSpinning = true;\n state.phase = 'outro';\n state.outroStartedAt = params.startedAt;\n state.activeSpinState = params.activeSpinState;\n state.shouldHighlightCurrentSpin = params.shouldHighlightCurrentSpin;\n}\n\nexport function finishSpin(state: RuntimeState, hasPendingInQueue: boolean, now: number): void {\n state.phase = 'idle';\n state.idleStartedAt = now;\n state.isSpinning = false;\n state.shouldHighlightCurrentSpin = false;\n state.queueFinished = !hasPendingInQueue;\n state.activeSpinState = null;\n}\n\nexport function startWinFlash(state: RuntimeState, startedAt: number): void {\n state.winFlashStartedAt = startedAt;\n state.phase = 'winFlash';\n}\n\nexport function destroyState(state: RuntimeState): void {\n state.isSpinning = false;\n state.queueFinished = true;\n}\n","import type { SymbolId } from '../types';\nimport { normalizeSegment } from '../utils/math';\n\ntype WebGLUniforms = {\n resolution: WebGLUniformLocation | null;\n destRect: WebGLUniformLocation | null;\n srcRect: WebGLUniformLocation | null;\n color: WebGLUniformLocation | null;\n useTexture: WebGLUniformLocation | null;\n shapeMode: WebGLUniformLocation | null;\n texture: WebGLUniformLocation | null;\n};\n\nconst VERTEX_SHADER_SOURCE = `\nattribute vec2 a_pos;\nuniform vec2 u_resolution;\nuniform vec4 u_destRect;\nvarying vec2 v_uv;\n\nvoid main() {\n vec2 local = a_pos;\n vec2 pixel = vec2(\n u_destRect.x + local.x * u_destRect.z,\n u_destRect.y + local.y * u_destRect.w\n );\n\n vec2 zeroToOne = pixel / u_resolution;\n vec2 clip = vec2(\n zeroToOne.x * 2.0 - 1.0,\n 1.0 - zeroToOne.y * 2.0\n );\n\n gl_Position = vec4(clip, 0.0, 1.0);\n v_uv = local;\n}\n`;\n\nconst FRAGMENT_SHADER_SOURCE = `\nprecision mediump float;\n\nuniform sampler2D u_texture;\nuniform vec4 u_srcRect;\nuniform vec4 u_color;\nuniform float u_useTexture;\nuniform float u_shapeMode;\nvarying vec2 v_uv;\n\nvoid main() {\n if (u_shapeMode > 0.5) {\n vec2 centered = v_uv - vec2(0.5, 0.5);\n float dist = length(centered) * 2.0;\n if (dist > 1.0) {\n discard;\n }\n float feather = smoothstep(1.0, 0.72, dist);\n gl_FragColor = vec4(u_color.rgb, u_color.a * feather);\n } else if (u_useTexture > 0.5) {\n vec2 uv = vec2(\n mix(u_srcRect.x, u_srcRect.z, v_uv.x),\n mix(u_srcRect.y, u_srcRect.w, v_uv.y)\n );\n vec4 tex = texture2D(u_texture, uv);\n gl_FragColor = tex * u_color;\n } else {\n gl_FragColor = u_color;\n }\n}\n`;\n\nexport class WebGLRenderer {\n private readonly canvas: HTMLCanvasElement;\n private readonly spriteImage: HTMLImageElement;\n private readonly spriteElementsCount: number;\n\n private readonly gl: WebGLRenderingContext;\n private readonly program: WebGLProgram;\n private readonly uniforms: WebGLUniforms;\n private readonly quadBuffer: WebGLBuffer;\n private readonly texture: WebGLTexture;\n\n private viewportW = 1;\n private viewportH = 1;\n private readonly spriteWidth: number;\n private readonly spriteHeight: number;\n private readonly spriteSegmentHeight: number;\n\n public constructor(params: {\n canvas: HTMLCanvasElement;\n spriteImage: HTMLImageElement;\n spriteElementsCount: number;\n }) {\n this.canvas = params.canvas;\n this.spriteImage = params.spriteImage;\n this.spriteElementsCount = Math.max(1, params.spriteElementsCount);\n this.spriteWidth = this.spriteImage.width;\n this.spriteHeight = this.spriteImage.height;\n this.spriteSegmentHeight = this.spriteHeight / this.spriteElementsCount;\n\n const gl =\n (this.canvas.getContext('webgl2', {\n alpha: true,\n antialias: false,\n }) as WebGLRenderingContext | null) ??\n this.canvas.getContext('webgl', { alpha: true, antialias: false });\n if (!gl) {\n throw new Error('WebGL context is not available');\n }\n this.gl = gl;\n\n const vertexShader = this.createShader(this.gl.VERTEX_SHADER, VERTEX_SHADER_SOURCE);\n const fragmentShader = this.createShader(this.gl.FRAGMENT_SHADER, FRAGMENT_SHADER_SOURCE);\n this.program = this.createProgram(vertexShader, fragmentShader);\n this.gl.deleteShader(vertexShader);\n this.gl.deleteShader(fragmentShader);\n\n const quadBuffer = this.gl.createBuffer();\n if (!quadBuffer) {\n throw new Error('Failed to create WebGL quad buffer');\n }\n this.quadBuffer = quadBuffer;\n\n this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.quadBuffer);\n this.gl.bufferData(\n this.gl.ARRAY_BUFFER,\n new Float32Array([0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1]),\n this.gl.STATIC_DRAW,\n );\n\n const texture = this.gl.createTexture();\n if (!texture) {\n throw new Error('Failed to create WebGL texture');\n }\n this.texture = texture;\n\n this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture);\n this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, 0);\n this.gl.pixelStorei(this.gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1);\n this.gl.texImage2D(\n this.gl.TEXTURE_2D,\n 0,\n this.gl.RGBA,\n this.gl.RGBA,\n this.gl.UNSIGNED_BYTE,\n this.spriteImage,\n );\n this.gl.pixelStorei(this.gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 0);\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR);\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR);\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE);\n\n this.gl.useProgram(this.program);\n const posLocation = this.gl.getAttribLocation(this.program, 'a_pos');\n this.gl.enableVertexAttribArray(posLocation);\n this.gl.vertexAttribPointer(posLocation, 2, this.gl.FLOAT, false, 8, 0);\n\n this.uniforms = {\n resolution: this.gl.getUniformLocation(this.program, 'u_resolution'),\n destRect: this.gl.getUniformLocation(this.program, 'u_destRect'),\n srcRect: this.gl.getUniformLocation(this.program, 'u_srcRect'),\n color: this.gl.getUniformLocation(this.program, 'u_color'),\n useTexture: this.gl.getUniformLocation(this.program, 'u_useTexture'),\n shapeMode: this.gl.getUniformLocation(this.program, 'u_shapeMode'),\n texture: this.gl.getUniformLocation(this.program, 'u_texture'),\n };\n\n this.gl.uniform1i(this.uniforms.texture, 0);\n this.gl.clearColor(0, 0, 0, 0);\n this.gl.enable(this.gl.BLEND);\n this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA);\n }\n\n public resize(width: number, height: number): void {\n this.viewportW = Math.max(1, Math.floor(width));\n this.viewportH = Math.max(1, Math.floor(height));\n this.gl.viewport(0, 0, this.viewportW, this.viewportH);\n }\n\n public beginFrame(): void {\n this.gl.useProgram(this.program);\n this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.quadBuffer);\n this.gl.activeTexture(this.gl.TEXTURE0);\n this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture);\n this.gl.uniform2f(this.uniforms.resolution, this.viewportW, this.viewportH);\n this.gl.uniform1f(this.uniforms.shapeMode, 0);\n this.gl.clear(this.gl.COLOR_BUFFER_BIT);\n }\n\n public drawSprite(\n symbolId: SymbolId,\n x: number,\n y: number,\n width: number,\n height: number,\n alpha = 1,\n ): void {\n const segmentIndex = normalizeSegment(symbolId, this.spriteElementsCount);\n const srcTop = segmentIndex * this.spriteSegmentHeight;\n const srcBottom = srcTop + this.spriteSegmentHeight;\n\n // Half-texel inset reduces sprite bleeding between vertical segments.\n const inset = 0.5;\n const u0 = inset / this.spriteWidth;\n const u1 = 1 - inset / this.spriteWidth;\n const v0 = 1 - (srcBottom - inset) / this.spriteHeight;\n const v1 = 1 - (srcTop + inset) / this.spriteHeight;\n\n this.gl.uniform4f(this.uniforms.destRect, x, y, width, height);\n this.gl.uniform4f(this.uniforms.srcRect, u0, v0, u1, v1);\n this.gl.uniform4f(this.uniforms.color, 1, 1, 1, alpha);\n this.gl.uniform1f(this.uniforms.shapeMode, 0);\n this.gl.uniform1f(this.uniforms.useTexture, 1);\n this.gl.drawArrays(this.gl.TRIANGLES, 0, 6);\n }\n\n public drawSolidRect(\n x: number,\n y: number,\n width: number,\n height: number,\n rgba: [number, number, number, number],\n ): void {\n this.gl.uniform4f(this.uniforms.destRect, x, y, width, height);\n this.gl.uniform4f(this.uniforms.srcRect, 0, 0, 1, 1);\n this.gl.uniform4f(this.uniforms.color, rgba[0], rgba[1], rgba[2], rgba[3]);\n this.gl.uniform1f(this.uniforms.shapeMode, 0);\n this.gl.uniform1f(this.uniforms.useTexture, 0);\n this.gl.drawArrays(this.gl.TRIANGLES, 0, 6);\n }\n\n public drawSoftCircle(\n centerX: number,\n centerY: number,\n radius: number,\n rgba: [number, number, number, number],\n ): void {\n const diameter = radius * 2;\n this.gl.uniform4f(\n this.uniforms.destRect,\n centerX - radius,\n centerY - radius,\n diameter,\n diameter,\n );\n this.gl.uniform4f(this.uniforms.srcRect, 0, 0, 1, 1);\n this.gl.uniform4f(this.uniforms.color, rgba[0], rgba[1], rgba[2], rgba[3]);\n this.gl.uniform1f(this.uniforms.useTexture, 0);\n this.gl.uniform1f(this.uniforms.shapeMode, 1);\n this.gl.drawArrays(this.gl.TRIANGLES, 0, 6);\n this.gl.uniform1f(this.uniforms.shapeMode, 0);\n }\n\n public beginAdditiveBlend(): void {\n this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE);\n }\n\n public endAdditiveBlend(): void {\n this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA);\n }\n\n public dispose(): void {\n this.gl.deleteTexture(this.texture);\n this.gl.deleteBuffer(this.quadBuffer);\n this.gl.deleteProgram(this.program);\n }\n\n private createShader(type: number, source: string): WebGLShader {\n const shader = this.gl.createShader(type);\n if (!shader) {\n throw new Error('Failed to create WebGL shader');\n }\n this.gl.shaderSource(shader, source);\n this.gl.compileShader(shader);\n\n if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) {\n const message = this.gl.getShaderInfoLog(shader) ?? 'unknown error';\n this.gl.deleteShader(shader);\n throw new Error(`WebGL shader compile failed: ${message}`);\n }\n return shader;\n }\n\n private createProgram(vertexShader: WebGLShader, fragmentShader: WebGLShader): WebGLProgram {\n const program = this.gl.createProgram();\n if (!program) {\n throw new Error('Failed to create WebGL program');\n }\n this.gl.attachShader(program, vertexShader);\n this.gl.attachShader(program, fragmentShader);\n this.gl.linkProgram(program);\n\n if (!this.gl.getProgramParameter(program, this.gl.LINK_STATUS)) {\n const message = this.gl.getProgramInfoLog(program) ?? 'unknown error';\n this.gl.deleteProgram(program);\n throw new Error(`WebGL program link failed: ${message}`);\n }\n return program;\n }\n}\n","import {\n DEFAULT_MOTION_PROFILE_NAME,\n DEFAULT_SPRITE_ELEMENTS_COUNT,\n FLOW_WIN_FLASH_MS,\n FLOW_WIN_PARTICLES_PER_CELL_HIGH,\n FLOW_WIN_PULSE_AMPLITUDE,\n FLOW_WIN_PULSE_PERIOD_MS,\n GRID_COLS,\n GRID_ROWS,\n getMotionProfile,\n INITIAL_WIN_FLASH_DELAY_MS,\n type MotionProfile,\n PARTICLE_FLY_DURATION_MS,\n type PixelSnapYMode,\n ROW_COMPACT_OFFSETS_RATIO,\n} from './constants';\nimport {\n createRandomGrid,\n createZeroOffsets,\n fillOffsets,\n findMostFrequentCells,\n} from './core/grid';\nimport { RafLoop } from './core/loop';\nimport type { CellVisibility, OutroMotionPlan } from './core/outro';\nimport { buildOutroMotionPlan, updateOutroOffsets } from './core/outro';\nimport { SpinQueueController } from './core/spinQueue';\nimport {\n beginSpin,\n createRuntimeState,\n destroyState,\n finishSpin,\n startWinFlash,\n} from './core/state';\nimport {\n normalizeInitialSegments,\n normalizeParticleColor,\n normalizeParticleColorMode,\n normalizeStopGrid,\n rowsToStopGrid,\n} from './normalize';\nimport { WebGLRenderer } from './render/webglRenderer';\nimport type { CascadingReelConfig, CellPosition, SymbolId } from './types';\n\ntype ParticleSeeds = {\n seedA: number;\n seedB: number;\n seedC: number;\n phaseOffset: number;\n twinkleSeed: number;\n};\n\ntype GridRenderSampler = {\n grid: SymbolId[][];\n skipWinningCells: boolean;\n sampleOffsetY: (col: number, row: number) => number;\n sampleAlpha: (col: number, row: number) => number;\n isVisible: (col: number, row: number) => boolean;\n};\n\nexport class CascadingReel {\n private static readonly RAINBOW_HUE_BUCKETS = 24;\n private static readonly PARTICLE_GLOBAL_ALPHA = 0.9;\n private static readonly PARTICLE_MAX_DISTANCE = 0.72;\n private static readonly PARTICLE_BASE_RADIUS = 0.028;\n\n private readonly canvas: HTMLCanvasElement;\n private readonly container: HTMLElement;\n private readonly button?: HTMLButtonElement;\n private readonly spinQueueController: SpinQueueController;\n private readonly spriteUrl?: string;\n private readonly spriteElementsCount: number;\n private readonly highlightInitialWinningCells: boolean;\n private readonly particleColorRgb: [number, number, number];\n private readonly particleColorMode: 'solid' | 'rainbow';\n private readonly motionProfile: MotionProfile;\n private readonly pixelSnapY: PixelSnapYMode;\n\n private spriteImage: HTMLImageElement | null = null;\n private webglRenderer: WebGLRenderer | null = null;\n private readonly rafLoop = new RafLoop();\n private readonly runtime = createRuntimeState();\n private width = 0;\n private height = 0;\n private cellW = 0;\n private cellH = 0;\n private boardX = 0;\n private boardY = 0;\n private scriptedCascadeQueue: number[][][] = [];\n private scriptedOutgoingGrid: SymbolId[][] | null = null;\n private scriptedPendingGrid: SymbolId[][] | null = null;\n private scriptedOutroStartedAt = 0;\n private scriptedOutroElapsedMs = 0;\n private outroMotionPlan: OutroMotionPlan | null = null;\n private scriptedOutgoingOffsets: number[][] = createZeroOffsets();\n private scriptedOutgoingOffsetsPrev: number[][] = createZeroOffsets();\n private scriptedIncomingOffsets: number[][] = createZeroOffsets();\n private scriptedIncomingOffsetsPrev: number[][] = createZeroOffsets();\n private scriptedIncomingAlpha: number[][] = createZeroOffsets();\n private scriptedIncomingAlphaPrev: number[][] = createZeroOffsets();\n private scriptedIncomingVisibility: CellVisibility[][] =\n CascadingReel.createVisibilityGrid('hidden');\n private scriptedIncomingVisibilityPrev: CellVisibility[][] =\n CascadingReel.createVisibilityGrid('hidden');\n private winningCells: CellPosition[] = [];\n private readonly winningCellKeys = new Set<string>();\n private grid: SymbolId[][];\n private readonly particlesPerCell = FLOW_WIN_PARTICLES_PER_CELL_HIGH;\n private lastRafTime = 0;\n private initialHighlightRequestedAt = 0;\n private simulationLastNow = 0;\n private simulationAccumulatorMs = 0;\n private outroInterpolationAlpha = 1;\n private static readonly PRE_SPIN_MS = 150;\n private static readonly WIN_EFFECTS_ENVELOPE_TAU_MS = 120;\n private static readonly MAX_FRAME_DELTA_MS = 250;\n private static readonly WIN_BORDER_ALPHA = 0.72;\n private static readonly WIN_BORDER_INSET_RATIO = 0.06;\n private static readonly particleSeedsCache = new Map<string, ParticleSeeds[]>();\n\n public constructor(config: CascadingReelConfig) {\n this.canvas = config.canvas;\n this.container = config.container;\n this.button = config.button;\n this.spriteUrl = config.sprite;\n this.spriteElementsCount = Math.max(\n 1,\n config.spriteElementsCount ?? DEFAULT_SPRITE_ELEMENTS_COUNT,\n );\n this.highlightInitialWinningCells = config.highlightInitialWinningCells !== false;\n this.spinQueueController = new SpinQueueController(config.queuedSpinStates);\n this.particleColorRgb = normalizeParticleColor(config.particleColorRgb);\n this.particleColorMode = normalizeParticleColorMode(config.particleColorMode);\n this.motionProfile = getMotionProfile(config.motionProfile ?? DEFAULT_MOTION_PROFILE_NAME);\n this.pixelSnapY = config.pixelSnapY ?? this.motionProfile.pixelSnapY;\n this.grid = config.initialSegments\n ? normalizeInitialSegments(config.initialSegments, this.spriteElementsCount)\n : createRandomGrid(this.spriteElementsCount);\n }\n\n public async init(): Promise<void> {\n this.bindEvents();\n this.resize();\n await this.loadSpriteIfProvided();\n if (!this.spriteImage) {\n throw new Error('sprite is required for WebGL renderer');\n }\n this.webglRenderer = new WebGLRenderer({\n canvas: this.canvas,\n spriteImage: this.spriteImage,\n spriteElementsCount: this.spriteElementsCount,\n });\n this.webglRenderer.resize(this.width, this.height);\n this.applyInitialHighlightIfNeeded();\n requestAnimationFrame((warmNow) => {\n this.render(warmNow);\n this.startLoop();\n });\n }\n\n public destroy(): void {\n this.unbindEvents();\n this.rafLoop.stop();\n this.simulationLastNow = 0;\n this.simulationAccumulatorMs = 0;\n destroyState(this.runtime);\n this.webglRenderer?.dispose();\n this.webglRenderer = null;\n this.clearWinningCells();\n }\n\n public spin(): void {\n if (this.runtime.isSpinning) return;\n if (this.runtime.queueFinished) return;\n if (!this.spinQueueController.hasPending()) {\n this.runtime.queueFinished = true;\n if (this.button) this.button.disabled = true;\n return;\n }\n\n const activeSpinState = this.spinQueueController.consume();\n const shouldHighlightCurrentSpin = activeSpinState?.highlightWin === true;\n this.runtime.isSpinning = true;\n this.runtime.phase = 'preSpin';\n this.runtime.preSpinStartedAt = performance.now();\n this.runtime.activeSpinState = activeSpinState;\n this.runtime.shouldHighlightCurrentSpin = shouldHighlightCurrentSpin;\n this.runtime.hasStartedFirstSpin = true;\n this.simulationLastNow = 0;\n this.simulationAccumulatorMs = 0;\n\n if (this.button) this.button.disabled = true;\n this.startLoop();\n }\n\n private bindEvents(): void {\n window.addEventListener('resize', this.resize);\n this.button?.addEventListener('click', this.onSpinClick);\n }\n\n private unbindEvents(): void {\n window.removeEventListener('resize', this.resize);\n this.button?.removeEventListener('click', this.onSpinClick);\n }\n\n private readonly onSpinClick = (): void => {\n // Подсветка до клика: в winFlash с подсветкой кнопка включена, по клику завершаем и при наличии очереди запускаем следующий спин\n if (\n this.runtime.phase === 'winFlash' &&\n (this.runtime.shouldHighlightCurrentSpin || !this.runtime.hasStartedFirstSpin)\n ) {\n // Только закрываем подсветку и запускаем следующий спин; callback уже вызван по окончании прокрутки\n this.finishSpinWithUi(true);\n if (this.spinQueueController.hasPending()) this.spin();\n return;\n }\n if (this.runtime.isSpinning) return;\n this.spin();\n };\n\n private getNextGrid(stopGrid?: number[][]): SymbolId[][] {\n if (!stopGrid) return createRandomGrid(this.spriteElementsCount);\n return normalizeStopGrid(stopGrid, this.spriteElementsCount);\n }\n\n private update(now: number): void {\n if (!this.runtime.isSpinning) return;\n\n if (this.runtime.phase === 'preSpin') {\n if (now - this.runtime.preSpinStartedAt < CascadingReel.PRE_SPIN_MS) {\n return;\n }\n beginSpin(this.runtime, {\n activeSpinState: this.runtime.activeSpinState,\n shouldHighlightCurrentSpin: this.runtime.shouldHighlightCurrentSpin,\n startedAt: now,\n });\n this.runtime.preSpinStartedAt = 0;\n\n const scriptedSource = this.runtime.activeSpinState?.finaleSequenceRows\n ? this.runtime.activeSpinState.finaleSequenceRows.map((rows) => rowsToStopGrid(rows))\n : (this.runtime.activeSpinState?.finaleSequence ?? []);\n this.scriptedCascadeQueue = scriptedSource.map((grid) => grid.map((column) => [...column]));\n this.clearWinningCells();\n\n const stopGridSource = this.runtime.activeSpinState?.stopRows\n ? rowsToStopGrid(this.runtime.activeSpinState.stopRows)\n : this.runtime.activeSpinState?.stopGrid;\n const nextGrid = this.getNextGrid(stopGridSource);\n this.startOutroTransition(nextGrid, now);\n }\n\n if (this.runtime.phase === 'outro') {\n return;\n }\n if (this.runtime.phase === 'winFlash') {\n // Подсветка без ограничения по времени: выход только по клику «Spin»\n if (\n !this.runtime.shouldHighlightCurrentSpin &&\n now - this.runtime.winFlashStartedAt >= FLOW_WIN_FLASH_MS\n ) {\n this.finishSpinWithUi();\n }\n return;\n }\n }\n\n private stepScriptedOutro(stepMs: number): void {\n if (!this.scriptedOutgoingGrid || !this.scriptedPendingGrid) {\n this.finishSpinWithUi();\n return;\n }\n if (!this.outroMotionPlan) {\n this.outroMotionPlan = buildOutroMotionPlan({\n height: this.height,\n boardY: this.boardY,\n cellH: this.cellH,\n motionProfile: this.motionProfile,\n });\n }\n this.scriptedOutroElapsedMs += stepMs;\n const { allOutgoingDone, allIncomingDone } = updateOutroOffsets({\n elapsedMs: this.scriptedOutroElapsedMs,\n scriptedOutgoingOffsets: this.scriptedOutgoingOffsets,\n scriptedIncomingOffsets: this.scriptedIncomingOffsets,\n scriptedIncomingAlpha: this.scriptedIncomingAlpha,\n scriptedIncomingVisibility: this.scriptedIncomingVisibility,\n motionPlan: this.outroMotionPlan,\n });\n\n if (!allOutgoingDone || !allIncomingDone) return;\n\n this.grid = this.scriptedPendingGrid;\n this.scriptedOutgoingGrid = null;\n this.scriptedPendingGrid = null;\n this.outroMotionPlan = null;\n this.clearWinningCells();\n this.resetOutroBuffers(1, 'active');\n\n if (this.tryStartScriptedCascade(this.scriptedOutroStartedAt + this.scriptedOutroElapsedMs))\n return;\n if (!this.runtime.shouldHighlightCurrentSpin) {\n this.finishSpinWithUi();\n return;\n }\n\n this.setWinningCells(findMostFrequentCells(this.grid));\n startWinFlash(this.runtime, this.scriptedOutroStartedAt + this.scriptedOutroElapsedMs);\n // Callback по окончании прокрутки, а не по клику\n this.runtime.activeSpinState?.callback?.();\n // Включаем кнопку только если в очереди есть следующий спин; после последнего — оставляем disabled\n if (\n this.button &&\n this.runtime.shouldHighlightCurrentSpin &&\n this.spinQueueController.hasPending()\n )\n this.button.disabled = false;\n }\n\n private tryStartScriptedCascade(now: number): boolean {\n if (this.scriptedCascadeQueue.length === 0) return false;\n const nextGrid = this.scriptedCascadeQueue.shift();\n if (!nextGrid) return false;\n this.startOutroTransition(normalizeStopGrid(nextGrid, this.spriteElementsCount), now);\n return true;\n }\n\n private startOutroTransition(nextGrid: SymbolId[][], now: number): void {\n this.scriptedOutgoingGrid = this.grid.map((column) => [...column]);\n this.scriptedPendingGrid = nextGrid.map((column) => [...column]);\n this.outroMotionPlan = buildOutroMotionPlan({\n height: this.height,\n boardY: this.boardY,\n cellH: this.cellH,\n motionProfile: this.motionProfile,\n });\n this.resetOutroBuffers(0, 'hidden');\n this.clearWinningCells();\n this.runtime.phase = 'outro';\n this.scriptedOutroStartedAt = now;\n this.scriptedOutroElapsedMs = 0;\n this.outroInterpolationAlpha = 1;\n }\n\n /** @param skipCallback true при закрытии подсветки по клику (callback уже вызван по окончании прокрутки) */\n private finishSpinWithUi(skipCallback = false): void {\n const finishedSpinState = this.runtime.activeSpinState;\n const callback = skipCallback ? undefined : finishedSpinState?.callback;\n const hasPending = this.spinQueueController.hasPending();\n finishSpin(this.runtime, hasPending, performance.now());\n if (this.button) this.button.disabled = this.runtime.queueFinished;\n callback?.();\n }\n\n private applyInitialHighlightIfNeeded(): void {\n if (!this.highlightInitialWinningCells) return;\n this.setWinningCells(findMostFrequentCells(this.grid));\n this.initialHighlightRequestedAt = performance.now();\n }\n\n private render(now: number): void {\n if (!this.webglRenderer) return;\n if (\n this.initialHighlightRequestedAt > 0 &&\n now - this.initialHighlightRequestedAt >= INITIAL_WIN_FLASH_DELAY_MS\n ) {\n startWinFlash(this.runtime, this.initialHighlightRequestedAt);\n this.runtime.winEffectsEnvelope = 1;\n this.initialHighlightRequestedAt = 0;\n if (this.button) this.button.disabled = false;\n }\n\n const winEffectsTarget =\n this.runtime.phase === 'preSpin' ? 0 : this.runtime.phase === 'winFlash' ? 1 : 0;\n if (this.lastRafTime > 0) {\n const dt = Math.min(now - this.lastRafTime, 50);\n const kWin = 1 - Math.exp(-dt / CascadingReel.WIN_EFFECTS_ENVELOPE_TAU_MS);\n this.runtime.winEffectsEnvelope +=\n (winEffectsTarget - this.runtime.winEffectsEnvelope) * kWin;\n }\n this.lastRafTime = now;\n\n this.webglRenderer.beginFrame();\n\n const skipWinningCells =\n this.runtime.phase === 'winFlash' ||\n this.runtime.phase === 'preSpin' ||\n (this.initialHighlightRequestedAt > 0 && this.winningCells.length > 0);\n\n if (this.runtime.phase === 'outro' && this.scriptedOutgoingGrid && this.scriptedPendingGrid) {\n this.drawGridInterpolated(\n this.scriptedOutgoingGrid,\n this.scriptedOutgoingOffsetsPrev,\n this.scriptedOutgoingOffsets,\n this.outroInterpolationAlpha,\n skipWinningCells,\n );\n this.drawGridInterpolated(\n this.scriptedPendingGrid,\n this.scriptedIncomingOffsetsPrev,\n this.scriptedIncomingOffsets,\n this.outroInterpolationAlpha,\n skipWinningCells,\n this.scriptedIncomingAlphaPrev,\n this.scriptedIncomingAlpha,\n this.scriptedIncomingVisibility,\n );\n } else {\n this.drawGrid(this.grid, null, skipWinningCells);\n }\n\n const winPhase = this.initialHighlightRequestedAt > 0 ? 'winFlash' : this.runtime.phase;\n const winFlashStartedAt =\n this.initialHighlightRequestedAt > 0\n ? this.initialHighlightRequestedAt\n : this.runtime.winFlashStartedAt;\n const winEffectsEnvelope =\n this.initialHighlightRequestedAt > 0 ? 1 : this.runtime.winEffectsEnvelope;\n\n this.drawWinningEffects({\n now,\n phase: winPhase,\n winFlashStartedAt,\n winEffectsEnvelope,\n });\n }\n\n private readonly isWinningCell = (col: number, row: number): boolean => {\n return this.winningCellKeys.has(`${col}:${row}`);\n };\n\n private getRowCompactOffset(row: number): number {\n return (ROW_COMPACT_OFFSETS_RATIO[row] ?? 0) * this.cellH;\n }\n\n private applyPixelSnapY(y: number): number {\n if (this.pixelSnapY === 'integer') return Math.round(y);\n if (this.pixelSnapY === 'half') return Math.round(y * 2) / 2;\n return y;\n }\n\n private drawGrid(\n grid: SymbolId[][],\n offsets: number[][] | null,\n skipWinningCells: boolean,\n ): void {\n this.drawGridWithSampler({\n grid,\n skipWinningCells,\n sampleOffsetY: (col, row) => (offsets ? offsets[col][row] : 0),\n sampleAlpha: () => 1,\n isVisible: () => true,\n });\n }\n\n private drawGridInterpolated(\n grid: SymbolId[][],\n prevOffsets: number[][],\n currOffsets: number[][],\n alpha: number,\n skipWinningCells: boolean,\n prevOpacity?: number[][],\n currOpacity?: number[][],\n visibility?: CellVisibility[][],\n ): void {\n this.drawGridWithSampler({\n grid,\n skipWinningCells,\n sampleOffsetY: (col, row) =>\n prevOffsets[col][row] + (currOffsets[col][row] - prevOffsets[col][row]) * alpha,\n sampleAlpha: (col, row) =>\n prevOpacity && currOpacity\n ? prevOpacity[col][row] + (currOpacity[col][row] - prevOpacity[col][row]) * alpha\n : 1,\n isVisible: (col, row) => !visibility || visibility[col][row] !== 'hidden',\n });\n }\n\n private drawGridWithSampler(sampler: GridRenderSampler): void {\n const renderer = this.webglRenderer;\n if (!renderer) return;\n for (let col = 0; col < GRID_COLS; col += 1) {\n const x = this.boardX + col * this.cellW;\n for (let row = 0; row < GRID_ROWS; row += 1) {\n if (sampler.skipWinningCells && this.isWinningCell(col, row)) continue;\n if (!sampler.isVisible(col, row)) continue;\n const offsetY = sampler.sampleOffsetY(col, row);\n const y = this.applyPixelSnapY(\n this.boardY + row * this.cellH + offsetY + this.getRowCompactOffset(row),\n );\n if (y > this.height || y + this.cellH < 0) continue;\n const spriteAlpha = sampler.sampleAlpha(col, row);\n if (spriteAlpha <= 0) continue;\n renderer.drawSprite(sampler.grid[col][row], x, y, this.cellW, this.cellH, spriteAlpha);\n }\n }\n }\n\n private drawWinningEffects(params: {\n now: number;\n phase: 'idle' | 'winFlash' | 'outro' | 'preSpin';\n winFlashStartedAt: number;\n winEffectsEnvelope: number;\n }): void {\n const renderer = this.webglRenderer;\n if (!renderer) return;\n if (this.winningCells.length === 0) return;\n if (params.phase !== 'winFlash' && params.phase !== 'preSpin') return;\n\n const envelope = Math.max(0, Math.min(1, params.winEffectsEnvelope));\n const elapsed = Math.max(0, params.now - params.winFlashStartedAt);\n const pulseProgress = (elapsed % FLOW_WIN_PULSE_PERIOD_MS) / FLOW_WIN_PULSE_PERIOD_MS;\n const pulse = 1 + Math.sin(pulseProgress * Math.PI * 2) * FLOW_WIN_PULSE_AMPLITUDE * envelope;\n const borderInset = Math.max(1, this.cellW * CascadingReel.WIN_BORDER_INSET_RATIO);\n const borderThickness = Math.max(1, this.cellW * 0.03);\n const particleModeActive =\n this.runtime.phase === 'winFlash' &&\n this.runtime.shouldHighlightCurrentSpin &&\n this.runtime.hasStartedFirstSpin;\n for (const cell of this.winningCells) {\n const baseX = this.boardX + cell.col * this.cellW;\n const baseY = this.boardY + cell.row * this.cellH + this.getRowCompactOffset(cell.row);\n const symbol = this.grid[cell.col][cell.row];\n const alpha = CascadingReel.WIN_BORDER_ALPHA * envelope;\n\n const borderColor =\n this.particleColorMode === 'rainbow'\n ? CascadingReel.hslToRgb01(\n (elapsed * 0.2 + cell.col * 36 + cell.row * 22) % 360,\n 0.96,\n 0.64,\n )\n : [\n this.particleColorRgb[0] / 255,\n this.particleColorRgb[1] / 255,\n this.particleColorRgb[2] / 255,\n ];\n\n const scaledW = this.cellW * pulse;\n const scaledH = this.cellH * pulse;\n const offsetX = (this.cellW - scaledW) * 0.5;\n const offsetY = (this.cellH - scaledH) * 0.5;\n renderer.drawSprite(symbol, baseX + offsetX, baseY + offsetY, scaledW, scaledH, 1);\n\n const innerX = baseX + borderInset;\n const innerY = baseY + borderInset;\n const innerW = this.cellW - borderInset * 2;\n const innerH = this.cellH - borderInset * 2;\n if (innerW <= borderThickness * 2 || innerH <= borderThickness * 2) continue;\n\n renderer.drawSolidRect(innerX, innerY, innerW, borderThickness, [\n borderColor[0],\n borderColor[1],\n borderColor[2],\n alpha,\n ]);\n renderer.drawSolidRect(innerX, innerY + innerH - borderThickness, innerW, borderThickness, [\n borderColor[0],\n borderColor[1],\n borderColor[2],\n alpha,\n ]);\n renderer.drawSolidRect(innerX, innerY, borderThickness, innerH, [\n borderColor[0],\n borderColor[1],\n borderColor[2],\n alpha,\n ]);\n renderer.drawSolidRect(innerX + innerW - borderThickness, innerY, borderThickness, innerH, [\n borderColor[0],\n borderColor[1],\n borderColor[2],\n alpha,\n ]);\n\n if (particleModeActive) {\n this.drawCellParticleBurst({\n renderer,\n cell,\n centerX: baseX + this.cellW * 0.5,\n centerY: baseY + this.cellH * 0.5,\n elapsed,\n envelope,\n });\n }\n }\n }\n\n private drawCellParticleBurst(params: {\n renderer: WebGLRenderer;\n cell: CellPosition;\n centerX: number;\n centerY: number;\n elapsed: number;\n envelope: number;\n }): void {\n const maxDistance = Math.min(this.cellW, this.cellH) * CascadingReel.PARTICLE_MAX_DISTANCE;\n const baseRadius = Math.min(this.cellW, this.cellH) * CascadingReel.PARTICLE_BASE_RADIUS;\n const seedsList = CascadingReel.getParticleSeeds(params.cell.col, params.cell.row);\n const solidColor: [number, number, number] = [\n this.particleColorRgb[0] / 255,\n this.particleColorRgb[1] / 255,\n this.particleColorRgb[2] / 255,\n ];\n\n params.renderer.beginAdditiveBlend();\n for (let i = 0; i < this.particlesPerCell; i += 1) {\n const s = seedsList[i];\n const startTime = s.phaseOffset * PARTICLE_FLY_DURATION_MS;\n const age = params.elapsed - startTime;\n if (age < 0) continue;\n const particleT = (age % PARTICLE_FLY_DURATION_MS) / PARTICLE_FLY_DURATION_MS;\n const direction = s.seedA * Math.PI * 2;\n const distance = maxDistance * particleT * (0.35 + s.seedB * 0.65);\n const px = params.centerX + Math.cos(direction) * distance;\n const py = params.centerY + Math.sin(direction) * distance;\n const twinkle =\n 0.7 +\n 0.9 * Math.max(0, Math.sin((params.elapsed * 0.012 + s.twinkleSeed * 2) * Math.PI * 2));\n const radius = Math.max(1, baseRadius * (0.55 + s.seedC * 0.6) * (1 - particleT * 0.5));\n const alpha = Math.max(\n 0,\n Math.min(1, (0.9 + twinkle * 0.2) * CascadingReel.PARTICLE_GLOBAL_ALPHA * params.envelope),\n );\n if (alpha <= 0) continue;\n\n const rgb =\n this.particleColorMode === 'rainbow'\n ? CascadingReel.getRainbowParticleColor(\n params.elapsed,\n params.cell.col,\n params.cell.row,\n s.seedA,\n )\n : solidColor;\n\n params.renderer.drawSoftCircle(px, py, radius, [rgb[0], rgb[1], rgb[2], alpha]);\n }\n params.renderer.endAdditiveBlend();\n }\n\n private static hslToRgb01(\n hueDeg: number,\n saturation: number,\n lightness: number,\n ): [number, number, number] {\n const h = ((hueDeg % 360) + 360) % 360;\n const s = Math.max(0, Math.min(1, saturation));\n const l = Math.max(0, Math.min(1, lightness));\n const c = (1 - Math.abs(2 * l - 1)) * s;\n const hp = h / 60;\n const x = c * (1 - Math.abs((hp % 2) - 1));\n let r = 0;\n let g = 0;\n let b = 0;\n if (hp >= 0 && hp < 1) {\n r = c;\n g = x;\n } else if (hp < 2) {\n r = x;\n g = c;\n } else if (hp < 3) {\n g = c;\n b = x;\n } else if (hp < 4) {\n g = x;\n b = c;\n } else if (hp < 5) {\n r = x;\n b = c;\n } else {\n r = c;\n b = x;\n }\n const m = l - c * 0.5;\n return [r + m, g + m, b + m];\n }\n\n private static getRainbowParticleColor(\n elapsed: number,\n col: number,\n row: number,\n seedA: number,\n ): [number, number, number] {\n const hueRaw = (seedA * 360 + elapsed * 0.24 + col * 38 + row * 22) % 360;\n const bucket = 360 / CascadingReel.RAINBOW_HUE_BUCKETS;\n const hue = Math.floor(hueRaw / bucket) * bucket;\n return CascadingReel.hslToRgb01(hue, 0.98, 0.64);\n }\n\n private static hash01(a: number, b: number, c: number, d: number): number {\n const value = Math.sin(a * 127.1 + b * 311.7 + c * 74.7 + d * 19.3) * 43758.5453;\n return value - Math.floor(value);\n }\n\n private static getParticleSeeds(col: number, row: number): ParticleSeeds[] {\n const key = `${col},${row}`;\n const cached = CascadingReel.particleSeedsCache.get(key);\n if (cached) return cached;\n\n const generated: ParticleSeeds[] = [];\n for (let i = 0; i < FLOW_WIN_PARTICLES_PER_CELL_HIGH; i += 1) {\n generated.push({\n seedA: CascadingReel.hash01(col, row, i, 1),\n seedB: CascadingReel.hash01(col, row, i, 2),\n seedC: CascadingReel.hash01(col, row, i, 3),\n phaseOffset: CascadingReel.hash01(col, row, i, 4),\n twinkleSeed: CascadingReel.hash01(col, row, i, 5),\n });\n }\n CascadingReel.particleSeedsCache.set(key, generated);\n return generated;\n }\n\n private clearWinningCells(): void {\n this.winningCells = [];\n this.winningCellKeys.clear();\n }\n\n private setWinningCells(cells: CellPosition[]): void {\n this.winningCells = cells;\n this.winningCellKeys.clear();\n for (const cell of cells) {\n this.winningCellKeys.add(`${cell.col}:${cell.row}`);\n }\n }\n\n private readonly resize = (): void => {\n const bounds = this.container.getBoundingClientRect();\n const dpr = Math.max(1, Math.min(window.devicePixelRatio || 1, 2));\n const side = Math.max(300, Math.floor(bounds.width * dpr));\n this.width = side;\n this.height = side;\n const squareSize = Math.floor(Math.min(this.width / GRID_COLS, this.height / GRID_ROWS));\n this.cellW = squareSize;\n this.cellH = squareSize;\n this.boardX = Math.floor((this.width - this.cellW * GRID_COLS) / 2);\n this.boardY = Math.floor((this.height - this.cellH * GRID_ROWS) / 2);\n\n this.canvas.width = this.width;\n this.canvas.height = this.height;\n this.webglRenderer?.resize(this.width, this.height);\n };\n\n private async loadSpriteIfProvided(): Promise<void> {\n if (!this.spriteUrl) return;\n const image = new Image();\n image.decoding = 'async';\n image.src = this.spriteUrl;\n try {\n await image.decode();\n this.spriteImage = image;\n } catch {\n this.spriteImage = null;\n }\n }\n\n private advanceSimulation(now: number): void {\n if (this.simulationLastNow <= 0) {\n this.simulationLastNow = now;\n this.update(now);\n return;\n }\n const frameDt = Math.max(\n 0,\n Math.min(now - this.simulationLastNow, CascadingReel.MAX_FRAME_DELTA_MS),\n );\n this.simulationLastNow = now;\n this.update(now);\n if (this.runtime.phase !== 'outro') {\n this.outroInterpolationAlpha = 1;\n return;\n }\n\n this.snapshotOutroState();\n this.simulationAccumulatorMs += frameDt;\n const stepMs = this.motionProfile.fixedStepMs;\n let steps = 0;\n while (\n this.simulationAccumulatorMs >= stepMs &&\n steps < this.motionProfile.maxCatchUpStepsPerFrame &&\n this.runtime.phase === 'outro'\n ) {\n this.stepScriptedOutro(stepMs);\n this.simulationAccumulatorMs -= stepMs;\n steps += 1;\n }\n if (this.runtime.phase !== 'outro') {\n this.outroInterpolationAlpha = 1;\n return;\n }\n this.outroInterpolationAlpha = Math.max(0, Math.min(1, this.simulationAccumulatorMs / stepMs));\n }\n\n private snapshotOutroState(): void {\n this.copyOffsets(this.scriptedOutgoingOffsetsPrev, this.scriptedOutgoingOffsets);\n this.copyOffsets(this.scriptedIncomingOffsetsPrev, this.scriptedIncomingOffsets);\n this.copyOffsets(this.scriptedIncomingAlphaPrev, this.scriptedIncomingAlpha);\n this.copyVisibility(this.scriptedIncomingVisibilityPrev, this.scriptedIncomingVisibility);\n }\n\n private copyOffsets(target: number[][], source: number[][]): void {\n for (let col = 0; col < GRID_COLS; col += 1) {\n for (let row = 0; row < GRID_ROWS; row += 1) {\n target[col][row] = source[col][row];\n }\n }\n }\n\n private copyVisibility(target: CellVisibility[][], source: CellVisibility[][]): void {\n for (let col = 0; col < GRID_COLS; col += 1) {\n for (let row = 0; row < GRID_ROWS; row += 1) {\n target[col][row] = source[col][row];\n }\n }\n }\n\n private resetOutroBuffers(incomingAlpha: number, incomingVisibility: CellVisibility): void {\n fillOffsets(this.scriptedOutgoingOffsets, 0);\n fillOffsets(this.scriptedIncomingOffsets, 0);\n fillOffsets(this.scriptedOutgoingOffsetsPrev, 0);\n fillOffsets(this.scriptedIncomingOffsetsPrev, 0);\n fillOffsets(this.scriptedIncomingAlpha, incomingAlpha);\n fillOffsets(this.scriptedIncomingAlphaPrev, incomingAlpha);\n this.fillVisibilityGrid(this.scriptedIncomingVisibility, incomingVisibility);\n this.fillVisibilityGrid(this.scriptedIncomingVisibilityPrev, incomingVisibility);\n }\n\n private static createVisibilityGrid(value: CellVisibility): CellVisibility[][] {\n return Array.from({ length: GRID_COLS }, () => Array.from({ length: GRID_ROWS }, () => value));\n }\n\n private fillVisibilityGrid(grid: CellVisibility[][], value: CellVisibility): void {\n for (let col = 0; col < GRID_COLS; col += 1) {\n for (let row = 0; row < GRID_ROWS; row += 1) {\n grid[col][row] = value;\n }\n }\n }\n\n private startLoop(): void {\n if (this.rafLoop.isRunning()) return;\n this.rafLoop.start((time: number): boolean => {\n this.advanceSimulation(time);\n this.render(time);\n return this.shouldKeepAnimating();\n });\n }\n\n private shouldKeepAnimating(): boolean {\n if (this.runtime.isSpinning) return true;\n if (this.initialHighlightRequestedAt > 0) return true;\n return this.runtime.winEffectsEnvelope > 0.001;\n }\n}\n"],"names":["DEFAULT_SPRITE_ELEMENTS_COUNT","GRID_COLS","GRID_ROWS","ROW_COMPACT_OFFSETS_RATIO","MOTION_PROFILES","DEFAULT_MOTION_PROFILE_NAME","getMotionProfile","name","FLOW_WIN_FLASH_MS","FLOW_WIN_PULSE_PERIOD_MS","FLOW_WIN_PULSE_AMPLITUDE","PARTICLE_FLY_DURATION_MS","FLOW_WIN_PARTICLES_PER_CELL_HIGH","DEFAULT_PARTICLE_COLOR_RGB","INITIAL_WIN_FLASH_DELAY_MS","clamp","value","min","max","randomInt","maxExclusive","normalizeSegment","segment","elementsCount","normalizeRgbChannel","createRandomGrid","spriteElementsCount","grid","col","column","row","findMostFrequentCells","counts","symbol","selectedSymbol","maxCount","count","cells","createZeroOffsets","fillOffsets","offsets","RafLoop","step","now","smootherStep","t","x","sampleSegment","nowMs","eased","buildSequentialRowStartDelays","fromRowOffsets","durationMs","gapMs","rowBaseSpacingRatio","delays","nextDelay","baseSpacing","buildOutroMotionPlan","params","outgoingDistance","outgoingOffsetsForOrder","updateOutroOffsets","allOutgoingDone","allIncomingDone","columnElapsed","rowElapsed","outgoing","incomingElapsed","incoming","normalizeParticleColor","color","normalizeParticleColorMode","mode","rowsToStopGrid","rows","normalizeStopGrid","stopGrid","next","normalizeInitialSegments","initialSegments","cloneSpinState","state","SpinQueueController","initialQueue","entry","createRuntimeState","beginSpin","finishSpin","hasPendingInQueue","startWinFlash","startedAt","destroyState","VERTEX_SHADER_SOURCE","FRAGMENT_SHADER_SOURCE","WebGLRenderer","gl","vertexShader","fragmentShader","quadBuffer","texture","posLocation","width","height","symbolId","y","alpha","srcTop","srcBottom","inset","u0","u1","v0","v1","rgba","centerX","centerY","radius","diameter","type","source","shader","message","program","CascadingReel","config","warmNow","activeSpinState","shouldHighlightCurrentSpin","scriptedSource","stopGridSource","nextGrid","stepMs","skipCallback","finishedSpinState","callback","hasPending","winEffectsTarget","dt","kWin","skipWinningCells","winPhase","winFlashStartedAt","winEffectsEnvelope","prevOffsets","currOffsets","prevOpacity","currOpacity","visibility","sampler","renderer","offsetY","spriteAlpha","envelope","elapsed","pulseProgress","pulse","borderInset","borderThickness","particleModeActive","cell","baseX","baseY","borderColor","scaledW","scaledH","offsetX","innerX","innerY","innerW","innerH","maxDistance","baseRadius","seedsList","solidColor","i","s","startTime","age","particleT","direction","distance","px","py","twinkle","rgb","hueDeg","saturation","lightness","h","l","c","hp","r","g","b","m","seedA","hueRaw","bucket","hue","a","d","key","cached","generated","bounds","dpr","side","squareSize","image","frameDt","steps","target","incomingAlpha","incomingVisibility","time"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs.js","sources":["../src/constants.ts","../src/utils/math.ts","../src/core/grid.ts","../src/core/loop.ts","../src/core/motionTimeline.ts","../src/core/outro.ts","../src/normalize.ts","../src/core/spinQueue.ts","../src/core/state.ts","../src/render/webglRenderer.ts","../src/CascadingReel.ts"],"sourcesContent":["export const DEFAULT_SPRITE_ELEMENTS_COUNT = 6;\nexport const GRID_COLS = 3;\nexport const GRID_ROWS = 3;\nexport const ROW_COMPACT_OFFSETS_RATIO: [number, number, number] = [0.04, 0, -0.04];\nexport type PixelSnapYMode = 'none' | 'half' | 'integer';\nexport type MotionProfile = {\n columnStaggerMs: number;\n fallMs: number;\n outroOverlapMs: number;\n outroRowGapMs: number;\n rowBaseSpacingRatio: number;\n incomingAlphaRampMs: number;\n fixedStepMs: number;\n maxCatchUpStepsPerFrame: number;\n pixelSnapY: PixelSnapYMode;\n};\nexport const MOTION_PROFILES = {\n normal: {\n columnStaggerMs: 76,\n fallMs: 800,\n outroOverlapMs: 88,\n outroRowGapMs: 14,\n rowBaseSpacingRatio: 0.05,\n incomingAlphaRampMs: 34,\n fixedStepMs: 1000 / 120,\n maxCatchUpStepsPerFrame: 6,\n pixelSnapY: 'none',\n },\n cinematic: {\n columnStaggerMs: 170,\n fallMs: 2200,\n outroOverlapMs: 300,\n outroRowGapMs: 28,\n rowBaseSpacingRatio: 0.05,\n incomingAlphaRampMs: 90,\n fixedStepMs: 1000 / 120,\n maxCatchUpStepsPerFrame: 6,\n pixelSnapY: 'none',\n },\n turbo: {\n columnStaggerMs: 120,\n fallMs: 1200,\n outroOverlapMs: 180,\n outroRowGapMs: 20,\n rowBaseSpacingRatio: 0.04,\n incomingAlphaRampMs: 50,\n fixedStepMs: 1000 / 120,\n maxCatchUpStepsPerFrame: 4,\n pixelSnapY: 'half',\n },\n} as const satisfies Record<string, MotionProfile>;\nexport type MotionProfileName = keyof typeof MOTION_PROFILES;\nexport const DEFAULT_MOTION_PROFILE_NAME: MotionProfileName = 'normal';\nexport function getMotionProfile(name: MotionProfileName): MotionProfile {\n return MOTION_PROFILES[name];\n}\nexport const FLOW_OUTRO_ROW_GAP_MS = MOTION_PROFILES.normal.outroRowGapMs;\nexport const FLOW_ROW_BASE_SPACING_RATIO = MOTION_PROFILES.normal.rowBaseSpacingRatio;\n/** Длительность фазы подсветки выигрыша (мс). Подсветка отключается по таймауту. */\nexport const FLOW_WIN_FLASH_MS = 5000;\nexport const FLOW_WIN_PULSE_PERIOD_MS = 1800;\nexport const FLOW_WIN_PULSE_AMPLITUDE = 0.1;\n/** Duration for one particle to fly from center to edge (continuous spray). */\nexport const PARTICLE_FLY_DURATION_MS = 720;\nexport const FLOW_WIN_PARTICLES_PER_CELL_HIGH = 34;\nexport const DEFAULT_PARTICLE_COLOR_RGB: [number, number, number] = [255, 235, 110];\nexport const FLOW_COLUMN_STAGGER_MS = MOTION_PROFILES.normal.columnStaggerMs;\nexport const FLOW_FALL_MS = MOTION_PROFILES.normal.fallMs;\nexport const FLOW_OUTRO_OVERLAP_MS = MOTION_PROFILES.normal.outroOverlapMs;\n/** Delay before starting initial win-effect so canvas/JIT can warm up. */\nexport const INITIAL_WIN_FLASH_DELAY_MS = 200;\n","export function clamp(value: number, min: number, max: number): number {\n if (value < min) return min;\n if (value > max) return max;\n return value;\n}\n\nexport function easeOutCubic(t: number): number {\n return 1 - (1 - t) ** 3;\n}\n\nexport function easeOutQuad(t: number): number {\n return 1 - (1 - t) ** 2;\n}\n\nexport function randomInt(maxExclusive: number): number {\n return Math.floor(Math.random() * maxExclusive);\n}\n\nexport function normalizeSegment(segment: number, elementsCount: number): number {\n return ((segment % elementsCount) + elementsCount) % elementsCount;\n}\n\nexport function normalizeRgbChannel(value: number): number {\n return clamp(Math.round(value), 0, 255);\n}\n\nexport function normalizeAlphaChannel(value: number): number {\n return clamp(value, 0, 1);\n}\n","import { GRID_COLS, GRID_ROWS } from '../constants';\nimport type { CellPosition, SymbolId } from '../types';\nimport { randomInt } from '../utils/math';\n\nexport function createRandomGrid(spriteElementsCount: number): SymbolId[][] {\n const grid: SymbolId[][] = [];\n for (let col = 0; col < GRID_COLS; col += 1) {\n const column: SymbolId[] = [];\n for (let row = 0; row < GRID_ROWS; row += 1) {\n column.push(randomInt(spriteElementsCount));\n }\n grid.push(column);\n }\n return grid;\n}\n\nexport function findMostFrequentCells(grid: SymbolId[][]): CellPosition[] {\n const counts = new Map<SymbolId, number>();\n for (let col = 0; col < GRID_COLS; col += 1) {\n for (let row = 0; row < GRID_ROWS; row += 1) {\n const symbol = grid[col][row];\n counts.set(symbol, (counts.get(symbol) ?? 0) + 1);\n }\n }\n\n let selectedSymbol: SymbolId = grid[0][0];\n let maxCount = -1;\n for (const [symbol, count] of counts.entries()) {\n if (count > maxCount) {\n maxCount = count;\n selectedSymbol = symbol;\n }\n }\n\n const cells: CellPosition[] = [];\n for (let col = 0; col < GRID_COLS; col += 1) {\n for (let row = 0; row < GRID_ROWS; row += 1) {\n if (grid[col][row] === selectedSymbol) {\n cells.push({ col, row });\n }\n }\n }\n return cells;\n}\n\nexport function createZeroOffsets(): number[][] {\n return Array.from({ length: GRID_COLS }, () => Array.from({ length: GRID_ROWS }, () => 0));\n}\n\nexport function fillOffsets(offsets: number[][], value: number): void {\n for (let col = 0; col < GRID_COLS; col += 1) {\n for (let row = 0; row < GRID_ROWS; row += 1) {\n offsets[col][row] = value;\n }\n }\n}\n","export type RafStep = (now: number) => boolean;\n\nexport class RafLoop {\n private rafId: number | null = null;\n private step: RafStep | null = null;\n\n public start(step: RafStep): void {\n if (this.rafId !== null) return;\n this.step = step;\n this.rafId = requestAnimationFrame(this.tick);\n }\n\n public stop(): void {\n if (this.rafId !== null) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n this.step = null;\n }\n\n public isRunning(): boolean {\n return this.rafId !== null;\n }\n\n private readonly tick = (now: number): void => {\n if (!this.step) {\n this.stop();\n return;\n }\n\n const shouldContinue = this.step(now);\n if (shouldContinue === false) {\n this.stop();\n return;\n }\n\n if (this.rafId === null) return;\n this.rafId = requestAnimationFrame(this.tick);\n };\n}\n","import { clamp } from '../utils/math';\n\nexport type MotionSegment = {\n startMs: number;\n endMs: number;\n from: number;\n to: number;\n};\n\nexport function smootherStep(t: number): number {\n const x = clamp(t, 0, 1);\n return x * x * x * (x * (x * 6 - 15) + 10);\n}\n\nexport function sampleSegment(\n segment: MotionSegment,\n nowMs: number,\n): {\n value: number;\n t: number;\n done: boolean;\n} {\n if (segment.endMs <= segment.startMs) {\n return { value: segment.to, t: 1, done: true };\n }\n const t = clamp((nowMs - segment.startMs) / (segment.endMs - segment.startMs), 0, 1);\n const eased = smootherStep(t);\n return {\n value: segment.from + (segment.to - segment.from) * eased,\n t,\n done: t >= 1,\n };\n}\n","import type { MotionProfile } from '../constants';\nimport { GRID_ROWS } from '../constants';\nimport { clamp } from '../utils/math';\nimport { sampleSegment } from './motionTimeline';\n\nexport type CellVisibility = 'hidden' | 'entering' | 'active' | 'exiting';\n\nexport type OutroMotionPlan = {\n columnStaggerMs: number;\n fallMs: number;\n incomingAlphaRampMs: number;\n outgoingDistance: number;\n incomingFromOffsets: [number, number, number];\n rowStartDelays: [number, number, number];\n incomingStartShift: number;\n};\n\nexport function buildSequentialRowStartDelays(\n fromRowOffsets: [number, number, number],\n durationMs: number,\n gapMs: number,\n rowBaseSpacingRatio: number,\n): [number, number, number] {\n const delays: [number, number, number] = [0, 0, 0];\n let nextDelay = 0;\n for (let row = GRID_ROWS - 1; row >= 0; row -= 1) {\n if (fromRowOffsets[row] === 0) {\n delays[row] = 0;\n continue;\n }\n delays[row] = nextDelay;\n const baseSpacing = Math.floor(durationMs * rowBaseSpacingRatio);\n nextDelay += baseSpacing + gapMs;\n }\n return delays;\n}\n\nexport function buildOutroMotionPlan(params: {\n height: number;\n boardY: number;\n cellH: number;\n motionProfile: MotionProfile;\n}): OutroMotionPlan {\n const exitEpsilon = 2;\n const outgoingDistance = params.height - params.boardY + params.cellH + exitEpsilon;\n const outgoingOffsetsForOrder: [number, number, number] = [\n outgoingDistance,\n outgoingDistance,\n outgoingDistance,\n ];\n return {\n columnStaggerMs: params.motionProfile.columnStaggerMs,\n fallMs: params.motionProfile.fallMs,\n incomingAlphaRampMs: params.motionProfile.incomingAlphaRampMs,\n outgoingDistance,\n incomingFromOffsets: [-params.cellH, -params.cellH * 2, -params.cellH * 3],\n rowStartDelays: buildSequentialRowStartDelays(\n outgoingOffsetsForOrder,\n params.motionProfile.fallMs,\n params.motionProfile.outroRowGapMs,\n params.motionProfile.rowBaseSpacingRatio,\n ),\n // Делаем небольшое перекрытие фаз: новые символы начинают входить\n // немного раньше окончания исходящих, чтобы движение воспринималось\n // как непрерывный поток.\n incomingStartShift: Math.max(\n 0,\n params.motionProfile.fallMs - params.motionProfile.outroOverlapMs,\n ),\n };\n}\n\nexport function updateOutroOffsets(params: {\n elapsedMs: number;\n scriptedOutgoingOffsets: number[][];\n scriptedIncomingOffsets: number[][];\n scriptedIncomingAlpha: number[][];\n scriptedIncomingVisibility: CellVisibility[][];\n motionPlan: OutroMotionPlan;\n}): { allOutgoingDone: boolean; allIncomingDone: boolean } {\n let allOutgoingDone = true;\n let allIncomingDone = true;\n\n for (let col = 0; col < params.scriptedOutgoingOffsets.length; col += 1) {\n const columnElapsed = params.elapsedMs - col * params.motionPlan.columnStaggerMs;\n for (let row = 0; row < GRID_ROWS; row += 1) {\n const rowElapsed = columnElapsed - params.motionPlan.rowStartDelays[row];\n\n if (rowElapsed <= 0) {\n params.scriptedOutgoingOffsets[col][row] = 0;\n params.scriptedIncomingOffsets[col][row] = params.motionPlan.incomingFromOffsets[row];\n params.scriptedIncomingAlpha[col][row] = 0;\n params.scriptedIncomingVisibility[col][row] = 'hidden';\n allOutgoingDone = false;\n allIncomingDone = false;\n continue;\n }\n\n const outgoing = sampleSegment(\n {\n startMs: 0,\n endMs: params.motionPlan.fallMs,\n from: 0,\n to: params.motionPlan.outgoingDistance,\n },\n rowElapsed,\n );\n params.scriptedOutgoingOffsets[col][row] = outgoing.value;\n params.scriptedIncomingVisibility[col][row] = outgoing.done ? 'entering' : 'exiting';\n if (!outgoing.done) allOutgoingDone = false;\n\n const incomingElapsed = rowElapsed - params.motionPlan.incomingStartShift;\n if (incomingElapsed <= 0) {\n params.scriptedIncomingOffsets[col][row] = params.motionPlan.incomingFromOffsets[row];\n params.scriptedIncomingAlpha[col][row] = 0;\n params.scriptedIncomingVisibility[col][row] = 'hidden';\n allIncomingDone = false;\n continue;\n }\n\n const incoming = sampleSegment(\n {\n startMs: 0,\n endMs: params.motionPlan.fallMs,\n from: params.motionPlan.incomingFromOffsets[row],\n to: 0,\n },\n incomingElapsed,\n );\n params.scriptedIncomingOffsets[col][row] = incoming.value;\n params.scriptedIncomingAlpha[col][row] = clamp(\n incomingElapsed / Math.max(1, params.motionPlan.incomingAlphaRampMs),\n 0,\n 1,\n );\n params.scriptedIncomingVisibility[col][row] = incoming.done ? 'active' : 'entering';\n if (!incoming.done) allIncomingDone = false;\n }\n }\n\n return { allOutgoingDone, allIncomingDone };\n}\n","import { DEFAULT_PARTICLE_COLOR_RGB, GRID_COLS, GRID_ROWS } from './constants';\nimport type { SpinState, SymbolId } from './types';\nimport { normalizeRgbChannel, normalizeSegment } from './utils/math';\n\nexport function normalizeParticleColor(color?: [number, number, number]): [number, number, number] {\n if (!color) return DEFAULT_PARTICLE_COLOR_RGB;\n return [\n normalizeRgbChannel(color[0]),\n normalizeRgbChannel(color[1]),\n normalizeRgbChannel(color[2]),\n ];\n}\n\nexport function normalizeParticleColorMode(mode?: 'solid' | 'rainbow'): 'solid' | 'rainbow' {\n if (mode === 'rainbow') return 'rainbow';\n return 'solid';\n}\n\nexport function rowsToStopGrid(rows: number[][]): number[][] {\n if (rows.length !== GRID_ROWS) {\n throw new Error(`rows must contain ${GRID_ROWS} rows`);\n }\n for (let row = 0; row < GRID_ROWS; row += 1) {\n if (!Array.isArray(rows[row]) || rows[row].length !== GRID_COLS) {\n throw new Error(`rows[${row}] must contain ${GRID_COLS} columns`);\n }\n }\n\n return [\n [rows[0][0], rows[1][0], rows[2][0]],\n [rows[0][1], rows[1][1], rows[2][1]],\n [rows[0][2], rows[1][2], rows[2][2]],\n ];\n}\n\nexport function normalizeStopGrid(stopGrid: number[][], elementsCount: number): SymbolId[][] {\n if (stopGrid.length !== GRID_COLS) {\n throw new Error(`stopGrid must contain ${GRID_COLS} columns`);\n }\n\n const next: SymbolId[][] = [];\n for (let col = 0; col < GRID_COLS; col += 1) {\n const column = stopGrid[col];\n if (!Array.isArray(column) || column.length !== GRID_ROWS) {\n throw new Error(`stopGrid[${col}] must contain ${GRID_ROWS} rows`);\n }\n next[col] = [\n normalizeSegment(column[0], elementsCount),\n normalizeSegment(column[1], elementsCount),\n normalizeSegment(column[2], elementsCount),\n ];\n }\n return next;\n}\n\nexport function normalizeInitialSegments(\n initialSegments: number[][],\n elementsCount: number,\n): SymbolId[][] {\n return normalizeStopGrid(rowsToStopGrid(initialSegments), elementsCount);\n}\n\nexport function cloneSpinState(state: SpinState): SpinState {\n return {\n stopGrid: state.stopGrid?.map((column) => [...column]),\n stopRows: state.stopRows?.map((row) => [...row]),\n finaleSequence: state.finaleSequence?.map((grid) => grid.map((column) => [...column])),\n finaleSequenceRows: state.finaleSequenceRows?.map((grid) => grid.map((row) => [...row])),\n highlightWin: state.highlightWin,\n callback: state.callback,\n };\n}\n","import { cloneSpinState } from '../normalize';\nimport type { SpinState } from '../types';\n\nexport class SpinQueueController {\n private queue: SpinState[];\n\n public constructor(initialQueue?: SpinState[]) {\n this.queue = (initialQueue ?? []).map((entry) => cloneSpinState(entry));\n }\n\n public hasPending(): boolean {\n return this.queue.length > 0;\n }\n\n public consume(): SpinState | null {\n if (this.queue.length === 0) return null;\n return this.queue.shift() ?? null;\n }\n}\n","import type { SpinPhase, SpinState } from '../types';\n\nexport type RuntimeState = {\n isSpinning: boolean;\n hasStartedFirstSpin: boolean;\n queueFinished: boolean;\n shouldHighlightCurrentSpin: boolean;\n activeSpinState: SpinState | null;\n phase: SpinPhase;\n winFlashStartedAt: number;\n outroStartedAt: number;\n idleStartedAt: number;\n preSpinStartedAt: number;\n winEffectsEnvelope: number;\n};\n\nexport function createRuntimeState(): RuntimeState {\n return {\n isSpinning: false,\n hasStartedFirstSpin: false,\n queueFinished: false,\n shouldHighlightCurrentSpin: false,\n activeSpinState: null,\n phase: 'idle',\n winFlashStartedAt: 0,\n outroStartedAt: 0,\n idleStartedAt: 0,\n preSpinStartedAt: 0,\n winEffectsEnvelope: 1,\n };\n}\n\nexport function beginSpin(\n state: RuntimeState,\n params: {\n activeSpinState: SpinState | null;\n shouldHighlightCurrentSpin: boolean;\n startedAt: number;\n },\n): void {\n state.hasStartedFirstSpin = true;\n state.isSpinning = true;\n state.phase = 'outro';\n state.outroStartedAt = params.startedAt;\n state.activeSpinState = params.activeSpinState;\n state.shouldHighlightCurrentSpin = params.shouldHighlightCurrentSpin;\n}\n\nexport function finishSpin(state: RuntimeState, hasPendingInQueue: boolean, now: number): void {\n state.phase = 'idle';\n state.idleStartedAt = now;\n state.isSpinning = false;\n state.shouldHighlightCurrentSpin = false;\n state.queueFinished = !hasPendingInQueue;\n state.activeSpinState = null;\n}\n\nexport function startWinFlash(state: RuntimeState, startedAt: number): void {\n state.winFlashStartedAt = startedAt;\n state.phase = 'winFlash';\n}\n\nexport function destroyState(state: RuntimeState): void {\n state.isSpinning = false;\n state.queueFinished = true;\n}\n","import type { SymbolId } from '../types';\nimport { normalizeSegment } from '../utils/math';\n\ntype WebGLUniforms = {\n resolution: WebGLUniformLocation | null;\n destRect: WebGLUniformLocation | null;\n srcRect: WebGLUniformLocation | null;\n color: WebGLUniformLocation | null;\n useTexture: WebGLUniformLocation | null;\n shapeMode: WebGLUniformLocation | null;\n texture: WebGLUniformLocation | null;\n};\n\nconst VERTEX_SHADER_SOURCE = `\nattribute vec2 a_pos;\nuniform vec2 u_resolution;\nuniform vec4 u_destRect;\nvarying vec2 v_uv;\n\nvoid main() {\n vec2 local = a_pos;\n vec2 pixel = vec2(\n u_destRect.x + local.x * u_destRect.z,\n u_destRect.y + local.y * u_destRect.w\n );\n\n vec2 zeroToOne = pixel / u_resolution;\n vec2 clip = vec2(\n zeroToOne.x * 2.0 - 1.0,\n 1.0 - zeroToOne.y * 2.0\n );\n\n gl_Position = vec4(clip, 0.0, 1.0);\n v_uv = local;\n}\n`;\n\nconst FRAGMENT_SHADER_SOURCE = `\nprecision mediump float;\n\nuniform sampler2D u_texture;\nuniform vec4 u_srcRect;\nuniform vec4 u_color;\nuniform float u_useTexture;\nuniform float u_shapeMode;\nvarying vec2 v_uv;\n\nvoid main() {\n if (u_shapeMode > 0.5) {\n vec2 centered = v_uv - vec2(0.5, 0.5);\n float dist = length(centered) * 2.0;\n if (dist > 1.0) {\n discard;\n }\n float feather = smoothstep(1.0, 0.72, dist);\n gl_FragColor = vec4(u_color.rgb, u_color.a * feather);\n } else if (u_useTexture > 0.5) {\n vec2 uv = vec2(\n mix(u_srcRect.x, u_srcRect.z, v_uv.x),\n mix(u_srcRect.y, u_srcRect.w, v_uv.y)\n );\n vec4 tex = texture2D(u_texture, uv);\n gl_FragColor = tex * u_color;\n } else {\n gl_FragColor = u_color;\n }\n}\n`;\n\nexport class WebGLRenderer {\n private readonly canvas: HTMLCanvasElement;\n private readonly spriteImage: HTMLImageElement;\n private readonly spriteElementsCount: number;\n\n private readonly gl: WebGLRenderingContext;\n private readonly program: WebGLProgram;\n private readonly uniforms: WebGLUniforms;\n private readonly quadBuffer: WebGLBuffer;\n private readonly texture: WebGLTexture;\n\n private viewportW = 1;\n private viewportH = 1;\n private readonly spriteWidth: number;\n private readonly spriteHeight: number;\n private readonly spriteSegmentHeight: number;\n\n public constructor(params: {\n canvas: HTMLCanvasElement;\n spriteImage: HTMLImageElement;\n spriteElementsCount: number;\n }) {\n this.canvas = params.canvas;\n this.spriteImage = params.spriteImage;\n this.spriteElementsCount = Math.max(1, params.spriteElementsCount);\n this.spriteWidth = this.spriteImage.width;\n this.spriteHeight = this.spriteImage.height;\n this.spriteSegmentHeight = this.spriteHeight / this.spriteElementsCount;\n\n const gl =\n (this.canvas.getContext('webgl2', {\n alpha: true,\n antialias: false,\n }) as WebGLRenderingContext | null) ??\n this.canvas.getContext('webgl', { alpha: true, antialias: false });\n if (!gl) {\n throw new Error('WebGL context is not available');\n }\n this.gl = gl;\n\n const vertexShader = this.createShader(this.gl.VERTEX_SHADER, VERTEX_SHADER_SOURCE);\n const fragmentShader = this.createShader(this.gl.FRAGMENT_SHADER, FRAGMENT_SHADER_SOURCE);\n this.program = this.createProgram(vertexShader, fragmentShader);\n this.gl.deleteShader(vertexShader);\n this.gl.deleteShader(fragmentShader);\n\n const quadBuffer = this.gl.createBuffer();\n if (!quadBuffer) {\n throw new Error('Failed to create WebGL quad buffer');\n }\n this.quadBuffer = quadBuffer;\n\n this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.quadBuffer);\n this.gl.bufferData(\n this.gl.ARRAY_BUFFER,\n new Float32Array([0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1]),\n this.gl.STATIC_DRAW,\n );\n\n const texture = this.gl.createTexture();\n if (!texture) {\n throw new Error('Failed to create WebGL texture');\n }\n this.texture = texture;\n\n this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture);\n this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, 0);\n this.gl.pixelStorei(this.gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1);\n this.gl.texImage2D(\n this.gl.TEXTURE_2D,\n 0,\n this.gl.RGBA,\n this.gl.RGBA,\n this.gl.UNSIGNED_BYTE,\n this.spriteImage,\n );\n this.gl.pixelStorei(this.gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 0);\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR);\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR);\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE);\n\n this.gl.useProgram(this.program);\n const posLocation = this.gl.getAttribLocation(this.program, 'a_pos');\n this.gl.enableVertexAttribArray(posLocation);\n this.gl.vertexAttribPointer(posLocation, 2, this.gl.FLOAT, false, 8, 0);\n\n this.uniforms = {\n resolution: this.gl.getUniformLocation(this.program, 'u_resolution'),\n destRect: this.gl.getUniformLocation(this.program, 'u_destRect'),\n srcRect: this.gl.getUniformLocation(this.program, 'u_srcRect'),\n color: this.gl.getUniformLocation(this.program, 'u_color'),\n useTexture: this.gl.getUniformLocation(this.program, 'u_useTexture'),\n shapeMode: this.gl.getUniformLocation(this.program, 'u_shapeMode'),\n texture: this.gl.getUniformLocation(this.program, 'u_texture'),\n };\n\n this.gl.uniform1i(this.uniforms.texture, 0);\n this.gl.clearColor(0, 0, 0, 0);\n this.gl.enable(this.gl.BLEND);\n this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA);\n }\n\n public resize(width: number, height: number): void {\n this.viewportW = Math.max(1, Math.floor(width));\n this.viewportH = Math.max(1, Math.floor(height));\n this.gl.viewport(0, 0, this.viewportW, this.viewportH);\n }\n\n public beginFrame(): void {\n this.gl.useProgram(this.program);\n this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.quadBuffer);\n this.gl.activeTexture(this.gl.TEXTURE0);\n this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture);\n this.gl.uniform2f(this.uniforms.resolution, this.viewportW, this.viewportH);\n this.gl.uniform1f(this.uniforms.shapeMode, 0);\n this.gl.clear(this.gl.COLOR_BUFFER_BIT);\n }\n\n public drawSprite(\n symbolId: SymbolId,\n x: number,\n y: number,\n width: number,\n height: number,\n alpha = 1,\n ): void {\n const segmentIndex = normalizeSegment(symbolId, this.spriteElementsCount);\n const srcTop = segmentIndex * this.spriteSegmentHeight;\n const srcBottom = srcTop + this.spriteSegmentHeight;\n\n // Half-texel inset reduces sprite bleeding between vertical segments.\n const inset = 0.5;\n const u0 = inset / this.spriteWidth;\n const u1 = 1 - inset / this.spriteWidth;\n const v0 = 1 - (srcBottom - inset) / this.spriteHeight;\n const v1 = 1 - (srcTop + inset) / this.spriteHeight;\n\n this.gl.uniform4f(this.uniforms.destRect, x, y, width, height);\n this.gl.uniform4f(this.uniforms.srcRect, u0, v0, u1, v1);\n this.gl.uniform4f(this.uniforms.color, 1, 1, 1, alpha);\n this.gl.uniform1f(this.uniforms.shapeMode, 0);\n this.gl.uniform1f(this.uniforms.useTexture, 1);\n this.gl.drawArrays(this.gl.TRIANGLES, 0, 6);\n }\n\n public drawSolidRect(\n x: number,\n y: number,\n width: number,\n height: number,\n rgba: [number, number, number, number],\n ): void {\n this.gl.uniform4f(this.uniforms.destRect, x, y, width, height);\n this.gl.uniform4f(this.uniforms.srcRect, 0, 0, 1, 1);\n this.gl.uniform4f(this.uniforms.color, rgba[0], rgba[1], rgba[2], rgba[3]);\n this.gl.uniform1f(this.uniforms.shapeMode, 0);\n this.gl.uniform1f(this.uniforms.useTexture, 0);\n this.gl.drawArrays(this.gl.TRIANGLES, 0, 6);\n }\n\n public drawSoftCircle(\n centerX: number,\n centerY: number,\n radius: number,\n rgba: [number, number, number, number],\n ): void {\n const diameter = radius * 2;\n this.gl.uniform4f(\n this.uniforms.destRect,\n centerX - radius,\n centerY - radius,\n diameter,\n diameter,\n );\n this.gl.uniform4f(this.uniforms.srcRect, 0, 0, 1, 1);\n this.gl.uniform4f(this.uniforms.color, rgba[0], rgba[1], rgba[2], rgba[3]);\n this.gl.uniform1f(this.uniforms.useTexture, 0);\n this.gl.uniform1f(this.uniforms.shapeMode, 1);\n this.gl.drawArrays(this.gl.TRIANGLES, 0, 6);\n this.gl.uniform1f(this.uniforms.shapeMode, 0);\n }\n\n public beginAdditiveBlend(): void {\n this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE);\n }\n\n public endAdditiveBlend(): void {\n this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA);\n }\n\n public dispose(): void {\n this.gl.deleteTexture(this.texture);\n this.gl.deleteBuffer(this.quadBuffer);\n this.gl.deleteProgram(this.program);\n }\n\n private createShader(type: number, source: string): WebGLShader {\n const shader = this.gl.createShader(type);\n if (!shader) {\n throw new Error('Failed to create WebGL shader');\n }\n this.gl.shaderSource(shader, source);\n this.gl.compileShader(shader);\n\n if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) {\n const message = this.gl.getShaderInfoLog(shader) ?? 'unknown error';\n this.gl.deleteShader(shader);\n throw new Error(`WebGL shader compile failed: ${message}`);\n }\n return shader;\n }\n\n private createProgram(vertexShader: WebGLShader, fragmentShader: WebGLShader): WebGLProgram {\n const program = this.gl.createProgram();\n if (!program) {\n throw new Error('Failed to create WebGL program');\n }\n this.gl.attachShader(program, vertexShader);\n this.gl.attachShader(program, fragmentShader);\n this.gl.linkProgram(program);\n\n if (!this.gl.getProgramParameter(program, this.gl.LINK_STATUS)) {\n const message = this.gl.getProgramInfoLog(program) ?? 'unknown error';\n this.gl.deleteProgram(program);\n throw new Error(`WebGL program link failed: ${message}`);\n }\n return program;\n }\n}\n","import {\n DEFAULT_MOTION_PROFILE_NAME,\n DEFAULT_SPRITE_ELEMENTS_COUNT,\n FLOW_WIN_FLASH_MS,\n FLOW_WIN_PARTICLES_PER_CELL_HIGH,\n FLOW_WIN_PULSE_AMPLITUDE,\n FLOW_WIN_PULSE_PERIOD_MS,\n GRID_COLS,\n GRID_ROWS,\n getMotionProfile,\n INITIAL_WIN_FLASH_DELAY_MS,\n type MotionProfile,\n PARTICLE_FLY_DURATION_MS,\n type PixelSnapYMode,\n ROW_COMPACT_OFFSETS_RATIO,\n} from './constants';\nimport {\n createRandomGrid,\n createZeroOffsets,\n fillOffsets,\n findMostFrequentCells,\n} from './core/grid';\nimport { RafLoop } from './core/loop';\nimport type { CellVisibility, OutroMotionPlan } from './core/outro';\nimport { buildOutroMotionPlan, updateOutroOffsets } from './core/outro';\nimport { SpinQueueController } from './core/spinQueue';\nimport {\n beginSpin,\n createRuntimeState,\n destroyState,\n finishSpin,\n startWinFlash,\n} from './core/state';\nimport {\n normalizeInitialSegments,\n normalizeParticleColor,\n normalizeParticleColorMode,\n normalizeStopGrid,\n rowsToStopGrid,\n} from './normalize';\nimport { WebGLRenderer } from './render/webglRenderer';\nimport type { CascadingReelConfig, CellPosition, SymbolId } from './types';\n\ntype ParticleSeeds = {\n seedA: number;\n seedB: number;\n seedC: number;\n phaseOffset: number;\n twinkleSeed: number;\n};\n\ntype GridRenderSampler = {\n grid: SymbolId[][];\n skipWinningCells: boolean;\n sampleOffsetY: (col: number, row: number) => number;\n sampleAlpha: (col: number, row: number) => number;\n isVisible: (col: number, row: number) => boolean;\n};\n\nexport class CascadingReel {\n private static readonly RAINBOW_HUE_BUCKETS = 24;\n private static readonly PARTICLE_GLOBAL_ALPHA = 0.9;\n private static readonly PARTICLE_MAX_DISTANCE = 0.72;\n private static readonly PARTICLE_BASE_RADIUS = 0.028;\n\n private readonly canvas: HTMLCanvasElement;\n private readonly container: HTMLElement;\n private readonly button?: HTMLButtonElement;\n private readonly spinQueueController: SpinQueueController;\n private readonly spriteUrl?: string;\n private readonly spriteElementsCount: number;\n private readonly highlightInitialWinningCells: boolean;\n private readonly particleColorRgb: [number, number, number];\n private readonly particleColorMode: 'solid' | 'rainbow';\n private readonly motionProfile: MotionProfile;\n private readonly pixelSnapY: PixelSnapYMode;\n\n private spriteImage: HTMLImageElement | null = null;\n private webglRenderer: WebGLRenderer | null = null;\n private readonly rafLoop = new RafLoop();\n private readonly runtime = createRuntimeState();\n private width = 0;\n private height = 0;\n private cellW = 0;\n private cellH = 0;\n private boardX = 0;\n private boardY = 0;\n private scriptedCascadeQueue: number[][][] = [];\n private scriptedOutgoingGrid: SymbolId[][] | null = null;\n private scriptedPendingGrid: SymbolId[][] | null = null;\n private scriptedOutroStartedAt = 0;\n private scriptedOutroElapsedMs = 0;\n private outroMotionPlan: OutroMotionPlan | null = null;\n private scriptedOutgoingOffsets: number[][] = createZeroOffsets();\n private scriptedOutgoingOffsetsPrev: number[][] = createZeroOffsets();\n private scriptedIncomingOffsets: number[][] = createZeroOffsets();\n private scriptedIncomingOffsetsPrev: number[][] = createZeroOffsets();\n private scriptedIncomingAlpha: number[][] = createZeroOffsets();\n private scriptedIncomingAlphaPrev: number[][] = createZeroOffsets();\n private scriptedIncomingVisibility: CellVisibility[][] =\n CascadingReel.createVisibilityGrid('hidden');\n private scriptedIncomingVisibilityPrev: CellVisibility[][] =\n CascadingReel.createVisibilityGrid('hidden');\n private winningCells: CellPosition[] = [];\n private readonly winningCellKeys = new Set<string>();\n private grid: SymbolId[][];\n private readonly particlesPerCell = FLOW_WIN_PARTICLES_PER_CELL_HIGH;\n private lastRafTime = 0;\n private initialHighlightRequestedAt = 0;\n private simulationLastNow = 0;\n private simulationAccumulatorMs = 0;\n private outroInterpolationAlpha = 1;\n private isOutroPipelineWarmedUp = false;\n private static readonly PRE_SPIN_MS = 150;\n private static readonly WIN_EFFECTS_ENVELOPE_TAU_MS = 120;\n private static readonly MAX_FRAME_DELTA_MS = 250;\n private static readonly WIN_BORDER_ALPHA = 0.72;\n private static readonly WIN_BORDER_INSET_RATIO = 0.06;\n private static readonly particleSeedsCache = new Map<string, ParticleSeeds[]>();\n\n public constructor(config: CascadingReelConfig) {\n this.canvas = config.canvas;\n this.container = config.container;\n this.button = config.button;\n this.spriteUrl = config.sprite;\n this.spriteElementsCount = Math.max(\n 1,\n config.spriteElementsCount ?? DEFAULT_SPRITE_ELEMENTS_COUNT,\n );\n this.highlightInitialWinningCells = config.highlightInitialWinningCells !== false;\n this.spinQueueController = new SpinQueueController(config.queuedSpinStates);\n this.particleColorRgb = normalizeParticleColor(config.particleColorRgb);\n this.particleColorMode = normalizeParticleColorMode(config.particleColorMode);\n this.motionProfile = getMotionProfile(config.motionProfile ?? DEFAULT_MOTION_PROFILE_NAME);\n this.pixelSnapY = config.pixelSnapY ?? this.motionProfile.pixelSnapY;\n this.grid = config.initialSegments\n ? normalizeInitialSegments(config.initialSegments, this.spriteElementsCount)\n : createRandomGrid(this.spriteElementsCount);\n }\n\n public async init(): Promise<void> {\n this.bindEvents();\n this.resize();\n await this.loadSpriteIfProvided();\n if (!this.spriteImage) {\n throw new Error('sprite is required for WebGL renderer');\n }\n this.webglRenderer = new WebGLRenderer({\n canvas: this.canvas,\n spriteImage: this.spriteImage,\n spriteElementsCount: this.spriteElementsCount,\n });\n this.webglRenderer.resize(this.width, this.height);\n this.warmUpOutroPipeline();\n this.applyInitialHighlightIfNeeded();\n requestAnimationFrame((warmNow) => {\n this.render(warmNow);\n this.startLoop();\n });\n }\n\n public destroy(): void {\n this.unbindEvents();\n this.rafLoop.stop();\n this.simulationLastNow = 0;\n this.simulationAccumulatorMs = 0;\n destroyState(this.runtime);\n this.webglRenderer?.dispose();\n this.webglRenderer = null;\n this.clearWinningCells();\n }\n\n public spin(): void {\n if (this.runtime.isSpinning) return;\n if (this.runtime.queueFinished) return;\n if (!this.spinQueueController.hasPending()) {\n this.runtime.queueFinished = true;\n if (this.button) this.button.disabled = true;\n return;\n }\n\n const activeSpinState = this.spinQueueController.consume();\n const shouldHighlightCurrentSpin = activeSpinState?.highlightWin === true;\n this.runtime.isSpinning = true;\n this.runtime.phase = 'preSpin';\n this.runtime.preSpinStartedAt = performance.now();\n this.runtime.activeSpinState = activeSpinState;\n this.runtime.shouldHighlightCurrentSpin = shouldHighlightCurrentSpin;\n this.runtime.hasStartedFirstSpin = true;\n this.simulationLastNow = 0;\n this.simulationAccumulatorMs = 0;\n\n if (this.button) this.button.disabled = true;\n this.startLoop();\n }\n\n private bindEvents(): void {\n window.addEventListener('resize', this.resize);\n this.button?.addEventListener('click', this.onSpinClick);\n }\n\n private unbindEvents(): void {\n window.removeEventListener('resize', this.resize);\n this.button?.removeEventListener('click', this.onSpinClick);\n }\n\n private readonly onSpinClick = (): void => {\n // Подсветка до клика: в winFlash с подсветкой кнопка включена, по клику завершаем и при наличии очереди запускаем следующий спин\n if (\n this.runtime.phase === 'winFlash' &&\n (this.runtime.shouldHighlightCurrentSpin || !this.runtime.hasStartedFirstSpin)\n ) {\n // Только закрываем подсветку и запускаем следующий спин; callback уже вызван по окончании прокрутки\n this.finishSpinWithUi(true);\n if (this.spinQueueController.hasPending()) this.spin();\n return;\n }\n if (this.runtime.isSpinning) return;\n this.spin();\n };\n\n private getNextGrid(stopGrid?: number[][]): SymbolId[][] {\n if (!stopGrid) return createRandomGrid(this.spriteElementsCount);\n return normalizeStopGrid(stopGrid, this.spriteElementsCount);\n }\n\n private update(now: number): void {\n if (!this.runtime.isSpinning) return;\n\n if (this.runtime.phase === 'preSpin') {\n if (now - this.runtime.preSpinStartedAt < CascadingReel.PRE_SPIN_MS) {\n return;\n }\n beginSpin(this.runtime, {\n activeSpinState: this.runtime.activeSpinState,\n shouldHighlightCurrentSpin: this.runtime.shouldHighlightCurrentSpin,\n startedAt: now,\n });\n this.runtime.preSpinStartedAt = 0;\n\n const scriptedSource = this.runtime.activeSpinState?.finaleSequenceRows\n ? this.runtime.activeSpinState.finaleSequenceRows.map((rows) => rowsToStopGrid(rows))\n : (this.runtime.activeSpinState?.finaleSequence ?? []);\n this.scriptedCascadeQueue = scriptedSource.map((grid) => grid.map((column) => [...column]));\n this.clearWinningCells();\n\n const stopGridSource = this.runtime.activeSpinState?.stopRows\n ? rowsToStopGrid(this.runtime.activeSpinState.stopRows)\n : this.runtime.activeSpinState?.stopGrid;\n const nextGrid = this.getNextGrid(stopGridSource);\n this.startOutroTransition(nextGrid, now);\n }\n\n if (this.runtime.phase === 'outro') {\n return;\n }\n if (this.runtime.phase === 'winFlash') {\n // Подсветка без ограничения по времени: выход только по клику «Spin»\n if (\n !this.runtime.shouldHighlightCurrentSpin &&\n now - this.runtime.winFlashStartedAt >= FLOW_WIN_FLASH_MS\n ) {\n this.finishSpinWithUi();\n }\n return;\n }\n }\n\n private stepScriptedOutro(stepMs: number): void {\n if (!this.scriptedOutgoingGrid || !this.scriptedPendingGrid) {\n this.finishSpinWithUi();\n return;\n }\n if (!this.outroMotionPlan) {\n this.outroMotionPlan = buildOutroMotionPlan({\n height: this.height,\n boardY: this.boardY,\n cellH: this.cellH,\n motionProfile: this.motionProfile,\n });\n }\n this.scriptedOutroElapsedMs += stepMs;\n const { allOutgoingDone, allIncomingDone } = updateOutroOffsets({\n elapsedMs: this.scriptedOutroElapsedMs,\n scriptedOutgoingOffsets: this.scriptedOutgoingOffsets,\n scriptedIncomingOffsets: this.scriptedIncomingOffsets,\n scriptedIncomingAlpha: this.scriptedIncomingAlpha,\n scriptedIncomingVisibility: this.scriptedIncomingVisibility,\n motionPlan: this.outroMotionPlan,\n });\n\n if (!allOutgoingDone || !allIncomingDone) return;\n\n this.grid = this.scriptedPendingGrid;\n this.scriptedOutgoingGrid = null;\n this.scriptedPendingGrid = null;\n this.outroMotionPlan = null;\n this.clearWinningCells();\n this.resetOutroBuffers(1, 'active');\n\n if (this.tryStartScriptedCascade(this.scriptedOutroStartedAt + this.scriptedOutroElapsedMs))\n return;\n if (!this.runtime.shouldHighlightCurrentSpin) {\n this.finishSpinWithUi();\n return;\n }\n\n this.setWinningCells(findMostFrequentCells(this.grid));\n startWinFlash(this.runtime, this.scriptedOutroStartedAt + this.scriptedOutroElapsedMs);\n // Callback по окончании прокрутки, а не по клику\n this.runtime.activeSpinState?.callback?.();\n // Включаем кнопку только если в очереди есть следующий спин; после последнего — оставляем disabled\n if (\n this.button &&\n this.runtime.shouldHighlightCurrentSpin &&\n this.spinQueueController.hasPending()\n )\n this.button.disabled = false;\n }\n\n private tryStartScriptedCascade(now: number): boolean {\n if (this.scriptedCascadeQueue.length === 0) return false;\n const nextGrid = this.scriptedCascadeQueue.shift();\n if (!nextGrid) return false;\n this.startOutroTransition(normalizeStopGrid(nextGrid, this.spriteElementsCount), now);\n return true;\n }\n\n private startOutroTransition(nextGrid: SymbolId[][], now: number): void {\n this.scriptedOutgoingGrid = this.grid.map((column) => [...column]);\n this.scriptedPendingGrid = nextGrid.map((column) => [...column]);\n this.outroMotionPlan = buildOutroMotionPlan({\n height: this.height,\n boardY: this.boardY,\n cellH: this.cellH,\n motionProfile: this.motionProfile,\n });\n this.resetOutroBuffers(0, 'hidden');\n this.clearWinningCells();\n this.runtime.phase = 'outro';\n this.scriptedOutroStartedAt = now;\n this.scriptedOutroElapsedMs = 0;\n this.outroInterpolationAlpha = 1;\n }\n\n /** @param skipCallback true при закрытии подсветки по клику (callback уже вызван по окончании прокрутки) */\n private finishSpinWithUi(skipCallback = false): void {\n const finishedSpinState = this.runtime.activeSpinState;\n const callback = skipCallback ? undefined : finishedSpinState?.callback;\n const hasPending = this.spinQueueController.hasPending();\n finishSpin(this.runtime, hasPending, performance.now());\n if (this.button) this.button.disabled = this.runtime.queueFinished;\n callback?.();\n }\n\n private applyInitialHighlightIfNeeded(): void {\n if (!this.highlightInitialWinningCells) return;\n this.setWinningCells(findMostFrequentCells(this.grid));\n this.initialHighlightRequestedAt = performance.now();\n }\n\n private warmUpOutroPipeline(): void {\n if (this.isOutroPipelineWarmedUp) return;\n const motionPlan = buildOutroMotionPlan({\n height: this.height,\n boardY: this.boardY,\n cellH: this.cellH,\n motionProfile: this.motionProfile,\n });\n const outgoingOffsets = createZeroOffsets();\n const incomingOffsets = createZeroOffsets();\n const incomingAlpha = createZeroOffsets();\n const incomingVisibility = CascadingReel.createVisibilityGrid('hidden');\n const maxRowDelay = Math.max(...motionPlan.rowStartDelays);\n const maxColumnDelay = (GRID_COLS - 1) * motionPlan.columnStaggerMs;\n const warmupMoments = [\n 0,\n this.motionProfile.fixedStepMs,\n motionPlan.incomingStartShift + this.motionProfile.fixedStepMs,\n motionPlan.fallMs + maxRowDelay + maxColumnDelay + this.motionProfile.fixedStepMs,\n ];\n // Warm up first-spin math/JIT path across multiple outro phases.\n for (const elapsedMs of warmupMoments) {\n updateOutroOffsets({\n elapsedMs,\n scriptedOutgoingOffsets: outgoingOffsets,\n scriptedIncomingOffsets: incomingOffsets,\n scriptedIncomingAlpha: incomingAlpha,\n scriptedIncomingVisibility: incomingVisibility,\n motionPlan,\n });\n }\n this.isOutroPipelineWarmedUp = true;\n }\n\n private render(now: number): void {\n if (!this.webglRenderer) return;\n if (\n this.initialHighlightRequestedAt > 0 &&\n now - this.initialHighlightRequestedAt >= INITIAL_WIN_FLASH_DELAY_MS\n ) {\n startWinFlash(this.runtime, this.initialHighlightRequestedAt);\n this.runtime.winEffectsEnvelope = 1;\n this.initialHighlightRequestedAt = 0;\n if (this.button) this.button.disabled = false;\n }\n\n const winEffectsTarget =\n this.runtime.phase === 'preSpin' ? 0 : this.runtime.phase === 'winFlash' ? 1 : 0;\n if (this.lastRafTime > 0) {\n const dt = Math.min(now - this.lastRafTime, 50);\n const kWin = 1 - Math.exp(-dt / CascadingReel.WIN_EFFECTS_ENVELOPE_TAU_MS);\n this.runtime.winEffectsEnvelope +=\n (winEffectsTarget - this.runtime.winEffectsEnvelope) * kWin;\n }\n this.lastRafTime = now;\n\n this.webglRenderer.beginFrame();\n\n const skipWinningCells =\n this.runtime.phase === 'winFlash' ||\n this.runtime.phase === 'preSpin' ||\n (this.initialHighlightRequestedAt > 0 && this.winningCells.length > 0);\n\n if (this.runtime.phase === 'outro' && this.scriptedOutgoingGrid && this.scriptedPendingGrid) {\n this.drawGridInterpolated(\n this.scriptedOutgoingGrid,\n this.scriptedOutgoingOffsetsPrev,\n this.scriptedOutgoingOffsets,\n this.outroInterpolationAlpha,\n skipWinningCells,\n );\n this.drawGridInterpolated(\n this.scriptedPendingGrid,\n this.scriptedIncomingOffsetsPrev,\n this.scriptedIncomingOffsets,\n this.outroInterpolationAlpha,\n skipWinningCells,\n this.scriptedIncomingAlphaPrev,\n this.scriptedIncomingAlpha,\n this.scriptedIncomingVisibility,\n );\n } else {\n this.drawGrid(this.grid, null, skipWinningCells);\n }\n\n const winPhase = this.initialHighlightRequestedAt > 0 ? 'winFlash' : this.runtime.phase;\n const winFlashStartedAt =\n this.initialHighlightRequestedAt > 0\n ? this.initialHighlightRequestedAt\n : this.runtime.winFlashStartedAt;\n const winEffectsEnvelope =\n this.initialHighlightRequestedAt > 0 ? 1 : this.runtime.winEffectsEnvelope;\n\n this.drawWinningEffects({\n now,\n phase: winPhase,\n winFlashStartedAt,\n winEffectsEnvelope,\n });\n }\n\n private readonly isWinningCell = (col: number, row: number): boolean => {\n return this.winningCellKeys.has(`${col}:${row}`);\n };\n\n private getRowCompactOffset(row: number): number {\n return (ROW_COMPACT_OFFSETS_RATIO[row] ?? 0) * this.cellH;\n }\n\n private applyPixelSnapY(y: number): number {\n if (this.pixelSnapY === 'integer') return Math.round(y);\n if (this.pixelSnapY === 'half') return Math.round(y * 2) / 2;\n return y;\n }\n\n private drawGrid(\n grid: SymbolId[][],\n offsets: number[][] | null,\n skipWinningCells: boolean,\n ): void {\n this.drawGridWithSampler({\n grid,\n skipWinningCells,\n sampleOffsetY: (col, row) => (offsets ? offsets[col][row] : 0),\n sampleAlpha: () => 1,\n isVisible: () => true,\n });\n }\n\n private drawGridInterpolated(\n grid: SymbolId[][],\n prevOffsets: number[][],\n currOffsets: number[][],\n alpha: number,\n skipWinningCells: boolean,\n prevOpacity?: number[][],\n currOpacity?: number[][],\n visibility?: CellVisibility[][],\n ): void {\n this.drawGridWithSampler({\n grid,\n skipWinningCells,\n sampleOffsetY: (col, row) =>\n prevOffsets[col][row] + (currOffsets[col][row] - prevOffsets[col][row]) * alpha,\n sampleAlpha: (col, row) =>\n prevOpacity && currOpacity\n ? prevOpacity[col][row] + (currOpacity[col][row] - prevOpacity[col][row]) * alpha\n : 1,\n isVisible: (col, row) => !visibility || visibility[col][row] !== 'hidden',\n });\n }\n\n private drawGridWithSampler(sampler: GridRenderSampler): void {\n const renderer = this.webglRenderer;\n if (!renderer) return;\n for (let col = 0; col < GRID_COLS; col += 1) {\n const x = this.boardX + col * this.cellW;\n for (let row = 0; row < GRID_ROWS; row += 1) {\n if (sampler.skipWinningCells && this.isWinningCell(col, row)) continue;\n if (!sampler.isVisible(col, row)) continue;\n const offsetY = sampler.sampleOffsetY(col, row);\n const y = this.applyPixelSnapY(\n this.boardY + row * this.cellH + offsetY + this.getRowCompactOffset(row),\n );\n if (y > this.height || y + this.cellH < 0) continue;\n const spriteAlpha = sampler.sampleAlpha(col, row);\n if (spriteAlpha <= 0) continue;\n renderer.drawSprite(sampler.grid[col][row], x, y, this.cellW, this.cellH, spriteAlpha);\n }\n }\n }\n\n private drawWinningEffects(params: {\n now: number;\n phase: 'idle' | 'winFlash' | 'outro' | 'preSpin';\n winFlashStartedAt: number;\n winEffectsEnvelope: number;\n }): void {\n const renderer = this.webglRenderer;\n if (!renderer) return;\n if (this.winningCells.length === 0) return;\n if (params.phase !== 'winFlash' && params.phase !== 'preSpin') return;\n\n const envelope = Math.max(0, Math.min(1, params.winEffectsEnvelope));\n const elapsed = Math.max(0, params.now - params.winFlashStartedAt);\n const pulseProgress = (elapsed % FLOW_WIN_PULSE_PERIOD_MS) / FLOW_WIN_PULSE_PERIOD_MS;\n const pulse = 1 + Math.sin(pulseProgress * Math.PI * 2) * FLOW_WIN_PULSE_AMPLITUDE * envelope;\n const borderInset = Math.max(1, this.cellW * CascadingReel.WIN_BORDER_INSET_RATIO);\n const borderThickness = Math.max(1, this.cellW * 0.03);\n const particleModeActive =\n this.runtime.phase === 'winFlash' &&\n this.runtime.shouldHighlightCurrentSpin &&\n this.runtime.hasStartedFirstSpin;\n for (const cell of this.winningCells) {\n const baseX = this.boardX + cell.col * this.cellW;\n const baseY = this.boardY + cell.row * this.cellH + this.getRowCompactOffset(cell.row);\n const symbol = this.grid[cell.col][cell.row];\n const alpha = CascadingReel.WIN_BORDER_ALPHA * envelope;\n\n const borderColor =\n this.particleColorMode === 'rainbow'\n ? CascadingReel.hslToRgb01(\n (elapsed * 0.2 + cell.col * 36 + cell.row * 22) % 360,\n 0.96,\n 0.64,\n )\n : [\n this.particleColorRgb[0] / 255,\n this.particleColorRgb[1] / 255,\n this.particleColorRgb[2] / 255,\n ];\n\n const scaledW = this.cellW * pulse;\n const scaledH = this.cellH * pulse;\n const offsetX = (this.cellW - scaledW) * 0.5;\n const offsetY = (this.cellH - scaledH) * 0.5;\n renderer.drawSprite(symbol, baseX + offsetX, baseY + offsetY, scaledW, scaledH, 1);\n\n const innerX = baseX + borderInset;\n const innerY = baseY + borderInset;\n const innerW = this.cellW - borderInset * 2;\n const innerH = this.cellH - borderInset * 2;\n if (innerW <= borderThickness * 2 || innerH <= borderThickness * 2) continue;\n\n renderer.drawSolidRect(innerX, innerY, innerW, borderThickness, [\n borderColor[0],\n borderColor[1],\n borderColor[2],\n alpha,\n ]);\n renderer.drawSolidRect(innerX, innerY + innerH - borderThickness, innerW, borderThickness, [\n borderColor[0],\n borderColor[1],\n borderColor[2],\n alpha,\n ]);\n renderer.drawSolidRect(innerX, innerY, borderThickness, innerH, [\n borderColor[0],\n borderColor[1],\n borderColor[2],\n alpha,\n ]);\n renderer.drawSolidRect(innerX + innerW - borderThickness, innerY, borderThickness, innerH, [\n borderColor[0],\n borderColor[1],\n borderColor[2],\n alpha,\n ]);\n\n if (particleModeActive) {\n this.drawCellParticleBurst({\n renderer,\n cell,\n centerX: baseX + this.cellW * 0.5,\n centerY: baseY + this.cellH * 0.5,\n elapsed,\n envelope,\n });\n }\n }\n }\n\n private drawCellParticleBurst(params: {\n renderer: WebGLRenderer;\n cell: CellPosition;\n centerX: number;\n centerY: number;\n elapsed: number;\n envelope: number;\n }): void {\n const maxDistance = Math.min(this.cellW, this.cellH) * CascadingReel.PARTICLE_MAX_DISTANCE;\n const baseRadius = Math.min(this.cellW, this.cellH) * CascadingReel.PARTICLE_BASE_RADIUS;\n const seedsList = CascadingReel.getParticleSeeds(params.cell.col, params.cell.row);\n const solidColor: [number, number, number] = [\n this.particleColorRgb[0] / 255,\n this.particleColorRgb[1] / 255,\n this.particleColorRgb[2] / 255,\n ];\n\n params.renderer.beginAdditiveBlend();\n for (let i = 0; i < this.particlesPerCell; i += 1) {\n const s = seedsList[i];\n const startTime = s.phaseOffset * PARTICLE_FLY_DURATION_MS;\n const age = params.elapsed - startTime;\n if (age < 0) continue;\n const particleT = (age % PARTICLE_FLY_DURATION_MS) / PARTICLE_FLY_DURATION_MS;\n const direction = s.seedA * Math.PI * 2;\n const distance = maxDistance * particleT * (0.35 + s.seedB * 0.65);\n const px = params.centerX + Math.cos(direction) * distance;\n const py = params.centerY + Math.sin(direction) * distance;\n const twinkle =\n 0.7 +\n 0.9 * Math.max(0, Math.sin((params.elapsed * 0.012 + s.twinkleSeed * 2) * Math.PI * 2));\n const radius = Math.max(1, baseRadius * (0.55 + s.seedC * 0.6) * (1 - particleT * 0.5));\n const alpha = Math.max(\n 0,\n Math.min(1, (0.9 + twinkle * 0.2) * CascadingReel.PARTICLE_GLOBAL_ALPHA * params.envelope),\n );\n if (alpha <= 0) continue;\n\n const rgb =\n this.particleColorMode === 'rainbow'\n ? CascadingReel.getRainbowParticleColor(\n params.elapsed,\n params.cell.col,\n params.cell.row,\n s.seedA,\n )\n : solidColor;\n\n params.renderer.drawSoftCircle(px, py, radius, [rgb[0], rgb[1], rgb[2], alpha]);\n }\n params.renderer.endAdditiveBlend();\n }\n\n private static hslToRgb01(\n hueDeg: number,\n saturation: number,\n lightness: number,\n ): [number, number, number] {\n const h = ((hueDeg % 360) + 360) % 360;\n const s = Math.max(0, Math.min(1, saturation));\n const l = Math.max(0, Math.min(1, lightness));\n const c = (1 - Math.abs(2 * l - 1)) * s;\n const hp = h / 60;\n const x = c * (1 - Math.abs((hp % 2) - 1));\n let r = 0;\n let g = 0;\n let b = 0;\n if (hp >= 0 && hp < 1) {\n r = c;\n g = x;\n } else if (hp < 2) {\n r = x;\n g = c;\n } else if (hp < 3) {\n g = c;\n b = x;\n } else if (hp < 4) {\n g = x;\n b = c;\n } else if (hp < 5) {\n r = x;\n b = c;\n } else {\n r = c;\n b = x;\n }\n const m = l - c * 0.5;\n return [r + m, g + m, b + m];\n }\n\n private static getRainbowParticleColor(\n elapsed: number,\n col: number,\n row: number,\n seedA: number,\n ): [number, number, number] {\n const hueRaw = (seedA * 360 + elapsed * 0.24 + col * 38 + row * 22) % 360;\n const bucket = 360 / CascadingReel.RAINBOW_HUE_BUCKETS;\n const hue = Math.floor(hueRaw / bucket) * bucket;\n return CascadingReel.hslToRgb01(hue, 0.98, 0.64);\n }\n\n private static hash01(a: number, b: number, c: number, d: number): number {\n const value = Math.sin(a * 127.1 + b * 311.7 + c * 74.7 + d * 19.3) * 43758.5453;\n return value - Math.floor(value);\n }\n\n private static getParticleSeeds(col: number, row: number): ParticleSeeds[] {\n const key = `${col},${row}`;\n const cached = CascadingReel.particleSeedsCache.get(key);\n if (cached) return cached;\n\n const generated: ParticleSeeds[] = [];\n for (let i = 0; i < FLOW_WIN_PARTICLES_PER_CELL_HIGH; i += 1) {\n generated.push({\n seedA: CascadingReel.hash01(col, row, i, 1),\n seedB: CascadingReel.hash01(col, row, i, 2),\n seedC: CascadingReel.hash01(col, row, i, 3),\n phaseOffset: CascadingReel.hash01(col, row, i, 4),\n twinkleSeed: CascadingReel.hash01(col, row, i, 5),\n });\n }\n CascadingReel.particleSeedsCache.set(key, generated);\n return generated;\n }\n\n private clearWinningCells(): void {\n this.winningCells = [];\n this.winningCellKeys.clear();\n }\n\n private setWinningCells(cells: CellPosition[]): void {\n this.winningCells = cells;\n this.winningCellKeys.clear();\n for (const cell of cells) {\n this.winningCellKeys.add(`${cell.col}:${cell.row}`);\n }\n }\n\n private readonly resize = (): void => {\n const bounds = this.container.getBoundingClientRect();\n const dpr = Math.max(1, Math.min(window.devicePixelRatio || 1, 2));\n const side = Math.max(300, Math.floor(bounds.width * dpr));\n this.width = side;\n this.height = side;\n const squareSize = Math.floor(Math.min(this.width / GRID_COLS, this.height / GRID_ROWS));\n this.cellW = squareSize;\n this.cellH = squareSize;\n this.boardX = Math.floor((this.width - this.cellW * GRID_COLS) / 2);\n this.boardY = Math.floor((this.height - this.cellH * GRID_ROWS) / 2);\n\n this.canvas.width = this.width;\n this.canvas.height = this.height;\n this.webglRenderer?.resize(this.width, this.height);\n };\n\n private async loadSpriteIfProvided(): Promise<void> {\n if (!this.spriteUrl) return;\n const image = new Image();\n image.decoding = 'async';\n image.src = this.spriteUrl;\n try {\n await image.decode();\n this.spriteImage = image;\n } catch {\n this.spriteImage = null;\n }\n }\n\n private advanceSimulation(now: number): void {\n if (this.simulationLastNow <= 0) {\n this.simulationLastNow = now;\n this.update(now);\n return;\n }\n const frameDt = Math.max(\n 0,\n Math.min(now - this.simulationLastNow, CascadingReel.MAX_FRAME_DELTA_MS),\n );\n this.simulationLastNow = now;\n this.update(now);\n if (this.runtime.phase !== 'outro') {\n this.outroInterpolationAlpha = 1;\n return;\n }\n\n this.snapshotOutroState();\n this.simulationAccumulatorMs += frameDt;\n const stepMs = this.motionProfile.fixedStepMs;\n let steps = 0;\n while (\n this.simulationAccumulatorMs >= stepMs &&\n steps < this.motionProfile.maxCatchUpStepsPerFrame &&\n this.runtime.phase === 'outro'\n ) {\n this.stepScriptedOutro(stepMs);\n this.simulationAccumulatorMs -= stepMs;\n steps += 1;\n }\n if (this.runtime.phase !== 'outro') {\n this.outroInterpolationAlpha = 1;\n return;\n }\n this.outroInterpolationAlpha = Math.max(0, Math.min(1, this.simulationAccumulatorMs / stepMs));\n }\n\n private snapshotOutroState(): void {\n this.copyOffsets(this.scriptedOutgoingOffsetsPrev, this.scriptedOutgoingOffsets);\n this.copyOffsets(this.scriptedIncomingOffsetsPrev, this.scriptedIncomingOffsets);\n this.copyOffsets(this.scriptedIncomingAlphaPrev, this.scriptedIncomingAlpha);\n this.copyVisibility(this.scriptedIncomingVisibilityPrev, this.scriptedIncomingVisibility);\n }\n\n private copyOffsets(target: number[][], source: number[][]): void {\n for (let col = 0; col < GRID_COLS; col += 1) {\n for (let row = 0; row < GRID_ROWS; row += 1) {\n target[col][row] = source[col][row];\n }\n }\n }\n\n private copyVisibility(target: CellVisibility[][], source: CellVisibility[][]): void {\n for (let col = 0; col < GRID_COLS; col += 1) {\n for (let row = 0; row < GRID_ROWS; row += 1) {\n target[col][row] = source[col][row];\n }\n }\n }\n\n private resetOutroBuffers(incomingAlpha: number, incomingVisibility: CellVisibility): void {\n fillOffsets(this.scriptedOutgoingOffsets, 0);\n fillOffsets(this.scriptedIncomingOffsets, 0);\n fillOffsets(this.scriptedOutgoingOffsetsPrev, 0);\n fillOffsets(this.scriptedIncomingOffsetsPrev, 0);\n fillOffsets(this.scriptedIncomingAlpha, incomingAlpha);\n fillOffsets(this.scriptedIncomingAlphaPrev, incomingAlpha);\n this.fillVisibilityGrid(this.scriptedIncomingVisibility, incomingVisibility);\n this.fillVisibilityGrid(this.scriptedIncomingVisibilityPrev, incomingVisibility);\n }\n\n private static createVisibilityGrid(value: CellVisibility): CellVisibility[][] {\n return Array.from({ length: GRID_COLS }, () => Array.from({ length: GRID_ROWS }, () => value));\n }\n\n private fillVisibilityGrid(grid: CellVisibility[][], value: CellVisibility): void {\n for (let col = 0; col < GRID_COLS; col += 1) {\n for (let row = 0; row < GRID_ROWS; row += 1) {\n grid[col][row] = value;\n }\n }\n }\n\n private startLoop(): void {\n if (this.rafLoop.isRunning()) return;\n this.rafLoop.start((time: number): boolean => {\n this.advanceSimulation(time);\n this.render(time);\n return this.shouldKeepAnimating();\n });\n }\n\n private shouldKeepAnimating(): boolean {\n if (this.runtime.isSpinning) return true;\n if (this.initialHighlightRequestedAt > 0) return true;\n return this.runtime.winEffectsEnvelope > 0.001;\n }\n}\n"],"names":["DEFAULT_SPRITE_ELEMENTS_COUNT","GRID_COLS","GRID_ROWS","ROW_COMPACT_OFFSETS_RATIO","MOTION_PROFILES","DEFAULT_MOTION_PROFILE_NAME","getMotionProfile","name","FLOW_WIN_FLASH_MS","FLOW_WIN_PULSE_PERIOD_MS","FLOW_WIN_PULSE_AMPLITUDE","PARTICLE_FLY_DURATION_MS","FLOW_WIN_PARTICLES_PER_CELL_HIGH","DEFAULT_PARTICLE_COLOR_RGB","INITIAL_WIN_FLASH_DELAY_MS","clamp","value","min","max","randomInt","maxExclusive","normalizeSegment","segment","elementsCount","normalizeRgbChannel","createRandomGrid","spriteElementsCount","grid","col","column","row","findMostFrequentCells","counts","symbol","selectedSymbol","maxCount","count","cells","createZeroOffsets","fillOffsets","offsets","RafLoop","step","now","smootherStep","t","x","sampleSegment","nowMs","eased","buildSequentialRowStartDelays","fromRowOffsets","durationMs","gapMs","rowBaseSpacingRatio","delays","nextDelay","baseSpacing","buildOutroMotionPlan","params","outgoingDistance","outgoingOffsetsForOrder","updateOutroOffsets","allOutgoingDone","allIncomingDone","columnElapsed","rowElapsed","outgoing","incomingElapsed","incoming","normalizeParticleColor","color","normalizeParticleColorMode","mode","rowsToStopGrid","rows","normalizeStopGrid","stopGrid","next","normalizeInitialSegments","initialSegments","cloneSpinState","state","SpinQueueController","initialQueue","entry","createRuntimeState","beginSpin","finishSpin","hasPendingInQueue","startWinFlash","startedAt","destroyState","VERTEX_SHADER_SOURCE","FRAGMENT_SHADER_SOURCE","WebGLRenderer","gl","vertexShader","fragmentShader","quadBuffer","texture","posLocation","width","height","symbolId","y","alpha","srcTop","srcBottom","inset","u0","u1","v0","v1","rgba","centerX","centerY","radius","diameter","type","source","shader","message","program","CascadingReel","config","warmNow","activeSpinState","shouldHighlightCurrentSpin","scriptedSource","stopGridSource","nextGrid","stepMs","skipCallback","finishedSpinState","callback","hasPending","motionPlan","outgoingOffsets","incomingOffsets","incomingAlpha","incomingVisibility","maxRowDelay","maxColumnDelay","warmupMoments","elapsedMs","winEffectsTarget","dt","kWin","skipWinningCells","winPhase","winFlashStartedAt","winEffectsEnvelope","prevOffsets","currOffsets","prevOpacity","currOpacity","visibility","sampler","renderer","offsetY","spriteAlpha","envelope","elapsed","pulseProgress","pulse","borderInset","borderThickness","particleModeActive","cell","baseX","baseY","borderColor","scaledW","scaledH","offsetX","innerX","innerY","innerW","innerH","maxDistance","baseRadius","seedsList","solidColor","i","s","startTime","age","particleT","direction","distance","px","py","twinkle","rgb","hueDeg","saturation","lightness","h","l","c","hp","r","g","b","m","seedA","hueRaw","bucket","hue","a","d","key","cached","generated","bounds","dpr","side","squareSize","image","frameDt","steps","target","time"],"mappings":"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"}