Skip to content

Commit cf80ae0

Browse files
committed
feat(compass): display cardinal directions available to character, click to move
1 parent 61d33c5 commit cf80ae0

File tree

1 file changed

+120
-0
lines changed

1 file changed

+120
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import { EuiIcon } from '@elastic/eui';
2+
import { css } from '@emotion/react';
3+
import type React from 'react';
4+
import { useMemo, useState } from 'react';
5+
import type { GameEvent } from '../../../common/game/types.js';
6+
import { GameEventType } from '../../../common/game/types.js';
7+
import { useSubscribe } from '../../hooks/pubsub.jsx';
8+
import { runInBackground } from '../../lib/async/run-in-background.js';
9+
10+
const compassStyle = css({
11+
display: 'flex',
12+
flexWrap: 'wrap',
13+
justifyContent: 'center',
14+
alignItems: 'center',
15+
position: 'relative',
16+
width: '50px',
17+
height: '50px',
18+
});
19+
20+
const centerStyle = css({
21+
position: 'absolute',
22+
display: 'flex',
23+
justifyContent: 'center',
24+
alignItems: 'center',
25+
width: '12px',
26+
height: '12px',
27+
});
28+
29+
const directionStyle = (direction: CompassDirection) => {
30+
const { rotation } = direction;
31+
return css({
32+
position: 'absolute',
33+
transform: `rotate(${rotation}deg) translate(20px) scale(1.2)`,
34+
transformOrigin: 'center',
35+
width: '12px',
36+
height: '12px',
37+
display: 'flex',
38+
justifyContent: 'center',
39+
alignItems: 'center',
40+
});
41+
};
42+
43+
// The arrow icon points east (-->), so rotations are relative to that.
44+
interface CompassDirection {
45+
name: 'n' | 'ne' | 'e' | 'se' | 's' | 'sw' | 'w' | 'nw';
46+
rotation: number;
47+
}
48+
const compassDirections: Array<CompassDirection> = [
49+
{ name: 'n', rotation: -90 },
50+
{ name: 'ne', rotation: -45 },
51+
{ name: 'e', rotation: 0 },
52+
{ name: 'se', rotation: 45 },
53+
{ name: 's', rotation: 90 },
54+
{ name: 'sw', rotation: 135 },
55+
{ name: 'w', rotation: 180 },
56+
{ name: 'nw', rotation: 225 },
57+
];
58+
59+
export const GameCompass: React.FC = () => {
60+
const [directions, setDirections] = useState<Array<string>>([]);
61+
62+
useSubscribe(['game:event'], (gameEvent: GameEvent) => {
63+
if (gameEvent.type === GameEventType.COMPASS) {
64+
setDirections(gameEvent.directions);
65+
}
66+
});
67+
68+
const centerIcon = useMemo(() => {
69+
if (directions.includes('out')) {
70+
return (
71+
<EuiIcon
72+
type="dot"
73+
color="danger"
74+
cursor="pointer"
75+
onClick={() => {
76+
runInBackground(async () => {
77+
await window.api.sendCommand('out');
78+
});
79+
}}
80+
/>
81+
);
82+
}
83+
return <EuiIcon title="out" type="dot" color="subdued" />;
84+
}, [directions]);
85+
86+
const compassIcons = useMemo(() => {
87+
return compassDirections.map((direction) => {
88+
if (directions.includes(direction.name)) {
89+
return (
90+
<div key={direction.name} css={directionStyle(direction)}>
91+
<EuiIcon
92+
type="sortRight"
93+
color="danger"
94+
cursor="pointer"
95+
onClick={() => {
96+
runInBackground(async () => {
97+
await window.api.sendCommand(direction.name);
98+
});
99+
}}
100+
/>
101+
</div>
102+
);
103+
}
104+
return (
105+
<div key={direction.name} css={directionStyle(direction)}>
106+
<EuiIcon type="sortRight" color="subdued" />
107+
</div>
108+
);
109+
});
110+
}, [directions]);
111+
112+
return (
113+
<div css={compassStyle}>
114+
{compassIcons}
115+
<div css={centerStyle}>{centerIcon}</div>
116+
</div>
117+
);
118+
};
119+
120+
GameCompass.displayName = 'GameCompass';

0 commit comments

Comments
 (0)