Skip to content

Commit fefc734

Browse files
committed
Squashed commit of the following:
commit fb009d731f4ce838540d30dd66a8772ebbce5f73 Author: James Monger <jameskmonger@hotmail.co.uk> Date: Thu Apr 10 19:40:53 2025 +0100 feat: stats screen commit 8b08f6ee27b2206fee4fb04330dcfe675ed145d8 Author: James Monger <jameskmonger@hotmail.co.uk> Date: Thu Apr 10 18:25:09 2025 +0100 feat: add "clone" markers commit 03255c1d8a80bc74c3ef521129c9a7287330599f Author: James Monger <jameskmonger@hotmail.co.uk> Date: Tue Apr 8 20:49:21 2025 +0100 show owned icon in new shop commit df2931fad7e5997da054a9c904563b5e33040bce Author: James Monger <jameskmonger@hotmail.co.uk> Date: Mon Apr 7 13:55:10 2025 +0100 feat: combine help + options overlays commit a8475e6818597c4d2f0b2677037bbf3963790733 Author: James Monger <jameskmonger@hotmail.co.uk> Date: Mon Apr 7 13:33:26 2025 +0100 feat: add "Locked" chip to shop nav button commit 70909b02d25508c6717ca8651d07c209cdc3e8aa Author: James Monger <jameskmonger@hotmail.co.uk> Date: Sun Apr 6 21:13:51 2025 +0100 feat: move Help to TabMenu commit 8c7a661fec44b528ab80c1805ab9941174103a0a Author: James Monger <jameskmonger@hotmail.co.uk> Date: Sun Apr 6 20:42:09 2025 +0100 feat: create TabMenu component commit ed8a6636871af30eb59c036348d1cd855583bfd3 Author: James Monger <jameskmonger@hotmail.co.uk> Date: Sun Apr 6 20:17:11 2025 +0100 feat: new 2d shop ui commit 60c177157f304b44e81c191d4c73c83419ed618e Author: James Monger <jameskmonger@hotmail.co.uk> Date: Sat Apr 5 03:09:13 2025 +0100 refactor: move CardShop under "3d" commit a8c750f3b7c6e510aca187597b91c631855a1e70 Author: James Monger <jameskmonger@hotmail.co.uk> Date: Sat Apr 5 03:06:54 2025 +0100 refactor: delete unused CurrentCard commit a5fa972cd10b3b9f928fbfe7281c17429580d1a9 Author: James Monger <jameskmonger@hotmail.co.uk> Date: Sat Apr 5 03:06:45 2025 +0100 refactor: delete unused cardSelector
1 parent 81f2e26 commit fefc734

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+1359
-363
lines changed

.storybook/preview-head.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<link rel="preconnect" href="https://fonts.googleapis.com" />
2+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
3+
<link
4+
href="https://fonts.googleapis.com/css2?family=Caveat+Brush&family=Roboto:ital,wght@0,100..900;1,100..900&display=swap"
5+
rel="stylesheet"
6+
/>

apps/server-game/src/player/net/outgoing/initialState.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,13 @@ export const sendInitialState = function* () {
2929
yield put(PlayerCommands.playerInfoCommands.updateHealthCommand(health));
3030

3131
const opponentId = yield* select(PlayerStateSelectors.getOpponentId);
32+
const opponentIsClone = yield* select(
33+
PlayerStateSelectors.getOpponentIsClone
34+
);
3235
yield put(
33-
PlayerCommands.playerInfoCommands.updateOpponentCommand(opponentId)
36+
PlayerCommands.playerInfoCommands.updateOpponentCommand({
37+
id: opponentId,
38+
isClone: opponentIsClone,
39+
})
3440
);
3541
};

apps/web-game/src/game/board/overlays/readyOverlay.tsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ const useStyles = createUseStyles({
4747
healthbar: {
4848
marginBottom: "0.25em",
4949
},
50+
cloneTag: {
51+
background: "#333333",
52+
marginLeft: "0.5em",
53+
padding: "0 0.5em",
54+
fontStyle: "italic",
55+
},
5056
});
5157

5258
const ReadyOverlay: React.FunctionComponent = () => {
@@ -65,6 +71,10 @@ const ReadyOverlay: React.FunctionComponent = () => {
6571
return state.game.playerList.find((p) => p.id === id);
6672
});
6773

