ENH: Refactoring. Making the animations smoother
This commit is contained in:
parent
2eef4e9c05
commit
bfc63050cf
|
@ -5,8 +5,8 @@ import "./globals.css";
|
||||||
const inter = Inter({ subsets: ["latin"] });
|
const inter = Inter({ subsets: ["latin"] });
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Create Next App",
|
title: "Pelita Tournament Heraklion 2024",
|
||||||
description: "Generated by create next app",
|
description: "ASPP2024",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function RootLayout({
|
export default function RootLayout({
|
||||||
|
|
168
app/maze.tsx
168
app/maze.tsx
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import React, {
|
import React, {
|
||||||
useEffect,
|
useEffect,
|
||||||
|
useId,
|
||||||
useMemo,
|
useMemo,
|
||||||
useRef,
|
useRef,
|
||||||
useState
|
useState
|
||||||
|
@ -261,7 +262,7 @@ function Food({ position, color }: { position: Pos, color: string }) {
|
||||||
opacity={1}
|
opacity={1}
|
||||||
className={color}
|
className={color}
|
||||||
// transition={{ duration: 1 }}
|
// transition={{ duration: 1 }}
|
||||||
// initial={{ opacity: 0 }}
|
initial={{ opacity: 1, r: cellSize / 5 }}
|
||||||
// animate={{ opacity: 1 }}
|
// animate={{ opacity: 1 }}
|
||||||
exit={{ opacity: 0, r: cellSize }}
|
exit={{ opacity: 0, r: cellSize }}
|
||||||
/>
|
/>
|
||||||
|
@ -299,13 +300,6 @@ function Pacman({ direction, mouthAngle, color }: { direction: number, mouthAngl
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<g transform={ `rotate(${direction})` }
|
<g transform={ `rotate(${direction})` }
|
||||||
// style={{
|
|
||||||
// transform: `rotate(${direction})`
|
|
||||||
// }}
|
|
||||||
// animate={{
|
|
||||||
// rotate: direction
|
|
||||||
// }}
|
|
||||||
// transition={{ type: "spring", stiffness: 100 }}
|
|
||||||
className={color}>
|
className={color}>
|
||||||
<path
|
<path
|
||||||
d={pacmanPath(mouthAngle)}
|
d={pacmanPath(mouthAngle)}
|
||||||
|
@ -321,7 +315,39 @@ function Pacman({ direction, mouthAngle, color }: { direction: number, mouthAngl
|
||||||
</g>)
|
</g>)
|
||||||
}
|
}
|
||||||
|
|
||||||
function Bot({ position, color, say, width, turnsAgo }: { position: Pos, color: string, say: string, width: number, turnsAgo: number }) {
|
function Ghost({ direction, color }: { direction: number, color: string }) {
|
||||||
|
return (<g
|
||||||
|
id="ghost"
|
||||||
|
className={`${color} ghost`}
|
||||||
|
>
|
||||||
|
|
||||||
|
{/* Round path: // M -8 0 C -8 -4.4 -4.4 -8 0 -8 C 4.4 -8 8 -4.4 8 0 L 8 8 C 8 9 6.6667 5.6 6 5.6 S 4.6667 8.19 4 8.19 S 2.6667 5.6 2 5.6 S 0.6667 8.19 0 8.19 S -1.3333 5.6 -2 5.6 S -3.3333 8.19 -4 8.19 S -5.3333 5.6 -6 5.6 S -8 9 -8 8 C -8 5.3333 -8 2.6667 -8 0 Z */}
|
||||||
|
{/* Straight path: // M -8 0 C -8 -4.4 -4.4 -8 0 -8 C 4.4 -8 8 -4.4 8 0 L 8 8 L 6 5.6 L 4 8 L 2 5.6 L 0 8 L -2 5.6 L -4 8 L -6 5.6 L -8 8 L -8 0 Z */}
|
||||||
|
|
||||||
|
<path d="M -8 0 C -8 -4.4 -4.4 -8 0 -8 C 4.4 -8 8 -4.4 8 0 L 8 8 C 8 9 6.6667 5.6 6 5.6 S 4.6667 8.19 4 8.19 S 2.6667 5.6 2 5.6 S 0.6667 8.19 0 8.19 S -1.3333 5.6 -2 5.6 S -3.3333 8.19 -4 8.19 S -5.3333 5.6 -6 5.6 S -8 9 -8 8 C -8 5.3333 -8 2.6667 -8 0 Z"
|
||||||
|
|
||||||
|
|
||||||
|
stroke="black"
|
||||||
|
strokeWidth={0.2}
|
||||||
|
opacity={0.9}
|
||||||
|
></path>
|
||||||
|
<path d="M -3.2 1.1 C -2.2 1.1 -1.4 0.1 -1.4 -1.2 C -1.4 -2.5 -2.2 -3.6 -3.2 -3.6 C -4.2 -3.6 -5.1 -2.5 -5.1 -1.2 C -5.1 0.1 -4.2 1.1 -3.2 1.1 Z
|
||||||
|
M 1.8 1.1 C 2.8 1.1 3.6 0.1 3.6 -1.2 C 3.6 -2.5 2.8 -3.6 1.8 -3.6 C 0.8 -3.6 -0 -2.5 -0 -1.2 C -0 0.1 0.8 1.1 1.8 1.1 Z"
|
||||||
|
stroke="black"
|
||||||
|
strokeWidth={0.2}
|
||||||
|
fill="white"
|
||||||
|
></path>
|
||||||
|
<path d="M -3.5 0 C -3.1 0 -2.8 -0.4 -2.8 -0.9 C -2.8 -1.5 -3.1 -1.9 -3.5 -1.9 C -3.9 -1.9 -4.2 -1.5 -4.2 -0.9 C -4.2 -0.4 -3.9 0 -3.5 0 Z
|
||||||
|
M 1.5 0 C 1.9 0 2.2 -0.4 2.2 -0.9 C 2.2 -1.5 1.9 -1.9 1.5 -1.9 C 1.1 -1.9 0.8 -1.5 0.8 -0.9 C 0.8 -0.4 1.1 0 1.5 0 Z"
|
||||||
|
stroke="black"
|
||||||
|
strokeWidth={0.2}
|
||||||
|
fill="black"
|
||||||
|
>
|
||||||
|
</path>
|
||||||
|
</g>);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Bot({ position, color, say, width, turnsAgo, fadeIn }: { position: Pos, color: string, say: string, width: number, turnsAgo: number, fadeIn: boolean }) {
|
||||||
const leftSide = position[0] < width / 2;
|
const leftSide = position[0] < width / 2;
|
||||||
const inHomezone = () => {
|
const inHomezone = () => {
|
||||||
switch (color) {
|
switch (color) {
|
||||||
|
@ -349,71 +375,45 @@ function Bot({ position, color, say, width, turnsAgo }: { position: Pos, color:
|
||||||
else if (dx > 0) setDirection(0);
|
else if (dx > 0) setDirection(0);
|
||||||
else if (dy > 0) setDirection(90);
|
else if (dy > 0) setDirection(90);
|
||||||
}
|
}
|
||||||
}, [oldPosition]);
|
}, [position, oldPosition]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (fadeIn)
|
||||||
anime.timeline()
|
anime.timeline()
|
||||||
.add({
|
.add({
|
||||||
targets: '#mazebox .blue',
|
targets: '.bot .blue',
|
||||||
opacity: [0, 1],
|
opacity: [0, 1],
|
||||||
easing: 'linear',
|
easing: 'linear',
|
||||||
duration: 2000,
|
duration: 2000,
|
||||||
}, 3500)
|
}, 3500)
|
||||||
.add({
|
.add({
|
||||||
targets: '#mazebox .red',
|
targets: '.bot .red',
|
||||||
opacity: [0, 1],
|
opacity: [0, 1],
|
||||||
easing: 'linear',
|
easing: 'linear',
|
||||||
duration: 2000,
|
duration: 2000,
|
||||||
}, 3500);;
|
}, 3500);
|
||||||
}, []);
|
}, [fadeIn]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<motion.g
|
<motion.g
|
||||||
style={{
|
transform={ `translate(${(position[0] + 0.5) * cellSize} ${(position[1] + 0.5) * cellSize}) scale(${cellSize / 16})` }
|
||||||
transform: `translateX(${(position[0] + 0.5) * cellSize}) translateY(${(position[0] + 0.5) * cellSize}) scale(${cellSize / 16})`,
|
className="bot"
|
||||||
zIndex: 50 - turnsAgo
|
|
||||||
}}
|
|
||||||
animate={{
|
animate={{
|
||||||
x: (position[0] + 0.5) * cellSize,
|
x: (position[0] + 0.5) * cellSize,
|
||||||
y: (position[1] + 0.5) * cellSize,
|
y: (position[1] + 0.5) * cellSize,
|
||||||
scale: cellSize / 16
|
scale: cellSize / 16
|
||||||
}}
|
}}
|
||||||
|
initial={{
|
||||||
|
x: (position[0] + 0.5) * cellSize,
|
||||||
|
y: (position[1] + 0.5) * cellSize,
|
||||||
|
scale: cellSize / 16
|
||||||
|
}}
|
||||||
transition={{ duration: 0.1 }}
|
transition={{ duration: 0.1 }}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
inHomezone() ? (
|
inHomezone()
|
||||||
<g
|
? (<Ghost direction={direction} color={`${color} ghost`}></Ghost>)
|
||||||
id="ghost"
|
: (<Pacman direction={direction} mouthAngle={mouthAngle} color={`${color} pacman`}></Pacman>)
|
||||||
className={`${color} ghost`}
|
|
||||||
>
|
|
||||||
|
|
||||||
{/* Round path: // M -8 0 C -8 -4.4 -4.4 -8 0 -8 C 4.4 -8 8 -4.4 8 0 L 8 8 C 8 9 6.6667 5.6 6 5.6 S 4.6667 8.19 4 8.19 S 2.6667 5.6 2 5.6 S 0.6667 8.19 0 8.19 S -1.3333 5.6 -2 5.6 S -3.3333 8.19 -4 8.19 S -5.3333 5.6 -6 5.6 S -8 9 -8 8 C -8 5.3333 -8 2.6667 -8 0 Z */}
|
|
||||||
{/* Straight path: // M -8 0 C -8 -4.4 -4.4 -8 0 -8 C 4.4 -8 8 -4.4 8 0 L 8 8 L 6 5.6 L 4 8 L 2 5.6 L 0 8 L -2 5.6 L -4 8 L -6 5.6 L -8 8 L -8 0 Z */}
|
|
||||||
|
|
||||||
<path d="M -8 0 C -8 -4.4 -4.4 -8 0 -8 C 4.4 -8 8 -4.4 8 0 L 8 8 C 8 9 6.6667 5.6 6 5.6 S 4.6667 8.19 4 8.19 S 2.6667 5.6 2 5.6 S 0.6667 8.19 0 8.19 S -1.3333 5.6 -2 5.6 S -3.3333 8.19 -4 8.19 S -5.3333 5.6 -6 5.6 S -8 9 -8 8 C -8 5.3333 -8 2.6667 -8 0 Z"
|
|
||||||
|
|
||||||
|
|
||||||
stroke="black"
|
|
||||||
strokeWidth={0.2}
|
|
||||||
opacity={0.9}
|
|
||||||
></path>
|
|
||||||
<path d="M -3.2 1.1 C -2.2 1.1 -1.4 0.1 -1.4 -1.2 C -1.4 -2.5 -2.2 -3.6 -3.2 -3.6 C -4.2 -3.6 -5.1 -2.5 -5.1 -1.2 C -5.1 0.1 -4.2 1.1 -3.2 1.1 Z
|
|
||||||
M 1.8 1.1 C 2.8 1.1 3.6 0.1 3.6 -1.2 C 3.6 -2.5 2.8 -3.6 1.8 -3.6 C 0.8 -3.6 -0 -2.5 -0 -1.2 C -0 0.1 0.8 1.1 1.8 1.1 Z"
|
|
||||||
stroke="black"
|
|
||||||
strokeWidth={0.2}
|
|
||||||
fill="white"
|
|
||||||
></path>
|
|
||||||
<path d="M -3.5 0 C -3.1 0 -2.8 -0.4 -2.8 -0.9 C -2.8 -1.5 -3.1 -1.9 -3.5 -1.9 C -3.9 -1.9 -4.2 -1.5 -4.2 -0.9 C -4.2 -0.4 -3.9 0 -3.5 0 Z
|
|
||||||
M 1.5 0 C 1.9 0 2.2 -0.4 2.2 -0.9 C 2.2 -1.5 1.9 -1.9 1.5 -1.9 C 1.1 -1.9 0.8 -1.5 0.8 -0.9 C 0.8 -0.4 1.1 0 1.5 0 Z"
|
|
||||||
stroke="black"
|
|
||||||
strokeWidth={0.2}
|
|
||||||
fill="black"
|
|
||||||
>
|
|
||||||
</path>
|
|
||||||
</g>
|
|
||||||
) : (
|
|
||||||
<Pacman direction={direction} mouthAngle={mouthAngle} color={`${color} pacman`}></Pacman>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
<text y="-10" className="sayBg">{say}</text>
|
<text y="-10" className="sayBg">{say}</text>
|
||||||
<text y="-10" className="say">{say}</text>
|
<text y="-10" className="say">{say}</text>
|
||||||
|
@ -426,7 +426,7 @@ function Walls({ shape, walls }: { shape: Pos, walls: Pos[] }) {
|
||||||
const [width, height] = shape;
|
const [width, height] = shape;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<g id="maze">
|
<g className="maze">
|
||||||
<line x1={(width) * cellSize / 2} y1={0.3 * cellSize}
|
<line x1={(width) * cellSize / 2} y1={0.3 * cellSize}
|
||||||
x2={width * cellSize / 2} y2={(height - 0.3) * cellSize} className="middleLine blackLine" />
|
x2={width * cellSize / 2} y2={(height - 0.3) * cellSize} className="middleLine blackLine" />
|
||||||
<line x1={(width - 0.1) * cellSize / 2} y1={0.3 * cellSize}
|
<line x1={(width - 0.1) * cellSize / 2} y1={0.3 * cellSize}
|
||||||
|
@ -465,7 +465,7 @@ function Walls({ shape, walls }: { shape: Pos, walls: Pos[] }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function Maze({ game_uuid, shape, walls, food, bots, team_names, say, whowins, gameover, round, turn }:
|
function Maze({ game_uuid, shape, walls, food, bots, team_names, say, whowins, gameover, round, turn, animate }:
|
||||||
{
|
{
|
||||||
game_uuid: string,
|
game_uuid: string,
|
||||||
shape: Pos,
|
shape: Pos,
|
||||||
|
@ -478,17 +478,22 @@ function Maze({ game_uuid, shape, walls, food, bots, team_names, say, whowins, g
|
||||||
gameover: boolean,
|
gameover: boolean,
|
||||||
round: number,
|
round: number,
|
||||||
turn: number,
|
turn: number,
|
||||||
|
animate: boolean
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
const [width, height] = shape;
|
const [width, height] = shape;
|
||||||
const [a, x, b, y] = bots;
|
const [a, x, b, y] = bots;
|
||||||
const [sayA, sayX, sayB, sayY] = say;
|
const [sayA, sayX, sayB, sayY] = say;
|
||||||
|
|
||||||
|
const mazeBoxRef = useRef<HTMLDivElement>(null);
|
||||||
|
// used so that we can revert the animation
|
||||||
|
const [hasWonScreen, setHasWonScreen] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (game_uuid) {
|
if (game_uuid && animate) {
|
||||||
let pathAnimation = anime.timeline()
|
let pathAnimation = anime.timeline()
|
||||||
.add({
|
.add({
|
||||||
targets: '#mazebox #maze path',
|
targets: mazeBoxRef.current?.querySelectorAll('.maze path'),
|
||||||
strokeDashoffset: [anime.setDashoffset, 0],
|
strokeDashoffset: [anime.setDashoffset, 0],
|
||||||
easing: 'easeInCubic',
|
easing: 'easeInCubic',
|
||||||
duration: 2000,
|
duration: 2000,
|
||||||
|
@ -497,59 +502,70 @@ function Maze({ game_uuid, shape, walls, food, bots, team_names, say, whowins, g
|
||||||
loop: false
|
loop: false
|
||||||
})
|
})
|
||||||
.add({
|
.add({
|
||||||
targets: '#mazebox #maze path',
|
targets: mazeBoxRef.current?.querySelectorAll('.maze path'),
|
||||||
//fill: ['rgb(214, 219, 220)', "#faa"], // ffa
|
//fill: ['rgb(214, 219, 220)', "#faa"], // ffa
|
||||||
fillOpacity: [0, 0.7], // ffa
|
fillOpacity: [0, 0.7], // ffa
|
||||||
easing: 'linear',
|
easing: 'linear',
|
||||||
duration: 2000
|
duration: 2000
|
||||||
}, 2000)
|
}, 2000)
|
||||||
.add({
|
.add({
|
||||||
targets: '#mazebox #maze path',
|
targets: mazeBoxRef.current?.querySelectorAll('.maze path'),
|
||||||
strokeWidth: 0,
|
strokeWidth: 0,
|
||||||
easing: 'linear',
|
easing: 'linear',
|
||||||
duration: 2000
|
duration: 2000
|
||||||
}, 4000)
|
}, 4000)
|
||||||
.add({
|
.add({
|
||||||
targets: '#mazebox .foodblue',
|
targets: mazeBoxRef.current?.querySelectorAll('.foodblue'),
|
||||||
opacity: [0, 1],
|
opacity: [0, 1],
|
||||||
easing: 'linear',
|
easing: 'linear',
|
||||||
duration: 2000,
|
duration: 2000,
|
||||||
}, 3000)
|
}, 3000)
|
||||||
.add({
|
.add({
|
||||||
targets: '#mazebox .foodred',
|
targets: mazeBoxRef.current?.querySelectorAll('.foodred'),
|
||||||
opacity: [0, 1],
|
opacity: [0, 1],
|
||||||
easing: 'linear',
|
easing: 'linear',
|
||||||
duration: 2000,
|
duration: 2000,
|
||||||
}, 3000)
|
}, 3000)
|
||||||
.add({
|
.add({
|
||||||
targets: '#mazebox .blue',
|
targets: mazeBoxRef.current?.querySelectorAll('.blue'),
|
||||||
opacity: [0, 1],
|
opacity: [0, 1],
|
||||||
easing: 'linear',
|
easing: 'linear',
|
||||||
duration: 2000,
|
duration: 2000,
|
||||||
}, 3500)
|
}, 3500)
|
||||||
.add({
|
.add({
|
||||||
targets: '#mazebox .red',
|
targets: mazeBoxRef.current?.querySelectorAll('.red'),
|
||||||
opacity: [0, 1],
|
opacity: [0, 1],
|
||||||
easing: 'linear',
|
easing: 'linear',
|
||||||
duration: 2000,
|
duration: 2000,
|
||||||
}, 3500)
|
}, 3500)
|
||||||
.add({
|
.add({
|
||||||
targets: '#mazebox .middleLine',
|
targets: mazeBoxRef.current?.querySelectorAll('.middleLine'),
|
||||||
opacity: [0, 1],
|
opacity: [0, 1],
|
||||||
easing: 'linear',
|
easing: 'linear',
|
||||||
duration: 2000,
|
duration: 2000,
|
||||||
}, 3500);
|
}, 3500);
|
||||||
}
|
}
|
||||||
}, [walls, game_uuid]);
|
}, [walls, game_uuid, animate, mazeBoxRef]);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log(gameover, whowins);
|
if (!gameover && hasWonScreen) {
|
||||||
|
let pathAnimation = anime.timeline()
|
||||||
|
.add({
|
||||||
|
targets: mazeBoxRef.current?.querySelectorAll('.maze path'),
|
||||||
|
fill: ["#000"],
|
||||||
|
easing: 'linear',
|
||||||
|
duration: 200
|
||||||
|
});
|
||||||
|
};
|
||||||
|
if (gameover) {
|
||||||
|
setHasWonScreen(true);
|
||||||
|
}
|
||||||
if (gameover && whowins === 0) {
|
if (gameover && whowins === 0) {
|
||||||
let pathAnimation = anime.timeline()
|
let pathAnimation = anime.timeline()
|
||||||
.add({
|
.add({
|
||||||
targets: '#mazebox #maze path',
|
targets: mazeBoxRef.current?.querySelectorAll('.maze path'),
|
||||||
fill: ["#faa", "rgb(94, 158, 217)"],
|
fill: ["#000", "rgb(94, 158, 217)"],
|
||||||
easing: 'linear',
|
easing: 'linear',
|
||||||
duration: 200
|
duration: 200
|
||||||
});
|
});
|
||||||
|
@ -557,8 +573,8 @@ function Maze({ game_uuid, shape, walls, food, bots, team_names, say, whowins, g
|
||||||
if (gameover && whowins === 1) {
|
if (gameover && whowins === 1) {
|
||||||
let pathAnimation = anime.timeline()
|
let pathAnimation = anime.timeline()
|
||||||
.add({
|
.add({
|
||||||
targets: '#mazebox #maze path',
|
targets: mazeBoxRef.current?.querySelectorAll('.maze path'),
|
||||||
fill: ["#faa", "rgb(235, 90, 90)"],
|
fill: ["#000", "rgb(235, 90, 90)"],
|
||||||
easing: 'linear',
|
easing: 'linear',
|
||||||
duration: 200
|
duration: 200
|
||||||
});
|
});
|
||||||
|
@ -566,17 +582,17 @@ function Maze({ game_uuid, shape, walls, food, bots, team_names, say, whowins, g
|
||||||
if (gameover && whowins === 2) {
|
if (gameover && whowins === 2) {
|
||||||
let pathAnimation = anime.timeline()
|
let pathAnimation = anime.timeline()
|
||||||
.add({
|
.add({
|
||||||
targets: '#mazebox #maze path',
|
targets: mazeBoxRef.current?.querySelectorAll('.maze path'),
|
||||||
fill: ["#faa", "#fff"],
|
fill: ["#000", "#ffa"],
|
||||||
easing: 'linear',
|
easing: 'linear',
|
||||||
duration: 200
|
duration: 200
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}, [gameover, whowins]);
|
}, [gameover, whowins, mazeBoxRef, hasWonScreen]);
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id="mazebox" className="object-fill">
|
<div ref={mazeBoxRef} className="mazebox object-fill">
|
||||||
<svg
|
<svg
|
||||||
// width={width * cellSize}
|
// width={width * cellSize}
|
||||||
// height={height * cellSize}
|
// height={height * cellSize}
|
||||||
|
@ -653,10 +669,10 @@ function Maze({ game_uuid, shape, walls, food, bots, team_names, say, whowins, g
|
||||||
))}
|
))}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
|
|
||||||
<Bot position={a} key="botA" color="blue" say={sayA} width={width} turnsAgo={turn}></Bot>
|
<Bot position={a} key="botA" color="blue" say={sayA} width={width} fadeIn={animate} turnsAgo={turn}></Bot>
|
||||||
<Bot position={x} key="botX" color="red" say={sayX} width={width} turnsAgo={(turn + 3) % 4}></Bot>
|
<Bot position={x} key="botX" color="red" say={sayX} width={width} fadeIn={animate} turnsAgo={(turn + 3) % 4}></Bot>
|
||||||
<Bot position={b} key="botB" color="blue" say={sayB} width={width} turnsAgo={(turn + 2) % 4}></Bot>
|
<Bot position={b} key="botB" color="blue" say={sayB} width={width} fadeIn={animate} turnsAgo={(turn + 2) % 4}></Bot>
|
||||||
<Bot position={y} key="botY" color="red" say={sayY} width={width} turnsAgo={(turn + 1) % 4}></Bot>
|
<Bot position={y} key="botY" color="red" say={sayY} width={width} fadeIn={animate} turnsAgo={(turn + 1) % 4}></Bot>
|
||||||
|
|
||||||
{
|
{
|
||||||
gameover ? (<>
|
gameover ? (<>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import Pelita from "./pelita";
|
import PelitaTournament from "./tournament";
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (<>
|
return (<>
|
||||||
<Pelita></Pelita>
|
<PelitaTournament></PelitaTournament>
|
||||||
</>);
|
</>);
|
||||||
}
|
}
|
||||||
|
|
183
app/pelita.tsx
183
app/pelita.tsx
|
@ -1,47 +1,12 @@
|
||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import React, {
|
|
||||||
Reducer,
|
|
||||||
useEffect,
|
|
||||||
useReducer
|
|
||||||
} from "react";
|
|
||||||
|
|
||||||
|
|
||||||
import Maze from "./maze";
|
import Maze from "./maze";
|
||||||
import ZMQReceiver from "./zmqreceiver";
|
import { GameState } from "./pelita_msg";
|
||||||
import TypewriterText from "./typewritertext";
|
|
||||||
import { GameState } from "./zmqreceiver";
|
|
||||||
import anime from "animejs";
|
|
||||||
|
|
||||||
type PelitaState = "initial" | "movie" | "intro" | "match" | "faulted";
|
function Pelita({ gameState, footer, animate }: { gameState: GameState, footer: string, animate: boolean }) {
|
||||||
type PelitaEvent = "start-movie" | "start-intro" | "game-playing" | "clear-page" | "fail";
|
|
||||||
|
|
||||||
const MAX_LINES = 20;
|
|
||||||
|
|
||||||
const reducer: Reducer<PelitaState, PelitaEvent> = (state, event) => {
|
|
||||||
switch (state) {
|
|
||||||
case "initial":
|
|
||||||
if (event === "start-movie") return "movie";
|
|
||||||
break;
|
|
||||||
case "movie":
|
|
||||||
if (event === "start-intro") return "intro";
|
|
||||||
break;
|
|
||||||
case "intro":
|
|
||||||
if (event === "game-playing") return "match";
|
|
||||||
if (event === "clear-page") return "intro";
|
|
||||||
break;
|
|
||||||
case "match":
|
|
||||||
if (event === "game-playing") return "match";
|
|
||||||
if (event === "clear-page") return "intro";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return state;
|
|
||||||
};
|
|
||||||
|
|
||||||
function PelitaMain({ gameState }: { gameState: GameState }) {
|
|
||||||
const [team1, team2] = gameState.team_names;
|
const [team1, team2] = gameState.team_names;
|
||||||
|
|
||||||
return <div id="main">
|
return <div className="pelita">
|
||||||
<h2 className="flex flex-row text-lg p-2">
|
<h2 className="flex flex-row text-lg p-2">
|
||||||
<span className="basis-1/2 text-right w-64 blue-bot"><b>{team1}</b> {gameState.game_stats.score[0]}</span>
|
<span className="basis-1/2 text-right w-64 blue-bot"><b>{team1}</b> {gameState.game_stats.score[0]}</span>
|
||||||
<span className="basis-1 px-2">:</span>
|
<span className="basis-1 px-2">:</span>
|
||||||
|
@ -65,153 +30,15 @@ function PelitaMain({ gameState }: { gameState: GameState }) {
|
||||||
gameover={gameState.gameover}
|
gameover={gameState.gameover}
|
||||||
round={gameState.round}
|
round={gameState.round}
|
||||||
turn={gameState.turn}
|
turn={gameState.turn}
|
||||||
|
animate={animate}
|
||||||
>
|
>
|
||||||
</Maze>
|
</Maze>
|
||||||
|
|
||||||
<div className="flex flex-row text-xs text-slate-600">
|
<div className="flex flex-row text-xs text-slate-600">
|
||||||
<div className="basis-1/2 w-64 px-2">ᗧ Pelita Tournament, ASPP 2024 Ηράκλειο</div>
|
<div className="basis-1/2 w-64 px-2">{ footer }</div>
|
||||||
<div className="basis-1/2 text-right w-64 px-2">Round {gameState.round ?? "-"}/{gameState.max_rounds}</div>
|
<div className="basis-1/2 text-right w-64 px-2">Round {gameState.round ?? "-"}/{gameState.max_rounds}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function Pelita() {
|
|
||||||
const initialState: PelitaState = "initial";
|
|
||||||
const [state, dispatch] = useReducer(reducer, initialState);
|
|
||||||
|
|
||||||
const [showPre, setShowPre] = React.useState(true);
|
|
||||||
const [showMain, setShowMain] = React.useState(true);
|
|
||||||
const [typewriterText, setTypewriterText] = React.useState<string[]>([]);
|
|
||||||
|
|
||||||
const [gameState, setGameState] = React.useState<GameState>();
|
|
||||||
|
|
||||||
const bg_color = ((state) => {
|
|
||||||
switch (state) {
|
|
||||||
case "initial":
|
|
||||||
case "movie":
|
|
||||||
case "intro":
|
|
||||||
return "#000"
|
|
||||||
|
|
||||||
case "match":
|
|
||||||
default:
|
|
||||||
return "#fff";
|
|
||||||
}
|
|
||||||
|
|
||||||
})(state);
|
|
||||||
|
|
||||||
|
|
||||||
const crt = ((state) => {
|
|
||||||
switch (state) {
|
|
||||||
case "initial":
|
|
||||||
case "movie":
|
|
||||||
case "intro":
|
|
||||||
return "crt"
|
|
||||||
|
|
||||||
case "match":
|
|
||||||
default:
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
})(state);
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
anime.timeline()
|
|
||||||
.add({
|
|
||||||
targets: 'body',
|
|
||||||
background: bg_color,
|
|
||||||
easing: 'linear',
|
|
||||||
duration: 2000,
|
|
||||||
}, 3000)
|
|
||||||
}, [bg_color]);
|
|
||||||
|
|
||||||
const flip = () => {
|
|
||||||
//setShowMain(!showMain);
|
|
||||||
//setShowPre(!showPre);
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateGameState = (gameState: GameState) => {
|
|
||||||
dispatch("game-playing");
|
|
||||||
setGameState((oldState) => {
|
|
||||||
if (oldState?.game_uuid === gameState.game_uuid) {
|
|
||||||
// we keep the walls array so that the effects are not re-run
|
|
||||||
// TODO: Maybe the effect should depend on only the game_uuid having changed?
|
|
||||||
const newState = {
|
|
||||||
...gameState,
|
|
||||||
"walls": oldState.walls,
|
|
||||||
};
|
|
||||||
return newState;
|
|
||||||
}
|
|
||||||
return gameState;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateMessage = (msg: string) => {
|
|
||||||
let split_str = msg.split(/\r?\n/);
|
|
||||||
setTypewriterText(oldText => [...oldText, ...split_str]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const clearPage = () => {
|
|
||||||
dispatch("clear-page");
|
|
||||||
setTypewriterText([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleClick = async () => {
|
|
||||||
switch (state) {
|
|
||||||
case "initial":
|
|
||||||
dispatch("start-movie");
|
|
||||||
break;
|
|
||||||
case "movie":
|
|
||||||
dispatch("start-intro");
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function showIntro() {
|
|
||||||
return <TypewriterText text={typewriterText} lines={MAX_LINES}></TypewriterText>;
|
|
||||||
}
|
|
||||||
|
|
||||||
function inner() {
|
|
||||||
|
|
||||||
if (state == "initial") {
|
|
||||||
return <button onClick={handleClick}>Start Movie</button>;
|
|
||||||
} else if (state == "movie") {
|
|
||||||
return <video autoPlay controls onEnded={handleClick}>
|
|
||||||
<source src={"Pelita Supercut ASPP Heraklion 2024.mp4"} type="video/mp4" />
|
|
||||||
</video>;
|
|
||||||
} else if (state == "intro" || state == "match") {
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h1 className="fixed top-0 left-0 z-20 w-full px-24 py-4 text-xl">
|
|
||||||
ᗧ Pelita Tournament 2024
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
{state === "intro" ? showIntro() : null}
|
|
||||||
|
|
||||||
{state === "match" && gameState ?
|
|
||||||
<PelitaMain gameState={gameState}></PelitaMain>
|
|
||||||
: null
|
|
||||||
}
|
|
||||||
|
|
||||||
<ZMQReceiver url='ws://localhost:5556' sendGameState={updateGameState} sendMessage={updateMessage} sendClearPage={clearPage}></ZMQReceiver>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<main className={`min-h-screen flex-col items-center justify-between px-24 py-12 ${crt}`}>
|
|
||||||
<div className="z-10 w-full max-w-screen items-center justify-between font-mono text-sm">
|
|
||||||
|
|
||||||
{ inner() }
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
export default Pelita;
|
export default Pelita;
|
||||||
|
|
|
@ -20,10 +20,11 @@ type RootMsg = {
|
||||||
__data__: null
|
__data__: null
|
||||||
};
|
};
|
||||||
|
|
||||||
type Pos = [number, number];
|
export type Pos = [number, number];
|
||||||
|
|
||||||
export type { RootMsg };
|
export type { RootMsg };
|
||||||
|
|
||||||
|
export type ObserveDataL = ObserveData[];
|
||||||
export interface ObserveData {
|
export interface ObserveData {
|
||||||
game_uuid: string;
|
game_uuid: string;
|
||||||
walls: Pos[];
|
walls: Pos[];
|
||||||
|
@ -64,3 +65,57 @@ export interface RequestedMove {
|
||||||
requested_position: number[];
|
requested_position: number[];
|
||||||
success: boolean;
|
success: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface GameStats {
|
||||||
|
score: [number, number];
|
||||||
|
num_errors: [number, number];
|
||||||
|
kills: [number, number, number, number];
|
||||||
|
deaths: [number, number, number, number];
|
||||||
|
team_time: [number, number];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GameState {
|
||||||
|
game_uuid: string;
|
||||||
|
shape: [number, number];
|
||||||
|
walls: [number, number][];
|
||||||
|
food: [number, number][];
|
||||||
|
bots: Tuple4<[number, number]>;
|
||||||
|
team_names: [string, string];
|
||||||
|
game_stats: GameStats;
|
||||||
|
whowins: number;
|
||||||
|
gameover: boolean;
|
||||||
|
say: Tuple4<string>;
|
||||||
|
round: number;
|
||||||
|
max_rounds: number;
|
||||||
|
turn: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function conv_game_state(gs: ObserveData): GameState {
|
||||||
|
// const bot_directions = gs.bots.map((pos, idx) => {
|
||||||
|
//
|
||||||
|
// });
|
||||||
|
|
||||||
|
return {
|
||||||
|
"game_uuid": gs.game_uuid,
|
||||||
|
"shape": gs.shape,
|
||||||
|
"walls": gs.walls,
|
||||||
|
"food": gs.food,
|
||||||
|
"bots": gs.bots,
|
||||||
|
// "bot_directions": bot_directions,
|
||||||
|
"say": gs.say,
|
||||||
|
"turn": gs.turn,
|
||||||
|
"round": gs.round,
|
||||||
|
"max_rounds": gs.max_rounds,
|
||||||
|
"team_names": gs.team_names,
|
||||||
|
"game_stats": {
|
||||||
|
"score": gs.score,
|
||||||
|
"num_errors": gs.num_errors,
|
||||||
|
"kills": gs.kills,
|
||||||
|
"deaths": gs.deaths,
|
||||||
|
"team_time": gs.team_time,
|
||||||
|
},
|
||||||
|
"whowins": gs.whowins,
|
||||||
|
"gameover": gs.gameover
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
160
app/tournament.tsx
Normal file
160
app/tournament.tsx
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
"use client"
|
||||||
|
|
||||||
|
import React, {
|
||||||
|
Reducer,
|
||||||
|
useEffect,
|
||||||
|
useReducer
|
||||||
|
} from "react";
|
||||||
|
|
||||||
|
import ZMQReceiver from "./zmqreceiver";
|
||||||
|
import TypewriterText from "./typewritertext";
|
||||||
|
import { GameState } from "./pelita_msg";
|
||||||
|
import Pelita from "./pelita";
|
||||||
|
import anime from "animejs";
|
||||||
|
|
||||||
|
type PelitaState = "initial" | "movie" | "intro" | "match" | "faulted";
|
||||||
|
type PelitaEvent = "start-movie" | "start-intro" | "game-playing" | "clear-page" | "fail";
|
||||||
|
|
||||||
|
const MAX_LINES = 20;
|
||||||
|
|
||||||
|
const reducer: Reducer<PelitaState, PelitaEvent> = (state, event) => {
|
||||||
|
switch (state) {
|
||||||
|
case "initial":
|
||||||
|
if (event === "start-movie") return "movie";
|
||||||
|
break;
|
||||||
|
case "movie":
|
||||||
|
if (event === "start-intro") return "intro";
|
||||||
|
break;
|
||||||
|
case "intro":
|
||||||
|
if (event === "game-playing") return "match";
|
||||||
|
if (event === "clear-page") return "intro";
|
||||||
|
break;
|
||||||
|
case "match":
|
||||||
|
if (event === "game-playing") return "match";
|
||||||
|
if (event === "clear-page") return "intro";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function PelitaTournament() {
|
||||||
|
const initialState: PelitaState = "initial";
|
||||||
|
const [state, dispatch] = useReducer(reducer, initialState);
|
||||||
|
|
||||||
|
const [typewriterText, setTypewriterText] = React.useState<string[]>([]);
|
||||||
|
|
||||||
|
const [gameState, setGameState] = React.useState<GameState>();
|
||||||
|
|
||||||
|
const bg_color = ((state) => {
|
||||||
|
switch (state) {
|
||||||
|
case "initial":
|
||||||
|
case "movie":
|
||||||
|
case "intro":
|
||||||
|
return "#000"
|
||||||
|
|
||||||
|
case "match":
|
||||||
|
default:
|
||||||
|
return "#fff";
|
||||||
|
}
|
||||||
|
|
||||||
|
})(state);
|
||||||
|
|
||||||
|
|
||||||
|
const crt = ((state) => {
|
||||||
|
switch (state) {
|
||||||
|
case "initial":
|
||||||
|
case "movie":
|
||||||
|
case "intro":
|
||||||
|
return "crt"
|
||||||
|
|
||||||
|
case "match":
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
})(state);
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
anime.timeline()
|
||||||
|
.add({
|
||||||
|
targets: 'body',
|
||||||
|
background: bg_color,
|
||||||
|
easing: 'linear',
|
||||||
|
duration: 2000,
|
||||||
|
}, 3000)
|
||||||
|
}, [bg_color]);
|
||||||
|
|
||||||
|
const updateGameState = (gameState: GameState) => {
|
||||||
|
dispatch("game-playing");
|
||||||
|
setGameState((oldState) => {
|
||||||
|
if (oldState?.game_uuid === gameState.game_uuid) {
|
||||||
|
// we keep the walls array so that the effects are not re-run
|
||||||
|
// TODO: Maybe the effect should depend on only the game_uuid having changed?
|
||||||
|
const newState = {
|
||||||
|
...gameState,
|
||||||
|
"walls": oldState.walls,
|
||||||
|
};
|
||||||
|
return newState;
|
||||||
|
}
|
||||||
|
return gameState;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateMessage = (msg: string) => {
|
||||||
|
let split_str = msg.split(/\r?\n/);
|
||||||
|
setTypewriterText(oldText => [...oldText, ...split_str]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const clearPage = () => {
|
||||||
|
dispatch("clear-page");
|
||||||
|
setTypewriterText([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClick = async () => {
|
||||||
|
switch (state) {
|
||||||
|
case "initial":
|
||||||
|
dispatch("start-movie");
|
||||||
|
break;
|
||||||
|
case "movie":
|
||||||
|
dispatch("start-intro");
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function showIntro() {
|
||||||
|
return <TypewriterText text={typewriterText} lines={MAX_LINES}></TypewriterText>;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main className={`min-h-screen flex-col items-center justify-between px-24 py-12 ${crt}`}>
|
||||||
|
<div className="z-10 w-full max-w-screen items-center justify-between font-mono text-sm">
|
||||||
|
|
||||||
|
{state == "initial" && <button onClick={handleClick}>Start Movie</button>}
|
||||||
|
|
||||||
|
{state == "movie" && <video autoPlay controls onEnded={handleClick}>
|
||||||
|
<source src={"Pelita Supercut ASPP Heraklion 2024.mp4"} type="video/mp4" />
|
||||||
|
</video>
|
||||||
|
}
|
||||||
|
|
||||||
|
{state === "intro" && showIntro()}
|
||||||
|
|
||||||
|
{state == "match" &&
|
||||||
|
<div>
|
||||||
|
<h1 className="fixed top-0 left-0 z-20 w-full px-24 py-4 text-xl">
|
||||||
|
ᗧ Pelita Tournament 2024
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
{ gameState && <Pelita gameState={gameState} footer="ᗧ Pelita Tournament, ASPP 2024 Ηράκλειο" animate={true}></Pelita> }
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<ZMQReceiver url='ws://localhost:5556' sendGameState={updateGameState} sendMessage={updateMessage} sendClearPage={clearPage}></ZMQReceiver>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default PelitaTournament;
|
|
@ -75,7 +75,7 @@ function TypewriterLine( { text, cursor, lineFinished }: { text: string, cursor:
|
||||||
|
|
||||||
//Clearing the interval
|
//Clearing the interval
|
||||||
return () => clearInterval(interval);
|
return () => clearInterval(interval);
|
||||||
}, [index]);
|
}, [index, text.length]);
|
||||||
|
|
||||||
if (current === "")
|
if (current === "")
|
||||||
return (<div><br/></div>);
|
return (<div><br/></div>);
|
||||||
|
|
|
@ -1,67 +1,11 @@
|
||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import type { Tuple4 } from './typeutils';
|
import { conv_game_state } from './pelita_msg';
|
||||||
import { ObserveData } from './pelita_msg';
|
import type { RootMsg, GameState } from './pelita_msg';
|
||||||
import type { RootMsg } from './pelita_msg';
|
|
||||||
import * as zmq from 'jszmq';
|
import * as zmq from 'jszmq';
|
||||||
|
|
||||||
|
|
||||||
interface GameStats {
|
|
||||||
score: [number, number];
|
|
||||||
num_errors: [number, number];
|
|
||||||
kills: [number, number, number, number];
|
|
||||||
deaths: [number, number, number, number];
|
|
||||||
team_time: [number, number];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface GameState {
|
|
||||||
game_uuid: string;
|
|
||||||
shape: [number, number];
|
|
||||||
walls: [number, number][];
|
|
||||||
food: [number, number][];
|
|
||||||
bots: Tuple4<[number, number]>;
|
|
||||||
team_names: [string, string];
|
|
||||||
game_stats: GameStats;
|
|
||||||
whowins: number;
|
|
||||||
gameover: boolean;
|
|
||||||
say: Tuple4<string>;
|
|
||||||
round: number;
|
|
||||||
max_rounds: number;
|
|
||||||
turn: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type { GameState };
|
|
||||||
|
|
||||||
function conv_game_state(gs: ObserveData): GameState {
|
|
||||||
// const bot_directions = gs.bots.map((pos, idx) => {
|
|
||||||
//
|
|
||||||
// });
|
|
||||||
|
|
||||||
return {
|
|
||||||
"game_uuid": gs.game_uuid,
|
|
||||||
"shape": gs.shape,
|
|
||||||
"walls": gs.walls,
|
|
||||||
"food": gs.food,
|
|
||||||
"bots": gs.bots,
|
|
||||||
// "bot_directions": bot_directions,
|
|
||||||
"say": gs.say,
|
|
||||||
"turn": gs.turn,
|
|
||||||
"round": gs.round,
|
|
||||||
"max_rounds": gs.max_rounds,
|
|
||||||
"team_names": gs.team_names,
|
|
||||||
"game_stats": {
|
|
||||||
"score": gs.score,
|
|
||||||
"num_errors": gs.num_errors,
|
|
||||||
"kills": gs.kills,
|
|
||||||
"deaths": gs.deaths,
|
|
||||||
"team_time": gs.team_time,
|
|
||||||
},
|
|
||||||
"whowins": gs.whowins,
|
|
||||||
"gameover": gs.gameover
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const ZMQReceiver = ({ url, sendGameState, sendMessage, sendClearPage }: { url: string,
|
const ZMQReceiver = ({ url, sendGameState, sendMessage, sendClearPage }: { url: string,
|
||||||
sendGameState: (gs: GameState) => any,
|
sendGameState: (gs: GameState) => any,
|
||||||
sendMessage: (msg: string) => any,
|
sendMessage: (msg: string) => any,
|
||||||
|
@ -83,6 +27,7 @@ const ZMQReceiver = ({ url, sendGameState, sendMessage, sendClearPage }: { url:
|
||||||
let parsed = JSON.parse(message) as RootMsg;
|
let parsed = JSON.parse(message) as RootMsg;
|
||||||
console.log(parsed);
|
console.log(parsed);
|
||||||
if (parsed.__action__ === 'SPEAK') {
|
if (parsed.__action__ === 'SPEAK') {
|
||||||
|
// Replacing all ANSI code here
|
||||||
sendMessage(parsed['__data__'].replaceAll(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, ''));
|
sendMessage(parsed['__data__'].replaceAll(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, ''));
|
||||||
} else if (parsed.__action__ === 'CLEAR') {
|
} else if (parsed.__action__ === 'CLEAR') {
|
||||||
sendClearPage();
|
sendClearPage();
|
||||||
|
@ -98,7 +43,7 @@ const ZMQReceiver = ({ url, sendGameState, sendMessage, sendClearPage }: { url:
|
||||||
socket.unsubscribe('');
|
socket.unsubscribe('');
|
||||||
socket.close();
|
socket.close();
|
||||||
};
|
};
|
||||||
}, [url]);
|
}, [url, sendGameState, sendClearPage, sendMessage]);
|
||||||
|
|
||||||
return (<></>
|
return (<></>
|
||||||
// <footer className="fixed bottom-0 left-0 z-20 w-full p-4 bg-white border-t border-gray-200 shadow md:flex md:items-center md:justify-between md:p-6 dark:bg-gray-800 dark:border-gray-600">
|
// <footer className="fixed bottom-0 left-0 z-20 w-full p-4 bg-white border-t border-gray-200 shadow md:flex md:items-center md:justify-between md:p-6 dark:bg-gray-800 dark:border-gray-600">
|
||||||
|
|
Loading…
Reference in a new issue