From 3d214641656919e1b4f09cfe047812ca2cb2ab57 Mon Sep 17 00:00:00 2001 From: aronmal Date: Sat, 14 Jan 2023 16:28:03 +0100 Subject: [PATCH] Big rework for mutliple different events --- leaky-ships/components/BorderTiles.tsx | 41 ++-- leaky-ships/components/Gamefield.tsx | 8 +- .../{Target.tsx => GamefieldPointer.tsx} | 24 +- leaky-ships/components/Homepage.tsx | 2 +- leaky-ships/components/Homepage2.tsx | 2 +- leaky-ships/components/useGameEvent.tsx | 212 +++++++----------- leaky-ships/helpers.ts | 2 + leaky-ships/interfaces.ts | 35 +-- leaky-ships/styles/App.scss | 59 ++--- 9 files changed, 163 insertions(+), 222 deletions(-) rename leaky-ships/components/{Target.tsx => GamefieldPointer.tsx} (53%) diff --git a/leaky-ships/components/BorderTiles.tsx b/leaky-ships/components/BorderTiles.tsx index 341efbe..6ea04c3 100644 --- a/leaky-ships/components/BorderTiles.tsx +++ b/leaky-ships/components/BorderTiles.tsx @@ -1,24 +1,24 @@ import { CSSProperties, Dispatch, SetStateAction } from 'react'; -import { borderCN, cornerCN, fieldIndex, isHit } from '../helpers'; -import { HitDispatchType, HitType, LastLeftTileType, TargetPreviewPosType, TargetType } from '../interfaces'; +import { borderCN, cornerCN, fieldIndex } from '../helpers'; +import { LastLeftTileType, TargetPreviewPosType } from '../interfaces'; -function BorderTiles({ props: { count, setTarget, setTargetPreviewPos, hits, DispatchHits, setLastLeftTile } }: { +type TilesType = { + key: number, + isGameTile: boolean, + classNameString: string, + x: number, + y: number +} + +function BorderTiles({ props: { count, settingTarget, setTargetPreviewPos, setLastLeftTile } }: { props: { count: number, - setTarget: Dispatch>, + settingTarget: (isGameTile: boolean, x: number, y: number) => void, setTargetPreviewPos: Dispatch>, - hits: HitType[], - DispatchHits: Dispatch, setLastLeftTile: Dispatch> } }) { - let tilesProperties: { - key: number, - isGameTile: boolean, - classNameString: string, - x: number, - y: number - }[] = []; + let tilesProperties: TilesType[] = []; for (let y = 0; y < count + 2; y++) { for (let x = 0; x < count + 2; x++) { @@ -41,20 +41,7 @@ function BorderTiles({ props: { count, setTarget, setTargetPreviewPos, hits, Dis key={key} className={classNameString} style={{ '--x': x, '--y': y } as CSSProperties} - onClick={() => { - if (!isGameTile || isHit(hits, x, y).length) - return; - setTargetPreviewPos(e => ({ ...e, shouldShow: false })) - setTarget(t => { - if (t.x === x && t.y === y && t.show) { - DispatchHits({ type: 'fireMissle', payload: { hit: (x + y) % 2 !== 0, x, y } }); - return { show: false, x, y }; - } else { - return { show: true, x, y }; - } - }); - - }} + onClick={() => settingTarget(isGameTile, x, y)} onMouseEnter={() => setTargetPreviewPos({ x, y, shouldShow: isGameTile })} onMouseLeave={() => setLastLeftTile({ x, y })} > diff --git a/leaky-ships/components/Gamefield.tsx b/leaky-ships/components/Gamefield.tsx index abea1db..884daa4 100644 --- a/leaky-ships/components/Gamefield.tsx +++ b/leaky-ships/components/Gamefield.tsx @@ -14,10 +14,9 @@ function Gamefield() { targets, eventBar, setLastLeftTile, - setTarget, + settingTarget, setTargetPreviewPos, - hits, - DispatchHits + hits } = useGameEvent(count); return ( @@ -25,7 +24,7 @@ function Gamefield() { {/* */}
{/* Bordes */} - + {/* Collumn lettes and row numbers */} @@ -38,6 +37,7 @@ function Gamefield() { {/* Fog images */} {/* */} {targets} + {/* Debug */}
{eventBar} diff --git a/leaky-ships/components/Target.tsx b/leaky-ships/components/GamefieldPointer.tsx similarity index 53% rename from leaky-ships/components/Target.tsx rename to leaky-ships/components/GamefieldPointer.tsx index 190921f..d2ac0c4 100644 --- a/leaky-ships/components/Target.tsx +++ b/leaky-ships/components/GamefieldPointer.tsx @@ -2,32 +2,34 @@ import { faCrosshairs } from '@fortawesome/pro-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { CSSProperties } from 'react'; import classNames from 'classnames'; -import { TargetType } from '../interfaces'; import { faRadar } from '@fortawesome/pro-thin-svg-icons'; -function Target({ props: { +function GamefieldPointer({ props: { preview, - type, - edges, - imply, x, y, - show + show, + type, + edges, + imply } }: { props: { - preview?: boolean, + preview: boolean, + x: number, + y: number, + show: boolean, type: string, edges: string[], imply: boolean, - } & TargetType + } }) { const isRadar = type === 'radar' - const style = !isRadar ? { '--x': x, '--y': y } : { '--x1': x - 1, '--x2': x + 2, '--y1': y - 1, '--y2': y + 2 } + const style = !(isRadar && !edges.filter(s => s).length) ? { '--x': x, '--y': y } : { '--x1': x - 1, '--x2': x + 2, '--y1': y - 1, '--y2': y + 2 } return ( -
+
) } -export default Target \ No newline at end of file +export default GamefieldPointer \ No newline at end of file diff --git a/leaky-ships/components/Homepage.tsx b/leaky-ships/components/Homepage.tsx index 01e7286..cdb1609 100644 --- a/leaky-ships/components/Homepage.tsx +++ b/leaky-ships/components/Homepage.tsx @@ -76,7 +76,7 @@ function Homepage() { return (
- {Array.from(Array(params.quantity)).map((_tile, index) => createTile(index))} + {Array.from(Array(params.quantity), (_tile, index) => createTile(index))}
) }, [params, position, active, count]) diff --git a/leaky-ships/components/Homepage2.tsx b/leaky-ships/components/Homepage2.tsx index 4440850..b1a1bda 100644 --- a/leaky-ships/components/Homepage2.tsx +++ b/leaky-ships/components/Homepage2.tsx @@ -81,7 +81,7 @@ function Homepage2() {

{sentences[count % sentences.length]}

- {Array.from(Array(params.quantity)).map((_tile, index) => createTile(index))} + {Array.from(Array(params.quantity), (_tile, index) => createTile(index))}
) }, [params, position, active, action, count]) diff --git a/leaky-ships/components/useGameEvent.tsx b/leaky-ships/components/useGameEvent.tsx index 6d7b2e2..bd24a59 100644 --- a/leaky-ships/components/useGameEvent.tsx +++ b/leaky-ships/components/useGameEvent.tsx @@ -1,152 +1,111 @@ import { useCallback, useEffect, useMemo, useReducer, useState } from 'react'; import { hitReducer, initlialLastLeftTile, initlialTarget, initlialTargetPreview, initlialTargetPreviewPos, isHit } from '../helpers'; -import { HitType, ItemsType, LastLeftTileType, TargetListType, TargetModifierType, TargetPreviewPosType, TargetPreviewType, TargetType } from '../interfaces'; +import { HitType, ItemsType, LastLeftTileType, ModeType, TargetPreviewPosType, TargetType } from '../interfaces'; import Item from './Item'; -import Target from './Target'; - -export const modes = { - none: { xEnable: false, yEnable: false, type: 'none' }, - radar: { xEnable: false, yEnable: false, type: 'radar' }, - hTorpedo: { xEnable: true, yEnable: false, type: 'torpedo' }, - vTorpedo: { xEnable: false, yEnable: true, type: 'torpedo' }, - missle: { xEnable: false, yEnable: false, type: 'missle' } -} +import GamefieldPointer from './GamefieldPointer'; function useGameEvent(count: number) { const [lastLeftTile, setLastLeftTile] = useState(initlialLastLeftTile); const [target, setTarget] = useState(initlialTarget); const [eventReady, setEventReady] = useState(false); const [appearOK, setAppearOK] = useState(false); - const [targetPreview, setTargetPreview] = useState(initlialTargetPreview); + const [targetPreview, setTargetPreview] = useState(initlialTargetPreview); const [targetPreviewPos, setTargetPreviewPos] = useState(initlialTargetPreviewPos); const [hits, DispatchHits] = useReducer(hitReducer, [] as HitType[]); - const [mode, setMode] = useState('none') - const [targetList, setTargetList] = useState([]) - const [targetPreviewList, setTargetPreviewList] = useState([]) + // const [mode, setMode] = useState<[number, string]>([0, '']) + const [mode, setMode] = useState(0) - function modXY(e: TargetType, mod: TargetModifierType) { - const { show, ...pos } = e - const { target, params } = mod - return { - target: { - show, - x: pos.x + (target.x ?? 0), - y: pos.y + (target.y ?? 0) - }, - params - } - } + const modes = useMemo(() => [ + { pointerGrid: Array.from(Array(3), () => Array.from(Array(3))), type: 'radar' }, + { pointerGrid: Array.from(Array(3), () => Array.from(Array(1))), type: 'htorpedo' }, + { pointerGrid: Array.from(Array(1), () => Array.from(Array(3))), type: 'vhtorpedo' }, + { pointerGrid: [[{ x: 0, y: 0 }]], type: 'missle' } + ], []) - const isSet = useCallback((x: number, y: number) => targetList.filter(({ target }) => x === target.x && y === target.y).length && target.show, [targetList, target]) - - const scopeGrid = useMemo(() => { - const { xEnable, yEnable } = modes[mode] - const matrix: TargetModifierType[][] = [] - let y = 0 - let x = 0 - const yLength = (yEnable ? 2 : 0) - const xLength = (xEnable ? 2 : 0) - for (let i = 0; i <= yLength; i++) { - for (let i2 = 0; i2 <= xLength; i2++) { - y = i + (yEnable ? -1 : 0); - x = i2 + (xEnable ? -1 : 0); - - (matrix[i] ??= [])[i2] = { - target: { - x, - y, - }, - params: { - edges: [ - i2 === 0 ? 'left' : '', - i2 === xLength ? 'right' : '', - i === 0 ? 'top' : '', - i === yLength ? 'bottom' : '', - ], - imply: false - } - } + const settingTarget = useCallback((isGameTile: boolean, x: number, y: number) => { + if (!isGameTile || isHit(hits, x, y).length) + return; + setTargetPreviewPos(e => ({ ...e, shouldShow: false })) + setTarget(t => { + if (t.x === x && t.y === y && t.show) { + DispatchHits({ type: 'fireMissle', payload: { hit: (x + y) % 2 !== 0, x, y } }); + return { preview: false, show: false, x, y }; + } else { + return { preview: false, show: true, x, y }; } - } - const fields = matrix.reduce((prev, curr) => [...prev, ...curr], []) - return fields - }, [mode]) - - const Targets = useCallback((targets: TargetListType[], preview?: boolean) => { - const { type } = modes[mode] - return targets.map(({ target, params }, i) => ) - }, [mode]) - - useEffect(() => { - const result = scopeGrid.map(e => modXY(target, e)) - .filter(({ target: { x, y } }) => { - const border = [ - x < 2, - x > count, - y < 2, - y > count, - ].reduce((prev, curr) => prev || curr, false) - return !border - }).map(field => { - const { target, params } = field - const { x, y } = target - if (isHit(hits, x, y).length) - return { ...field, params: { ...params, imply: true } } - return field - }) - setTargetList(e => { - if (JSON.stringify(e) === JSON.stringify(result)) - return e - return result }) - }, [scopeGrid, target, count, hits]); + }, []) - useEffect(() => { - const result = scopeGrid.map(e => modXY(targetPreview, e)) - .filter(({ target: { x, y } }) => { - const border = [ - x < 2, - x > count + 1, - y < 2, - y > count + 1, - ].reduce((prev, curr) => prev || curr, false) - return !border - }).map(field => { - const { target, params } = field - const { x, y } = target - if (isHit(hits, x, y).length || isSet(x, y)) - return { ...field, params: { ...params, imply: true } } - return field + const targetList = useCallback((target: TargetType) => { + const { pointerGrid, type } = modes[mode] + const xLength = pointerGrid.length + const yLength = pointerGrid[0].length + const { x: targetX, y: targetY } = target + return pointerGrid.map((arr, i) => { + return arr.map((_, i2) => { + const relativeX = -Math.floor(xLength / 2) + i; + const relativeY = -Math.floor(yLength / 2) + i2; + const x = targetX + (relativeX ?? 0) + const y = targetY + (relativeY ?? 0) + return { + x, + y, + type, + edges: [ + i === 0 ? 'left' : '', + i === xLength - 1 ? 'right' : '', + i2 === 0 ? 'top' : '', + i2 === yLength - 1 ? 'bottom' : '', + ] + } }) - if (!targetPreviewPos.shouldShow) - return - setTargetPreviewList(e => { - if (JSON.stringify(e) === JSON.stringify(result)) - return e - return result }) - }, [scopeGrid, targetPreview, count, hits, isSet, targetPreviewPos.shouldShow]); + .reduce((prev, curr) => [...prev, ...curr], []) + }, [mode, modes]) + + const isSet = useCallback((x: number, y: number) => !!targetList(target).filter(field => x === field.x && y === field.y).length && target.show, [target, targetList]) + + const composeTargetTiles = useCallback((target: TargetType) => { + const { preview, show } = target + const result = targetList(target).map(({ x, y, type, edges }) => { + return { + preview, + x, + y, + show, + type, + edges, + imply: !!isHit(hits, x, y).length || (!!isSet(x, y) && preview), + // isborder: x < 2 || x > count + 1 || y < 2 || y > count + 1 + } + }) + // .filter(({ isborder }) => !isborder) + return result + }, [count, hits, isSet, targetList]) + + // if (!targetPreviewPos.shouldShow) + // return // handle visibility and position change of targetPreview useEffect(() => { const { show, x, y } = targetPreview; // if mouse has moved too quickly and last event was entering and leaving the same field, it must have gone outside the grid const hasLeft = x === lastLeftTile.x && y === lastLeftTile.y + const isSet = x === target.x && y === target.y && target.show if (show && !appearOK) setTargetPreview(e => ({ ...e, show: false })); - if (!show && targetPreviewPos.shouldShow && eventReady && appearOK && !isHit(hits, x, y).length && !hasLeft) + if (!show && targetPreviewPos.shouldShow && eventReady && appearOK && !isHit(hits, x, y).length && !isSet && !hasLeft) setTargetPreview(e => ({ ...e, show: true })); - }, [targetPreview, targetPreviewPos.shouldShow, hits, eventReady, appearOK, lastLeftTile]) + }, [targetPreview, targetPreviewPos.shouldShow, hits, eventReady, appearOK, lastLeftTile, target]) // enable targetPreview event again after 200 mil. sec. useEffect(() => { - const { x: newX, y: newY } = targetPreviewPos; setEventReady(false); if (targetPreview.show || !appearOK) return; const autoTimeout = setTimeout(() => { - setTargetPreview(e => ({ ...e, x: newX, y: newY })); + setTargetPreview(e => ({ ...e, x: targetPreviewPos.x, y: targetPreviewPos.y })); setEventReady(true); setAppearOK(true); }, 300); @@ -155,14 +114,14 @@ function useGameEvent(count: number) { return () => { clearTimeout(autoTimeout); } - }, [targetPreviewPos, targetPreview.show, appearOK]); + }, [appearOK, targetPreview.show, targetPreviewPos.x, targetPreviewPos.y]); // approve targetPreview new position after 200 mil. sec. useEffect(() => { // early return to start cooldown only when about to show up const autoTimeout = setTimeout(() => { setAppearOK(!targetPreview.show) - }, 600); + }, targetPreview.show ? 500 : 300); // or abort if movement is repeated early return () => { @@ -170,23 +129,23 @@ function useGameEvent(count: number) { } }, [targetPreview.show]); - const targets = useMemo(() => <> - {Targets(targetPreviewList, true)} - {Targets(targetList)} - , [Targets, targetList, targetPreviewList]) + const targets = useMemo(() => [ + ...composeTargetTiles(target).map((props, i) => ), + ...composeTargetTiles(targetPreview).map((props, i) => ) + ], [composeTargetTiles, target, targetPreview]) const eventBar = useMemo(() => { const items: ItemsType[] = [ { icon: 'burger-menu', text: 'Menu' }, - { icon: 'radar', text: 'Radar scan', type: 'radar', amount: 1 }, - { icon: 'missle', text: 'Fire torpedo', type: 'hTorpedo', amount: 1 }, - { icon: 'scope', text: 'Fire missle', type: 'missle' }, + { icon: 'radar', text: 'Radar scan', mode: 0, amount: 1 }, + { icon: 'missle', text: 'Fire torpedo', mode: 1, amount: 1 }, + { icon: 'scope', text: 'Fire missle', mode: 2 }, { icon: 'gear', text: 'Settings' } ] return (
{items.map((e, i) => ( - { setMode(e.type as any); setTarget(e => ({ ...e, show: false })) } }} /> + { e.mode !== undefined ? setMode(e.mode) : {}; setTarget(e => ({ ...e, show: false })) } }} /> ))}
) @@ -195,10 +154,9 @@ function useGameEvent(count: number) { targets, eventBar, setLastLeftTile, - setTarget, + settingTarget, setTargetPreviewPos, - hits, - DispatchHits + hits } } diff --git a/leaky-ships/helpers.ts b/leaky-ships/helpers.ts index a0a2460..40133b9 100644 --- a/leaky-ships/helpers.ts +++ b/leaky-ships/helpers.ts @@ -40,11 +40,13 @@ export const initlialLastLeftTile = { y: 0 }; export const initlialTarget = { + preview: false, show: false, x: 2, y: 2 }; export const initlialTargetPreview = { + preview: true, show: false, x: 2, y: 2 diff --git a/leaky-ships/interfaces.ts b/leaky-ships/interfaces.ts index 83bd8ec..a3abed1 100644 --- a/leaky-ships/interfaces.ts +++ b/leaky-ships/interfaces.ts @@ -1,15 +1,9 @@ -import { modes } from "./components/useGameEvent"; - export type LastLeftTileType = { x: number, y: number } export type TargetType = { - show: boolean, - x: number, - y: number -}; -export type TargetPreviewType = { + preview: boolean, show: boolean, x: number, y: number @@ -20,30 +14,19 @@ export type TargetPreviewPosType = { y: number } export type TargetListType = { - target: { - show: boolean, - x: number, - y: number, - }, - params: { - edges: string[], - imply: boolean - } + x: number, + y: number, + type: string, + edges: string[] } -export type TargetModifierType = { - target: { - x: number, - y: number, - }, - params: { - edges: string[], - imply: boolean - } +export type ModeType = { + pointerGrid: any[][], + type: string } export type ItemsType = { icon: string, text: string, - type?: keyof typeof modes, + mode?: number, amount?: number, } export type FieldType = { diff --git a/leaky-ships/styles/App.scss b/leaky-ships/styles/App.scss index 335bf39..0526b17 100644 --- a/leaky-ships/styles/App.scss +++ b/leaky-ships/styles/App.scss @@ -159,35 +159,42 @@ body { --color: red; color: var(--color); opacity: 0; + + &.preview { + --color: orange; + } } - &.target-preview { - --color: orange; - color: var(--color); + &.preview { opacity: 0; @include transition(.5s); } &.radar { --color: limegreen; - border: 5px solid var(--color); - grid-column-start: var(--x1) !important; - grid-column-end: var(--x2) !important; - grid-row-start: var(--y1) !important; - grid-row-end: var(--y2) !important; + + svg { + opacity: 0; + } + + &:not(.left):not(.right):not(.top):not(.bottom) { + // border: 5px solid var(--color); + grid-area: var(--y1) /var(--x1) /var(--y2) /var(--x2); + + svg { + opacity: 1; + padding: 12.5%; + } + } &.imply { box-shadow: 0 0 6px 6px #fff4; } - &.target-preview { + &.preview { --color: lawngreen; background-color: #0001; - border: 5px dashed var(--color); - } - - svg { - padding: 12.5%; + // border: 5px dashed var(--color); } } @@ -215,20 +222,22 @@ body { border-bottom: 2px solid var(--color); } - &.edge.imply.left { - border-left: 2px dashed white; - } + &.imply { + &.left { + border-left: 2px dashed white; + } - &.edge.imply.right { - border-right: 2px dashed white; - } + &.right { + border-right: 2px dashed white; + } - &.edge.imply.top { - border-top: 2px dashed white; - } + &.top { + border-top: 2px dashed white; + } - &.imply.bottom { - border-bottom: 2px dashed white; + &.bottom { + border-bottom: 2px dashed white; + } } &.left.top {