Battler-Generator/index.ts
2024-08-13 13:13:43 +01:00

645 lines
18 KiB
TypeScript

import { createCanvas, loadImage, GlobalFonts } from "@napi-rs/canvas";
import {
GenerateBattlerOptions,
GenerateBattleImageOptions,
PlayerActionOptions,
Opponents,
CustomActionOptions,
CommunityOpponents
} from "./types";
import {
generateBattler, drawText, applyText,
validOpponents, validPlayerActions
} from "./utils";
import { createServer } from "node:http";
import GenerateParty from "./party"
GlobalFonts
.registerFromPath(import.meta.dirname + "/LilitaOne.ttf", "Lilita One");
const opponents = {
[Opponents.BattleBeginner]: {
text: "Battle Beginner",
colour: ["#007500", "#FFFFFF"]
},
[Opponents.BattleBeginner2024]: {
text: "Battle Beginner",
colour: ["#007500", "#FFFFFF"]
},
[Opponents.BattleCasual]: {
text: "Battle Casual",
colour: ["#757500", "#FFFFFF"]
},
[Opponents.BattleMaster]: {
text: "Battle Master",
colour: ["#750000", "#FFFFFF"]
},
[Opponents.BattlePro]: {
text: "Battle Pro",
colour: ["#753000", "#FFFFFF"]
},
[Opponents.Battler]: {
text: "Battler",
colour: ["#FFFFFF", "#000000"]
},
[Opponents.BattlerElite]: {
text: "Battler Elite",
colour: ["#FFFFFF", "#FF0000"]
},
[Opponents.None]: {
text: "",
colour: ["#FFFFFF", "#FFFFFF"]
},
[Opponents.RapStar]: {
text: "Rap Star",
colour: ["#FF0000", "#FFFFFF"]
},
[Opponents.ArtMaster]: {
text: "Art Master",
colour: ["#000000", "#FFFFFF"]
},
[Opponents.TheInterrogator]: {
text: "The Interrogator",
colour: ["#FFFFFF", "#000000"]
},
[Opponents.CommonOverseer]: {
text: "Common Overseer",
colour: ["#000000", "#32CD32"]
},
[Opponents.Moonlite]: {
text: "Moonlite",
colour: ["#000000", "#FFFFFF"]
},
[Opponents.Dicey]: {
text: "Dicey",
colour: ["#000000", "#FFFFFF"]
},
[Opponents.Kacey]: {
text: "Kacey",
colour: ["#000000", "#FFFFFF"]
},
[Opponents.Lexa]: {
text: "Lexa",
colour: ["#FFFFFF", "#FF00FF"]
},
[Opponents.Delta]: {
text: "Delta",
colour: ["#000000", "#FFFFFF"]
},
[Opponents.DoodlePro]: {
text: "Doodle Pro",
colour: ["#000000", "#FFFFFF"]
},
[Opponents.GuitarHero]: {
text: "Guitar Hero",
colour: ["#FFFFFF", "#FF8000"]
},
[Opponents.SmallKeyMaster]: {
text: "Small KeyMaster",
colour: ["#FFFFFF", "#32CD32"]
},
[Opponents.RareOverseer]: {
text: "Rare Overseer",
colour: ["#000000", "#00FFFF"]
},
[Opponents.DEMUL]: {
text: "DEMUL",
colour: ["#FFFFFF", "#000000"]
},
[Opponents.AgentZ]: {
text: "Agent Z",
colour: ["#FFFFFF", "#000000"]
},
[Opponents.MediumKeyMaster]: {
text: "Medium KeyMaster",
colour: ["#000000", "#00FFFF"]
},
[Opponents.IncomingSword]: {
text: "Incoming Sword",
colour: ["#FF0000", "#FFFFFF"]
},
[Opponents.Eda]: {
text: "Eda",
colour: ["#9000FF", "#FFFFFF"]
},
[Opponents.TheFirewall]: {
text: "The Firewall",
colour: ["#FFFFFF", "#FFAA00"]
},
[Opponents.HouseannorSupport]: {
text: "Houseannor Support",
colour: ["#FF00FF", "#FFFFFF"]
},
[Opponents.Dicey2024]: {
text: "Dicey",
colour: ["#000000", "#FFFFFF"]
},
[Opponents.CakeDay]: {
text: "Cake Day",
colour: ["#FA61FF", "#FFFFFF"]
},
[Opponents.MasterOG]: {
text: "Master OG",
colour: ["#FF0000", "#FFFFFF"]
}
}
const communityopponents = {
[CommunityOpponents.Stik]: {
text: "Stik",
colour: ["#FF0000", "#000000"]
}
}
const server = createServer(async (req, res) => {
if (req.method === "OPTIONS") {
res.setHeader("access-control-allow-origin", "*")
return res.end();
}
const url = new URL(req.url!, "https://loc.al");
if (req.method == "GET" && url.pathname == "/status") {
return res.end("OK")
}
if (req.method == "GET" && url.pathname == "/battler.png") {
console.log(`Generating battler`)
const opts = Object.fromEntries(
Array.from(url.searchParams.entries())
.map(([key, value]) => [key, value.replaceAll('+', ' ')]),
) as GenerateBattlerOptions;
const battler = await generateBattler(opts);
res.setHeader('content-type', 'image/png');
return res.end(battler.toBuffer("image/png"), 'binary')
}
if (req.method == "GET" && url.pathname == "/profilebattler.png") {
const opts = Object.fromEntries(
Array.from(url.searchParams.entries())
.map(([key, value]) => [key, value.replaceAll('+', ' ')]),
) as GenerateBattlerOptions & { username: string };
console.log(`Generating profile battler for ${opts.username}`)
const canvas = createCanvas(1280, 1280);
const ctx = canvas.getContext("2d");
const battler = await generateBattler(opts)
ctx.drawImage(battler, 0, 0);
ctx.textAlign = "center"
ctx.font = applyText(canvas, opts.username, canvas.width - 20)
drawText([640, 1100], opts.username, opts.username == "homeannor" ? "#AA00FF" : "#000000", { colour: "#FFFFFF", width: 20 }, ctx)
res.setHeader('content-type', 'image/png');
return res.end(canvas.toBuffer("image/png"), 'binary')
}
if (
req.method == "GET" &&
(url.pathname == "/battlesquadfight.png" ||
url.pathname == "/battlestart.png")
) {
const opts = Object.fromEntries(
Array.from(url.searchParams.entries())
.map(([key, value]) => [key, value.replaceAll('+', ' ')]),
) as GenerateBattlerOptions & GenerateBattleImageOptions;
if (!opts.opponent) {
res.writeHead(400, { 'content-type': "application/json" })
return res.end(
JSON.stringify({
error: '"opponent" query parameter not specified',
})
);
}
console.log(`Generating battle start for ${opts.username} and ${opts.opponent}`)
// Image dimensions
const canvas = createCanvas(1920, 1080);
const context = canvas.getContext("2d");
if (!validOpponents.includes(opts.opponent)) opts.opponent = Opponents.None
const SquadBackground = await loadImage(
`./assets/battlebgs/${opts.opponent}.png`,
);
context.drawImage(SquadBackground, 0, 0, canvas.width, canvas.height);
const battler = await generateBattler(opts);
context.drawImage(battler, -140, -100, battler.width, battler.height);
context.textAlign = "center"
context.font = applyText(canvas, opponents[opts.opponent].text, 875)
drawText([1450, 1000], opponents[opts.opponent].text, opponents[opts.opponent].colour[1], { colour: opponents[opts.opponent].colour[0], width: 20 }, context);
context.font = applyText(canvas, opts.username, 675)
drawText([470, 1000], opts.username, opts.username == "homeannor" ? "#AA00FF" : "#000000", { colour: "#FFFFFF", width: 20 }, context)
res.setHeader('content-type', 'image/png');
return res.end(canvas.toBuffer("image/png"), 'binary')
}
if (
req.method == "GET" &&
url.pathname == "/communityvs.png"
) {
const opts = Object.fromEntries(
Array.from(url.searchParams.entries())
.map(([key, value]) => [key, value.replaceAll('+', ' ')]),
) as GenerateBattlerOptions & GenerateBattleImageOptions;
if (!opts.opponent) {
res.writeHead(400, { 'content-type': "application/json" })
return res.end(
JSON.stringify({
error: '"opponent" query parameter not specified',
})
);
}
console.log(`Generating battle start for ${opts.username} and ${opts.opponent}`)
// Image dimensions
const canvas = createCanvas(1920, 1080);
const context = canvas.getContext("2d");
if (!validOpponents.includes(opts.opponent)) opts.opponent = CommunityOpponents.None
const SquadBackground = await loadImage(
`./assets/communityvs/${opts.opponent}.png`,
);
context.drawImage(SquadBackground, 0, 0, canvas.width, canvas.height);
const battler = await generateBattler(opts);
context.drawImage(battler, -140, -100, battler.width, battler.height);
context.textAlign = "center"
context.font = applyText(canvas, opponents[opts.opponent].text, 875)
drawText([1450, 1000], opponents[opts.opponent].text, opponents[opts.opponent].colour[1], { colour: opponents[opts.opponent].colour[0], width: 20 }, context);
context.font = applyText(canvas, opts.username, 675)
drawText([470, 1000], opts.username, opts.username == "homeannor" ? "#AA00FF" : "#000000", { colour: "#FFFFFF", width: 20 }, context)
res.setHeader('content-type', 'image/png');
return res.end(canvas.toBuffer("image/png"), 'binary')
}
if (
req.method == "GET" &&
url.pathname == "/playeraction.png"
) {
const opts = Object.fromEntries(
Array.from(url.searchParams.entries())
.map(([key, value]) => [key, value.replaceAll('+', ' ')]),
) as PlayerActionOptions;
if (!opts.action) {
res.writeHead(400, { 'content-type': "application/json" })
return res.end(
JSON.stringify({
error: '"action" query parameter not specified',
})
);
}
if (!validPlayerActions.includes(opts.action)) {
res.writeHead(400, { 'content-type': "application/json" })
return res.end(
JSON.stringify({
error:
'"action" query parameter not formatted correctly. Got "' +
opts.action +
'".',
})
);
}
console.log(`Generating ${opts.action} for ${opts.username}`)
// Image dimensions
const canvas = createCanvas(1920, 1080);
const context = canvas.getContext("2d");
const actionBackground = await loadImage(
`./assets/actionbgs/${opts.action}.png`,
);
context.drawImage(actionBackground, 0, 0, canvas.width, canvas.height);
const pUrl = new URL(encodeURI(opts.player))
const player = Object.fromEntries(
Array.from(pUrl.searchParams.entries())
.map(([key, value]) => [key, value.replaceAll('+', ' ')]),
) as GenerateBattlerOptions
if (opts.action.endsWith("Left")) {
const battler = await generateBattler(player);
context.drawImage(battler, -50, -100, battler.width, battler.height);
} else if (opts.action.endsWith("Right")) {
const battler = await generateBattler({ ...player, direction: "left" });
context.drawImage(battler, (canvas.width / 2) - 300, -100, battler.width, battler.height);
}
res.setHeader('content-type', 'image/png');
return res.end(canvas.toBuffer("image/png"), 'binary')
}
if (
req.method == "GET" &&
url.pathname == "/customaction.png"
) {
const opts = Object.fromEntries(
Array.from(url.searchParams.entries())
.map(([key, value]) => [key, value.replaceAll('+', ' ')]),
) as CustomActionOptions;
if (!opts.background) {
res.writeHead(400, { 'content-type': "application/json" })
return res.end(
JSON.stringify({
error:
'"background" query parameter not found',
})
);
}
console.log(`Generating custom action (${opts.background})`)
// Image dimensions
const canvas = createCanvas(1920, 1080);
const context = canvas.getContext("2d");
const actionBackground = await loadImage(
opts.background,
);
context.drawImage(actionBackground, 0, 0, canvas.width, canvas.height);
const pUrl = new URL(encodeURI(opts.player))
const player = Object.fromEntries(
Array.from(pUrl.searchParams.entries())
.map(([key, value]) => [key, value.replaceAll('+', ' ')]),
) as GenerateBattlerOptions
const battler = await generateBattler(player);
if (opts.position == "middle") {
context.drawImage(battler, (1920 / 2) - 640, -100, battler.width, battler.height);
} else {
context.drawImage(battler, -50, -100, battler.width, battler.height);
}
res.setHeader('content-type', 'image/png');
return res.end(canvas.toBuffer("image/png"), 'binary')
}
if (
req.method == "GET" &&
url.pathname == "/playermap.png"
) {
const opts = Object.fromEntries(
Array.from(url.searchParams.entries())
.map(([key, value]) => [key, value.replaceAll('+', ' ')]),
) as { player1: string, player2: string, map: string };
if (!opts.player1 || !opts.player2 || !opts.map) {
res.writeHead(400, { 'content-type': "application/json" })
return res.end(
JSON.stringify({
error:
'Did not pass all parameters',
})
);
}
console.log(`Generating player map (${opts.map})`)
const p1URL = new URL(encodeURI(opts.player1))
const p2URL = new URL(encodeURI(opts.player2))
const p1Opts = Object.fromEntries(
Array.from(p1URL.searchParams.entries())
.map(([key, value]) => [key, value.replaceAll('+', ' ')]),
) as GenerateBattlerOptions
const p2Opts = Object.fromEntries(
Array.from(p2URL.searchParams.entries())
.map(([key, value]) => [key, value.replaceAll('+', ' ')]),
) as GenerateBattlerOptions
const p1 = await generateBattler(p1Opts);
const p2 = await generateBattler({ ...p2Opts, direction: "left" })
// Image dimensions
const canvas = createCanvas(1920, 1080);
const context = canvas.getContext("2d");
const Background = await loadImage(
`./assets/playerlocations/${opts.map}.png`,
);
context.drawImage(Background, 0, 0, canvas.width, canvas.height);
context.drawImage(p1, 140, 313, p1.width / 2, p1.height / 2);
context.drawImage(p2, (canvas.width / 2) + 177, 313, p2.width / 2, p2.height / 2);
res.setHeader('content-type', 'image/png');
return res.end(canvas.toBuffer("image/png"), 'binary')
}
if (
req.method == "GET" &&
url.pathname == "/map.png"
) {
const opts = Object.fromEntries(
Array.from(url.searchParams.entries())
.map(([key, value]) => [key, value.replaceAll('+', ' ')]),
) as { player: string, map: string };
if (!opts.player || !opts.map) {
res.writeHead(400, { 'content-type': "application/json" })
return res.end(
JSON.stringify({
error:
'Did not pass all parameters',
})
);
}
console.log(`Generating map (${opts.map})`)
const p1URL = new URL(encodeURI(opts.player))
const p1Opts = Object.fromEntries(
Array.from(p1URL.searchParams.entries())
.map(([key, value]) => [key, value.replaceAll('+', ' ')]),
) as GenerateBattlerOptions
const p1 = await generateBattler(p1Opts);
// Image dimensions
const canvas = createCanvas(1920, 1080);
const context = canvas.getContext("2d");
const Background = await loadImage(
`./assets/locations/${opts.map}.png`,
);
context.drawImage(Background, 0, 0, canvas.width, canvas.height);
context.drawImage(p1, 140, 313, p1.width / 2, p1.height / 2);
res.setHeader('content-type', 'image/png');
return res.end(canvas.toBuffer("image/png"), 'binary')
}
if (
req.method == "GET" &&
url.pathname == "/communitymap.png"
) {
const opts = Object.fromEntries(
Array.from(url.searchParams.entries())
.map(([key, value]) => [key, value.replaceAll('+', ' ')]),
) as { player: string, map: string };
if (!opts.player || !opts.map) {
res.writeHead(400, { 'content-type': "application/json" })
return res.end(
JSON.stringify({
error:
'Did not pass all parameters',
})
);
}
console.log(`Generating map (${opts.map})`)
const p1URL = new URL(encodeURI(opts.player))
const p1Opts = Object.fromEntries(
Array.from(p1URL.searchParams.entries())
.map(([key, value]) => [key, value.replaceAll('+', ' ')]),
) as GenerateBattlerOptions
const p1 = await generateBattler(p1Opts);
// Image dimensions
const canvas = createCanvas(1920, 1080);
const context = canvas.getContext("2d");
const Background = await loadImage(
`./assets/communitymaps/${opts.map}.png`,
);
context.drawImage(Background, 0, 0, canvas.width, canvas.height);
context.drawImage(p1, 140, 313, p1.width / 2, p1.height / 2);
res.setHeader('content-type', 'image/png');
return res.end(canvas.toBuffer("image/png"), 'binary')
}
if (
req.method == "GET" &&
url.pathname == "/pvp.png"
) {
const opts = Object.fromEntries(
Array.from(url.searchParams.entries())
.map(([key, value]) => [key, value.replaceAll('+', ' ')]),
) as { player1: string, player2: string, username1: string, username2: string };
if (!opts.player1 || !opts.player2 || !opts.username1 || !opts.username2) {
res.writeHead(400, { 'content-type': "application/json" })
return res.end(
JSON.stringify({
error:
'Did not pass all parameters',
})
);
}
console.log(`Generating PvP image for ${opts.username1} and ${opts.username2}`)
const p1URL = new URL(encodeURI(opts.player1))
const p2URL = new URL(encodeURI(opts.player2))
const p1Opts = Object.fromEntries(
Array.from(p1URL.searchParams.entries())
.map(([key, value]) => [key, value.replaceAll('+', ' ')]),
) as GenerateBattlerOptions
const p2Opts = Object.fromEntries(
Array.from(p2URL.searchParams.entries())
.map(([key, value]) => [key, value.replaceAll('+', ' ')]),
) as GenerateBattlerOptions
const p1 = await generateBattler(p1Opts);
const p2 = await generateBattler({ ...p2Opts, direction: "left" })
// Image dimensions
const canvas = createCanvas(1920, 1080);
const context = canvas.getContext("2d");
const Background = await loadImage(
`./assets/battlebgs/PvP.png`,
);
context.drawImage(Background, 0, 0, canvas.width, canvas.height);
context.drawImage(p1, -140, -100, p1.width, p1.height);
context.drawImage(p2, (canvas.width / 2) - 180, -100, p2.width, p2.height);
context.textAlign = "center"
context.font = applyText(canvas, opts.username2, 675)
drawText([1450, 1000], opts.username2, opts.username2 == "homeannor" ? "#AA00FF" : "#000000", { colour: "#FFFFFF", width: 20 }, context)
context.font = applyText(canvas, opts.username1, 675)
drawText([470, 1000], opts.username1, opts.username1 == "homeannor" ? "#AA00FF" : "#000000", { colour: "#FFFFFF", width: 20 }, context)
res.setHeader('content-type', 'image/png');
return res.end(canvas.toBuffer("image/png"), 'binary')
}
console.log(`Failed request (${req.method}, ${url.pathname})`)
res.writeHead(404)
return res.end("404 Not Found")
})
server.listen(60125, () => {
console.log(`Listening on localhost:60125`)
})