diff --git a/Dockerfile b/Dockerfile
index 92e0e9b..b88c475 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -7,6 +7,7 @@ WORKDIR /usr/src/app
ENV WAR_ID="801"
ENV RATE_LIMIT="200"
ENV DATABASE_URL="file:./database/data.db"
+ENV STRATAGEM_IMAGE_URL="/api/static/images/stratagems"
ENV API_URL="https://api.live.prod.thehelldiversgame.com/api"
# install dependencies into temp directory
diff --git a/prisma/migrations/20240319124513_stratagems/migration.sql b/prisma/migrations/20240319124513_stratagems/migration.sql
new file mode 100644
index 0000000..20d4867
--- /dev/null
+++ b/prisma/migrations/20240319124513_stratagems/migration.sql
@@ -0,0 +1,23 @@
+-- CreateTable
+CREATE TABLE "StratagemGroup" (
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
+ "name" TEXT NOT NULL,
+ "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updatedAt" DATETIME NOT NULL
+);
+
+-- CreateTable
+CREATE TABLE "Stratagem" (
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
+ "codename" TEXT,
+ "name" TEXT NOT NULL,
+ "keys" TEXT NOT NULL,
+ "uses" TEXT NOT NULL,
+ "cooldown" INTEGER,
+ "activation" INTEGER,
+ "imageUrl" TEXT NOT NULL,
+ "groupId" INTEGER,
+ "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updatedAt" DATETIME NOT NULL,
+ CONSTRAINT "Stratagem_groupId_fkey" FOREIGN KEY ("groupId") REFERENCES "StratagemGroup" ("id") ON DELETE SET NULL ON UPDATE CASCADE
+);
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 5113c87..af5d504 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -131,3 +131,26 @@ model Attack {
updatedAt DateTime @updatedAt
createdAt DateTime @default(now())
}
+
+model StratagemGroup {
+ id Int @id @default(autoincrement())
+ name String
+ stratagems Stratagem[]
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+}
+
+model Stratagem {
+ id Int @id @default(autoincrement())
+ codename String?
+ name String
+ keys String
+ uses String
+ cooldown Int?
+ activation Int?
+ imageUrl String
+ group StratagemGroup? @relation(fields: [groupId], references: [id])
+ groupId Int?
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+}
diff --git a/src/controllers/stratagems.ts b/src/controllers/stratagems.ts
new file mode 100644
index 0000000..64ed5a4
--- /dev/null
+++ b/src/controllers/stratagems.ts
@@ -0,0 +1,83 @@
+import type { Context } from "hono";
+import { PrismaClient } from "@prisma/client";
+
+import witCache from "utils/cache";
+import parseIntParam from "utils/params";
+import parseQueryParams from "utils/query";
+
+const prisma = new PrismaClient();
+
+export const getStratagemById = await witCache(async (ctx: Context) => {
+ try {
+ const id = parseIntParam(ctx, "id");
+ const query = await parseQueryParams(ctx);
+
+ delete query.orderBy;
+ delete query.where;
+ delete query.orderBy;
+ delete (query as any).skip;
+ delete (query as any).take;
+
+ const stratagem = await prisma.stratagem.findUnique({
+ ...(query as any),
+ where: { id },
+ });
+
+ if (!stratagem) {
+ ctx.status(404);
+ return ctx.json({
+ data: null,
+ error: { details: [`Stratagem with id (${id}) not found`] },
+ });
+ }
+
+ // slightly transform the data
+ const imageBaseUrl = process.env.STRATAGEM_IMAGE_URL || "";
+ stratagem.imageUrl = `${imageBaseUrl}${stratagem.imageUrl}`;
+ (stratagem as any).keys = stratagem.keys.split(",");
+
+ return ctx.json({ data: stratagem, error: null });
+ } catch (error: any) {
+ console.error(error);
+ ctx.status(500);
+ return ctx.json({
+ data: null,
+ error: { details: [error.message] },
+ });
+ }
+});
+
+export const getAllStratagems = await witCache(async (ctx: Context) => {
+ try {
+ const query = await parseQueryParams(ctx);
+
+ const [count, stratagems] = await Promise.all([
+ prisma.stratagem.count({ where: query.where }),
+ prisma.stratagem.findMany(query),
+ ]);
+
+ return ctx.json({
+ data: stratagems.map(stratagem => {
+ // slightly transform the data
+ const imageBaseUrl = process.env.STRATAGEM_IMAGE_URL || "";
+ stratagem.imageUrl = `${imageBaseUrl}${stratagem.imageUrl}`;
+ (stratagem as any).keys = stratagem.keys.split(",");
+ return stratagem;
+ }),
+ error: null,
+ pagination: {
+ page: query.skip / query.take + 1,
+ pageSize: query.take,
+ pageCount: Math.ceil((count as number) / query.take),
+ total: count,
+ },
+ });
+ } catch (error: any) {
+ console.error(error);
+ ctx.status(500);
+ return ctx.json({
+ data: null,
+ error: { details: [error.message] },
+ });
+ }
+});
diff --git a/src/index.ts b/src/index.ts
index baf1478..9ebb726 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -2,6 +2,7 @@ import "jobs/refresh";
import { Hono } from "hono";
+import files from "middleware/files";
import cache from "middleware/cache";
import rateLimit from "middleware/rate-limit";
@@ -12,14 +13,27 @@ import planets from "routes/planets";
import sectors from "routes/sectors";
import attacks from "routes/attacks";
import factions from "routes/factions";
+import stratagems from "routes/stratagems";
// initiate hono api
const app = new Hono().basePath("/api");
+
+// middleware for the api
+app.use("/static/*", files);
app.use(rateLimit);
app.use(cache);
// routes for the api
-const routes = [planets, sectors, wars, factions, attacks, events, orders];
-for (const route of routes) await route(app);
+const routes = [
+ planets,
+ sectors,
+ wars,
+ factions,
+ attacks,
+ events,
+ orders,
+ stratagems,
+];
+for (const route of routes) await route(app);
export default app;
diff --git a/src/middleware/files.ts b/src/middleware/files.ts
new file mode 100644
index 0000000..9d64922
--- /dev/null
+++ b/src/middleware/files.ts
@@ -0,0 +1,97 @@
+import path from "path";
+import type { Context, Next } from "hono";
+
+const getMimeType = (filename: string) => {
+ const mimes = {
+ aac: "audio/aac",
+ avi: "video/x-msvideo",
+ avif: "image/avif",
+ av1: "video/av1",
+ bin: "application/octet-stream",
+ bmp: "image/bmp",
+ css: "text/css",
+ csv: "text/csv",
+ eot: "application/vnd.ms-fontobject",
+ epub: "application/epub+zip",
+ gif: "image/gif",
+ gz: "application/gzip",
+ htm: "text/html",
+ html: "text/html",
+ ico: "image/x-icon",
+ ics: "text/calendar",
+ jpeg: "image/jpeg",
+ jpg: "image/jpeg",
+ js: "text/javascript",
+ json: "application/json",
+ jsonld: "application/ld+json",
+ map: "application/json",
+ mid: "audio/x-midi",
+ midi: "audio/x-midi",
+ mjs: "text/javascript",
+ mp3: "audio/mpeg",
+ mp4: "video/mp4",
+ mpeg: "video/mpeg",
+ oga: "audio/ogg",
+ ogv: "video/ogg",
+ ogx: "application/ogg",
+ opus: "audio/opus",
+ otf: "font/otf",
+ pdf: "application/pdf",
+ png: "image/png",
+ rtf: "application/rtf",
+ svg: "image/svg+xml",
+ tif: "image/tiff",
+ tiff: "image/tiff",
+ ts: "video/mp2t",
+ ttf: "font/ttf",
+ txt: "text/plain",
+ wasm: "application/wasm",
+ webm: "video/webm",
+ weba: "audio/webm",
+ webp: "image/webp",
+ woff: "font/woff",
+ woff2: "font/woff2",
+ xhtml: "application/xhtml+xml",
+ xml: "application/xml",
+ zip: "application/zip",
+ "3gp": "video/3gpp",
+ "3g2": "video/3gpp2",
+ gltf: "model/gltf+json",
+ glb: "model/gltf-binary",
+ };
+
+ const regexp = /\.([a-zA-Z0-9]+?)$/;
+ const match = filename.match(regexp);
+ if (!match) return;
+ let mimeType = mimes[match[1] as keyof typeof mimes];
+ if (
+ (mimeType && mimeType.startsWith("text")) ||
+ mimeType === "application/json"
+ ) {
+ mimeType += "; charset=utf-8";
+ }
+ return mimeType;
+};
+
+export default async function files(ctx: Context, next: Next) {
+ try {
+ const _ctx = {
+ ...ctx,
+ req: { ...ctx.req, url: ctx.req.url.replace("/api", "") },
+ };
+
+ const { pathname } = new URL(_ctx.req.url);
+ if (!pathname.startsWith("/static/")) await next();
+
+ const file = Bun.file(path.join(process.cwd(), "src", pathname));
+ if (!file) await next();
+
+ const mimeType = getMimeType(`..${pathname}`);
+ if (!mimeType) return await next();
+
+ const content = await file.arrayBuffer();
+ return ctx.newResponse(content, 200, { "Content-Type": mimeType });
+ } catch {
+ return ctx.newResponse("Not Found", 404);
+ }
+}
diff --git a/src/routes/stratagems.ts b/src/routes/stratagems.ts
new file mode 100644
index 0000000..f559afd
--- /dev/null
+++ b/src/routes/stratagems.ts
@@ -0,0 +1,9 @@
+import type { Hono } from "hono";
+
+import * as Stratagems from "controllers/stratagems";
+
+export default async function stratagems(app: Hono) {
+ app.get("/stratagems", Stratagems.getAllStratagems);
+ app.get("/stratagems/:id", Stratagems.getStratagemById);
+}
+//https://api-helldivers-companion.koyeb.app
diff --git a/src/static/images/stratagems/1/1.svg b/src/static/images/stratagems/1/1.svg
new file mode 100644
index 0000000..44a2f83
--- /dev/null
+++ b/src/static/images/stratagems/1/1.svg
@@ -0,0 +1,24 @@
+
diff --git a/src/static/images/stratagems/1/2.svg b/src/static/images/stratagems/1/2.svg
new file mode 100644
index 0000000..dfdeee5
--- /dev/null
+++ b/src/static/images/stratagems/1/2.svg
@@ -0,0 +1,25 @@
+
diff --git a/src/static/images/stratagems/1/3.svg b/src/static/images/stratagems/1/3.svg
new file mode 100644
index 0000000..144371f
--- /dev/null
+++ b/src/static/images/stratagems/1/3.svg
@@ -0,0 +1,22 @@
+
diff --git a/src/static/images/stratagems/1/4.svg b/src/static/images/stratagems/1/4.svg
new file mode 100644
index 0000000..272d80b
--- /dev/null
+++ b/src/static/images/stratagems/1/4.svg
@@ -0,0 +1,21 @@
+
diff --git a/src/static/images/stratagems/1/5.svg b/src/static/images/stratagems/1/5.svg
new file mode 100644
index 0000000..f5f5069
--- /dev/null
+++ b/src/static/images/stratagems/1/5.svg
@@ -0,0 +1,21 @@
+
diff --git a/src/static/images/stratagems/1/6.svg b/src/static/images/stratagems/1/6.svg
new file mode 100644
index 0000000..0ab4c3f
--- /dev/null
+++ b/src/static/images/stratagems/1/6.svg
@@ -0,0 +1,21 @@
+
diff --git a/src/static/images/stratagems/1/7.svg b/src/static/images/stratagems/1/7.svg
new file mode 100644
index 0000000..11a2a4b
--- /dev/null
+++ b/src/static/images/stratagems/1/7.svg
@@ -0,0 +1,22 @@
+
diff --git a/src/static/images/stratagems/2/1.svg b/src/static/images/stratagems/2/1.svg
new file mode 100644
index 0000000..b8f2600
--- /dev/null
+++ b/src/static/images/stratagems/2/1.svg
@@ -0,0 +1,23 @@
+
diff --git a/src/static/images/stratagems/2/2.svg b/src/static/images/stratagems/2/2.svg
new file mode 100644
index 0000000..baba06e
--- /dev/null
+++ b/src/static/images/stratagems/2/2.svg
@@ -0,0 +1,21 @@
+
diff --git a/src/static/images/stratagems/2/3.svg b/src/static/images/stratagems/2/3.svg
new file mode 100644
index 0000000..fb62476
--- /dev/null
+++ b/src/static/images/stratagems/2/3.svg
@@ -0,0 +1,22 @@
+
diff --git a/src/static/images/stratagems/2/4.svg b/src/static/images/stratagems/2/4.svg
new file mode 100644
index 0000000..5664859
--- /dev/null
+++ b/src/static/images/stratagems/2/4.svg
@@ -0,0 +1,21 @@
+
diff --git a/src/static/images/stratagems/2/5.svg b/src/static/images/stratagems/2/5.svg
new file mode 100644
index 0000000..14d19cf
--- /dev/null
+++ b/src/static/images/stratagems/2/5.svg
@@ -0,0 +1,21 @@
+
diff --git a/src/static/images/stratagems/2/6.svg b/src/static/images/stratagems/2/6.svg
new file mode 100644
index 0000000..524400c
--- /dev/null
+++ b/src/static/images/stratagems/2/6.svg
@@ -0,0 +1,23 @@
+
diff --git a/src/static/images/stratagems/2/7.svg b/src/static/images/stratagems/2/7.svg
new file mode 100644
index 0000000..de5ec71
--- /dev/null
+++ b/src/static/images/stratagems/2/7.svg
@@ -0,0 +1,21 @@
+
diff --git a/src/static/images/stratagems/2/8.svg b/src/static/images/stratagems/2/8.svg
new file mode 100644
index 0000000..7dbf346
--- /dev/null
+++ b/src/static/images/stratagems/2/8.svg
@@ -0,0 +1,22 @@
+
diff --git a/src/static/images/stratagems/2/9.svg b/src/static/images/stratagems/2/9.svg
new file mode 100644
index 0000000..b18ff0a
--- /dev/null
+++ b/src/static/images/stratagems/2/9.svg
@@ -0,0 +1,21 @@
+
diff --git a/src/static/images/stratagems/3/1.svg b/src/static/images/stratagems/3/1.svg
new file mode 100644
index 0000000..0239aef
--- /dev/null
+++ b/src/static/images/stratagems/3/1.svg
@@ -0,0 +1,22 @@
+
diff --git a/src/static/images/stratagems/3/2.svg b/src/static/images/stratagems/3/2.svg
new file mode 100644
index 0000000..3af214b
--- /dev/null
+++ b/src/static/images/stratagems/3/2.svg
@@ -0,0 +1,40 @@
+
diff --git a/src/static/images/stratagems/3/3.svg b/src/static/images/stratagems/3/3.svg
new file mode 100644
index 0000000..22eda0f
--- /dev/null
+++ b/src/static/images/stratagems/3/3.svg
@@ -0,0 +1,23 @@
+
diff --git a/src/static/images/stratagems/3/4.svg b/src/static/images/stratagems/3/4.svg
new file mode 100644
index 0000000..bb0b4bb
--- /dev/null
+++ b/src/static/images/stratagems/3/4.svg
@@ -0,0 +1,24 @@
+
diff --git a/src/static/images/stratagems/3/5.svg b/src/static/images/stratagems/3/5.svg
new file mode 100644
index 0000000..4824da6
--- /dev/null
+++ b/src/static/images/stratagems/3/5.svg
@@ -0,0 +1,22 @@
+
diff --git a/src/static/images/stratagems/3/6.svg b/src/static/images/stratagems/3/6.svg
new file mode 100644
index 0000000..fd4ada4
--- /dev/null
+++ b/src/static/images/stratagems/3/6.svg
@@ -0,0 +1,28 @@
+
diff --git a/src/static/images/stratagems/3/7.svg b/src/static/images/stratagems/3/7.svg
new file mode 100644
index 0000000..156669d
--- /dev/null
+++ b/src/static/images/stratagems/3/7.svg
@@ -0,0 +1,23 @@
+
diff --git a/src/static/images/stratagems/3/8.svg b/src/static/images/stratagems/3/8.svg
new file mode 100644
index 0000000..4b72e0d
--- /dev/null
+++ b/src/static/images/stratagems/3/8.svg
@@ -0,0 +1,22 @@
+
diff --git a/src/static/images/stratagems/4/1.svg b/src/static/images/stratagems/4/1.svg
new file mode 100644
index 0000000..8400137
--- /dev/null
+++ b/src/static/images/stratagems/4/1.svg
@@ -0,0 +1,26 @@
+
diff --git a/src/static/images/stratagems/4/2.svg b/src/static/images/stratagems/4/2.svg
new file mode 100644
index 0000000..506c4f8
--- /dev/null
+++ b/src/static/images/stratagems/4/2.svg
@@ -0,0 +1,20 @@
+
diff --git a/src/static/images/stratagems/4/3.svg b/src/static/images/stratagems/4/3.svg
new file mode 100644
index 0000000..3a8cec2
--- /dev/null
+++ b/src/static/images/stratagems/4/3.svg
@@ -0,0 +1,21 @@
+
diff --git a/src/static/images/stratagems/4/4.svg b/src/static/images/stratagems/4/4.svg
new file mode 100644
index 0000000..e9ff34f
--- /dev/null
+++ b/src/static/images/stratagems/4/4.svg
@@ -0,0 +1,22 @@
+
diff --git a/src/static/images/stratagems/4/5.svg b/src/static/images/stratagems/4/5.svg
new file mode 100644
index 0000000..680ad14
--- /dev/null
+++ b/src/static/images/stratagems/4/5.svg
@@ -0,0 +1,22 @@
+
diff --git a/src/static/images/stratagems/4/6.svg b/src/static/images/stratagems/4/6.svg
new file mode 100644
index 0000000..f3be370
--- /dev/null
+++ b/src/static/images/stratagems/4/6.svg
@@ -0,0 +1,21 @@
+
diff --git a/src/static/images/stratagems/4/7.svg b/src/static/images/stratagems/4/7.svg
new file mode 100644
index 0000000..50e6147
--- /dev/null
+++ b/src/static/images/stratagems/4/7.svg
@@ -0,0 +1,20 @@
+
diff --git a/src/static/images/stratagems/4/8.svg b/src/static/images/stratagems/4/8.svg
new file mode 100644
index 0000000..d5fd52b
--- /dev/null
+++ b/src/static/images/stratagems/4/8.svg
@@ -0,0 +1,29 @@
+
diff --git a/src/static/images/stratagems/4/9.svg b/src/static/images/stratagems/4/9.svg
new file mode 100644
index 0000000..00e3098
--- /dev/null
+++ b/src/static/images/stratagems/4/9.svg
@@ -0,0 +1,26 @@
+
diff --git a/src/static/images/stratagems/5/1.svg b/src/static/images/stratagems/5/1.svg
new file mode 100644
index 0000000..bf8918c
--- /dev/null
+++ b/src/static/images/stratagems/5/1.svg
@@ -0,0 +1,23 @@
+
diff --git a/src/static/images/stratagems/5/2.svg b/src/static/images/stratagems/5/2.svg
new file mode 100644
index 0000000..cbd2eca
--- /dev/null
+++ b/src/static/images/stratagems/5/2.svg
@@ -0,0 +1,26 @@
+
diff --git a/src/static/images/stratagems/5/3.svg b/src/static/images/stratagems/5/3.svg
new file mode 100644
index 0000000..b45af17
--- /dev/null
+++ b/src/static/images/stratagems/5/3.svg
@@ -0,0 +1,39 @@
+
diff --git a/src/static/images/stratagems/5/4.svg b/src/static/images/stratagems/5/4.svg
new file mode 100644
index 0000000..8cb9ca8
--- /dev/null
+++ b/src/static/images/stratagems/5/4.svg
@@ -0,0 +1,29 @@
+
diff --git a/src/static/images/stratagems/5/5.svg b/src/static/images/stratagems/5/5.svg
new file mode 100644
index 0000000..0bcb75f
--- /dev/null
+++ b/src/static/images/stratagems/5/5.svg
@@ -0,0 +1,28 @@
+
diff --git a/src/static/images/stratagems/5/6.svg b/src/static/images/stratagems/5/6.svg
new file mode 100644
index 0000000..13a5e25
--- /dev/null
+++ b/src/static/images/stratagems/5/6.svg
@@ -0,0 +1,22 @@
+
diff --git a/src/static/images/stratagems/5/7.svg b/src/static/images/stratagems/5/7.svg
new file mode 100644
index 0000000..e0c89d6
--- /dev/null
+++ b/src/static/images/stratagems/5/7.svg
@@ -0,0 +1,33 @@
+
diff --git a/src/static/images/stratagems/6/1.svg b/src/static/images/stratagems/6/1.svg
new file mode 100644
index 0000000..df9361a
--- /dev/null
+++ b/src/static/images/stratagems/6/1.svg
@@ -0,0 +1,21 @@
+
diff --git a/src/static/images/stratagems/6/2.svg b/src/static/images/stratagems/6/2.svg
new file mode 100644
index 0000000..79957e6
--- /dev/null
+++ b/src/static/images/stratagems/6/2.svg
@@ -0,0 +1,21 @@
+
diff --git a/src/static/images/stratagems/6/3.svg b/src/static/images/stratagems/6/3.svg
new file mode 100644
index 0000000..eb42fca
--- /dev/null
+++ b/src/static/images/stratagems/6/3.svg
@@ -0,0 +1,21 @@
+
diff --git a/src/static/images/stratagems/6/4.svg b/src/static/images/stratagems/6/4.svg
new file mode 100644
index 0000000..dde00e7
--- /dev/null
+++ b/src/static/images/stratagems/6/4.svg
@@ -0,0 +1,21 @@
+
diff --git a/src/static/images/stratagems/6/5.svg b/src/static/images/stratagems/6/5.svg
new file mode 100644
index 0000000..5dd9fdd
--- /dev/null
+++ b/src/static/images/stratagems/6/5.svg
@@ -0,0 +1,23 @@
+
diff --git a/src/static/images/stratagems/6/6.svg b/src/static/images/stratagems/6/6.svg
new file mode 100644
index 0000000..0373750
--- /dev/null
+++ b/src/static/images/stratagems/6/6.svg
@@ -0,0 +1,20 @@
+
diff --git a/src/static/images/stratagems/6/7.svg b/src/static/images/stratagems/6/7.svg
new file mode 100644
index 0000000..6ad890c
--- /dev/null
+++ b/src/static/images/stratagems/6/7.svg
@@ -0,0 +1,21 @@
+
diff --git a/src/static/images/stratagems/6/8.svg b/src/static/images/stratagems/6/8.svg
new file mode 100644
index 0000000..4e38d24
--- /dev/null
+++ b/src/static/images/stratagems/6/8.svg
@@ -0,0 +1,23 @@
+
diff --git a/src/static/images/stratagems/6/9.svg b/src/static/images/stratagems/6/9.svg
new file mode 100644
index 0000000..95f208d
--- /dev/null
+++ b/src/static/images/stratagems/6/9.svg
@@ -0,0 +1,21 @@
+
diff --git a/src/static/images/stratagems/8/1.svg b/src/static/images/stratagems/8/1.svg
new file mode 100644
index 0000000..4e9d020
--- /dev/null
+++ b/src/static/images/stratagems/8/1.svg
@@ -0,0 +1,23 @@
+
diff --git a/src/static/images/stratagems/8/2.svg b/src/static/images/stratagems/8/2.svg
new file mode 100644
index 0000000..1733bff
--- /dev/null
+++ b/src/static/images/stratagems/8/2.svg
@@ -0,0 +1,22 @@
+
diff --git a/src/static/images/stratagems/8/3.svg b/src/static/images/stratagems/8/3.svg
new file mode 100644
index 0000000..6913f6f
--- /dev/null
+++ b/src/static/images/stratagems/8/3.svg
@@ -0,0 +1,27 @@
+
diff --git a/src/static/images/stratagems/8/4.svg b/src/static/images/stratagems/8/4.svg
new file mode 100644
index 0000000..a338299
--- /dev/null
+++ b/src/static/images/stratagems/8/4.svg
@@ -0,0 +1,21 @@
+
diff --git a/src/static/images/stratagems/8/5.svg b/src/static/images/stratagems/8/5.svg
new file mode 100644
index 0000000..ed61472
--- /dev/null
+++ b/src/static/images/stratagems/8/5.svg
@@ -0,0 +1,21 @@
+
diff --git a/src/static/images/stratagems/8/6.svg b/src/static/images/stratagems/8/6.svg
new file mode 100644
index 0000000..103160a
--- /dev/null
+++ b/src/static/images/stratagems/8/6.svg
@@ -0,0 +1,22 @@
+
diff --git a/src/static/images/stratagems/8/7.svg b/src/static/images/stratagems/8/7.svg
new file mode 100644
index 0000000..b0f9b44
--- /dev/null
+++ b/src/static/images/stratagems/8/7.svg
@@ -0,0 +1,23 @@
+
diff --git a/src/static/images/stratagems/8/8.svg b/src/static/images/stratagems/8/8.svg
new file mode 100644
index 0000000..edc31f0
--- /dev/null
+++ b/src/static/images/stratagems/8/8.svg
@@ -0,0 +1,21 @@
+
diff --git a/src/static/effects.json b/src/static/json/effects.json
similarity index 100%
rename from src/static/effects.json
rename to src/static/json/effects.json
diff --git a/src/static/factions.json b/src/static/json/factions.json
similarity index 100%
rename from src/static/factions.json
rename to src/static/json/factions.json
diff --git a/src/static/planets.json b/src/static/json/planets.json
similarity index 100%
rename from src/static/planets.json
rename to src/static/json/planets.json
diff --git a/src/static/sectors.json b/src/static/json/sectors.json
similarity index 100%
rename from src/static/sectors.json
rename to src/static/json/sectors.json
diff --git a/src/static/json/stratagems.json b/src/static/json/stratagems.json
new file mode 100644
index 0000000..4cf14b8
--- /dev/null
+++ b/src/static/json/stratagems.json
@@ -0,0 +1,550 @@
+{
+ "1": {
+ "name": "Bridge",
+ "entries": [
+ {
+ "codename": "E/MG-101",
+ "name": "HMG Emplacement",
+ "keys": ["down", "up", "left", "right", "right", "left"],
+ "uses": "Unlimited",
+ "cooldown": 180,
+ "activation": 3,
+ "imageUrl": "/1/1.svg"
+ },
+ {
+ "codename": null,
+ "name": "Orbital EMS Strike",
+ "keys": ["right", "right", "left", "down"],
+ "uses": "Unlimited",
+ "cooldown": 75,
+ "activation": 1,
+ "imageUrl": "/1/2.svg"
+ },
+ {
+ "codename": null,
+ "name": "Orbital Gas Strike",
+ "keys": ["right", "right", "down", "right"],
+ "uses": "Unlimited",
+ "cooldown": 75,
+ "activation": 1,
+ "imageUrl": "/1/3.svg"
+ },
+ {
+ "codename": null,
+ "name": "Orbital Precision Strike",
+ "keys": ["right", "right", "up"],
+ "uses": "Unlimited",
+ "cooldown": 100,
+ "activation": 3,
+ "imageUrl": "/1/4.svg"
+ },
+ {
+ "codename": null,
+ "name": "Orbital Smoke Strike",
+ "keys": ["right", "right", "down", "up"],
+ "uses": "Unlimited",
+ "cooldown": 100,
+ "activation": 1,
+ "imageUrl": "/1/5.svg"
+ },
+ {
+ "codename": "FX-12",
+ "name": "Shield Generator Relay",
+ "keys": ["down", "down", "left", "right", "left", "right"],
+ "uses": "Unlimited",
+ "cooldown": 90,
+ "activation": 3,
+ "imageUrl": "/1/6.svg"
+ },
+ {
+ "codename": "A/ARC-3",
+ "name": "Tesla Tower",
+ "keys": ["down", "up", "right", "up", "left", "right"],
+ "uses": "Unlimited",
+ "cooldown": 150,
+ "activation": 3,
+ "imageUrl": "/1/7.svg"
+ }
+ ]
+ },
+ "2": {
+ "name": "Engineering Bay",
+ "entries": [
+ {
+ "codename": "MD-6",
+ "name": "Anti-Personnel Minefield",
+ "keys": ["down", "right", "up", "right"],
+ "uses": "Unlimited",
+ "cooldown": 180,
+ "activation": 3,
+ "imageUrl": "/2/1.svg"
+ },
+ {
+ "codename": "ARC-3",
+ "name": "Arc Thrower",
+ "keys": ["down", "right", "down", "up", "left", "left"],
+ "uses": "Unlimited",
+ "cooldown": 480,
+ "activation": 3,
+ "imageUrl": "/2/2.svg"
+ },
+ {
+ "codename": "SH-20",
+ "name": "Ballistic Shield Backpack",
+ "keys": ["down", "left", "down", "down", "up", "left"],
+ "uses": "Unlimited",
+ "cooldown": 300,
+ "activation": 5,
+ "imageUrl": "/2/3.svg"
+ },
+ {
+ "codename": "GL-21",
+ "name": "Grenade Launcher",
+ "keys": ["down", "left", "up", "left", "down"],
+ "uses": "Unlimited",
+ "cooldown": 480,
+ "activation": 3,
+ "imageUrl": "/2/4.svg"
+ },
+ {
+ "codename": "AX/LAS-5",
+ "name": "\"Guard Dog\" Rover",
+ "keys": ["down", "up", "left", "up", "right", "right"],
+ "uses": "Unlimited",
+ "cooldown": 480,
+ "activation": 5,
+ "imageUrl": "/2/5.svg"
+ },
+ {
+ "codename": "MD-I4",
+ "name": "Incendiary Mines",
+ "keys": ["down", "left", "left", "down"],
+ "uses": "Unlimited",
+ "cooldown": 180,
+ "activation": 3,
+ "imageUrl": "/2/6.svg"
+ },
+ {
+ "codename": "LAS-98",
+ "name": "Laser Cannon",
+ "keys": ["down", "left", "down", "up", "left"],
+ "uses": "Unlimited",
+ "cooldown": 480,
+ "activation": 3,
+ "imageUrl": "/2/7.svg"
+ },
+ {
+ "codename": "SH-20",
+ "name": "Shield Generator Pack",
+ "keys": ["down", "up", "left", "right", "left", "right"],
+ "uses": "Unlimited",
+ "cooldown": 480,
+ "activation": 5,
+ "imageUrl": "/2/8.svg"
+ },
+ {
+ "codename": "B-1",
+ "name": "Supply Pack",
+ "keys": ["down", "left", "down", "up", "up", "down"],
+ "uses": "Unlimited",
+ "cooldown": 480,
+ "activation": 5,
+ "imageUrl": "/2/9.svg"
+ }
+ ]
+ },
+ "3": {
+ "name": "General Stratagems",
+ "entries": [
+ {
+ "codename": "NUX-223",
+ "name": "Hellbomb",
+ "keys": ["down", "up", "left", "down", "up", "right", "down", "up"],
+ "uses": "Unlimited",
+ "cooldown": null,
+ "activation": null,
+ "imageUrl": "/3/1.svg"
+ },
+ {
+ "codename": null,
+ "name": "Orbital Illumination Flare",
+ "keys": ["right", "right", "left", "left"],
+ "uses": "Unlimited",
+ "cooldown": null,
+ "activation": null,
+ "imageUrl": "/3/2.svg"
+ },
+ {
+ "codename": null,
+ "name": "Reinforce",
+ "keys": ["up", "down", "right", "left", "up"],
+ "uses": "5 per Person",
+ "cooldown": 120,
+ "activation": 5,
+ "imageUrl": "/3/3.svg"
+ },
+ {
+ "codename": null,
+ "name": "Resupply",
+ "keys": ["down", "down", "up", "right"],
+ "uses": "Unlimited",
+ "cooldown": 160,
+ "activation": 12,
+ "imageUrl": "/3/4.svg"
+ },
+ {
+ "codename": null,
+ "name": "SEAF Artillery",
+ "keys": ["right", "up", "up", "down"],
+ "uses": "5 Uses",
+ "cooldown": null,
+ "activation": null,
+ "imageUrl": "/3/5.svg"
+ },
+ {
+ "codename": null,
+ "name": "SOS Beacon",
+ "keys": ["up", "down", "right", "up"],
+ "uses": "Single use",
+ "cooldown": null,
+ "activation": 5,
+ "imageUrl": "/3/6.svg"
+ },
+ {
+ "codename": null,
+ "name": "Seismic Probe",
+ "keys": ["up", "up", "left", "right", "down", "down"],
+ "uses": "Single use",
+ "cooldown": null,
+ "activation": 9,
+ "imageUrl": "/3/7.svg"
+ },
+ {
+ "codename": null,
+ "name": "Upload Data",
+ "keys": ["down", "down", "up", "up", "up"],
+ "uses": "Unlimited",
+ "cooldown": null,
+ "activation": null,
+ "imageUrl": "/3/8.svg"
+ }
+ ]
+ },
+ "4": {
+ "name": "Hangar",
+ "entries": [
+ {
+ "codename": null,
+ "name": "Eagle 110MM Rocket Pods",
+ "keys": ["up", "right", "up", "left"],
+ "uses": "2 Uses",
+ "cooldown": 8,
+ "activation": 3,
+ "imageUrl": "/4/1.svg"
+ },
+ {
+ "codename": null,
+ "name": "Eagle 500KG Bomb",
+ "keys": ["up", "right", "down", "down", "down"],
+ "uses": "1 Use",
+ "cooldown": 8,
+ "activation": 3,
+ "imageUrl": "/4/2.svg"
+ },
+ {
+ "codename": null,
+ "name": "Eagle Airstrike",
+ "keys": ["up", "right", "down", "right"],
+ "uses": "2 Uses",
+ "cooldown": 8,
+ "activation": 3,
+ "imageUrl": "/4/3.svg"
+ },
+ {
+ "codename": null,
+ "name": "Eagle Cluster Bomb",
+ "keys": ["up", "right", "down", "down", "right"],
+ "uses": "4 Uses",
+ "cooldown": 8,
+ "activation": 3,
+ "imageUrl": "/4/4.svg"
+ },
+ {
+ "codename": null,
+ "name": "Eagle Napalm Airstrike",
+ "keys": ["up", "right", "down", "up"],
+ "uses": "2 Uses",
+ "cooldown": 8,
+ "activation": 3,
+ "imageUrl": "/4/5.svg"
+ },
+ {
+ "codename": null,
+ "name": "Eagle Rearm",
+ "keys": ["up", "up", "left", "up", "right"],
+ "uses": "Unlimited",
+ "cooldown": 120,
+ "activation": null,
+ "imageUrl": "/4/6.svg"
+ },
+ {
+ "codename": null,
+ "name": "Eagle Smoke Strike",
+ "keys": ["up", "right", "up", "down"],
+ "uses": "2 Uses",
+ "cooldown": 8,
+ "activation": 3,
+ "imageUrl": "/4/7.svg"
+ },
+ {
+ "codename": null,
+ "name": "Eagle Strafing Run",
+ "keys": ["up", "right", "down"],
+ "uses": "3 Uses",
+ "cooldown": 8,
+ "activation": 1,
+ "imageUrl": "/4/8.svg"
+ },
+ {
+ "codename": "LIFT-850",
+ "name": "Jump Pack",
+ "keys": ["down", "up", "up", "down", "up"],
+ "uses": "",
+ "cooldown": 0,
+ "activation": 0,
+ "imageUrl": "/4/9.svg"
+ }
+ ]
+ },
+ "5": {
+ "name": "Orbital Canons",
+ "entries": [
+ {
+ "codename": null,
+ "name": "Orbital 120MM HE Barrage",
+ "keys": ["right", "right", "down", "left", "right", "down"],
+ "uses": "Unlimited",
+ "cooldown": 240,
+ "activation": 4,
+ "imageUrl": "/5/1.svg"
+ },
+ {
+ "codename": null,
+ "name": "Orbital 380MM HE Barrage",
+ "keys": ["right", "down", "up", "up", "left", "down", "down"],
+ "uses": "Unlimited",
+ "cooldown": 240,
+ "activation": 5,
+ "imageUrl": "/5/2.svg"
+ },
+ {
+ "codename": null,
+ "name": "Orbital Airburst Strike",
+ "keys": ["right", "right", "right"],
+ "uses": "Unlimited",
+ "cooldown": 120,
+ "activation": 3,
+ "imageUrl": "/5/3.svg"
+ },
+ {
+ "codename": null,
+ "name": "Orbital Gatling Barrage",
+ "keys": ["right", "down", "left", "up", "up"],
+ "uses": "Unlimited",
+ "cooldown": 80,
+ "activation": 1,
+ "imageUrl": "/5/4.svg"
+ },
+ {
+ "codename": null,
+ "name": "Orbital Laser",
+ "keys": ["right", "down", "up", "right", "down"],
+ "uses": "3 Uses",
+ "cooldown": 300,
+ "activation": 1,
+ "imageUrl": "/5/5.svg"
+ },
+ {
+ "codename": null,
+ "name": "Orbital Railcannon Strike",
+ "keys": ["right", "up", "down", "down", "right"],
+ "uses": "Unlimited",
+ "cooldown": 120,
+ "activation": 2,
+ "imageUrl": "/5/6.svg"
+ },
+ {
+ "codename": null,
+ "name": "Orbital Walking Barrage",
+ "keys": ["right", "down", "right", "down", "right", "down"],
+ "uses": "Unlimited",
+ "cooldown": 240,
+ "activation": 2,
+ "imageUrl": "/5/7.svg"
+ }
+ ]
+ },
+ "6": {
+ "name": "Patriotic Administration Center",
+ "entries": [
+ {
+ "codename": "APW-1",
+ "name": "Anti-Materiel Rifle",
+ "keys": ["down", "left", "right", "up", "down"],
+ "uses": "Unlimited",
+ "cooldown": 480,
+ "activation": 3,
+ "imageUrl": "/6/1.svg"
+ },
+ {
+ "codename": "AC-8",
+ "name": "Autocannon",
+ "keys": ["down", "left", "down", "up", "up", "right"],
+ "uses": "Unlimited",
+ "cooldown": 480,
+ "activation": 3,
+ "imageUrl": "/6/2.svg"
+ },
+ {
+ "codename": "EAT-17",
+ "name": "Expendable Anti-tank",
+ "keys": ["down", "down", "left", "up", "right"],
+ "uses": "Unlimited",
+ "cooldown": 70,
+ "activation": 2,
+ "imageUrl": "/6/3.svg"
+ },
+ {
+ "codename": "FLAM-40",
+ "name": "Flamethrower",
+ "keys": ["down", "left", "up", "down", "up"],
+ "uses": "Unlimited",
+ "cooldown": 480,
+ "activation": 3,
+ "imageUrl": "/6/4.svg"
+ },
+ {
+ "codename": "MG-43",
+ "name": "Machine Gun",
+ "keys": ["down", "left", "down", "up", "right"],
+ "uses": "Unlimited",
+ "cooldown": 480,
+ "activation": 3,
+ "imageUrl": "/6/5.svg"
+ },
+ {
+ "codename": "RS-422",
+ "name": "Railgun",
+ "keys": ["down", "right", "down", "up", "left", "right"],
+ "uses": "Unlimited",
+ "cooldown": 480,
+ "activation": 3,
+ "imageUrl": "/6/6.svg"
+ },
+ {
+ "codename": "GR-8",
+ "name": "Recoilless Rifle",
+ "keys": ["down", "left", "right", "right", "left"],
+ "uses": "Unlimited",
+ "cooldown": 480,
+ "activation": 3,
+ "imageUrl": "/6/7.svg"
+ },
+ {
+ "codename": "FAF-14",
+ "name": "SPEAR Launcher",
+ "keys": ["down", "down", "up", "down", "down"],
+ "uses": "Unlimited",
+ "cooldown": 480,
+ "activation": 3,
+ "imageUrl": "/6/8.svg"
+ },
+ {
+ "codename": "M-105",
+ "name": "Stalwart",
+ "keys": ["down", "left", "down", "up", "up", "right"],
+ "uses": "Unlimited",
+ "cooldown": 480,
+ "activation": 3,
+ "imageUrl": "/6/9.svg"
+ }
+ ]
+ },
+ "7": {
+ "name": "Robotics Workshop",
+ "entries": [
+ {
+ "codename": "A/AC-8",
+ "name": "Autocannon Sentry",
+ "keys": ["down", "up", "right", "up", "left", "up"],
+ "uses": "Unlimited",
+ "cooldown": 180,
+ "activation": 3,
+ "imageUrl": "/7/1.svg"
+ },
+ {
+ "codename": "A/M-23",
+ "name": "EMS Mortar Sentry",
+ "keys": ["down", "up", "right", "down", "right"],
+ "uses": "Unlimited",
+ "cooldown": 180,
+ "activation": 3,
+ "imageUrl": "/7/2.svg"
+ },
+ {
+ "codename": "A/G-16",
+ "name": "Gatling Sentry",
+ "keys": ["down", "up", "right", "left"],
+ "uses": "Unlimited",
+ "cooldown": 180,
+ "activation": 3,
+ "imageUrl": "/7/3.svg"
+ },
+ {
+ "codename": "AD-334",
+ "name": "Guard Dog",
+ "keys": ["down", "up", "left", "up", "right", "down"],
+ "uses": "Unlimited",
+ "cooldown": 480,
+ "activation": 5,
+ "imageUrl": "/7/4.svg"
+ },
+ {
+ "codename": "A/MG-43",
+ "name": "Machine Gun Sentry",
+ "keys": ["down", "up", "right", "right", "up"],
+ "uses": "Unlimited",
+ "cooldown": 180,
+ "activation": 3,
+ "imageUrl": "/7/5.svg"
+ },
+ {
+ "codename": "A/M-12",
+ "name": "Mortar Sentry",
+ "keys": ["down", "up", "right", "right", "down"],
+ "uses": "Unlimited",
+ "cooldown": 180,
+ "activation": 3,
+ "imageUrl": "/7/6.svg"
+ },
+ {
+ "codename": "EXO-45",
+ "name": "Patriot Exosuit",
+ "keys": ["left", "down", "right", "up", "left", "down", "down"],
+ "uses": "2 Uses",
+ "cooldown": 600,
+ "activation": 0,
+ "imageUrl": "/7/7.svg"
+ },
+ {
+ "codename": "A/MLS-4X",
+ "name": "Rocket Sentry",
+ "keys": ["down", "up", "right", "right", "left"],
+ "uses": "Unlimited",
+ "cooldown": 180,
+ "activation": 3,
+ "imageUrl": "/7/8.svg"
+ }
+ ]
+ }
+}
diff --git a/src/types/stratagem.ts b/src/types/stratagem.ts
new file mode 100644
index 0000000..3d5d773
--- /dev/null
+++ b/src/types/stratagem.ts
@@ -0,0 +1,18 @@
+export interface Stratagem {
+ name: string;
+ keys: string[];
+ uses: string;
+ imageUrl: string;
+ codename: string | null;
+ cooldown: number | null;
+ activation: number | null;
+}
+
+export interface StratagemCategory {
+ name: string;
+ entries: Stratagem[];
+}
+
+export interface StratagemMap {
+ [key: string]: StratagemCategory;
+}
diff --git a/src/utils/generate.ts b/src/utils/generate.ts
index a95041e..7615382 100644
--- a/src/utils/generate.ts
+++ b/src/utils/generate.ts
@@ -2,6 +2,7 @@ import path from "path";
import fs from "fs/promises";
import { PrismaClient } from "@prisma/client";
+import type { StratagemMap } from "types/stratagem";
import type { WarInfo, WarStatus } from "types/source";
export interface NameEntry {
@@ -28,21 +29,27 @@ export async function prepareForSourceData() {
SectorEntry[],
] = [[], [], []];
- const [planetsData, sectorsData, factionsData] = await Promise.all([
- fs.readFile(
- path.join(process.cwd(), "src", "static/planets.json"),
- "utf-8",
- ),
- fs.readFile(
- path.join(process.cwd(), "src", "static/sectors.json"),
- "utf-8",
- ),
- fs.readFile(
- path.join(process.cwd(), "src", "static/factions.json"),
- "utf-8",
- ),
- ]);
-
+ const [planetsData, sectorsData, factionsData, stratagemsData] =
+ await Promise.all([
+ fs.readFile(
+ path.join(process.cwd(), "src", "static/json/planets.json"),
+ "utf-8",
+ ),
+ fs.readFile(
+ path.join(process.cwd(), "src", "static/json/sectors.json"),
+ "utf-8",
+ ),
+ fs.readFile(
+ path.join(process.cwd(), "src", "static/json/factions.json"),
+ "utf-8",
+ ),
+ fs.readFile(
+ path.join(process.cwd(), "src", "static/json/stratagems.json"),
+ "utf-8",
+ ),
+ ]);
+
+ const stratagemsJSON: StratagemMap = JSON.parse(stratagemsData);
const planetsJSON: Record = JSON.parse(planetsData);
const factionsJSON: Record = JSON.parse(factionsData);
const sectorsJSON: Record> = JSON.parse(sectorsData);
@@ -74,7 +81,7 @@ export async function prepareForSourceData() {
factions.push({ index: parseInt(key), name });
}
- return { factions, planets, sectors };
+ return { factions, planets, sectors, stratagems: stratagemsJSON };
}
/**
@@ -111,9 +118,33 @@ export async function fetchSourceData() {
* database.
*/
export async function transformAndStoreSourceData() {
- const { factions, planets, sectors } = await prepareForSourceData();
const { warInfo, warStatus } = await fetchSourceData();
+ const { factions, planets, sectors, stratagems } =
+ await prepareForSourceData();
+
+ // index all stratagems
+ await Promise.all([
+ prisma.stratagem.deleteMany(),
+ prisma.stratagemGroup.deleteMany(),
+ ]);
+
+ for (const key in stratagems) {
+ const group = await prisma.stratagemGroup.create({
+ data: { name: stratagems[key].name },
+ });
+
+ for (const stratagem of stratagems[key].entries) {
+ await prisma.stratagem.create({
+ data: {
+ ...stratagem,
+ keys: stratagem.keys.join(","),
+ group: { connect: { id: group.id } },
+ },
+ });
+ }
+ }
+ // create the war data
await prisma.war.create({
data: {
index: warInfo.warId,