From 89ec24b8034d80c1d6de887f0513bdabc238fa97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Grzegorz=20Ta=C5=84czyk?= Date: Sun, 21 Apr 2024 14:57:23 +0200 Subject: [PATCH] selection control --- games/nukes/src/controls/selection.tsx | 60 +++++++++++++++++++ .../src/game-states/state-tech-world.tsx | 38 ++++++++---- games/nukes/src/render/launch-site-render.tsx | 11 ++++ games/nukes/src/world/world-state-updates.ts | 2 + 4 files changed, 99 insertions(+), 12 deletions(-) create mode 100644 games/nukes/src/controls/selection.tsx diff --git a/games/nukes/src/controls/selection.tsx b/games/nukes/src/controls/selection.tsx new file mode 100644 index 00000000..4b945208 --- /dev/null +++ b/games/nukes/src/controls/selection.tsx @@ -0,0 +1,60 @@ +import React, { createContext, useContext, useReducer } from 'react'; + +import { LaunchSite } from '../world/world-state-types'; + +type SelectionDispatchAction = + | { + action: 'clear'; + } + | { + action: 'set'; + object: LaunchSite; + }; + +type Selection = { + selectedObject?: LaunchSite; +}; + +const initialSelection: Selection = {}; + +const selectionReducer: React.Reducer = ( + selection: Selection, + action: SelectionDispatchAction, +) => { + if (action.action === 'clear') { + return initialSelection; + } else if (action.action === 'set') { + return { ...selection, selectedObject: action.object }; + } else { + return selection; + } +}; + +// definition of react context for selection +const SelectionContext = createContext(initialSelection); + +// definition of dispatch function for selection context +const SelectionDispatchContext = createContext>(selectionReducer); + +export function SelectionContextWrapper({ children }: { children: React.ReactNode }) { + const [selection, reducer] = useReducer(selectionReducer, initialSelection); + + return ( + + {children} + + ); +} + +export function useObjectSelection(object: LaunchSite) { + const dispatch = useContext(SelectionDispatchContext); + const selection = useContext(SelectionContext); + + return [selection.selectedObject?.id === object.id, () => dispatch({ action: 'set', object })] as const; +} + +export function useClearSelection() { + const dispatch = useContext(SelectionDispatchContext); + + return () => dispatch({ action: 'clear' }); +} diff --git a/games/nukes/src/game-states/state-tech-world.tsx b/games/nukes/src/game-states/state-tech-world.tsx index 81f30360..bb54eb7b 100644 --- a/games/nukes/src/game-states/state-tech-world.tsx +++ b/games/nukes/src/game-states/state-tech-world.tsx @@ -4,7 +4,7 @@ import styled from 'styled-components'; import { createWorldState } from '../world/world-state-create'; import { updateWorldState } from '../world/world-state-updates'; import { WorldState } from '../world/world-state-types'; - +import { SelectionContextWrapper } from '../controls/selection'; import { WorldStateRender } from '../render/world-state-render'; import { GameState, GameStateComponent } from './types'; @@ -17,20 +17,34 @@ const WorldComponent: GameStateComponent = ({ setGameState }) => { ); return ( - -
-
Timestamp: {worldState.timestamp}
-
- - - -
-
- -
+ + + + + + ); }; +function TimeControls({ + worldState, + updateWorld, +}: { + worldState: WorldState; + updateWorld: (worldState: WorldState, deltaTime: number) => void; +}) { + return ( +
+
Timestamp: {worldState.timestamp}
+
+ + + +
+
+ ); +} + const StateContainer = styled.div` position: absolute; left: 0; diff --git a/games/nukes/src/render/launch-site-render.tsx b/games/nukes/src/render/launch-site-render.tsx index 7cdd0eaf..c77f2d4c 100644 --- a/games/nukes/src/render/launch-site-render.tsx +++ b/games/nukes/src/render/launch-site-render.tsx @@ -1,16 +1,21 @@ import styled from 'styled-components'; import { LaunchSite } from '../world/world-state-types'; +import { useObjectSelection } from '../controls/selection'; export function LaunchSiteRender({ launchSite }: { launchSite: LaunchSite }) { + const [isSelected, select] = useObjectSelection(launchSite); + return ( select()} style={ { '--x': launchSite.position.x, '--y': launchSite.position.y, } as React.CSSProperties } + data-selected={isSelected} /> ); } @@ -21,4 +26,10 @@ const LaunchSiteContainer = styled.div` width: 5px; height: 5px; background: rgb(255, 0, 0); + + cursor: pointer; + + &[data-selected='true'] { + box-shadow: 0 0 10px 5px rgb(255, 0, 0); + } `; diff --git a/games/nukes/src/world/world-state-updates.ts b/games/nukes/src/world/world-state-updates.ts index d01d7f5f..e98ab5b4 100644 --- a/games/nukes/src/world/world-state-updates.ts +++ b/games/nukes/src/world/world-state-updates.ts @@ -13,5 +13,7 @@ export function updateWorldState(state: WorldState, deltaTime: number): WorldSta sectors: state.sectors, }; + // convert explosions to population changes + return result; }