Skip to content

Commit f8aa600

Browse files
committed
[feat] Finalize snake game, add background and improve on game mechanics with countdown
1 parent cc51f87 commit f8aa600

File tree

11 files changed

+444
-195
lines changed

11 files changed

+444
-195
lines changed

src/components/Layout.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import {css} from '../css';
22
import { Footer } from './atoms/Footer';
3+
import { Background } from './atoms/Background';
34

45
type LayoutOptions = {
5-
children:JSX.Element;
6+
children:any;
67
title?:string;
78
};
89

@@ -33,6 +34,7 @@ export function Layout ({children, title = "atomic-tetris"}:LayoutOptions) {
3334
background: css.$t.bg,
3435
color: css.$t.fg,
3536
})}>
37+
<Background />
3638
{children}
3739
<Footer />
3840
</body>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { css } from "~/css";
2+
3+
export function Background () {
4+
return (<div className={css.use('cover', {
5+
overflow: 'hidden',
6+
zIndex: -1,
7+
background: css.$t.bgSynth,
8+
opacity: .8,
9+
[css.before]: css.mix('cover', {
10+
content: '""',
11+
opacity: .6,
12+
backgroundImage: css.$t.bgSynthLines,
13+
maskImage: 'linear-gradient(to top, transparent, black 50%)',
14+
}),
15+
})}></div>);
16+
}

src/components/atoms/Game.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { CFG, css } from '~/css';
22
import { GameCanvas } from './GameCanvas';
33
import { type RelayEvents, Script } from '~/script';
4+
import { GameCountdown } from './GameCountdown';
45

