diff --git a/frontend/app/game/layout.tsx b/frontend/app/game/layout.tsx
new file mode 100644
index 0000000..0e11c3d
--- /dev/null
+++ b/frontend/app/game/layout.tsx
@@ -0,0 +1,7 @@
+export default function GameLayout({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ return
{children}
;
+}
diff --git a/frontend/app/game/page.tsx b/frontend/app/game/page.tsx
new file mode 100644
index 0000000..6a0a4ee
--- /dev/null
+++ b/frontend/app/game/page.tsx
@@ -0,0 +1,16 @@
+import { GameMap } from "@/components/map";
+import { Background } from "@/components/background";
+
+export default function GamePage() {
+ return (
+ <>
+
+
+ >
+ );
+}
diff --git a/frontend/app/globals.css b/frontend/app/globals.css
index ba38cd0..3e9e9a3 100644
--- a/frontend/app/globals.css
+++ b/frontend/app/globals.css
@@ -13,19 +13,25 @@
--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;
- --primary: 222.2 47.4% 11.2%;
+ --primary: 129 17% 51%;
--primary-foreground: 210 40% 98%;
- --secondary: 210 40% 96.1%;
+ --secondary: 163 66% 14%;
--secondary-foreground: 222.2 47.4% 11.2%;
+ --tertiary: 32 44% 53%;
+
+ --yellow: 44 100% 50%;
+ --green: 155 51% 34%;
+ --brown: 33 48% 32%;
+
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
- --destructive: 0 84.2% 60.2%;
+ --destructive: 3 90% 54%;
--destructive-foreground: 210 40% 98%;
--border: 214.3 31.8% 91.4%;
@@ -73,38 +79,16 @@
body {
@apply bg-background text-foreground;
}
- html,
- body {
- height: 100%;
- }
}
-@layer utilities {
- .text-outline-2 {
- text-shadow: -2px -2px 0 #000, -2px -1px 0 #000, -2px 0px 0 #000, -2px 1px 0 #000,
- -2px 2px 0 #000, -1px -2px 0 #000, -1px -1px 0 #000, -1px 0px 0 #000, -1px 1px 0 #000,
- -1px 2px 0 #000, 0px -2px 0 #000, 0px -1px 0 #000, 0px 0px 0 #000, 0px 1px 0 #000,
- 0px 2px 0 #000, 1px -2px 0 #000, 1px -1px 0 #000, 1px 0px 0 #000, 1px 1px 0 #000,
- 1px 2px 0 #000, 2px -2px 0 #000, 2px -1px 0 #000, 2px 0px 0 #000, 2px 1px 0 #000,
- 2px 2px 0 #000;
- }
- .text-outline-4 {
- text-shadow: -4px -4px 0 #000, -4px -3px 0 #000, -4px -2px 0 #000, -4px -1px 0 #000,
- -4px 0px 0 #000, -4px 1px 0 #000, -4px 2px 0 #000, -4px 3px 0 #000, -4px 4px 0 #000,
- -3px -4px 0 #000, -3px -3px 0 #000, -3px -2px 0 #000, -3px -1px 0 #000, -3px 0px 0 #000,
- -3px 1px 0 #000, -3px 2px 0 #000, -3px 3px 0 #000, -3px 4px 0 #000, -2px -4px 0 #000,
- -2px -3px 0 #000, -2px -2px 0 #000, -2px -1px 0 #000, -2px 0px 0 #000, -2px 1px 0 #000,
- -2px 2px 0 #000, -2px 3px 0 #000, -2px 4px 0 #000, -1px -4px 0 #000, -1px -3px 0 #000,
- -1px -2px 0 #000, -1px -1px 0 #000, -1px 0px 0 #000, -1px 1px 0 #000, -1px 2px 0 #000,
- -1px 3px 0 #000, -1px 4px 0 #000, 0px -4px 0 #000, 0px -3px 0 #000, 0px -2px 0 #000,
- 0px -1px 0 #000, 0px 0px 0 #000, 0px 1px 0 #000, 0px 2px 0 #000, 0px 3px 0 #000,
- 0px 4px 0 #000, 1px -4px 0 #000, 1px -3px 0 #000, 1px -2px 0 #000, 1px -1px 0 #000,
- 1px 0px 0 #000, 1px 1px 0 #000, 1px 2px 0 #000, 1px 3px 0 #000, 1px 4px 0 #000,
- 2px -4px 0 #000, 2px -3px 0 #000, 2px -2px 0 #000, 2px -1px 0 #000, 2px 0px 0 #000,
- 2px 1px 0 #000, 2px 2px 0 #000, 2px 3px 0 #000, 2px 4px 0 #000, 3px -4px 0 #000,
- 3px -3px 0 #000, 3px -2px 0 #000, 3px -1px 0 #000, 3px 0px 0 #000, 3px 1px 0 #000,
- 3px 2px 0 #000, 3px 3px 0 #000, 3px 4px 0 #000, 4px -4px 0 #000, 4px -3px 0 #000,
- 4px -2px 0 #000, 4px -1px 0 #000, 4px 0px 0 #000, 4px 1px 0 #000, 4px 2px 0 #000,
- 4px 3px 0 #000, 4px 4px 0 #000;
- }
+.title-stroke {
+ text-shadow: 3px 3px 3px var(--secondary), -3px -3px 3px var(--secondary),
+ 3px -3px 3px var(--secondary), -3px 3px 3px var(--secondary),
+ 3px 3px 3px var(--secondary);
+}
+
+.stroke {
+ text-shadow: 2px 2px 2px var(--secondary), -2px -2px 2px var(--secondary),
+ 2px -2px 2px var(--secondary), -2px 2px 2px var(--secondary),
+ 2px 2px 2px var(--secondary);
}
diff --git a/frontend/assets/background.png b/frontend/assets/background.png
new file mode 100644
index 0000000..8d29dce
Binary files /dev/null and b/frontend/assets/background.png differ
diff --git a/frontend/components/background.tsx b/frontend/components/background.tsx
new file mode 100644
index 0000000..4a10f39
--- /dev/null
+++ b/frontend/components/background.tsx
@@ -0,0 +1,20 @@
+import bgImage from "@/assets/background.png";
+import { cn } from "@/lib/utils";
+import Image from "next/image";
+
+interface Props {
+ isFullScreen?: boolean
+ cls?: string
+}
+export const Background = ({ isFullScreen = false, cls }: Props) => {
+ return (
+
+
+
+ );
+};
diff --git a/frontend/components/map/constants/index.ts b/frontend/components/map/constants/index.ts
new file mode 100644
index 0000000..3185289
--- /dev/null
+++ b/frontend/components/map/constants/index.ts
@@ -0,0 +1,3 @@
+export * from './map-data'
+export * from './nodes'
+export * from './styling'
\ No newline at end of file
diff --git a/frontend/components/map/constants/map-data.ts b/frontend/components/map/constants/map-data.ts
new file mode 100644
index 0000000..d330928
--- /dev/null
+++ b/frontend/components/map/constants/map-data.ts
@@ -0,0 +1,98 @@
+import { MapData } from "../map.interface";
+import {
+ BoardwalkNode,
+ CafeNode,
+ ChurchNode,
+ DinerNode,
+ DocksNode,
+ FactoryNode,
+ FarmsteadNode,
+ GraveyardNode,
+ GreatHallNode,
+ HistoricInnNode,
+ HospitalNode,
+ JunkyardNode,
+ MarketNode,
+ OldMillNode,
+ ParkNode,
+ PawnShopNode,
+ PoliceStationNode,
+ SecretLodgeNode,
+ SwampNode,
+ TheaterNode,
+ TrainStationNode,
+ UniversityNode,
+ WharfNode,
+ WoodsNode,
+} from "./nodes";
+
+export const mapData: MapData = {
+ nodes: [
+ // Arkham Nodes
+ TrainStationNode,
+ UniversityNode,
+ PoliceStationNode,
+ DinerNode,
+ SecretLodgeNode,
+ ParkNode,
+
+ // Dunwich Nodes
+ CafeNode,
+ OldMillNode,
+ FarmsteadNode,
+ ChurchNode,
+ SwampNode,
+ HistoricInnNode,
+
+ // Kingsport Nodes
+ GreatHallNode,
+ TheaterNode,
+ GraveyardNode,
+ WharfNode,
+ MarketNode,
+ WoodsNode,
+
+ // Innsmouth Nodes
+ JunkyardNode,
+ PawnShopNode,
+ DocksNode,
+ HospitalNode,
+ FactoryNode,
+ BoardwalkNode,
+ ],
+ links: [
+ { source: TrainStationNode, target: UniversityNode },
+ { source: UniversityNode, target: PoliceStationNode },
+ { source: PoliceStationNode, target: ParkNode },
+ { source: ParkNode, target: UniversityNode },
+
+ { source: PoliceStationNode, target: SecretLodgeNode },
+ { source: SecretLodgeNode, target: DinerNode },
+ { source: ParkNode, target: SecretLodgeNode },
+ { source: TrainStationNode, target: CafeNode },
+ { source: DinerNode, target: JunkyardNode },
+
+ { source: CafeNode, target: ChurchNode },
+ { source: ChurchNode, target: OldMillNode },
+ { source: ChurchNode, target: HistoricInnNode },
+ { source: ChurchNode, target: FarmsteadNode },
+ { source: HistoricInnNode, target: FarmsteadNode },
+ { source: FarmsteadNode, target: SwampNode },
+ { source: SwampNode, target: GreatHallNode },
+
+ { source: JunkyardNode, target: PawnShopNode },
+ { source: PawnShopNode, target: HospitalNode },
+ { source: HospitalNode, target: FactoryNode },
+ { source: FactoryNode, target: PawnShopNode },
+ { source: FactoryNode, target: BoardwalkNode },
+ { source: BoardwalkNode, target: DocksNode },
+ { source: DocksNode, target: WoodsNode },
+
+ { source: GreatHallNode, target: WoodsNode },
+ { source: WoodsNode, target: MarketNode },
+ { source: MarketNode, target: GreatHallNode },
+ { source: MarketNode, target: TheaterNode },
+ { source: MarketNode, target: WharfNode },
+ { source: WharfNode, target: GraveyardNode },
+ ],
+};
diff --git a/frontend/components/map/constants/nodes.ts b/frontend/components/map/constants/nodes.ts
new file mode 100644
index 0000000..0886f29
--- /dev/null
+++ b/frontend/components/map/constants/nodes.ts
@@ -0,0 +1,228 @@
+// Arkham Nodes
+export const TrainStationNode = {
+ id: "Train Station",
+ city: "Arkham" as City,
+ fx: 42.1875,
+ fy: 154.6875,
+ labelx: 0,
+ labely: 95,
+ isBusStation: true
+};
+
+export const UniversityNode = {
+ id: "University",
+ city: "Arkham" as City,
+ fx: 168.75,
+ fy: 75.3125,
+ labelx: 133.75,
+ labely: 45.3125,
+};
+
+export const PoliceStationNode = {
+ id: "Police Station",
+ city: "Arkham" as City,
+ fx: 379.6875,
+ fy: 68.28125,
+ labelx: 335,
+ labely: 38.28125,
+};
+
+export const SecretLodgeNode = {
+ id: "Secret Lodge",
+ city: "Arkham" as City,
+ fx: 337.5,
+ fy: 203.90625,
+ labelx: 300,
+ labely: 250.90625,
+};
+
+export const DinerNode = {
+ id: "Diner",
+ city: "Arkham" as City,
+ fx: 506.25,
+ fy: 168.75,
+ labelx: 485.25,
+ labely: 105,
+ isBusStation: true
+};
+
+export const ParkNode = {
+ id: "Park",
+ city: "Arkham" as City,
+ fx: 196.875,
+ fy: 281.25,
+ labelx: 230,
+ labely: 290.25,
+ isGate: true
+};
+
+// Dunwich Nodes
+export const CafeNode = {
+ id: "Cafe",
+ city: "Dunwich" as City,
+ fx: 126.5625,
+ fy: 386.71875,
+ labelx: 60,
+ labely: 370,
+};
+
+export const OldMillNode = {
+ id: "Old Mill",
+ city: "Dunwich" as City,
+ fx: 70.3125,
+ fy: 506.25,
+ labelx: 42.3125,
+ labely: 475.25,
+ isGate: true
+};
+
+export const ChurchNode = {
+ id: "Church",
+ city: "Dunwich" as City,
+ fx: 253.125,
+ fy: 471.09375,
+ labelx: 227.125,
+ labely: 437.59375,
+};
+
+export const FarmsteadNode = {
+ id: "Farmstead",
+ city: "Dunwich" as City,
+ fx: 196.875,
+ fy: 675,
+ labelx: 110,
+ labely: 640,
+};
+
+export const SwampNode = {
+ id: "Swamp",
+ city: "Dunwich" as City,
+ fx: 386.71875,
+ fy: 618.75,
+ labelx: 360,
+ labely: 585,
+};
+
+export const HistoricInnNode = {
+ id: "Historic Inn",
+ city: "Dunwich" as City,
+ fx: 450,
+ fy: 421.875,
+ labelx: 410.5,
+ labely: 360,
+ isBusStation: true
+};
+
+// Kingsport Nodes
+export const GreatHallNode = {
+ id: "Great Hall",
+ city: "Kingsport" as City,
+ fx: 590.625,
+ fy: 562.5,
+ labelx: 492.5,
+ labely: 530,
+};
+
+export const TheaterNode = {
+ id: "Theater",
+ city: "Kingsport" as City,
+ fx: 787.5,
+ fy: 618.75,
+ labelx: 690,
+ labely: 620,
+};
+
+export const GraveyardNode = {
+ id: "Graveyard",
+ city: "Kingsport" as City,
+ fx: 914.0625,
+ fy: 689.0625,
+ labelx: 950,
+ labely: 695,
+ isGate: true
+};
+
+export const WharfNode = {
+ id: "Wharf",
+ city: "Kingsport" as City,
+ fx: 956.25,
+ fy: 506.25,
+ labelx: 932.5,
+ labely: 472.5,
+};
+
+export const MarketNode = {
+ id: "Market",
+ city: "Kingsport" as City,
+ fx: 773.4375,
+ fy: 464.0625,
+ labelx: 805,
+ labely: 430,
+ isBusStation: true
+};
+
+export const WoodsNode = {
+ id: "Woods",
+ city: "Kingsport" as City,
+ fx: 604.6875,
+ fy: 393.75,
+ labelx: 578,
+ labely: 360,
+};
+
+// Innsmouth Nodes
+export const JunkyardNode = {
+ id: "Junkyard",
+ city: "Innsmouth" as City,
+ fx: 618.75,
+ fy: 168.75,
+ labelx: 583.75,
+ labely: 135.75,
+};
+
+export const PawnShopNode = {
+ id: "Pawn Shop",
+ city: "Innsmouth" as City,
+ fx: 731.25,
+ fy: 253.125,
+ labelx: 610,
+ labely: 260,
+};
+
+export const DocksNode = {
+ id: "Docks",
+ city: "Innsmouth" as City,
+ fx: 900,
+ fy: 323.4375,
+ labelx: 938,
+ labely: 330,
+};
+
+export const HospitalNode = {
+ id: "Hospital",
+ city: "Innsmouth" as City,
+ fx: 787.5,
+ fy: 70.3125,
+ labelx: 752.5,
+ labely: 35,
+ isGate: true
+};
+
+export const FactoryNode = {
+ id: "Factory",
+ city: "Innsmouth" as City,
+ fx: 900,
+ fy: 154.6875,
+ labelx: 870,
+ labely: 95,
+ isBusStation: true
+};
+
+export const BoardwalkNode = {
+ id: "Boardwalk",
+ city: "Innsmouth" as City,
+ fx: 1047.65625,
+ fy: 196.875,
+ labelx: 985,
+ labely: 160,
+};
diff --git a/frontend/components/map/constants/styling.ts b/frontend/components/map/constants/styling.ts
new file mode 100644
index 0000000..d0671e3
--- /dev/null
+++ b/frontend/components/map/constants/styling.ts
@@ -0,0 +1,9 @@
+export const CityColorMapping: Record<
+ City,
+ { startColor: string; endColor: string }
+> = {
+ Arkham: { startColor: "#055459", endColor: "#077353" },
+ Dunwich: { startColor: "#D55424", endColor: "#EB9E18" },
+ Kingsport: { startColor: "#EC0E47", endColor: "#E1368E" },
+ Innsmouth: { startColor: "#5E49A2", endColor: "#9E54A6" },
+};
diff --git a/frontend/components/map/draw-map.ts b/frontend/components/map/draw-map.ts
new file mode 100644
index 0000000..9250c17
--- /dev/null
+++ b/frontend/components/map/draw-map.ts
@@ -0,0 +1,69 @@
+import { CityColorMapping } from "./constants";
+import { MapLink, MapNode } from "./map.interface";
+
+export const RADIUS = 22.5;
+
+export const drawMap = (
+ context: CanvasRenderingContext2D,
+ width: number,
+ height: number,
+ nodes: MapNode[],
+ links: MapLink[]
+) => {
+ context.clearRect(0, 0, width, height);
+
+ // Draw the links first
+ links.forEach((link) => {
+ context.beginPath();
+ context.moveTo(link.source.fx, link.source.fy);
+ context.lineTo(link.target.fx, link.target.fy);
+ context.strokeStyle = '#fff'
+ context.stroke();
+ });
+
+ // Draw the nodes
+ nodes.forEach((node) => {
+ if (!node.fx || !node.fy) return;
+ if(node.isGate) {
+ const gate = new Image()
+ gate.src = './props/gate.png'
+ gate.onload = () => { context.drawImage(gate, node.fx - RADIUS, node.fy - RADIUS, RADIUS * 2, RADIUS * 2)}
+ }
+
+ if(node.isBusStation) {
+ const labelWidth = context.measureText(node.id).width
+ const bus = new Image()
+ bus.src = './props/bus.png'
+ bus.onload = () => { context.drawImage(bus, node.labelx + labelWidth / 2 - 12.5, node.labely, 25, 25)}
+ }
+
+ /* draw node */
+ context.beginPath();
+ context.moveTo(node.fx + RADIUS, node.fy);
+ context.arc(node.fx, node.fy, RADIUS, 0, 2 * Math.PI);
+ context.closePath()
+
+ /* node styling */
+ const gradient = context.createLinearGradient(node.fx - RADIUS, node.fy - RADIUS, node.fx + RADIUS, node.fy + RADIUS)
+ const cityColor = CityColorMapping[node.city]
+ gradient.addColorStop(0, cityColor.startColor)
+ gradient.addColorStop(1, cityColor.endColor)
+ context.fillStyle = gradient
+
+ context.shadowColor = "#333";
+ context.shadowOffsetX = 4;
+ context.shadowOffsetY = 4
+ context.shadowBlur = 16;
+
+ context.strokeStyle = '#fff'
+ context.lineWidth = 2
+ context.fill();
+ context.stroke();
+
+ /* node label */
+ context.beginPath();
+ context.font = 'bold 16px Noto Sans'
+ context.fillStyle = '#fff'
+ context.fillText(node.id, node.labelx, node.isBusStation ? node.labely - 7.5 : node.labely)
+ });
+};
diff --git a/frontend/components/map/index.tsx b/frontend/components/map/index.tsx
new file mode 100644
index 0000000..c1fa8a0
--- /dev/null
+++ b/frontend/components/map/index.tsx
@@ -0,0 +1,57 @@
+"use client";
+import * as d3 from "d3";
+import { useEffect, useRef } from "react";
+import { RADIUS, drawMap } from "./draw-map";
+import { MapLink, MapNode } from "./map.interface";
+import { mapData } from "./constants";
+
+type NetworkDiagramProps = {
+ width: number;
+ height: number;
+};
+
+export const GameMap = ({
+ width,
+ height,
+}: NetworkDiagramProps) => {
+ // The force simulation mutates links and nodes, so create a copy first
+ // Node positions are initialized by d3
+ const links: MapLink[] = mapData.links.map((d) => ({ ...d }));
+ const nodes: MapNode[] = mapData.nodes.map((d) => ({ ...d }));
+
+ const canvasRef = useRef(null);
+
+ useEffect(() => {
+ // set dimension of the canvas element
+ const canvas = canvasRef.current;
+ const context = canvas?.getContext("2d");
+
+ if (!context) {
+ return;
+ }
+
+ // run d3-force to find the position of nodes on the canvas
+ d3.forceSimulation(nodes)
+ .force(
+ "link",
+ d3.forceLink(links).id((d) => d.id)
+ )
+ .force("collide", d3.forceCollide().radius(RADIUS))
+ .force("charge", d3.forceManyBody().strength(-30))
+ .force("center", d3.forceCenter(width / 2, height / 2))
+
+ // at each iteration of the simulation, draw the network diagram with the new node positions
+ .on("tick", () => {
+ drawMap(context, width, height, nodes, links);
+ });
+ }, [width, height, nodes, links]);
+
+ return (
+
+ );
+};
diff --git a/frontend/components/map/map.interface.ts b/frontend/components/map/map.interface.ts
new file mode 100644
index 0000000..436c851
--- /dev/null
+++ b/frontend/components/map/map.interface.ts
@@ -0,0 +1,22 @@
+import { SimulationLinkDatum, SimulationNodeDatum } from "d3";
+
+export interface MapNode extends SimulationNodeDatum {
+ id: string
+ city: City
+ fx: number
+ fy: number
+ labelx: number
+ labely: number
+ isGate?: boolean
+ isBusStation?: boolean
+}
+
+export interface MapLink extends SimulationLinkDatum {
+ source: MapNode
+ target: MapNode
+}
+
+export type MapData = {
+ nodes: MapNode[]
+ links: MapLink[]
+}
\ No newline at end of file
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 8108a4d..b1d5eae 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -13,6 +13,7 @@
"@radix-ui/react-slot": "^1.0.2",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
+ "d3": "^7.8.5",
"lucide-react": "^0.288.0",
"next": "13.5.5",
"react": "^18",
@@ -22,6 +23,7 @@
"tailwindcss-animate": "^1.0.7"
},
"devDependencies": {
+ "@types/d3": "^7.4.3",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
@@ -740,6 +742,265 @@
"tslib": "^2.4.0"
}
},
+ "node_modules/@types/d3": {
+ "version": "7.4.3",
+ "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz",
+ "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==",
+ "dev": true,
+ "dependencies": {
+ "@types/d3-array": "*",
+ "@types/d3-axis": "*",
+ "@types/d3-brush": "*",
+ "@types/d3-chord": "*",
+ "@types/d3-color": "*",
+ "@types/d3-contour": "*",
+ "@types/d3-delaunay": "*",
+ "@types/d3-dispatch": "*",
+ "@types/d3-drag": "*",
+ "@types/d3-dsv": "*",
+ "@types/d3-ease": "*",
+ "@types/d3-fetch": "*",
+ "@types/d3-force": "*",
+ "@types/d3-format": "*",
+ "@types/d3-geo": "*",
+ "@types/d3-hierarchy": "*",
+ "@types/d3-interpolate": "*",
+ "@types/d3-path": "*",
+ "@types/d3-polygon": "*",
+ "@types/d3-quadtree": "*",
+ "@types/d3-random": "*",
+ "@types/d3-scale": "*",
+ "@types/d3-scale-chromatic": "*",
+ "@types/d3-selection": "*",
+ "@types/d3-shape": "*",
+ "@types/d3-time": "*",
+ "@types/d3-time-format": "*",
+ "@types/d3-timer": "*",
+ "@types/d3-transition": "*",
+ "@types/d3-zoom": "*"
+ }
+ },
+ "node_modules/@types/d3-array": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz",
+ "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==",
+ "dev": true
+ },
+ "node_modules/@types/d3-axis": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz",
+ "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==",
+ "dev": true,
+ "dependencies": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "node_modules/@types/d3-brush": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz",
+ "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==",
+ "dev": true,
+ "dependencies": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "node_modules/@types/d3-chord": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz",
+ "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==",
+ "dev": true
+ },
+ "node_modules/@types/d3-color": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
+ "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==",
+ "dev": true
+ },
+ "node_modules/@types/d3-contour": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz",
+ "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==",
+ "dev": true,
+ "dependencies": {
+ "@types/d3-array": "*",
+ "@types/geojson": "*"
+ }
+ },
+ "node_modules/@types/d3-delaunay": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
+ "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==",
+ "dev": true
+ },
+ "node_modules/@types/d3-dispatch": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.6.tgz",
+ "integrity": "sha512-4fvZhzMeeuBJYZXRXrRIQnvUYfyXwYmLsdiN7XXmVNQKKw1cM8a5WdID0g1hVFZDqT9ZqZEY5pD44p24VS7iZQ==",
+ "dev": true
+ },
+ "node_modules/@types/d3-drag": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz",
+ "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "node_modules/@types/d3-dsv": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz",
+ "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==",
+ "dev": true
+ },
+ "node_modules/@types/d3-ease": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz",
+ "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==",
+ "dev": true
+ },
+ "node_modules/@types/d3-fetch": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz",
+ "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==",
+ "dev": true,
+ "dependencies": {
+ "@types/d3-dsv": "*"
+ }
+ },
+ "node_modules/@types/d3-force": {
+ "version": "3.0.9",
+ "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.9.tgz",
+ "integrity": "sha512-IKtvyFdb4Q0LWna6ymywQsEYjK/94SGhPrMfEr1TIc5OBeziTi+1jcCvttts8e0UWZIxpasjnQk9MNk/3iS+kA==",
+ "dev": true
+ },
+ "node_modules/@types/d3-format": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz",
+ "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==",
+ "dev": true
+ },
+ "node_modules/@types/d3-geo": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz",
+ "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/geojson": "*"
+ }
+ },
+ "node_modules/@types/d3-hierarchy": {
+ "version": "3.1.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.6.tgz",
+ "integrity": "sha512-qlmD/8aMk5xGorUvTUWHCiumvgaUXYldYjNVOWtYoTYY/L+WwIEAmJxUmTgr9LoGNG0PPAOmqMDJVDPc7DOpPw==",
+ "dev": true
+ },
+ "node_modules/@types/d3-interpolate": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
+ "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
+ "dev": true,
+ "dependencies": {
+ "@types/d3-color": "*"
+ }
+ },
+ "node_modules/@types/d3-path": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.0.2.tgz",
+ "integrity": "sha512-WAIEVlOCdd/NKRYTsqCpOMHQHemKBEINf8YXMYOtXH0GA7SY0dqMB78P3Uhgfy+4X+/Mlw2wDtlETkN6kQUCMA==",
+ "dev": true
+ },
+ "node_modules/@types/d3-polygon": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz",
+ "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==",
+ "dev": true
+ },
+ "node_modules/@types/d3-quadtree": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz",
+ "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==",
+ "dev": true
+ },
+ "node_modules/@types/d3-random": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz",
+ "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==",
+ "dev": true
+ },
+ "node_modules/@types/d3-scale": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz",
+ "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/d3-time": "*"
+ }
+ },
+ "node_modules/@types/d3-scale-chromatic": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.3.tgz",
+ "integrity": "sha512-laXM4+1o5ImZv3RpFAsTRn3TEkzqkytiOY0Dz0sq5cnd1dtNlk6sHLon4OvqaiJb28T0S/TdsBI3Sjsy+keJrw==",
+ "dev": true
+ },
+ "node_modules/@types/d3-selection": {
+ "version": "3.0.10",
+ "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.10.tgz",
+ "integrity": "sha512-cuHoUgS/V3hLdjJOLTT691+G2QoqAjCVLmr4kJXR4ha56w1Zdu8UUQ5TxLRqudgNjwXeQxKMq4j+lyf9sWuslg==",
+ "dev": true
+ },
+ "node_modules/@types/d3-shape": {
+ "version": "3.1.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.6.tgz",
+ "integrity": "sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==",
+ "dev": true,
+ "dependencies": {
+ "@types/d3-path": "*"
+ }
+ },
+ "node_modules/@types/d3-time": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz",
+ "integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==",
+ "dev": true
+ },
+ "node_modules/@types/d3-time-format": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz",
+ "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==",
+ "dev": true
+ },
+ "node_modules/@types/d3-timer": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",
+ "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==",
+ "dev": true
+ },
+ "node_modules/@types/d3-transition": {
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.8.tgz",
+ "integrity": "sha512-ew63aJfQ/ms7QQ4X7pk5NxQ9fZH/z+i24ZfJ6tJSfqxJMrYLiK01EAs2/Rtw/JreGUsS3pLPNV644qXFGnoZNQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "node_modules/@types/d3-zoom": {
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz",
+ "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==",
+ "dev": true,
+ "dependencies": {
+ "@types/d3-interpolate": "*",
+ "@types/d3-selection": "*"
+ }
+ },
+ "node_modules/@types/geojson": {
+ "version": "7946.0.13",
+ "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.13.tgz",
+ "integrity": "sha512-bmrNrgKMOhM3WsafmbGmC+6dsF2Z308vLFsQ3a/bT8X8Sv5clVYpPars/UPq+sAaJP+5OoLAYgwbkS5QEJdLUQ==",
+ "dev": true
+ },
"node_modules/@types/json-schema": {
"version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
@@ -1832,6 +2093,384 @@
"integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==",
"devOptional": true
},
+ "node_modules/d3": {
+ "version": "7.8.5",
+ "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.5.tgz",
+ "integrity": "sha512-JgoahDG51ncUfJu6wX/1vWQEqOflgXyl4MaHqlcSruTez7yhaRKR9i8VjjcQGeS2en/jnFivXuaIMnseMMt0XA==",
+ "dependencies": {
+ "d3-array": "3",
+ "d3-axis": "3",
+ "d3-brush": "3",
+ "d3-chord": "3",
+ "d3-color": "3",
+ "d3-contour": "4",
+ "d3-delaunay": "6",
+ "d3-dispatch": "3",
+ "d3-drag": "3",
+ "d3-dsv": "3",
+ "d3-ease": "3",
+ "d3-fetch": "3",
+ "d3-force": "3",
+ "d3-format": "3",
+ "d3-geo": "3",
+ "d3-hierarchy": "3",
+ "d3-interpolate": "3",
+ "d3-path": "3",
+ "d3-polygon": "3",
+ "d3-quadtree": "3",
+ "d3-random": "3",
+ "d3-scale": "4",
+ "d3-scale-chromatic": "3",
+ "d3-selection": "3",
+ "d3-shape": "3",
+ "d3-time": "3",
+ "d3-time-format": "4",
+ "d3-timer": "3",
+ "d3-transition": "3",
+ "d3-zoom": "3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-array": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
+ "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
+ "dependencies": {
+ "internmap": "1 - 2"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-axis": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz",
+ "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-brush": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz",
+ "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==",
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-drag": "2 - 3",
+ "d3-interpolate": "1 - 3",
+ "d3-selection": "3",
+ "d3-transition": "3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-chord": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz",
+ "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==",
+ "dependencies": {
+ "d3-path": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-color": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
+ "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-contour": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz",
+ "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==",
+ "dependencies": {
+ "d3-array": "^3.2.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-delaunay": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
+ "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==",
+ "dependencies": {
+ "delaunator": "5"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-dispatch": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
+ "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-drag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz",
+ "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-selection": "3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-dsv": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz",
+ "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==",
+ "dependencies": {
+ "commander": "7",
+ "iconv-lite": "0.6",
+ "rw": "1"
+ },
+ "bin": {
+ "csv2json": "bin/dsv2json.js",
+ "csv2tsv": "bin/dsv2dsv.js",
+ "dsv2dsv": "bin/dsv2dsv.js",
+ "dsv2json": "bin/dsv2json.js",
+ "json2csv": "bin/json2dsv.js",
+ "json2dsv": "bin/json2dsv.js",
+ "json2tsv": "bin/json2dsv.js",
+ "tsv2csv": "bin/dsv2dsv.js",
+ "tsv2json": "bin/dsv2json.js"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-dsv/node_modules/commander": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/d3-ease": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
+ "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-fetch": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz",
+ "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==",
+ "dependencies": {
+ "d3-dsv": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-force": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",
+ "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==",
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-quadtree": "1 - 3",
+ "d3-timer": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-format": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
+ "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-geo": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.0.tgz",
+ "integrity": "sha512-JEo5HxXDdDYXCaWdwLRt79y7giK8SbhZJbFWXqbRTolCHFI5jRqteLzCsq51NKbUoX0PjBVSohxrx+NoOUujYA==",
+ "dependencies": {
+ "d3-array": "2.5.0 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-hierarchy": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz",
+ "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-interpolate": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
+ "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
+ "dependencies": {
+ "d3-color": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-path": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
+ "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-polygon": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz",
+ "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-quadtree": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz",
+ "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-random": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz",
+ "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-scale": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
+ "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
+ "dependencies": {
+ "d3-array": "2.10.0 - 3",
+ "d3-format": "1 - 3",
+ "d3-interpolate": "1.2.0 - 3",
+ "d3-time": "2.1.1 - 3",
+ "d3-time-format": "2 - 4"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-scale-chromatic": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz",
+ "integrity": "sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==",
+ "dependencies": {
+ "d3-color": "1 - 3",
+ "d3-interpolate": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-selection": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
+ "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-shape": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
+ "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
+ "dependencies": {
+ "d3-path": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-time": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
+ "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
+ "dependencies": {
+ "d3-array": "2 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-time-format": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
+ "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
+ "dependencies": {
+ "d3-time": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-timer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
+ "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-transition": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz",
+ "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
+ "dependencies": {
+ "d3-color": "1 - 3",
+ "d3-dispatch": "1 - 3",
+ "d3-ease": "1 - 3",
+ "d3-interpolate": "1 - 3",
+ "d3-timer": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "peerDependencies": {
+ "d3-selection": "2 - 3"
+ }
+ },
+ "node_modules/d3-zoom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz",
+ "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-drag": "2 - 3",
+ "d3-interpolate": "1 - 3",
+ "d3-selection": "2 - 3",
+ "d3-transition": "2 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/damerau-levenshtein": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
@@ -1914,6 +2553,14 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/delaunator": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz",
+ "integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==",
+ "dependencies": {
+ "robust-predicates": "^3.0.0"
+ }
+ },
"node_modules/dequal": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
@@ -3016,6 +3663,17 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
@@ -3102,6 +3760,14 @@
"node": ">= 0.4"
}
},
+ "node_modules/internmap": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
+ "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
@@ -4566,6 +5232,11 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/robust-predicates": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz",
+ "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg=="
+ },
"node_modules/run-parallel": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
@@ -4588,6 +5259,11 @@
"queue-microtask": "^1.2.2"
}
},
+ "node_modules/rw": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
+ "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ=="
+ },
"node_modules/safe-array-concat": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz",
@@ -4639,6 +5315,11 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+ },
"node_modules/scheduler": {
"version": "0.23.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index 132652b..46eded8 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -15,6 +15,7 @@
"@radix-ui/react-slot": "^1.0.2",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
+ "d3": "^7.8.5",
"lucide-react": "^0.288.0",
"next": "13.5.5",
"react": "^18",
@@ -24,6 +25,7 @@
"tailwindcss-animate": "^1.0.7"
},
"devDependencies": {
+ "@types/d3": "^7.4.3",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
diff --git a/frontend/public/props/bus.png b/frontend/public/props/bus.png
new file mode 100644
index 0000000..8d4f1f7
Binary files /dev/null and b/frontend/public/props/bus.png differ
diff --git a/frontend/public/props/gate.png b/frontend/public/props/gate.png
new file mode 100644
index 0000000..61420a3
Binary files /dev/null and b/frontend/public/props/gate.png differ
diff --git a/frontend/shared/type/type.d.ts b/frontend/shared/type/type.d.ts
index c090641..3f11364 100644
--- a/frontend/shared/type/type.d.ts
+++ b/frontend/shared/type/type.d.ts
@@ -7,6 +7,7 @@ type Investigator =
| "occultist"
| "reporter";
type Sanity = "sane" | "insane";
+type City = 'Arkham' | 'Dunwich' | 'Kingsport' | 'Innsmouth';
interface InvestigatorData {
name: Investigator;
@@ -14,3 +15,4 @@ interface InvestigatorData {
insaneImage: string;
sanity: Sanity;
}
+
diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js
index a2ab943..6475f1a 100644
--- a/frontend/tailwind.config.js
+++ b/frontend/tailwind.config.js
@@ -29,27 +29,30 @@ module.exports = {
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
- DEFAULT: "#6D9773",
- foreground: "#6D9773",
+ DEFAULT: 'hsl(var(--primary))',
+ foreground: 'hsl(var(--primary-foreground))',
},
secondary: {
- DEFAULT: "#0C3B2E",
+ DEFAULT: 'hsl(var(--secondary))',
foreground: "#0C3B2E",
},
tertiary: {
- DEFAULT: "#BB8A52",
+ DEFAULT: "hsl(var(--tertiary))",
foreground: "#BB8A52",
},
yellow: {
- DEFAULT: "#FFBA00",
+ DEFAULT: 'hsl(var(--yellow))',
foreground: "#FFBA00",
},
+ green: {
+ DEFAULT: 'hsl(var(--green))',
+ },
brown: {
- DEFAULT: "#77542A",
+ DEFAULT: 'hsl(var(--brown))',
foreground: "#77542A",
},
destructive: {
- DEFAULT: "#F32C22",
+ DEFAULT: 'hsl(var(--destructive))',
foreground: "#F32C22",
},
muted: {