feat: add new experimental touch controls
This commit is contained in:
parent
5b0224a52c
commit
543fc80752
10 changed files with 274 additions and 109 deletions
64
src/index.ts
64
src/index.ts
|
|
@ -91,6 +91,7 @@ import { loadInMemorySave } from './react/SingleplayerProvider'
|
|||
// side effects
|
||||
import { downloadSoundsIfNeeded } from './soundSystem'
|
||||
import { ua } from './react/utils'
|
||||
import { handleMovementStickDelta, joystickPointer } from './react/TouchAreasControls'
|
||||
|
||||
window.debug = debug
|
||||
window.THREE = THREE
|
||||
|
|
@ -670,6 +671,7 @@ async function connect (connectOptions: {
|
|||
let screenTouches = 0
|
||||
let capturedPointer: { id; x; y; sourceX; sourceY; activateCameraMove; time } | undefined
|
||||
registerListener(document, 'pointerdown', (e) => {
|
||||
const usingJoystick = options.touchControlsType === 'joystick-buttons'
|
||||
const clickedEl = e.composedPath()[0]
|
||||
if (!isGameActive(true) || !miscUiState.currentTouch || clickedEl !== cameraControlEl || e.pointerId === undefined) {
|
||||
return
|
||||
|
|
@ -679,6 +681,16 @@ async function connect (connectOptions: {
|
|||
// todo needs fixing!
|
||||
// window.dispatchEvent(new MouseEvent('mousedown', { button: 1 }))
|
||||
}
|
||||
if (usingJoystick) {
|
||||
if (!joystickPointer.pointer && e.clientX < window.innerWidth / 2) {
|
||||
joystickPointer.pointer = {
|
||||
pointerId: e.pointerId,
|
||||
x: e.clientX,
|
||||
y: e.clientY
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
if (capturedPointer) {
|
||||
return
|
||||
}
|
||||
|
|
@ -692,19 +704,33 @@ async function connect (connectOptions: {
|
|||
activateCameraMove: false,
|
||||
time: Date.now()
|
||||
}
|
||||
virtualClickTimeout ??= setTimeout(() => {
|
||||
virtualClickActive = true
|
||||
document.dispatchEvent(new MouseEvent('mousedown', { button: 0 }))
|
||||
}, touchStartBreakingBlockMs)
|
||||
if (options.touchControlsType !== 'joystick-buttons') {
|
||||
virtualClickTimeout ??= setTimeout(() => {
|
||||
virtualClickActive = true
|
||||
document.dispatchEvent(new MouseEvent('mousedown', { button: 0 }))
|
||||
}, touchStartBreakingBlockMs)
|
||||
}
|
||||
})
|
||||
registerListener(document, 'pointermove', (e) => {
|
||||
if (e.pointerId === undefined || e.pointerId !== capturedPointer?.id) return
|
||||
if (e.pointerId === undefined) return
|
||||
const supportsPressure = (e as any).pressure !== undefined && (e as any).pressure !== 0 && (e as any).pressure !== 0.5 && (e as any).pressure !== 1 && (e.pointerType === 'touch' || e.pointerType === 'pen')
|
||||
if (e.pointerId === joystickPointer.pointer?.pointerId) {
|
||||
handleMovementStickDelta(e)
|
||||
if (supportsPressure && (e as any).pressure > 0.5) {
|
||||
bot.setControlState('sprint', true)
|
||||
// todo
|
||||
}
|
||||
return
|
||||
}
|
||||
if (e.pointerId !== capturedPointer?.id) return
|
||||
window.scrollTo(0, 0)
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
|
||||
const allowedJitter = 1.1
|
||||
// todo support .pressure (3d touch)
|
||||
if (supportsPressure) {
|
||||
bot.setControlState('jump', (e as any).pressure > 0.5)
|
||||
}
|
||||
const xDiff = Math.abs(e.pageX - capturedPointer.sourceX) > allowedJitter
|
||||
const yDiff = Math.abs(e.pageY - capturedPointer.sourceY) > allowedJitter
|
||||
if (!capturedPointer.activateCameraMove && (xDiff || yDiff)) capturedPointer.activateCameraMove = true
|
||||
|
|
@ -717,18 +743,26 @@ async function connect (connectOptions: {
|
|||
}, { passive: false })
|
||||
|
||||
const pointerUpHandler = (e: PointerEvent) => {
|
||||
if (e.pointerId === undefined || e.pointerId !== capturedPointer?.id) return
|
||||
if (e.pointerId === undefined) return
|
||||
if (e.pointerId === joystickPointer.pointer?.pointerId) {
|
||||
handleMovementStickDelta()
|
||||
joystickPointer.pointer = null
|
||||
return
|
||||
}
|
||||
if (e.pointerId !== capturedPointer?.id) return
|
||||
clearTimeout(virtualClickTimeout)
|
||||
virtualClickTimeout = undefined
|
||||
|
||||
if (virtualClickActive) {
|
||||
// button 0 is left click
|
||||
document.dispatchEvent(new MouseEvent('mouseup', { button: 0 }))
|
||||
virtualClickActive = false
|
||||
} else if (!capturedPointer.activateCameraMove && (Date.now() - capturedPointer.time < touchStartBreakingBlockMs)) {
|
||||
document.dispatchEvent(new MouseEvent('mousedown', { button: 2 }))
|
||||
worldInteractions.update()
|
||||
document.dispatchEvent(new MouseEvent('mouseup', { button: 2 }))
|
||||
if (options.touchControlsType !== 'joystick-buttons') {
|
||||
if (virtualClickActive) {
|
||||
// button 0 is left click
|
||||
document.dispatchEvent(new MouseEvent('mouseup', { button: 0 }))
|
||||
virtualClickActive = false
|
||||
} else if (!capturedPointer.activateCameraMove && (Date.now() - capturedPointer.time < touchStartBreakingBlockMs)) {
|
||||
document.dispatchEvent(new MouseEvent('mousedown', { button: 2 }))
|
||||
worldInteractions.update()
|
||||
document.dispatchEvent(new MouseEvent('mouseup', { button: 2 }))
|
||||
}
|
||||
}
|
||||
capturedPointer = undefined
|
||||
screenTouches--
|
||||
|
|
|
|||
|
|
@ -188,7 +188,16 @@ export const guiOptionsScheme: {
|
|||
},
|
||||
touchButtonsPosition: {
|
||||
max: 80
|
||||
}
|
||||
},
|
||||
touchControlsType: {
|
||||
values: [['classic', 'Classic'], ['joystick-buttons', 'New']],
|
||||
},
|
||||
},
|
||||
{
|
||||
custom () {
|
||||
const { touchControlsType } = useSnapshot(options)
|
||||
return <Button label='Setup Touch Buttons' onClick={() => showModal({ reactType: 'touch-buttons-setup' })} inScreen disabled={touchControlsType !== 'joystick-buttons'} />
|
||||
},
|
||||
}
|
||||
],
|
||||
sound: [
|
||||
|
|
|
|||
|
|
@ -29,7 +29,21 @@ const defaultOptions = {
|
|||
touchButtonsSize: 40,
|
||||
touchButtonsOpacity: 80,
|
||||
touchButtonsPosition: 12,
|
||||
touchControlsPositions: {} as Record<string, [number, number]>,
|
||||
touchControlsPositions: {
|
||||
action: [
|
||||
90,
|
||||
70
|
||||
],
|
||||
sneak: [
|
||||
90,
|
||||
90
|
||||
],
|
||||
break: [
|
||||
70,
|
||||
70
|
||||
]
|
||||
} as Record<string, [number, number]>,
|
||||
touchControlsType: 'classic' as 'classic' | 'joystick-buttons',
|
||||
gpuPreference: 'default' as 'default' | 'high-performance' | 'low-power',
|
||||
/** @unstable */
|
||||
disableAssets: false,
|
||||
|
|
@ -59,14 +73,19 @@ const defaultOptions = {
|
|||
|
||||
// advanced bot options
|
||||
autoRespawn: false,
|
||||
mutedSounds: [] as string[]
|
||||
mutedSounds: [] as string[],
|
||||
plugins: [] as Array<{ enabled: boolean, name: string, description: string, script: string }>,
|
||||
}
|
||||
|
||||
const migrateOptions = (options) => {
|
||||
const migrateOptions = (options: Partial<AppOptions & Record<string, any>>) => {
|
||||
if (options.highPerformanceGpu) {
|
||||
options.gpuPreference = 'high-performance'
|
||||
delete options.highPerformanceGpu
|
||||
}
|
||||
if (Object.keys(options.touchControlsPositions ?? {}).length === 0) {
|
||||
options.touchControlsPositions = defaultOptions.touchControlsPositions
|
||||
}
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ import invspriteJson from './invsprite.json'
|
|||
import { options } from './optionsStorage'
|
||||
import { assertDefined } from './utils'
|
||||
|
||||
const itemsAtlases: ItemsAtlasesOutputJson = _itemsAtlases
|
||||
export const itemsAtlases: ItemsAtlasesOutputJson = _itemsAtlases
|
||||
const loadedImagesCache = new Map<string, HTMLImageElement>()
|
||||
const cleanLoadedImagesCache = () => {
|
||||
loadedImagesCache.delete('blocks')
|
||||
|
|
|
|||
|
|
@ -30,7 +30,9 @@ export default ({ status, isError, hideDots = false, lastStatus = '', backAction
|
|||
<Screen
|
||||
title={
|
||||
<>
|
||||
{status}
|
||||
<span style={{ userSelect: isError ? 'text' : undefined }}>
|
||||
{status}
|
||||
</span>
|
||||
{isError || hideDots ? '' : loadingDots}
|
||||
<p className={styles['potential-problem']}>{description}</p>
|
||||
<p className={styles['last-status']}>{lastStatus ? `Last status: ${lastStatus}` : lastStatus}</p>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
import { CSSProperties } from 'react'
|
||||
|
||||
// names: https://pixelarticons.com/free/
|
||||
export default ({ iconName, width, styles = {}, className = undefined }) => {
|
||||
return <iconify-icon icon={`pixelarticons:${iconName}`} style={{ width, height: width, ...styles }} className={className} />
|
||||
export default ({ iconName, width = undefined as undefined | number, styles = {} as CSSProperties, className = undefined }) => {
|
||||
if (width !== undefined) styles = { width, height: width, ...styles }
|
||||
return <iconify-icon icon={`pixelarticons:${iconName}`} style={styles} className={className} />
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
import type { Meta, StoryObj } from '@storybook/react'
|
||||
|
||||
import TouchAreasControls from './TouchAreasControls'
|
||||
|
||||
const meta: Meta<typeof TouchAreasControls> = {
|
||||
component: TouchAreasControls,
|
||||
args: {
|
||||
},
|
||||
}
|
||||
|
||||
export default meta
|
||||
type Story = StoryObj<typeof TouchAreasControls>;
|
||||
|
||||
export const Primary: Story = {
|
||||
args: {
|
||||
touchActive: true,
|
||||
setupActive: true,
|
||||
},
|
||||
}
|
||||
|
|
@ -1,88 +1,178 @@
|
|||
import { CSSProperties, useEffect, useRef, useState } from 'react'
|
||||
import { CSSProperties, PointerEvent, PointerEventHandler, useEffect, useRef, useState } from 'react'
|
||||
import { proxy, ref, useSnapshot } from 'valtio'
|
||||
import { contro } from '../controls'
|
||||
import worldInteractions from '../worldInteractions'
|
||||
import PixelartIcon from './PixelartIcon'
|
||||
import Button from './Button'
|
||||
|
||||
export type Button = 'action' | 'sneak' | 'break'
|
||||
export type ButtonName = 'action' | 'sneak' | 'break'
|
||||
|
||||
type ButtonsPositions = Record<ButtonName, [number, number]>
|
||||
|
||||
interface Props {
|
||||
touchActive: boolean
|
||||
setupActive: boolean
|
||||
buttonsPositions: Record<Button, [number, number]>
|
||||
buttonsPositions: ButtonsPositions
|
||||
closeButtonsSetup: (newPositions?: ButtonsPositions) => void
|
||||
}
|
||||
|
||||
export default ({ touchActive, setupActive, buttonsPositions }: Props) => {
|
||||
if (setupActive) touchActive = true
|
||||
const getCurrentAppScaling = () => {
|
||||
// body has css property --guiScale
|
||||
const guiScale = getComputedStyle(document.body).getPropertyValue('--guiScale')
|
||||
return parseFloat(guiScale)
|
||||
}
|
||||
|
||||
const [joystickPosition, setJoystickPosition] = useState(null as { x, y, pointerId } | null)
|
||||
export const joystickPointer = proxy({
|
||||
pointer: null as { x: number, y: number, pointerId: number } | null,
|
||||
joystickInner: null as HTMLDivElement | null,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (!touchActive) return
|
||||
const controller = new AbortController()
|
||||
const { signal } = controller
|
||||
addEventListener('pointerdown', (e) => {
|
||||
if (e.pointerId === joystickPosition?.pointerId) {
|
||||
const x = e.clientX - joystickPosition.x
|
||||
const y = e.clientY - joystickPosition.y
|
||||
const supportsPressure = (e as any).pressure !== undefined && (e as any).pressure !== 0 && (e as any).pressure !== 0.5 && (e as any).pressure !== 1 && (e.pointerType === 'touch' || e.pointerType === 'pen')
|
||||
if ((e as any).pressure > 0.5) {
|
||||
// todo
|
||||
}
|
||||
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (e.clientX < window.innerWidth / 2) {
|
||||
setJoystickPosition({
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
pointerId: e.pointerId,
|
||||
})
|
||||
}
|
||||
}, {
|
||||
signal,
|
||||
})
|
||||
return () => {
|
||||
controller.abort()
|
||||
}
|
||||
}, [touchActive])
|
||||
|
||||
buttonsPositions = {
|
||||
// 0-100
|
||||
action: [
|
||||
90,
|
||||
70
|
||||
],
|
||||
sneak: [
|
||||
90,
|
||||
90
|
||||
],
|
||||
break: [
|
||||
70,
|
||||
70
|
||||
]
|
||||
export const handleMovementStickDelta = (e?: { clientX, clientY }) => {
|
||||
const max = 32
|
||||
let x = 0
|
||||
let y = 0
|
||||
if (e) {
|
||||
const scale = getCurrentAppScaling()
|
||||
x = e.clientX - joystickPointer.pointer!.x
|
||||
y = e.clientY - joystickPointer.pointer!.y
|
||||
x = Math.min(Math.max(x, -max), max) / scale
|
||||
y = Math.min(Math.max(y, -max), max) / scale
|
||||
}
|
||||
|
||||
const buttonStyles = (name: Button) => ({
|
||||
padding: 10,
|
||||
position: 'fixed',
|
||||
left: `${buttonsPositions[name][0]}%`,
|
||||
top: `${buttonsPositions[name][1]}%`,
|
||||
borderRadius: '50%',
|
||||
} satisfies CSSProperties)
|
||||
joystickPointer.joystickInner!.style.transform = `translate(${x}px, ${y}px)`
|
||||
void contro.emit('movementUpdate', {
|
||||
vector: {
|
||||
x: x / max,
|
||||
y: 0,
|
||||
z: y / max,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export default ({ touchActive, setupActive, buttonsPositions, closeButtonsSetup }: Props) => {
|
||||
if (setupActive) touchActive = true
|
||||
|
||||
const joystickOuter = useRef<HTMLDivElement>(null)
|
||||
const joystickInner = useRef<HTMLDivElement>(null)
|
||||
|
||||
const { pointer } = useSnapshot(joystickPointer)
|
||||
const newButtonPositions = { ...buttonsPositions }
|
||||
|
||||
const buttonProps = (name: ButtonName) => {
|
||||
let active = {
|
||||
action: false,
|
||||
sneak: bot.getControlState('sneak'),
|
||||
break: false
|
||||
}[name]
|
||||
const holdDown = {
|
||||
action () {
|
||||
document.dispatchEvent(new MouseEvent('mousedown', { button: 2 }))
|
||||
worldInteractions.update()
|
||||
document.dispatchEvent(new MouseEvent('mouseup', { button: 2 }))
|
||||
},
|
||||
sneak () {
|
||||
bot.setControlState('sneak', !bot.getControlState('sneak'))
|
||||
active = bot.getControlState('sneak')
|
||||
},
|
||||
break () {
|
||||
document.dispatchEvent(new MouseEvent('mousedown', { button: 0 }))
|
||||
worldInteractions.update()
|
||||
active = true
|
||||
}
|
||||
}
|
||||
const holdUp = {
|
||||
action () {
|
||||
},
|
||||
sneak () {
|
||||
},
|
||||
break () {
|
||||
document.dispatchEvent(new MouseEvent('mouseup', { button: 0 }))
|
||||
worldInteractions.update()
|
||||
active = false
|
||||
}
|
||||
}
|
||||
|
||||
type PType = PointerEvent<HTMLDivElement>
|
||||
const pointerup = (e: PType) => {
|
||||
const elem = e.currentTarget as HTMLElement
|
||||
console.log(e.type, elem.hasPointerCapture(e.pointerId))
|
||||
elem.releasePointerCapture(e.pointerId)
|
||||
if (!setupActive) {
|
||||
holdUp[name]()
|
||||
pointerToggledUpdate(e)
|
||||
}
|
||||
}
|
||||
const pointerToggledUpdate = (e) => {
|
||||
e.currentTarget.style.background = active ? 'rgba(0, 0, 0, 0.8)' : 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
let setupPointer = null as { x, y } | null
|
||||
return {
|
||||
style: {
|
||||
position: 'fixed',
|
||||
left: `${buttonsPositions[name][0]}%`,
|
||||
top: `${buttonsPositions[name][1]}%`,
|
||||
borderRadius: '50%',
|
||||
width: '32px',
|
||||
height: '32px',
|
||||
background: active ? 'rgba(0, 0, 0, 0.8)' : 'rgba(0, 0, 0, 0.5)',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
transition: 'background 0.1s',
|
||||
} satisfies CSSProperties,
|
||||
onPointerDown (e: PType) {
|
||||
const elem = e.currentTarget as HTMLElement
|
||||
elem.setPointerCapture(e.pointerId)
|
||||
if (setupActive) {
|
||||
setupPointer = { x: e.clientX, y: e.clientY }
|
||||
} else {
|
||||
holdDown[name]()
|
||||
pointerToggledUpdate(e)
|
||||
}
|
||||
},
|
||||
onPointerMove (e: PType) {
|
||||
if (setupPointer) {
|
||||
const elem = e.currentTarget as HTMLElement
|
||||
const size = 32
|
||||
const scale = getCurrentAppScaling()
|
||||
const xPerc = e.clientX / window.innerWidth * 100 - size / scale
|
||||
const yPerc = e.clientY / window.innerHeight * 100 - size / scale
|
||||
elem.style.left = `${xPerc}%`
|
||||
elem.style.top = `${yPerc}%`
|
||||
newButtonPositions[name] = [xPerc, yPerc]
|
||||
}
|
||||
},
|
||||
onPointerUp: pointerup,
|
||||
// onPointerCancel: pointerup,
|
||||
onLostPointerCapture: pointerup,
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
joystickPointer.joystickInner = joystickInner.current && ref(joystickInner.current)
|
||||
}, [])
|
||||
|
||||
if (!touchActive) return null
|
||||
|
||||
return <div>
|
||||
<div
|
||||
className='movement_joystick_outer'
|
||||
ref={joystickOuter}
|
||||
style={{
|
||||
display: joystickPosition ? 'block' : 'none',
|
||||
display: pointer ? 'flex' : 'none',
|
||||
borderRadius: '50%',
|
||||
width: 50,
|
||||
height: 50,
|
||||
border: '2px solid rgba(0, 0, 0, 0.5)',
|
||||
backgroundColor: 'rgba(255, 255, div, 0.5)',
|
||||
position: 'fixed',
|
||||
left: joystickPosition?.x,
|
||||
top: joystickPosition?.y,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
translate: '-50% -50%',
|
||||
...pointer ? {
|
||||
left: `${pointer.x / window.innerWidth * 100}%`,
|
||||
top: `${pointer.y / window.innerHeight * 100}%`
|
||||
} : {}
|
||||
}}>
|
||||
<div
|
||||
className='movement_joystick_inner'
|
||||
|
|
@ -92,18 +182,38 @@ export default ({ touchActive, setupActive, buttonsPositions }: Props) => {
|
|||
height: 20,
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||
position: 'absolute',
|
||||
|
||||
}}
|
||||
ref={joystickInner}
|
||||
/>
|
||||
</div>
|
||||
<div style={buttonStyles('action')}>
|
||||
<PixelartIcon width={10} iconName='circle' />
|
||||
<div {...buttonProps('action')}>
|
||||
<PixelartIcon iconName='circle' />
|
||||
</div>
|
||||
<div style={buttonStyles('sneak')}>
|
||||
<PixelartIcon width={10} iconName='arrow-down' />
|
||||
<div {...buttonProps('sneak')}>
|
||||
<PixelartIcon iconName='arrow-down' />
|
||||
</div>
|
||||
<div style={buttonStyles('break')}>
|
||||
<PixelartIcon width={10} iconName='arrow-down' />
|
||||
<div {...buttonProps('break')}>
|
||||
<MineIcon />
|
||||
</div>
|
||||
{setupActive && <div style={{
|
||||
position: 'fixed',
|
||||
bottom: 0,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
gap: 3
|
||||
}}>
|
||||
<Button onClick={() => {
|
||||
closeButtonsSetup()
|
||||
}}>Cancel</Button>
|
||||
<Button onClick={() => {
|
||||
closeButtonsSetup(newButtonPositions)
|
||||
}}>Apply</Button>
|
||||
</div>}
|
||||
</div>
|
||||
}
|
||||
|
||||
const MineIcon = () => {
|
||||
return <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 26 26" width={22} height={22}>
|
||||
<path d="M 8 0 L 8 2 L 18 2 L 18 0 L 8 0 z M 18 2 L 18 4 L 20 4 L 20 6 L 22 6 L 22 8 L 24 8 L 24 2 L 22 2 L 18 2 z M 24 8 L 24 18 L 26 18 L 26 8 L 24 8 z M 24 18 L 22 18 L 22 20 L 24 20 L 24 18 z M 22 18 L 22 10 L 20 10 L 20 18 L 22 18 z M 20 10 L 20 8 L 18 8 L 18 10 L 20 10 z M 18 10 L 16 10 L 16 12 L 18 12 L 18 10 z M 16 12 L 14 12 L 14 14 L 16 14 L 16 12 z M 14 14 L 12 14 L 12 16 L 14 16 L 14 14 z M 12 16 L 10 16 L 10 18 L 12 18 L 12 16 z M 10 18 L 8 18 L 8 20 L 10 20 L 10 18 z M 8 20 L 6 20 L 6 22 L 8 22 L 8 20 z M 6 22 L 4 22 L 4 24 L 6 24 L 6 22 z M 4 24 L 2 24 L 2 22 L 0 22 L 0 24 L 0 26 L 2 26 L 4 26 L 4 24 z M 2 22 L 4 22 L 4 20 L 2 20 L 2 22 z M 4 20 L 6 20 L 6 18 L 4 18 L 4 20 z M 6 18 L 8 18 L 8 16 L 6 16 L 6 18 z M 8 16 L 10 16 L 10 14 L 8 14 L 8 16 z M 10 14 L 12 14 L 12 12 L 10 12 L 10 14 z M 12 12 L 14 12 L 14 10 L 12 10 L 12 12 z M 14 10 L 16 10 L 16 8 L 14 8 L 14 10 z M 16 8 L 18 8 L 18 6 L 16 6 L 16 8 z M 16 6 L 16 4 L 8 4 L 8 6 L 16 6 z M 8 4 L 8 2 L 6 2 L 6 4 L 8 4 z" stroke='white' />
|
||||
</svg>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useSnapshot } from 'valtio'
|
||||
import { activeModalStack } from '../globalState'
|
||||
import { activeModalStack, hideModal } from '../globalState'
|
||||
import { options } from '../optionsStorage'
|
||||
import TouchAreasControls from './TouchAreasControls'
|
||||
import { useIsModalActive, useUsingTouch } from './utils'
|
||||
|
|
@ -7,7 +7,13 @@ import { useIsModalActive, useUsingTouch } from './utils'
|
|||
export default () => {
|
||||
const usingTouch = useUsingTouch()
|
||||
const hasModals = useSnapshot(activeModalStack).length !== 0
|
||||
const setupActive = useIsModalActive('touch-areas-setup')
|
||||
const setupActive = useIsModalActive('touch-buttons-setup')
|
||||
const { touchControlsPositions, touchControlsType } = useSnapshot(options)
|
||||
|
||||
return <TouchAreasControls touchActive={!!(usingTouch && hasModals)} setupActive={setupActive} buttonsPositions={options.touchControlsPositions} />
|
||||
return <TouchAreasControls touchActive={!!usingTouch && !hasModals && touchControlsType === 'joystick-buttons'} setupActive={setupActive} buttonsPositions={touchControlsPositions as any} closeButtonsSetup={(newPositions) => {
|
||||
if (newPositions) {
|
||||
options.touchControlsPositions = newPositions
|
||||
}
|
||||
hideModal()
|
||||
}} />
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,8 +49,9 @@ export default () => {
|
|||
const usingTouch = useUsingTouch()
|
||||
const { usingGamepadInput } = useSnapshot(miscUiState)
|
||||
const modals = useSnapshot(activeModalStack)
|
||||
const { touchControlsType } = useSnapshot(options)
|
||||
|
||||
if (!usingTouch || usingGamepadInput) return null
|
||||
if (!usingTouch || usingGamepadInput || touchControlsType !== 'classic') return null
|
||||
return (
|
||||
<div
|
||||
style={{ zIndex: modals.length ? 7 : 8 }}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue