feat(guides) : add team transfer and visibility controls
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { supportsWeaponType } from "@/lib/catalog/lunaris";
|
||||
import * as z from "zod";
|
||||
|
||||
export interface TeamToolMember {
|
||||
characterKey: string;
|
||||
@@ -42,6 +43,87 @@ export interface TeamPreset {
|
||||
team: Omit<TeamToolValue, "clientId">;
|
||||
}
|
||||
|
||||
const transferMemberSchema = z.strictObject({
|
||||
characterKey: z.string().trim().min(1).max(64),
|
||||
weaponKey: z.string().trim().min(1).max(64).nullable(),
|
||||
artifactKey: z.string().trim().min(1).max(64).nullable(),
|
||||
secondArtifactKey: z.string().trim().min(1).max(64).nullable(),
|
||||
constellation: z.number().int().min(0).max(6),
|
||||
refinement: z.number().int().min(1).max(5),
|
||||
damage: z.string().refine((value) => {
|
||||
const number = Number(value);
|
||||
return Number.isFinite(number) && number >= 0 && number <= 999_999_999_999;
|
||||
}),
|
||||
});
|
||||
|
||||
const transferTeamSchema = z.strictObject({
|
||||
title: z.string().trim().min(1).max(160),
|
||||
rotationSeconds: z.string().refine((value) => {
|
||||
const number = Number(value);
|
||||
return Number.isFinite(number) && number > 0 && number <= 9999;
|
||||
}),
|
||||
combo: z.string().trim().min(1).max(2_000),
|
||||
members: z.array(transferMemberSchema).length(4)
|
||||
.refine(
|
||||
(members) => new Set(members.map((member) => member.characterKey)).size === 4,
|
||||
)
|
||||
.refine((members) => members.some((member) => Number(member.damage) > 0)),
|
||||
});
|
||||
|
||||
const teamTransferSchema = z.strictObject({
|
||||
type: z.literal("buzz-sheet.team"),
|
||||
version: z.literal(1),
|
||||
team: transferTeamSchema,
|
||||
});
|
||||
|
||||
export function serializeTeamTransfer(team: TeamToolValue): string {
|
||||
const payload = teamTransferSchema.parse({
|
||||
type: "buzz-sheet.team",
|
||||
version: 1,
|
||||
team: {
|
||||
title: team.title,
|
||||
rotationSeconds: team.rotationSeconds,
|
||||
combo: team.combo,
|
||||
members: team.members,
|
||||
},
|
||||
});
|
||||
return JSON.stringify(payload, null, 2);
|
||||
}
|
||||
|
||||
export function parseTeamTransfer(serialized: string): Omit<TeamToolValue, "clientId"> {
|
||||
const payload = teamTransferSchema.parse(JSON.parse(serialized) as unknown);
|
||||
return {
|
||||
...payload.team,
|
||||
members: payload.team.members.map((member) => ({ ...member })),
|
||||
};
|
||||
}
|
||||
|
||||
export function findInvalidTeamCatalogReference(
|
||||
team: Omit<TeamToolValue, "clientId">,
|
||||
characters: readonly TeamToolCatalogItem[],
|
||||
weapons: readonly TeamToolCatalogItem[],
|
||||
artifacts: readonly TeamToolCatalogItem[],
|
||||
): string | null {
|
||||
const characterByKey = new Map(characters.map((item) => [item.key, item]));
|
||||
const weaponByKey = new Map(weapons.map((item) => [item.key, item]));
|
||||
const artifactByKey = new Map(artifacts.map((item) => [item.key, item]));
|
||||
for (const member of team.members) {
|
||||
const character = characterByKey.get(member.characterKey);
|
||||
if (!character) return `ไม่พบตัวละคร ${member.characterKey} ใน Catalog`;
|
||||
const weapon = member.weaponKey ? weaponByKey.get(member.weaponKey) : null;
|
||||
if (member.weaponKey && !weapon) return `ไม่พบอาวุธ ${member.weaponKey} ใน Catalog`;
|
||||
if (weapon && !supportsWeaponType(character.weaponType, weapon.weaponType)) {
|
||||
return `${character.name} ไม่รองรับอาวุธ ${weapon.name}`;
|
||||
}
|
||||
for (const artifactKey of [member.artifactKey, member.secondArtifactKey]) {
|
||||
if (artifactKey && !artifactByKey.has(artifactKey)) {
|
||||
return `ไม่พบ Artifact ${artifactKey} ใน Catalog`;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
interface TeamToolCatalogItem {
|
||||
key: string;
|
||||
name: string;
|
||||
@@ -109,9 +191,16 @@ export function applyTeamFields(
|
||||
const memberIndex = Number(match[1]);
|
||||
const memberField = match[2] as TeamMemberApplyField;
|
||||
const sourceMember = source.members[memberIndex];
|
||||
const targetMember = next.members[memberIndex];
|
||||
const matchingMemberIndex = sourceMember?.characterKey
|
||||
? next.members.findIndex(
|
||||
(member) => member.characterKey === sourceMember.characterKey,
|
||||
)
|
||||
: -1;
|
||||
const targetMemberIndex =
|
||||
matchingMemberIndex === -1 ? memberIndex : matchingMemberIndex;
|
||||
const targetMember = next.members[targetMemberIndex];
|
||||
if (!sourceMember || !targetMember || !teamMemberApplyFields.includes(memberField)) continue;
|
||||
next.members[memberIndex] = {
|
||||
next.members[targetMemberIndex] = {
|
||||
...targetMember,
|
||||
[memberField]: sourceMember[memberField],
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user