From 3a46d4e5bead61c95a13443def63f9078e5a8e6e Mon Sep 17 00:00:00 2001 From: Z8MB1E Date: Mon, 10 Aug 2026 16:03:52 -0400 Subject: [PATCH] feat(npcs): add game-npcs collection for vendors Developer-gated collection for NPC characters, including the vendor fields (enabled, priceModifier, sells[]) used by the marketplace to attribute auto-generated listings to background merchants. --- src/collections/game/GameNpcs.ts | 114 +++++++++++++++++++++++++++++++ src/payload.config.ts | 2 + 2 files changed, 116 insertions(+) create mode 100644 src/collections/game/GameNpcs.ts diff --git a/src/collections/game/GameNpcs.ts b/src/collections/game/GameNpcs.ts new file mode 100644 index 0000000..ea47eb8 --- /dev/null +++ b/src/collections/game/GameNpcs.ts @@ -0,0 +1,114 @@ +import { CollectionConfig } from "payload"; +import { isDeveloper } from "@/utils/access-control/isRole"; + +export const GameNpcs: CollectionConfig = { + slug: "game-npcs", + admin: { + group: "Game", + useAsTitle: "name", + defaultColumns: ["name", "occupation", "location", "faction", "isGenerated", "isInGame"], + listSearchableFields: ["name", "occupation", "bio"], + pagination: { defaultLimit: 50 }, + }, + access: { + read: ({ req }) => !!req.user, + create: isDeveloper, + update: isDeveloper, + delete: isDeveloper, + }, + fields: [ + { + name: "name", + type: "text", + required: true, + admin: { + description: "The NPC's name as shown in-game and on the frontend.", + }, + }, + { + name: "occupation", + type: "text", + admin: { + description: "What this NPC does (e.g. 'Weapons Dealer', 'Field Medic').", + }, + }, + { + name: "location", + type: "relationship", + relationTo: "game-structures", + admin: { + description: "Where this NPC is stationed in-game.", + }, + }, + { + name: "faction", + type: "relationship", + relationTo: "factions", + admin: { + description: "The faction this NPC belongs to, if any.", + }, + }, + { + name: "bio", + type: "textarea", + admin: { + description: "A short background/profile for this NPC.", + }, + }, + { + name: "isGenerated", + type: "checkbox", + defaultValue: false, + admin: { + description: + "True for NPCs auto-created by the market tick to cover unstocked goods. They are placeholders until included in the game.", + }, + }, + { + name: "isInGame", + type: "checkbox", + defaultValue: true, + admin: { + description: + "Whether this NPC is officially part of the game. Generated NPCs start off; flip this on to include them.", + }, + }, + { + name: "vendor", + label: "Vendor", + type: "group", + fields: [ + { + name: "enabled", + type: "checkbox", + defaultValue: false, + admin: { + description: + "Whether this NPC runs a market stall. Auto-generated vendor stock is attributed to them.", + }, + }, + { + name: "priceModifier", + type: "number", + defaultValue: 1, + min: 0.25, + max: 5, + admin: { + description: + "Multiplies the auto-generated price for this NPC's stock (1 = base market price).", + }, + }, + { + name: "sells", + type: "relationship", + relationTo: "assets", + hasMany: true, + admin: { + description: + "The goods this NPC sells. The market tick prefers these NPCs when stocking those assets.", + }, + }, + ], + }, + ], +}; diff --git a/src/payload.config.ts b/src/payload.config.ts index dc8f9b3..14b1433 100644 --- a/src/payload.config.ts +++ b/src/payload.config.ts @@ -31,6 +31,7 @@ import { GameStructures } from "@/collections/game/GameStructures"; import { GameHardResources } from "@/collections/game/GameHardResources"; import { GameEventLogs } from "@/collections/game/GameEventLogs"; import { GameVehicles } from "@/collections/game/GameVehicles"; +import { GameNpcs } from "@/collections/game/GameNpcs"; import { Experience } from "@/collections/users/Experience"; import { BankAccounts } from "@/collections/banking/BankAccounts"; import { BankTransactions } from "@/collections/banking/BankTransactions"; @@ -78,6 +79,7 @@ export default buildConfig({ GameHardResources, GameStructures, GameVehicles, + GameNpcs, GameEventLogs, // Users