33< head >
44< meta charset ="utf-8 ">
55< meta name ="viewport " content ="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no ">
6- < title > No Screen! – v7.5</ title >
6+ < title > No Screen! – v7.5.1 </ title >
77< style >
88 * {box-sizing : border-box;-webkit-user-select : none;user-select : none;-webkit-touch-callout : none}
99 html , body {margin : 0 ;height : 100% ;background : # 72d6ff ;font-family : 'Comic Sans MS' , 'Trebuchet MS' , system-ui, sans-serif;overflow : hidden;overscroll-behavior : none}
2121</ head >
2222< body >
2323 < div id ="ui ">
24- < div id ="title "> NO SCREEN! – v7.5</ div >
24+ < div id ="title "> NO SCREEN! – v7.5.1 </ div >
2525 < div id ="stats ">
2626 < span > ⏱️ < span id ="time "> 120</ span > s</ span >
2727 < span > ✅ < span id ="good "> 0</ span > </ span >
3636 < div id ="taplayer " aria-hidden ="true "> </ div >
3737
3838< script >
39- /* ===== No Screen! – v7.5 (global input capture) ===== */
39+ /* ===== No Screen! – v7.5.1 ===== */
4040const canvas = document . getElementById ( 'game' ) ;
4141const taplayer = document . getElementById ( 'taplayer' ) ;
4242const ctx = canvas . getContext ( '2d' , { alpha :false } ) ;
8989function hills ( ) { const bands = [ '#c7f36f' , '#9fe24f' , '#67c91a' , '#2ea01a' ] ;
9090 for ( let i = 0 ; i < bands . length ; i ++ ) { ctx . fillStyle = bands [ i ] ; const y = H * 0.6 + i * 25 ; ctx . beginPath ( ) ; ctx . moveTo ( 0 , y ) ; for ( let x = 0 ; x <= W ; x += 40 ) { ctx . quadraticCurveTo ( x + 20 , y - 20 - ( i * 3 ) , x + 40 , y ) ; } ctx . lineTo ( W , H ) ; ctx . lineTo ( 0 , H ) ; ctx . closePath ( ) ; ctx . fill ( ) ; } }
9191function rr ( x , y , w , h , r , f , s ) { ctx . beginPath ( ) ; ctx . moveTo ( x + r , y ) ; ctx . arcTo ( x + w , y , x + w , y + h , r ) ; ctx . arcTo ( x + w , y + h , x , y + h , r ) ; ctx . arcTo ( x , y + h , x , y , r ) ; ctx . arcTo ( x , y , x + w , y , r ) ; if ( f ) ctx . fill ( ) ; if ( s ) ctx . stroke ( ) ; }
92- function drawSplash ( ) { ctx . fillStyle = '#72d6ff' ; ctx . fillRect ( 0 , 0 , W , H ) ; hills ( ) ; const bw = Math . min ( W * 0.8 , 800 ) , bh = Math . min ( H * 0.5 , 380 ) , x = ( W - bw ) / 2 , y = ( H - bh ) / 2 ; ctx . save ( ) ; ctx . translate ( x + 5 , y + 5 ) ; ctx . fillStyle = '#000' ; rr ( 0 , 0 , bw , bh , 20 , true ) ; ctx . translate ( - 5 , - 5 ) ; ctx . fillStyle = '#ff3e3e' ; rr ( 0 , 0 , bw , bh , 20 , true ) ; ctx . lineWidth = 10 ; ctx . strokeStyle = '#000' ; rr ( 0 , 0 , bw , bh , 20 , false , true ) ; ctx . fillStyle = '#fff' ; ctx . textAlign = 'center' ; ctx . font = `${ Math . floor ( bh * 0.14 ) } px Impact, Arial Black` ; ctx . fillText ( "NO SCREEN! – v7.5" , bw / 2 , bh * 0.33 ) ; ctx . font = `${ Math . floor ( bh * 0.07 ) } px Arial Black` ; ctx . fillText ( "Get to 20 points to win" , bw / 2 , bh * 0.55 ) ; ctx . restore ( ) ; }
92+ function drawSplash ( ) { ctx . fillStyle = '#72d6ff' ; ctx . fillRect ( 0 , 0 , W , H ) ; hills ( ) ; const bw = Math . min ( W * 0.8 , 800 ) , bh = Math . min ( H * 0.5 , 380 ) , x = ( W - bw ) / 2 , y = ( H - bh ) / 2 ; ctx . save ( ) ; ctx . translate ( x + 5 , y + 5 ) ; ctx . fillStyle = '#000' ; rr ( 0 , 0 , bw , bh , 20 , true ) ; ctx . translate ( - 5 , - 5 ) ; ctx . fillStyle = '#ff3e3e' ; rr ( 0 , 0 , bw , bh , 20 , true ) ; ctx . lineWidth = 10 ; ctx . strokeStyle = '#000' ; rr ( 0 , 0 , bw , bh , 20 , false , true ) ; ctx . fillStyle = '#fff' ; ctx . textAlign = 'center' ; ctx . font = `${ Math . floor ( bh * 0.14 ) } px Impact, Arial Black` ; ctx . fillText ( "NO SCREEN! – v7.5.1 " , bw / 2 , bh * 0.33 ) ; ctx . font = `${ Math . floor ( bh * 0.07 ) } px Arial Black` ; ctx . fillText ( "Get to 20 points to win" , bw / 2 , bh * 0.55 ) ; ctx . restore ( ) ; }
9393
9494/* ---- game state & difficulty ---- */
9595let running = false , gameOver = false , tLeft = 120 , good = 0 , bad = 0 ;
143143function drawRipples ( dt ) { ripples = ripples . filter ( r => r . age < 220 ) ; for ( const r of ripples ) { r . age += dt ; const t = r . age / 220 ; ctx . globalAlpha = 1 - t ; ctx . lineWidth = 6 ; ctx . strokeStyle = '#ffffff' ; ctx . beginPath ( ) ; ctx . arc ( r . x , r . y , 22 + 95 * t , 0 , Math . PI * 2 ) ; ctx . stroke ( ) ; ctx . globalAlpha = 1 ; } }
144144
145145/* ---- HUD + progress ---- */
146- function hud ( ) { const pad = Math . max ( 12 , Math . min ( W , H ) * 0.02 ) , bw = W - pad * 2 , bh = Math . max ( 48 , Math . min ( W , H ) * 0.08 ) ; ctx . save ( ) ; ctx . translate ( pad , pad ) ; ctx . fillStyle = 'rgba(0,0,0,0.35)' ; ctx . fillRect ( 0 , 0 , bw , bh ) ; ctx . lineWidth = 4 ; ctx . strokeStyle = '#000' ; ctx . strokeRect ( 0 , 0 , bw , bh ) ; ctx . fillStyle = '#fff' ; ctx . textBaseline = 'middle' ; ctx . font = Math . floor ( bh * 0.55 ) + 'px Arial Black, Impact' ; ctx . fillText ( "NO SCREEN! – v7.5" , pad * 0.3 , bh / 2 ) ; const stats = `⏱ ${ tLeft } s ✅ ${ good } ❌ ${ bad } ` ; const w = ctx . measureText ( stats ) . width ; ctx . fillText ( stats , bw - w - pad * 0.3 , bh / 2 ) ; const total = Math . max ( 0 , good - bad ) , max = 20 , ratio = Math . min ( 1 , total / max ) ; const y = bh + pad * 0.5 , h = 28 ; ctx . fillStyle = '#555' ; ctx . fillRect ( 0 , y , bw , h ) ; ctx . fillStyle = '#22c55e' ; ctx . fillRect ( 0 , y , bw * ratio , h ) ; ctx . lineWidth = 3 ; ctx . strokeStyle = '#000' ; ctx . strokeRect ( 0 , y , bw , h ) ; ctx . fillStyle = '#fff' ; ctx . font = 'bold 20px Arial Black, Impact' ; ctx . textAlign = 'left' ; ctx . fillText ( `SCORE: ${ good - bad } ` , 0 , y + h + 22 ) ; ctx . restore ( ) ; }
146+ function hud ( ) { const pad = Math . max ( 12 , Math . min ( W , H ) * 0.02 ) , bw = W - pad * 2 , bh = Math . max ( 48 , Math . min ( W , H ) * 0.08 ) ; ctx . save ( ) ; ctx . translate ( pad , pad ) ; ctx . fillStyle = 'rgba(0,0,0,0.35)' ; ctx . fillRect ( 0 , 0 , bw , bh ) ; ctx . lineWidth = 4 ; ctx . strokeStyle = '#000' ; ctx . strokeRect ( 0 , 0 , bw , bh ) ; ctx . fillStyle = '#fff' ; ctx . textBaseline = 'middle' ; ctx . font = Math . floor ( bh * 0.55 ) + 'px Arial Black, Impact' ; ctx . fillText ( "NO SCREEN! – v7.5.1 " , pad * 0.3 , bh / 2 ) ; const stats = `⏱ ${ tLeft } s ✅ ${ good } ❌ ${ bad } ` ; const w = ctx . measureText ( stats ) . width ; ctx . fillText ( stats , bw - w - pad * 0.3 , bh / 2 ) ; const total = Math . max ( 0 , good - bad ) , max = 20 , ratio = Math . min ( 1 , total / max ) ; const y = bh + pad * 0.5 , h = 28 ; ctx . fillStyle = '#555' ; ctx . fillRect ( 0 , y , bw , h ) ; ctx . fillStyle = '#22c55e' ; ctx . fillRect ( 0 , y , bw * ratio , h ) ; ctx . lineWidth = 3 ; ctx . strokeStyle = '#000' ; ctx . strokeRect ( 0 , y , bw , h ) ; ctx . fillStyle = '#fff' ; ctx . font = 'bold 20px Arial Black, Impact' ; ctx . textAlign = 'left' ; ctx . fillText ( `SCORE: ${ good - bad } ` , 0 , y + h + 22 ) ; ctx . restore ( ) ; }
147147
148148/* ---- loop ---- */
149149function loop ( now ) {
158158 if ( ( good - bad ) >= 20 ) { endOverlay ( ) ; return ; }
159159 requestAnimationFrame ( loop ) ;
160160}
161- function endOverlay ( ) { running = false ; gameOver = true ; const bw = Math . min ( W * 0.8 , 820 ) , bh = Math . min ( H * 0.45 , 360 ) , x = ( W - bw ) / 2 , y = ( H - bh ) / 2 ; ctx . save ( ) ; ctx . globalAlpha = .85 ; ctx . fillStyle = '#000' ; ctx . fillRect ( 0 , 0 , W , H ) ; ctx . globalAlpha = 1 ; ctx . translate ( x + 5 , y + 5 ) ; ctx . fillStyle = '#000' ; rr ( 0 , 0 , bw , bh , 18 , true ) ; ctx . translate ( - 5 , - 5 ) ; ctx . fillStyle = '#ffce3a' ; rr ( 0 , 0 , bw , bh , 18 , true ) ; ctx . lineWidth = 8 ; ctx . strokeStyle = '#000' ; rr ( 0 , 0 , bw , bh , 18 , false , true ) ; ctx . fillStyle = '#000' ; ctx . textAlign = 'center' ; ctx . font = `${ Math . floor ( bh * 0.18 ) } px Impact` ; ctx . fillText ( 'GAME OVER' , bw / 2 , bh * 0.35 ) ; ctx . font = `${ Math . floor ( bh * 0.12 ) } px Arial Black` ; ctx . fillText ( `Score: ${ good - bad } ` , bw / 2 , bh * 0.60 ) ; ctx . font = `${ Math . floor ( bh * 0.10 ) } px Arial Black` ; ctx . fillText ( 'Tap “Replay”' , bw / 2 , bh * 0.80 ) ; ctx . restore ( ) ; ui . replayBtn . disabled = false ; }
161+ function endOverlay ( ) { disableGlobalInput ( ) ; running = false ; gameOver = true ; const bw = Math . min ( W * 0.8 , 820 ) , bh = Math . min ( H * 0.45 , 360 ) , x = ( W - bw ) / 2 , y = ( H - bh ) / 2 ; ctx . save ( ) ; ctx . globalAlpha = .85 ; ctx . fillStyle = '#000' ; ctx . fillRect ( 0 , 0 , W , H ) ; ctx . globalAlpha = 1 ; ctx . translate ( x + 5 , y + 5 ) ; ctx . fillStyle = '#000' ; rr ( 0 , 0 , bw , bh , 18 , true ) ; ctx . translate ( - 5 , - 5 ) ; ctx . fillStyle = '#ffce3a' ; rr ( 0 , 0 , bw , bh , 18 , true ) ; ctx . lineWidth = 8 ; ctx . strokeStyle = '#000' ; rr ( 0 , 0 , bw , bh , 18 , false , true ) ; ctx . fillStyle = '#000' ; ctx . textAlign = 'center' ; ctx . font = `${ Math . floor ( bh * 0.18 ) } px Impact` ; ctx . fillText ( 'GAME OVER' , bw / 2 , bh * 0.35 ) ; ctx . font = `${ Math . floor ( bh * 0.12 ) } px Arial Black` ; ctx . fillText ( `Score: ${ good - bad } ` , bw / 2 , bh * 0.60 ) ; ctx . font = `${ Math . floor ( bh * 0.10 ) } px Arial Black` ; ctx . fillText ( 'Tap “Replay”' , bw / 2 , bh * 0.80 ) ; ctx . restore ( ) ; ui . replayBtn . disabled = false ; }
162162
163163/* ---- start/timers ---- */
164- function start ( ) { document . body . classList . add ( 'playing' ) ; ensureAudio ( ) ; tLeft = 120 ; good = 0 ; bad = 0 ; gameOver = false ; ui . time . textContent = tLeft ; ui . good . textContent = good ; ui . bad . textContent = bad ; entities . length = 0 ; puffs . length = 0 ; ripples . length = 0 ; lastSpawnAt = 0 ; lastFrame = 0 ; lastUnauthorizedSeen = performance . now ( ) ; running = true ; ui . playBtn . disabled = true ; ui . replayBtn . disabled = false ; ctx . clearRect ( 0 , 0 , W , H ) ; countdown ( ) ; requestAnimationFrame ( loop ) ; }
164+ function start ( ) { document . body . classList . add ( 'playing' ) ; ensureAudio ( ) ; tLeft = 120 ; good = 0 ; bad = 0 ; gameOver = false ; ui . time . textContent = tLeft ; ui . good . textContent = good ; ui . bad . textContent = bad ; entities . length = 0 ; puffs . length = 0 ; ripples . length = 0 ; lastSpawnAt = 0 ; lastFrame = 0 ; lastUnauthorizedSeen = performance . now ( ) ; running = true ; ui . playBtn . disabled = true ; ui . replayBtn . disabled = false ; ctx . clearRect ( 0 , 0 , W , H ) ; enableGlobalInput ( ) ; countdown ( ) ; requestAnimationFrame ( loop ) ; }
165165function restart ( ) { running = false ; drawSplash ( ) ; start ( ) ; }
166166function countdown ( ) { if ( ! running || gameOver ) return ; if ( tLeft <= 0 ) { endOverlay ( ) ; return ; } setTimeout ( ( ) => { tLeft -- ; ui . time . textContent = tLeft ; countdown ( ) ; } , 1000 ) ; }
167167
168- /* ==== GLOBAL INPUT CAPTURE ==== */
168+ /* ==== INPUT ======================================================= ==== */
169169function onRawTap ( e ) {
170- // bump debug
170+ // Ignore taps on UI when not playing
171+ if ( ! running ) {
172+ return ; // allow Play button to work
173+ }
171174 tapCount ++ ; dbg . textContent = 'TAPS:' + tapCount ;
172175
173- // best-effort coords
174176 let cx , cy ;
175177 if ( e . changedTouches && e . changedTouches [ 0 ] ) { cx = e . changedTouches [ 0 ] . clientX ; cy = e . changedTouches [ 0 ] . clientY ; }
176178 else { cx = e . clientX ; cy = e . clientY ; }
177179 if ( typeof cx !== 'number' || typeof cy !== 'number' ) return ;
178180
179- // compute canvas-space
180181 const rect = canvas . getBoundingClientRect ( ) ;
181182 const dpr = Math . min ( 2 , window . devicePixelRatio || 1 ) ;
182183 const x = ( cx - rect . left ) * dpr , y = ( cy - rect . top ) * dpr ;
183- ripples . push ( { x, y, age :0 } ) ; // show feedback always
184-
185- if ( ! running || gameOver ) { if ( e . cancelable ) e . preventDefault ( ) ; return ; }
184+ ripples . push ( { x, y, age :0 } ) ;
186185
187186 const now = performance . now ( ) ;
188187 for ( let i = entities . length - 1 ; i >= 0 ; i -- ) {
196195 const ok = ( en . type === 'kid' && en . status === 'unauthorized' ) ;
197196 if ( ok ) { good ++ ; ui . good . textContent = good ; sGood ( ) ; addPuff ( x , y , '#22c55e' ) ; }
198197 else { bad ++ ; ui . bad . textContent = bad ; sBad ( ) ; addPuff ( x , y , '#ff3e3e' ) ; }
199- en . inactive = true ;
200- break ;
198+ en . inactive = true ; break ;
201199 }
202200 }
203201 if ( e . cancelable ) e . preventDefault ( ) ;
204202}
205203
206- // overlay listeners
207- [ 'pointerdown' , 'mousedown' , 'click' ] . forEach ( t => taplayer . addEventListener ( t , onRawTap , { passive :false } ) ) ;
208- taplayer . addEventListener ( 'touchstart' , onRawTap , { passive :false } ) ;
209- // window/document capture (belt & suspenders)
210- [ 'pointerdown' , 'mousedown' , 'click' , 'touchstart' ] . forEach ( t => {
211- window . addEventListener ( t , onRawTap , { capture :true , passive :false } ) ;
212- document . addEventListener ( t , onRawTap , { capture :true , passive :false } ) ;
213- } ) ;
204+ let attached = false ;
205+ function enableGlobalInput ( ) {
206+ if ( attached ) return ; attached = true ;
207+ // overlay
208+ [ 'pointerdown' , 'mousedown' , 'click' ] . forEach ( t => taplayer . addEventListener ( t , onRawTap , { passive :false } ) ) ;
209+ taplayer . addEventListener ( 'touchstart' , onRawTap , { passive :false } ) ;
210+ // window/document capture
211+ [ 'pointerdown' , 'mousedown' , 'click' , 'touchstart' ] . forEach ( t => {
212+ window . addEventListener ( t , onRawTap , { capture :true , passive :false } ) ;
213+ document . addEventListener ( t , onRawTap , { capture :true , passive :false } ) ;
214+ } ) ;
215+ }
216+ function disableGlobalInput ( ) {
217+ if ( ! attached ) return ; attached = false ;
218+ [ 'pointerdown' , 'mousedown' , 'click' ] . forEach ( t => taplayer . removeEventListener ( t , onRawTap , { passive :false } ) ) ;
219+ taplayer . removeEventListener ( 'touchstart' , onRawTap , { passive :false } ) ;
220+ [ 'pointerdown' , 'mousedown' , 'click' , 'touchstart' ] . forEach ( t => {
221+ window . removeEventListener ( t , onRawTap , { capture :true , passive :false } ) ;
222+ document . removeEventListener ( t , onRawTap , { capture :true , passive :false } ) ;
223+ } ) ;
224+ }
214225
215- // buttons
226+ /* ---- buttons ---- */
216227ui . playBtn . addEventListener ( 'click' , start ) ;
217228ui . replayBtn . addEventListener ( 'click' , restart ) ;
218229
219- // first frame
230+ /* ---- first frame ---- */
220231drawSplash ( ) ;
232+
233+ /* ---- small render helpers for ripples/HUD ---- */
234+ let lastFrame = 0 ;
235+ function drawSplashExtra ( ) { /* kept minimal on purpose */ }
221236</ script >
222237</ body >
223238</ html >
0 commit comments