1- import React , { useEffect , useRef } from 'react'
1+ import React , { useEffect , useRef , useState } from 'react'
22import type { FrameInfo , KeyInfo , MonochromeBitmap } from '@lib/bitmap'
33import { useAppDispatch , useAppSelector , type RootState } from '../store'
44import { togglePause , showMap , hideMap , pause , unpause } from '../gameSlice'
@@ -11,6 +11,7 @@ import { getDebug } from '../debug'
1111import type { SpriteService } from '@/core/sprites'
1212import { SCENTER } from '@/core/figs'
1313import { useStore } from 'react-redux'
14+ import { TouchControlsOverlay } from '../mobile/TouchControlsOverlay'
1415
1516type GameRendererProps = {
1617 renderer : ( frame : FrameInfo , controls : ControlMatrix ) => MonochromeBitmap
@@ -47,9 +48,27 @@ const GameRenderer: React.FC<GameRendererProps> = ({
4748 const paused = useAppSelector ( state => state . game . paused )
4849 const showMapState = useAppSelector ( state => state . game . showMap )
4950 const bindings = useAppSelector ( state => state . controls . bindings )
51+ const touchControlsEnabled = useAppSelector (
52+ state => state . app . touchControlsEnabled
53+ )
5054 const store = useStore ( )
5155 const dispatch = useAppDispatch ( )
5256
57+ // Track touch controls state
58+ const [ touchControls , setTouchControls ] = useState < ControlMatrix > ( {
59+ thrust : false ,
60+ left : false ,
61+ right : false ,
62+ fire : false ,
63+ shield : false ,
64+ selfDestruct : false ,
65+ pause : false ,
66+ quit : false ,
67+ nextLevel : false ,
68+ extraLife : false ,
69+ map : false
70+ } )
71+
5372 useEffect ( ( ) => {
5473 const canvas = canvasRef . current
5574 if ( ! canvas ) return
@@ -113,6 +132,25 @@ const GameRenderer: React.FC<GameRendererProps> = ({
113132 keysPressed : keysPressed ,
114133 keysReleased : keysReleased
115134 }
135+ // Get keyboard controls
136+ const keyboardControls = getControls ( keyInfo , bindings )
137+
138+ // Merge keyboard and touch controls (OR logic for each control)
139+ const mergedControls : ControlMatrix = {
140+ thrust : keyboardControls . thrust || touchControls . thrust ,
141+ left : keyboardControls . left || touchControls . left ,
142+ right : keyboardControls . right || touchControls . right ,
143+ fire : keyboardControls . fire || touchControls . fire ,
144+ shield : keyboardControls . shield || touchControls . shield ,
145+ selfDestruct :
146+ keyboardControls . selfDestruct || touchControls . selfDestruct ,
147+ pause : keyboardControls . pause || touchControls . pause ,
148+ quit : keyboardControls . quit || touchControls . quit ,
149+ nextLevel : keyboardControls . nextLevel || touchControls . nextLevel ,
150+ extraLife : keyboardControls . extraLife || touchControls . extraLife ,
151+ map : keyboardControls . map || touchControls . map
152+ }
153+
116154 // If map is showing, use blank controls (all false) to prevent game input
117155 const controls = showMapState
118156 ? {
@@ -127,9 +165,9 @@ const GameRenderer: React.FC<GameRendererProps> = ({
127165 nextLevel : false ,
128166 extraLife : false ,
129167 // need to still detect the map key
130- map : getControls ( keyInfo , bindings ) . map
168+ map : mergedControls . map
131169 }
132- : getControls ( keyInfo , bindings )
170+ : mergedControls
133171
134172 if ( controls . map ) {
135173 if ( showMapState ) {
@@ -298,25 +336,34 @@ const GameRenderer: React.FC<GameRendererProps> = ({
298336 dispatch ,
299337 collisionService ,
300338 spriteService ,
301- store
339+ store ,
340+ touchControls
302341 ] )
303342
304343 return (
305- < div style = { { position : 'relative' , display : 'inline-block' } } >
306- < canvas
307- ref = { canvasRef }
308- width = { width * scale }
309- height = { height * scale }
310- style = { {
311- imageRendering : 'pixelated' ,
312- // @ts -ignore - vendor prefixes
313- WebkitImageRendering : 'pixelated' ,
314- MozImageRendering : 'crisp-edges' ,
315- display : 'block'
316- } }
317- />
318- { showMapState && < Map scale = { scale } /> }
319- </ div >
344+ < >
345+ < div style = { { position : 'relative' , display : 'inline-block' } } >
346+ < canvas
347+ ref = { canvasRef }
348+ width = { width * scale }
349+ height = { height * scale }
350+ style = { {
351+ imageRendering : 'pixelated' ,
352+ // @ts -ignore - vendor prefixes
353+ WebkitImageRendering : 'pixelated' ,
354+ MozImageRendering : 'crisp-edges' ,
355+ display : 'block'
356+ } }
357+ />
358+ { showMapState && < Map scale = { scale } /> }
359+ </ div >
360+ { touchControlsEnabled && (
361+ < TouchControlsOverlay
362+ scale = { scale }
363+ onControlsChange = { setTouchControls }
364+ />
365+ ) }
366+ </ >
320367 )
321368}
322369
0 commit comments