feat(collections): add grid fields to game-structures and resources schemas
GameStructures: - Add gridX, gridY, rotated fields to storedResources rows - Remove beforeValidate hook that merged duplicate resources - Add read access (was missing) - Add min: 1 constraint on amount Resources: - Add gridWidth, gridHeight, stackable, maxStackSize, rotatable fields - Add read access (was missing) - Wrap game-rules afterRead hook in try/catch to handle missing global
This commit is contained in:
parent
275cb49a92
commit
159755af8d
3 changed files with 136 additions and 60 deletions
|
|
@ -1,6 +1,5 @@
|
|||
import { CollectionConfig } from "payload";
|
||||
import { isDeveloper } from "@/utils/access-control/isRole";
|
||||
import { GameStructure } from "@/payload-types";
|
||||
|
||||
export const GameStructures: CollectionConfig = {
|
||||
slug: "game-structures",
|
||||
|
|
@ -9,59 +8,11 @@ export const GameStructures: CollectionConfig = {
|
|||
useAsTitle: "name",
|
||||
},
|
||||
access: {
|
||||
read: ({ req }) => !!req.user,
|
||||
create: isDeveloper,
|
||||
update: isDeveloper,
|
||||
delete: isDeveloper,
|
||||
},
|
||||
hooks: {
|
||||
beforeValidate: [
|
||||
({ data }) => {
|
||||
const rows = data?.storedResources;
|
||||
|
||||
if (!Array.isArray(rows) || rows.length === 0) return data;
|
||||
|
||||
type NormalizedRow = { id?: string | null; resource: unknown; amount: number };
|
||||
|
||||
const byResource = new Map<string, NormalizedRow>();
|
||||
|
||||
for (const row of rows) {
|
||||
if (!row) continue;
|
||||
|
||||
// Normalize relationship value to an ID-like scalar when possible
|
||||
const rawResource = (row as any).resource;
|
||||
const resourceId =
|
||||
rawResource && typeof rawResource === "object" && "id" in rawResource
|
||||
? (rawResource as any).id
|
||||
: rawResource;
|
||||
|
||||
if (resourceId == null) continue;
|
||||
|
||||
const resourceKey = String(resourceId);
|
||||
const amount =
|
||||
typeof (row as any).amount === "number" && Number.isFinite((row as any).amount)
|
||||
? (row as any).amount
|
||||
: 0;
|
||||
|
||||
const existing = byResource.get(resourceKey);
|
||||
|
||||
if (existing) {
|
||||
existing.amount += amount;
|
||||
// Keep the first row's id to preserve admin UI row identity
|
||||
} else {
|
||||
byResource.set(resourceKey, {
|
||||
id: (row as any).id ?? undefined,
|
||||
resource: resourceId,
|
||||
amount,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
data!.storedResources = Array.from(byResource.values());
|
||||
|
||||
return data;
|
||||
},
|
||||
],
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: "name",
|
||||
|
|
@ -113,6 +64,33 @@ export const GameStructures: CollectionConfig = {
|
|||
name: "amount",
|
||||
type: "number",
|
||||
required: true,
|
||||
min: 1,
|
||||
},
|
||||
{
|
||||
name: "gridX",
|
||||
type: "number",
|
||||
required: true,
|
||||
min: 0,
|
||||
admin: {
|
||||
description: "Column position in the inventory grid (0-indexed).",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "gridY",
|
||||
type: "number",
|
||||
required: true,
|
||||
min: 0,
|
||||
admin: {
|
||||
description: "Row position in the inventory grid (0-indexed).",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "rotated",
|
||||
type: "checkbox",
|
||||
defaultValue: false,
|
||||
admin: {
|
||||
description: "Whether this item is rotated 90 degrees (swaps width and height).",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ export const Resources: CollectionConfig = {
|
|||
group: "Logistics",
|
||||
},
|
||||
access: {
|
||||
read: ({ req }) => !!req.user,
|
||||
update: onlyIfNotPrimaryCurrency,
|
||||
delete: onlyIfNotPrimaryCurrency,
|
||||
},
|
||||
|
|
@ -76,18 +77,22 @@ export const Resources: CollectionConfig = {
|
|||
hooks: {
|
||||
afterRead: [
|
||||
async ({ data, req }) => {
|
||||
const gameRules = await req.payload.findGlobal({
|
||||
slug: "game-rules",
|
||||
depth: 0,
|
||||
});
|
||||
try {
|
||||
const gameRules = await req.payload.findGlobal({
|
||||
slug: "game-rules",
|
||||
depth: 0,
|
||||
});
|
||||
|
||||
const primaryCurrencyId =
|
||||
typeof gameRules.mainCurrency === "object"
|
||||
? gameRules.mainCurrency?.id
|
||||
: gameRules.mainCurrency;
|
||||
const primaryCurrencyId =
|
||||
typeof gameRules.mainCurrency === "object"
|
||||
? gameRules.mainCurrency?.id
|
||||
: gameRules.mainCurrency;
|
||||
|
||||
if (primaryCurrencyId) {
|
||||
return data?.id === primaryCurrencyId;
|
||||
if (primaryCurrencyId) {
|
||||
return data?.id === primaryCurrencyId;
|
||||
}
|
||||
} catch (_) {
|
||||
// Silently fail — game-rules global may not exist yet
|
||||
}
|
||||
},
|
||||
],
|
||||
|
|
@ -281,6 +286,59 @@ export const Resources: CollectionConfig = {
|
|||
"Maximum amount that fits in a single standard cargo slot (if applicable.)",
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Grid Dimensions",
|
||||
type: "group",
|
||||
fields: [
|
||||
{
|
||||
name: "gridWidth",
|
||||
type: "number",
|
||||
required: true,
|
||||
defaultValue: 1,
|
||||
min: 1,
|
||||
admin: {
|
||||
description: "Width of this resource in grid cells.",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "gridHeight",
|
||||
type: "number",
|
||||
required: true,
|
||||
defaultValue: 1,
|
||||
min: 1,
|
||||
admin: {
|
||||
description: "Height of this resource in grid cells.",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "stackable",
|
||||
type: "checkbox",
|
||||
defaultValue: true,
|
||||
admin: {
|
||||
description:
|
||||
"Can multiple units of this resource stack in a single grid cell? If checked, the amount displayed on the grid reflects the stack count.",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "maxStackSize",
|
||||
type: "number",
|
||||
admin: {
|
||||
description:
|
||||
"Maximum number of units that can exist in a single grid stack. Leave empty for unlimited. Only applies if stackable is checked.",
|
||||
condition: (_, siblingData) => siblingData?.stackable === true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "rotatable",
|
||||
type: "checkbox",
|
||||
defaultValue: true,
|
||||
admin: {
|
||||
description:
|
||||
"Can this resource be rotated 90 degrees in the grid? When rotated, width and height are swapped.",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
|
|
|||
|
|
@ -239,6 +239,18 @@ export interface GameStructure {
|
|||
| {
|
||||
resource: number | Resource;
|
||||
amount: number;
|
||||
/**
|
||||
* Column position in the inventory grid (0-indexed).
|
||||
*/
|
||||
gridX: number;
|
||||
/**
|
||||
* Row position in the inventory grid (0-indexed).
|
||||
*/
|
||||
gridY: number;
|
||||
/**
|
||||
* Whether this item is rotated 90 degrees (swaps width and height).
|
||||
*/
|
||||
rotated?: boolean | null;
|
||||
id?: string | null;
|
||||
}[]
|
||||
| null;
|
||||
|
|
@ -516,6 +528,26 @@ export interface Resource {
|
|||
* Maximum amount that fits in a single standard cargo slot (if applicable.)
|
||||
*/
|
||||
stackSize?: number | null;
|
||||
/**
|
||||
* Width of this resource in grid cells.
|
||||
*/
|
||||
gridWidth: number;
|
||||
/**
|
||||
* Height of this resource in grid cells.
|
||||
*/
|
||||
gridHeight: number;
|
||||
/**
|
||||
* Can multiple units of this resource stack in a single grid cell? If checked, the amount displayed on the grid reflects the stack count.
|
||||
*/
|
||||
stackable?: boolean | null;
|
||||
/**
|
||||
* Maximum number of units that can exist in a single grid stack. Leave empty for unlimited. Only applies if stackable is checked.
|
||||
*/
|
||||
maxStackSize?: number | null;
|
||||
/**
|
||||
* Can this resource be rotated 90 degrees in the grid? When rotated, width and height are swapped.
|
||||
*/
|
||||
rotatable?: boolean | null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
|
|
@ -1558,6 +1590,9 @@ export interface GameStructuresSelect<T extends boolean = true> {
|
|||
| {
|
||||
resource?: T;
|
||||
amount?: T;
|
||||
gridX?: T;
|
||||
gridY?: T;
|
||||
rotated?: T;
|
||||
id?: T;
|
||||
};
|
||||
updatedAt?: T;
|
||||
|
|
@ -1982,6 +2017,11 @@ export interface ResourcesSelect<T extends boolean = true> {
|
|||
unitOfMeasure?: T;
|
||||
massPerUnit?: T;
|
||||
stackSize?: T;
|
||||
gridWidth?: T;
|
||||
gridHeight?: T;
|
||||
stackable?: T;
|
||||
maxStackSize?: T;
|
||||
rotatable?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue