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,