56
type GameOptions = {
67
sound: RelayEvents['audio:register'];
@@ -21,6 +22,7 @@ export type GameStore = {
2122

2223
export type GameEvents = {
2324
'game:evt:boot': void;
25+
'game:evt:countdown': void;
2426
};
2527

2628
export function Game({
@@ -50,6 +52,7 @@ export function Game({
5052
>
5153
<GameCanvas columns={columns} rows={rows} size={CFG.SIZE} />
5254
{children}
55+
<GameCountdown evtStart={evtStart} />
5356
<div>
5457
<Script data={{ gameSound, evtStart, evtPause, evtOver }}>
5558
{({ el, data, $ }) => {
@@ -90,7 +93,9 @@ export function Game({
9093
el.$publish(data.evtPause);
9194
} else if (e.key === 'r') {
9295
isPaused = false;
93-
el.$publish(data.evtStart);
96+
el.$publish('audio:pause');
97+
el.$publish(data.evtPause);
98+
el.$publish('game:evt:countdown');
9499
}
95100
});
96101

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { css } from '~/css';
2+
import {type RelayEvents, Script} from '~/script';
3+
4+
type GameCountdownProps = {
5+
evtStart:keyof RelayEvents;
6+
};
7+
8+
export function GameCountdown ({evtStart}:GameCountdownProps) {
9+
return (<div className={css({
10+
display: 'none',
11+
[css.attr('data-running')]: css.mix('f', 'fj_c', 'fa_c', 'title', {
12+
position: 'absolute',
13+
inset: 0,
14+
background: css.$t.overlayBg,
15+
color: css.$t.fg,
16+
fontSize: '7rem',
17+
pointerEvents: 'none',
18+
zIndex: 999,
19+
}),
20+
})}>
21+
<Script data={{evtStart}}>{({el,data}) => {
22+
let timer:number|null = null;
23+
let count = 3;
24+
25+
function render () {
26+
el.textContent = String(count);
27+
}
28+
29+
function clearTimer () {
30+
el.removeAttribute('data-running');
31+
if (timer) clearInterval(timer);
32+
timer = null;
33+
}
34+
35+
function setTimer () {
36+
clearTimer();
37+
el.setAttribute('data-running', '');
38+
count = 3;
39+
render();
40+
timer = setInterval(() => {
41+
count -= 1;
42+
render();
43+
if (count === 0) {
44+
clearTimer();
45+
el.$publish(data.evtStart);
46+
}
47+
}, 1000);
48+
}
49+
50+
el.$subscribe('game:evt:countdown', () => setTimer());
51+
52+
el.$unmount = () => clearTimer();
53+
}}</Script>
54+
</div>);
55+
}

src/components/atoms/GamePreview.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export function GamePreview({
2929
padding: css.$v.spaceM,
3030
textAlign: 'center',
3131
cursor: 'crosshair',
32+
boxShadow: '0 6px 10px 5px rgba(0, 0, 0, .3)',
3233
h2: css.mix('header', { marginBottom: css.$v.spaceM }),
3334
p: css.mix('body', { marginBottom: css.$v.spaceM}),
3435
div: {

src/components/atoms/GameStartButton.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,13 @@ export function GameStartButton(data: GameStartButtonOptions) {
3535

3636
function setTimer() {
3737
clearTimer();
38-
setInterval(() => data.$set('timer', data.timer - 1), 1000);
38+
int = setInterval(() => data.$set('timer', data.timer - 1), 1000);
3939
}
4040

4141
function start() {
42-
el.$publish(data.eventStart);
42+
el.$publish('game:evt:countdown');
4343
clearTimer();
44+
el.setAttribute('data-hidden', '');
4445
counter.style.display = 'none';
4546
}
4647

@@ -56,9 +57,16 @@ export function GameStartButton(data: GameStartButtonOptions) {
5657
/* On game over, enable our button again */
5758
el.$subscribe(data.eventOver, () => {
5859
$.clear(el);
60+
el.removeAttribute('data-hidden');
5961
el.textContent = 'Play again?';
6062
});
6163

64+
el.$subscribe('game:evt:countdown', () => {
65+
counter.style.display = 'none';
66+
clearTimer();
67+
el.setAttribute('data-hidden', '');
68+
});
69+
6270
/* On game start, hide element */
6371
el.$subscribe(data.eventStart, () => (el.setAttribute('data-hidden', '')));
6472

src/css.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export const css = createCss({
2222
fg: '#ffffff',
2323
board_bg: '#050b24',
2424
board_fg: '#ffffff',
25-
board_grid: '#2B2B2B',
25+
board_grid: '#3B3B3B',
2626
board_border: '#ffffff',
2727
panelBg: '#02080F',
2828
panelFg: '#FFFFFF',
@@ -37,6 +37,8 @@ export const css = createCss({
3737
keyCapSide1: '#d2d2d2',
3838
keyCapSide2: '#c2c2c2',
3939
overlayBg: 'rgba(12, 18, 32, .75)',
40+
bgSynth: 'linear-gradient(to top, #0f0c29, #302b63, #24243e)',
41+
bgSynthLines: 'repeating-linear-gradient(to right,rgba(255, 0, 150, 0.2) 0,rgba(255, 0, 150, 0.2) 1px,transparent 1px,transparent 40px),repeating-linear-gradient(to top,rgba(0, 200, 255, 0.2) 0,rgba(0, 200, 255, 0.2) 1px,transparent 1px,transparent 40px)',
4042
},
4143
var: {
4244
fontSizeBody: '1.6rem',
@@ -101,6 +103,25 @@ export const css = createCss({
101103
duration: '200ms',
102104
easingFunction: 'ease-in-out',
103105
},
106+
goldenGlow: {
107+
keyframes: {
108+
'0%': {
109+
filter: 'drop-shadow(0 0 1px #fff8d1) brightness(1)',
110+
transform: 'scale(1)',
111+
},
112+
'50%': {
113+
filter: 'drop-shadow(0 0 5px #ffe066) brightness(1.3)',
114+
transform: 'scale(1.08)',
115+
},
116+
'100%': {
117+
filter: 'drop-shadow(0 0 1px #fff8d1) brightness(1)',
118+
transform: 'scale(1)',
119+
},
120+
},
121+
easingFunction: 'ease-in-out',
122+
duration: '1000ms',
123+
iterationCount: 'infinite',
124+
},
104125
},
105126
definitions: (mod) => ({
106127
f: () => ({ display: 'flex' }),
@@ -111,6 +132,13 @@ export const css = createCss({
111132
fj_sb: () => ({ justifyContent: 'space-between' }),
112133
fh: () => ({ flexDirection: 'row' }),
113134
fv: () => ({ flexDirection: 'column' }),
135+
cover: () => ({
136+
position: 'absolute',
137+
top: 0,
138+
left: 0,
139+
right: 0,
140+
bottom: 0,
141+
}),
114142
title: () => ({
115143
fontFamily: mod.$v.fontFamilyHeader,
116144
fontSize: mod.$v.fontSizeTitle,

src/routes/index.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,8 @@ export async function routes<State extends Record<string, unknown>>(
6666
</GamePreview>
6767
))}
6868
</section>
69-
<button
70-
className={css.use('button', {
71-
marginTop: 'calc(' + css.$v.spaceL + ' * 2)',
72-
})}
73-
>
74-
Choose for me
69+
<button className={css.use('linkButton', {marginTop: css.$v.spaceL})}>
70+
I don't know what to choose
7571
<Script data={{ length: PREVIEWS.length }}>
7672
{({ el, data, $ }) => {
7773
$.on(el, 'click', () =>

0 commit comments

Comments
 (0)