Skip to content

Commit

Permalink
Implement replay feature (#23)
Browse files Browse the repository at this point in the history
  • Loading branch information
konsumlamm committed May 17, 2024
1 parent 99b45b7 commit 763c94e
Show file tree
Hide file tree
Showing 11 changed files with 342 additions and 26 deletions.
Binary file modified liberica/bun.lockb
Binary file not shown.
4 changes: 3 additions & 1 deletion liberica/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
"react-i18next": "^14.1.1",
"react-icons": "^5.2.1",
"react-leaflet": "^4.2.1",
"react-router-dom": "^6.23.1"
"react-router-dom": "^6.23.1",
"runtypes": "^6.7.0",
"use-interval": "^1.4.0"
},
"devDependencies": {
"@eslint/js": "^9.2.0",
Expand Down
13 changes: 13 additions & 0 deletions liberica/src/components/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { PropsWithChildren } from "react";
import { Button } from "./InputElements";
import { FaHome } from "react-icons/fa";
import { useNavigate } from "react-router-dom";

export function Navbar(props: PropsWithChildren) {
return (
Expand All @@ -15,3 +18,13 @@ export function Navbar(props: PropsWithChildren) {
</div>
);
}

export function HomeButton() {
const navigate = useNavigate();

return (
<Button onClick={() => navigate("/")}>
<FaHome />
</Button>
);
}
4 changes: 3 additions & 1 deletion liberica/src/components/map/Map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,9 @@ export function Map(
const gs = useContext(GameStateContext);
const [stops, setStops] = useState<Stop[]>([]);
useEffect(() => {
getStops().then(setStops);
getStops()
.then(setStops)
.catch(() => console.warn("failed to get stops"));
}, []);

return (
Expand Down
5 changes: 4 additions & 1 deletion liberica/src/i18n/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,8 @@
"to": "{{line}} nach {{direction}}",
"MrXMarker": "Mr. X war hier",
"AdminPage": "Admin-Seite",
"time": "{{time, datetime(dateStyle: medium; timeStyle: long)}}"
"time": "{{time, datetime(dateStyle: medium; timeStyle: long)}}",
"FailedParseReplay": "Replay-Datei konnte nicht geparst werden",
"ReplayTooBig": "Replay-Datei ist zu groß",
"Speed": "Geschwindigkeit"
}
5 changes: 4 additions & 1 deletion liberica/src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,8 @@
"to": "{{line}} to {{direction}}",
"MrXMarker": "Mr. X was here",
"AdminPage": "Admin page",
"time": "{{time, datetime(dateStyle: medium; timeStyle: long)}}"
"time": "{{time, datetime(dateStyle: medium; timeStyle: long)}}",
"FailedParseReplay": "failed to parse replay file",
"ReplayTooBig": "replay file is too big",
"Speed": "Speed"
}
39 changes: 39 additions & 0 deletions liberica/src/lib/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Array, Literal, Null, Number, Record, String, Union } from "runtypes";

const TeamKind = Union(
Literal("MrX"),
Literal("Detective"),
Literal("Observer"),
);

const Team = Record({
id: Number,
name: String,
color: String,
kind: TeamKind,
});

const TeamState = Record({
team: Team,
long: Number,
lat: Number,
on_train: Union(String, Null),
});

const Train = Record({
id: Number,
long: Number,
lat: Number,
line_id: String,
line_name: String,
direction: String,
});

const GameState = Record({
teams: Array(TeamState),
trains: Array(Train),
});

export default {
GameState,
};
4 changes: 4 additions & 0 deletions liberica/src/lib/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,7 @@ export const getContrastingTextColor = (bg: string) => {

return luminance > 0.5 ? "#000000" : "#FFFFFF";
};

export const clamp = (x: number, min: number, max: number) => {
return Math.min(Math.max(x, min), max);
};
4 changes: 3 additions & 1 deletion liberica/src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { CreateTeam } from "page/CreateTeam";
import { Game } from "page/Game";
import { Home } from "page/Home";
import { Replay } from "page/Replay";
import { Admin } from "page/Admin";
import ReactDOM from "react-dom/client";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import "style/main.css";
import { Admin } from "page/Admin";
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import LanguageDetector from "i18next-browser-languagedetector";
Expand Down Expand Up @@ -33,6 +34,7 @@ ReactDOM.createRoot(rootElement).render(
<Route path="/" element={<Home />} />
<Route path="/create" element={<CreateTeam />} />
<Route path="/game" element={<Game />} />
<Route path="/replay" element={<Replay />} />
<Route path="/admin" element={<Admin />} />
</Routes>
</BrowserRouter>,
Expand Down
38 changes: 17 additions & 21 deletions liberica/src/page/Game.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,16 @@ import { createWebSocketConnection } from "lib/api";
import { GameState, Team, Train } from "lib/bindings";
import { WebSocketApi } from "lib/websockets";
import { useEffect, useState } from "react";
import { useLocation, useNavigate } from "react-router-dom";
import { Navbar } from "components/Navbar";
import { useLocation } from "react-router-dom";
import { HomeButton, Navbar } from "components/Navbar";
import { Button } from "components/InputElements";
import { FaHome } from "react-icons/fa";
import { useTranslation } from "react-i18next";

export function Game() {
const [ws, setWS] = useState<WebSocketApi>();
const [gs, setGameState] = useState<GameState>({ teams: [], trains: [] });
const [embarkedTrain, setEmbarkedTrain] = useState<Train>();
const team: Team = useLocation().state; // this is how Home passes the team
const navigate = useNavigate();
const { t } = useTranslation();

function disembark() {
Expand Down Expand Up @@ -70,8 +68,8 @@ export function Game() {
}, [ws]);

const Game = (
<GameStateContext.Provider value={gs}>
<div className="flex h-max w-max flex-col">
<div className="flex h-max w-max flex-col">
<GameStateContext.Provider value={gs}>
<Map
tileProps={{ updateInterval: 500 }}
containerProps={{ preferCanvas: true }}
Expand All @@ -88,24 +86,22 @@ export function Game() {
}}
onTrainClick={embark}
/>
</GameStateContext.Provider>

<Navbar>
<Button onClick={() => navigate("/")}>
<FaHome />
</Button>
<Navbar>
<HomeButton />

{embarkedTrain && (
<span>
{embarkedTrain.line_name} {embarkedTrain.direction}
</span>
)}
{embarkedTrain && (
<span>
{embarkedTrain.line_name} {embarkedTrain.direction}
</span>
)}

<Button disabled={!embarkedTrain} onClick={disembark}>
{t("Disembark")}
</Button>
</Navbar>
</div>
</GameStateContext.Provider>
<Button disabled={!embarkedTrain} onClick={disembark}>
{t("Disembark")}
</Button>
</Navbar>
</div>
);

const LandingPage = (
Expand Down
Loading

0 comments on commit 763c94e

Please sign in to comment.