74+
const opponentIsClone = useSelector(
75+
(state: AppState) => state.game.playerInfo.opponentIsClone
76+
);
77+
6878
const spectatingPlayer = useSelector<AppState, boolean>(
6979
(state) => state.game.spectating.id !== null
7080
);
@@ -125,7 +135,12 @@ const ReadyOverlay: React.FunctionComponent = () => {
125135
</Half>
126136
<Half>
127137
<Group>
128-
<Header4>{opponent.name}</Header4>
138+
<Header4>
139+
{opponent.name}
140+
{opponentIsClone && (
141+
<span className={styles.cloneTag}>CLONE</span>
142+
)}
143+
</Header4>
129144
<Title title={opponent.profile?.title || null} />
130145
</Group>
131146
<Group className={styles.healthbar}>

apps/web-game/src/game/layouts/desktop/DesktopGame.stories.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,13 @@ Phase_1_Ready.args = {
112112
opponentId: "5678",
113113
};
114114

115+
export const Phase_1_Ready_vs_Clone = Template.bind({});
116+
Phase_1_Ready_vs_Clone.args = {
117+
phase: GamePhase.READY,
118+
opponentId: "5678",
119+
opponentIsClone: true,
120+
};
121+
115122
export const Phase_2_Playing = Template.bind({});
116123
Phase_2_Playing.args = {
117124
phase: GamePhase.PLAYING,

apps/web-game/src/game/layouts/desktop/DesktopGame.tsx

Lines changed: 63 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
11
import * as React from "react";
22

33
import { createUseStyles } from "react-jss";
4+
import { useSelector } from "react-redux";
45

6+
import { BoardSelectors } from "@shoki/board";
7+
8+
import { GamePhase, PieceModel } from "@creature-chess/models";
9+
10+
import { useLocalPlayerId } from "@cc-web/auth/context";
511
import { Footer, Layout } from "@cc-web/ui";
12+
import { TabMenu } from "@cc-web/ui/components/TabMenu";
613
import { LandscapeGameScreen } from "@cc-web/ui/gameScreen";
14+
import { PieceBattleStats } from "@cc-web/ui/src/stats/PieceBattleStats";
715

16+
import { AppState } from "../../../store";
817
import { BoardContainer } from "../../board";
918
import { Controls } from "../../board/overlays";
1019
import {
@@ -17,13 +26,21 @@ import {
1726
NowPlaying,
1827
QuitGameButton,
1928
} from "../../module";
29+
import { StatsState } from "../../module/stats";
2030

2131
const useStyles = createUseStyles({
2232
helpContainer: {
23-
overflowY: "scroll",
2433
padding: "0.5em",
2534
background: "#566c86",
2635
},
36+
leftColumn: {
37+
height: "100%",
38+
display: "flex",
39+
flexDirection: "column",
40+
},
41+
tabMenu: {
42+
flex: 1,
43+
},
2744
rightColumn: {
2845
height: "100%",
2946
},
@@ -36,20 +53,62 @@ const useStyles = createUseStyles({
3653
const DesktopGame: React.FunctionComponent = () => {
3754
const styles = useStyles();
3855

56+
const localPlayerId = useLocalPlayerId();
57+
58+
const ownedPieces = useSelector<AppState, PieceModel[]>((state) =>
59+
[
60+
...BoardSelectors.getAllPieces(state.game.board),
61+
...BoardSelectors.getAllPieces(state.game.bench),
62+
].filter((p) => p.ownerId === localPlayerId)
63+
);
64+
65+
const stats = useSelector<AppState, StatsState>((state) => state.game.stats);
66+
67+
const inPreparingPhase = useSelector<AppState, boolean>(
68+
(state) => state.game.roundInfo.phase === GamePhase.PREPARING
69+
);
70+
71+
const leftTabs = React.useMemo(
72+
() => [
73+
{
74+
label: "Players",
75+
content: <PlayerList />,
76+
},
77+
{
78+
label: "Stats",
79+
content: inPreparingPhase ? (
80+
<PieceBattleStats pieces={ownedPieces} stats={stats} />
81+
) : (
82+
<span>Stats are only available in preparing phase</span>
83+
),
84+
},
85+
{
86+
label: "Help",
87+
content: (
88+
<div className={styles.helpContainer}>
89+
<Help hideFooter />
90+
<Footer />
91+
</div>
92+
),
93+
},
94+
],
95+
[inPreparingPhase, ownedPieces, stats, styles.helpContainer]
96+
);
97+
3998
return (
4099
<LandscapeGameScreen
41100
leftColumnContent={
42-
<>
101+
<div className={styles.leftColumn}>
43102
<RoundIndicator />
44103

45104
<PhaseInfo />
46105

47106
<NowPlaying />
48107

49-
<PlayerList />
108+
<TabMenu tabs={leftTabs} className={styles.tabMenu} />
50109

51110
<QuitGameButton />
52-
</>
111+
</div>
53112
}
54113
middleColumnContent={
55114
<>
@@ -65,11 +124,6 @@ const DesktopGame: React.FunctionComponent = () => {
65124
<CardShop />
66125

67126
<Profile />
68-
69-
<div className={styles.helpContainer}>
70-
<Help hideFooter />
71-
<Footer />
72-
</div>
73127
</Layout>
74128
}
75129
/>

apps/web-game/src/game/layouts/mobile/MobileGame.stories.tsx

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { useGlobalStyles } from "@cc-web/ui";
1111
import { GamemodeSettingsContextProvider } from "@cc-web/ui/GamemodeSettingsContext";
1212

1313
import { ConnectionStatus } from "../../connection-status";
14+
import { GameState } from "../../state";
1415
import { Overlay } from "../../ui";
1516
import { createMockStore } from "../stories-utils";
1617
import { MobileGame } from "./MobileGame";
@@ -25,7 +26,7 @@ const Template: Story<any> = (args) => {
2526
useGlobalStyles();
2627

2728
const store = createMockStore(args.phase === GamePhase.PREPARING, (state) => {
28-
const newState = {
29+
const newState: GameState = {
2930
...state,
3031
ui: {
3132
...state.ui,
@@ -50,6 +51,13 @@ const Template: Story<any> = (args) => {
5051
opponentId: args.opponentId
5152
? args.opponentId
5253
: state.playerInfo.opponentId,
54+
opponentIsClone: args.opponentIsClone
55+
? args.opponentIsClone
56+
: state.playerInfo.opponentIsClone,
57+
},
58+
cardShop: {
59+
...state.cardShop,
60+
...args.cardShop,
5361
},
5462
};
5563

@@ -113,6 +121,14 @@ Phase_1_Ready.args = {
113121
opponentId: "5678",
114122
};
115123

124+
export const Phase_1_Ready_vs_Clone = Template.bind({});
125+
Phase_1_Ready_vs_Clone.args = {
126+
overlay: null,
127+
phase: GamePhase.READY,
128+
opponentId: "5678",
129+
opponentIsClone: true,
130+
};
131+
116132
export const Phase_2_Playing = Template.bind({});
117133
Phase_2_Playing.args = {
118134
overlay: null,
@@ -194,16 +210,27 @@ export const Tab_2_Shop_Overlay = Template.bind({});
194210
Tab_2_Shop_Overlay.args = {
195211
phase: GamePhase.PREPARING,
196212
overlay: Overlay.SHOP,
213+
cardShop: {
214+
locked: false,
215+
},
216+
};
217+
export const Tab_2_Shop_Overlay_Locked = Template.bind({});
218+
Tab_2_Shop_Overlay_Locked.args = {
219+
phase: GamePhase.PREPARING,
220+
overlay: Overlay.SHOP,
221+
cardShop: {
222+
locked: true,
223+
},
197224
};
198225

199-
export const Tab_3_Help_Overlay = Template.bind({});
200-
Tab_3_Help_Overlay.args = {
226+
export const Tab_3_Stats_Overlay = Template.bind({});
227+
Tab_3_Stats_Overlay.args = {
201228
phase: GamePhase.PREPARING,
202-
overlay: Overlay.HELP,
229+
overlay: Overlay.STATS,
203230
};
204231

205-
export const Tab_4_Settings_Overlay = Template.bind({});
206-
Tab_4_Settings_Overlay.args = {
232+
export const Tab_4_Options_Overlay = Template.bind({});
233+
Tab_4_Options_Overlay.args = {
207234
phase: GamePhase.PREPARING,
208235
overlay: Overlay.SETTINGS,
209236
};

apps/web-game/src/game/layouts/mobile/MobileGame.tsx

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,22 @@ import * as React from "react";
22

33
import { useSelector } from "react-redux";
44

5+
import { BoardSelectors } from "@shoki/board";
6+
57
import { getPlayerMoney } from "@creature-chess/gamemode";
6-
import { GamePhase } from "@creature-chess/models";
8+
import { GamePhase, PieceModel } from "@creature-chess/models";
79

10+
import { useLocalPlayerId } from "@cc-web/auth/context";
11+
import { TabMenu } from "@cc-web/ui/components/TabMenu";
812
import { PortraitGameScreen } from "@cc-web/ui/gameScreen";
913
import { BalanceChip } from "@cc-web/ui/src/cardShop";
14+
import { PieceBattleStats } from "@cc-web/ui/src/stats/PieceBattleStats";
1015

1116
import { AppState } from "../../../store";
1217
import { BoardContainer } from "../../board";
1318
import { Controls } from "../../board/overlays";
1419
import { PlayerList, CardShop, Help, Settings, Profile } from "../../module";
20+
import { StatsState } from "../../module/stats";
1521
import { Overlay } from "../../ui/overlay";
1622
import { MobileContentPane } from "./MobileContentPane";
1723
import { NavBar } from "./NavBar";
@@ -21,6 +27,8 @@ import { TopBar } from "./TopBar";
2127
const GameOverlay: React.FunctionComponent<{ currentOverlay: Overlay }> = ({
2228
currentOverlay,
2329
}) => {
30+
const localPlayerId = useLocalPlayerId();
31+
2432
const currentBalance = useSelector<AppState, number>((state) =>
2533
getPlayerMoney(state.game)
2634
);
@@ -31,6 +39,14 @@ const GameOverlay: React.FunctionComponent<{ currentOverlay: Overlay }> = ({
3139
state.game.roundInfo.phase === GamePhase.READY
3240
);
3341

42+
const ownedPieces = useSelector<AppState, PieceModel[]>((state) =>
43+
[
44+
...BoardSelectors.getAllPieces(state.game.board),
45+
...BoardSelectors.getAllPieces(state.game.bench),
46+
].filter((p) => p.ownerId === localPlayerId)
47+
);
48+
const stats = useSelector<AppState, StatsState>((state) => state.game.stats);
49+
3450
if (currentOverlay === Overlay.PLAYERS) {
3551
return (
3652
<OverlayComponent title="Players">
@@ -58,18 +74,29 @@ const GameOverlay: React.FunctionComponent<{ currentOverlay: Overlay }> = ({
5874
);
5975
}
6076

61-
if (currentOverlay === Overlay.HELP) {
77+
if (currentOverlay === Overlay.SETTINGS) {
6278
return (
63-
<OverlayComponent title="Help">
64-
<Help />
79+
<OverlayComponent title="Options">
80+
<TabMenu
81+
tabs={[
82+
{
83+
label: "Help",
84+
content: <Help />,
85+
},
86+
{
87+
label: "Settings",
88+
content: <Settings />,
89+
},
90+
]}
91+
/>
6592
</OverlayComponent>
6693
);
6794
}
6895

69-
if (currentOverlay === Overlay.SETTINGS) {
96+
if (currentOverlay === Overlay.STATS) {
7097
return (
71-
<OverlayComponent title="Settings">
72-
<Settings />
98+
<OverlayComponent title="Stats">
99+
<PieceBattleStats pieces={ownedPieces} stats={stats} />
73100
</OverlayComponent>
74101
);
75102
}

apps/web-game/src/game/layouts/mobile/NavBar.styles.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,24 @@ export const useStyles = createUseStyles({
1818
"&:not(:last-child)": {
1919
borderRight: "3px solid #3c3c3c",
2020
},
21+
22+
"position": "relative",
2123
},
2224
navBarItemActive: {
2325
textDecoration: "underline",
2426
background: "#3c3c3c",
2527
},
28+
chip: {
29+
position: "absolute",
30+
top: "2.5%",
31+
width: "100%",
32+
},
33+
chipText: {
34+
fontSize: "12px",
35+
padding: "0.25em",
36+
borderRadius: "4px",
37+
},
38+
shopLockChip: {
39+
background: "#b13e53",
40+
},
2641
});

0 commit comments

Comments
 (0)