From 877f8531aa337e4dddc2726a8742507ef0430106 Mon Sep 17 00:00:00 2001 From: Z8MB1E Date: Mon, 22 Dec 2025 12:00:34 -0500 Subject: [PATCH] feat: add Hard Resources for system-managed game resources --- src/collections/game/GameHardResources.ts | 35 + src/payload-types.ts | 1329 +++++++++++---------- src/payload.config.ts | 8 +- 3 files changed, 727 insertions(+), 645 deletions(-) create mode 100644 src/collections/game/GameHardResources.ts diff --git a/src/collections/game/GameHardResources.ts b/src/collections/game/GameHardResources.ts new file mode 100644 index 0000000..584ecdc --- /dev/null +++ b/src/collections/game/GameHardResources.ts @@ -0,0 +1,35 @@ +import { CollectionConfig } from "payload"; +import { isDeveloper } from "@/utils/access-control/isRole"; + +export const GameHardResources: CollectionConfig = { + slug: "game-hard-resources", + access: { + create: isDeveloper, + update: isDeveloper, + delete: isDeveloper, + }, + admin: { + group: "Game", + useAsTitle: "name", + description: "\"Hard Resources\" represent the hard-coded, integral resource types used to power the game. It includes things like Intelligence Points, Command Points, and other non-physical resources that are essential for gameplay mechanics. Unlike regular (\"soft\") resources, hard resources are not calculated by transactions and are instead stored as numeric amounts that are updated by the backend. Soft resources are designed by the players, are defined in the Resources category under Logistics, and are calculated by transactions (ledgers) to ensure their accuracy.", + }, + fields: [ + { + name: "name", + type: "text", + required: true, + unique: true, + }, + { + name: "amount", + type: "number", + required: true, + defaultValue: 0, + }, + { + name: "description", + type: "richText", + required: true, + }, + ], +}; diff --git a/src/payload-types.ts b/src/payload-types.ts index 2be9a56..4a83c80 100644 --- a/src/payload-types.ts +++ b/src/payload-types.ts @@ -68,6 +68,8 @@ export interface Config { blocks: {}; collections: { media: Media; + 'game-hard-resources': GameHardResource; + 'game-structures': GameStructure; users: User; ranks: Rank; profiles: Profile; @@ -86,7 +88,6 @@ export interface Config { 'narrative-events': NarrativeEvent; 'mission-files': MissionFile; 'mod-lists': ModList; - 'game-structures': GameStructure; 'payload-kv': PayloadKv; 'payload-locked-documents': PayloadLockedDocument; 'payload-preferences': PayloadPreference; @@ -102,6 +103,8 @@ export interface Config { }; collectionsSelect: { media: MediaSelect | MediaSelect; + 'game-hard-resources': GameHardResourcesSelect | GameHardResourcesSelect; + 'game-structures': GameStructuresSelect | GameStructuresSelect; users: UsersSelect | UsersSelect; ranks: RanksSelect | RanksSelect; profiles: ProfilesSelect | ProfilesSelect; @@ -120,7 +123,6 @@ export interface Config { 'narrative-events': NarrativeEventsSelect | NarrativeEventsSelect; 'mission-files': MissionFilesSelect | MissionFilesSelect; 'mod-lists': ModListsSelect | ModListsSelect; - 'game-structures': GameStructuresSelect | GameStructuresSelect; 'payload-kv': PayloadKvSelect | PayloadKvSelect; 'payload-locked-documents': PayloadLockedDocumentsSelect | PayloadLockedDocumentsSelect; 'payload-preferences': PayloadPreferencesSelect | PayloadPreferencesSelect; @@ -180,6 +182,652 @@ export interface Media { focalX?: number | null; focalY?: number | null; } +/** + * "Hard Resources" represent the hard-coded, integral resource types used to power the game. It includes things like Intelligence Points, Command Points, and other non-physical resources that are essential for gameplay mechanics. Unlike regular ("soft") resources, hard resources are not calculated by transactions and are instead stored as numeric amounts that are updated by the backend. Soft resources are designed by the players, are defined in the Resources category under Logistics, and are calculated by transactions (ledgers) to ensure their accuracy. + * + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "game-hard-resources". + */ +export interface GameHardResource { + id: number; + name: string; + amount: number; + description: { + root: { + type: string; + children: { + type: any; + version: number; + [k: string]: unknown; + }[]; + direction: ('ltr' | 'rtl') | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + [k: string]: unknown; + }; + updatedAt: string; + createdAt: string; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "game-structures". + */ +export interface GameStructure { + id: number; + /** + * The name of the structure as displayed in-game and on the frontend. + */ + name: string; + /** + * The type of structure this is. + */ + type: number | Structure; + /** + * @minItems 2 + * @maxItems 2 + */ + coordinates?: [number, number] | null; + storedResources?: + | { + resource: number | Resource; + amount: number; + id?: string | null; + }[] + | null; + updatedAt: string; + createdAt: string; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "structures". + */ +export interface Structure { + id: number; + /** + * The display name (e.g., "Forward Operating Base"). + */ + name: string; + /** + * Internal ID for scripts (e.g., "fob_alpha_tier1"). + */ + codeName: string; + /** + * A detailed description of the structure, including details like tactical advantages, resource production, and/or world lore regarding this structure. + */ + description: { + root: { + type: string; + children: { + type: any; + version: number; + [k: string]: unknown; + }[]; + direction: ('ltr' | 'rtl') | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + [k: string]: unknown; + }; + /** + * An image depicting the structure. While not required, it is highly recommended as the existence of an image will affect the layout of the structure's page on the front-end. + */ + image?: (number | null) | Media; + category: 'logistics' | 'military' | 'civilian'; + materials: { + resource: number | Resource; + amount?: number | null; + id?: string | null; + }[]; + constructionTime: number; + /** + * What research must be done before this can be built. + */ + requiredTech?: (number | Technology)[] | null; + /** + * If true, this structure can only be constructed as the result of an upgrade to an existing structure. If false, this structure can be constructed independently of any other structure's upgrade tree, even if another structure can upgrade into this one. + */ + requiresUpgrade?: boolean | null; + /** + * When this structure is upgraded, it becomes this type of structure. Note that the costs of the new structure apply - if this field is set, the new structure's build costs will still be consumed when this building is upgraded. To make it cheaper to upgrade, lower the "Upgrade Cost Multiplier". + */ + upgradesInto?: (number | null) | Structure; + /** + * The multiplier applied to the base build cost of the structure being upgraded into. A value of one (1) means the cost will exactly equal the structure's normal build cost, while a value of zero (0) would make the upgrade entirely free, only taking up construction time instead. + */ + upgradeCostMultiplier?: number | null; + terrainType: 'land' | 'water'; + /** + * What resources, if any, must be within a certain distance of this structure on placement. + */ + resourcesInRange?: + | { + resource?: (number | null) | Resource; + range?: number | null; + id?: string | null; + }[] + | null; + /** + * What structures, if any, must be within a certain distance of this structure on placement. + */ + structuresInRange?: + | { + structure?: (number | null) | Structure; + range?: number | null; + id?: string | null; + }[] + | null; + /** + * Recurring costs to keep this structure online. + */ + upkeep?: + | { + resource?: (number | null) | Resource; + /** + * How much of this resource is consumed per "game tick" (every hour.) + */ + amount?: number | null; + id?: string | null; + }[] + | null; + /** + * A base price that this structure could sell for when fully operational. Leaving this empty will make this structure unsellable. + */ + sellPrice?: number | null; + /** + * The app will use this type to determine what processes should run for this structure type. + */ + functionType?: ('producer' | 'storage' | 'sensor' | 'converter' | 'defense' | 'miscellaneous') | null; + canProduce?: + | ( + | { + relationTo: 'assets'; + value: number | Asset; + } + | { + relationTo: 'vehicles'; + value: number | Vehicle; + } + | { + relationTo: 'resources'; + value: number | Resource; + } + )[] + | null; + autoGeneratedResources?: + | { + resource: number | Resource; + /** + * The minimum amount of this resource produced each production cycle. + */ + minAmount: number; + /** + * The maximum amount of this resource produced each production cycle. If this field is left empty, will produce a static amount equal to the minimum value. + */ + maxAmount?: number | null; + /** + * The minimum number of ticks it takes to produce this resource (e.g., '5' would produce this resource, at minimum, every 5 ticks). + */ + minFrequency: string; + /** + * The maximum number of ticks it takes to produce this resource (e.g., '5' would produce this resource, at maximum, every 5 ticks, ignoring production chance). + */ + maxFrequency?: string | null; + /** + * The chance (1-100) that this resource will be produced each production cycle. + */ + produceChance?: number | null; + id?: string | null; + }[] + | null; + /** + * The maximum storage capacity of this structure as mass (retrieved from the mass defined for every asset stored here.) + */ + maxCapacityMass?: number | null; + xSize?: number | null; + ySize?: number | null; + allowedStorage?: + | { + item?: + | ({ + relationTo: 'assets'; + value: number | Asset; + } | null) + | ({ + relationTo: 'vehicles'; + value: number | Vehicle; + } | null) + | ({ + relationTo: 'resources'; + value: number | Resource; + } | null); + /** + * How many of this asset, vehicle, or resource can be stored here. + */ + amount?: number | null; + id?: string | null; + }[] + | null; + /** + * Any items from this list can never be stored here, even if they meet all other criteria. + */ + prohibitedStorage?: + | { + item?: + | ({ + relationTo: 'assets'; + value: number | Asset; + } | null) + | ({ + relationTo: 'vehicles'; + value: number | Vehicle; + } | null) + | ({ + relationTo: 'resources'; + value: number | Resource; + } | null); + id?: string | null; + }[] + | null; + maxHealth: number; + /** + * So long as the structure meets its operational requirements, this is the amount of health restored to the structure every game tick. + */ + repairSpeed?: number | null; + updatedAt: string; + createdAt: string; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "resources". + */ +export interface Resource { + id: number; + showWarning?: boolean | null; + /** + * Human-readable name for resources (e.g., "Fuel"). + */ + name: string; + /** + * Internal ID for scripts (e.g., "res_fuel"). + */ + codeName: string; + type: 'physical' | 'fluid' | 'energy' | 'abstract'; + /** + * A description of the resource, including where it comes from, how it is used, and any lore-related details. + */ + description?: { + root: { + type: string; + children: { + type: any; + version: number; + [k: string]: unknown; + }[]; + direction: ('ltr' | 'rtl') | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + [k: string]: unknown; + } | null; + /** + * While not required, an icon is absolutely recommended and will affect the way this resource is displayed in the application UI. + */ + icon?: (number | null) | Media; + /** + * A HEX color code used by this resource in the application UI (e.g., progress bars, text highlighting, etc.) + */ + color?: string | null; + /** + * The inherent "gold standard" value of one (1) unit of this resource. Used for calculating exchange rates and total asset worth. + */ + baseValue: number; + /** + * Can this be bought and/or sold on the market? + */ + tradeable?: boolean | null; + rarity: 'common' | 'uncommon' | 'rare' | 'epic' | 'legendary' | 'exotic' | 'unique'; + /** + * The unit of measure for this resource in singular form (no plurals, e.g., liter, kg, point, unit, megawatt, person, etc.) + */ + unitOfMeasure: string; + /** + * The weight of one (1) unit (can be "0" for abstract resources like "Intel", "Labor", etc.) Critical for logistics transport calculations. + */ + massPerUnit: number; + /** + * Maximum amount that fits in a single standard cargo slot (if applicable.) + */ + stackSize?: number | null; + updatedAt: string; + createdAt: string; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "technologies". + */ +export interface Technology { + id: number; + name: string; + /** + * An "Asset" technology will unlock a new item, vehicle, gear piece, structure, etc. An "Upgrade" technology will unlock an improvement to an Asset, like faster build speed, more storage capacity, etc. + */ + type: 'asset' | 'upgrade'; + summary: string; + description?: { + root: { + type: string; + children: { + type: any; + version: number; + [k: string]: unknown; + }[]; + direction: ('ltr' | 'rtl') | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + [k: string]: unknown; + } | null; + requirements?: { + intelligencePoints?: number | null; + prerequisiteTechnologies?: + | { + /** + * ALL of these technologies MUST be unlocked before this one can be. + */ + requiresAnd?: { + technology?: (number | Technology)[] | null; + }; + /** + * One or more of these technologies MUST be unlocked before this one can be. + */ + requiresOr?: { + technology?: (number | Technology)[] | null; + }; + id?: string | null; + }[] + | null; + }; + researchCosts: { + /** + * The minimum amount of time in minutes that it takes to research this technology. If no maximum duration is provided, this technology will be researched in an amount of time exactly equal to this number. + */ + minimumResearchDuration: number; + /** + * The maximum amount of time in minutes that it takes to research this technology. + */ + maximumResearchDuration?: number | null; + }; + updatedAt: string; + createdAt: string; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "assets". + */ +export interface Asset { + id: number; + name: string; + className: string; + assetType: + | 'weapon' + | 'magazine' + | 'optic' + | 'muzzle' + | 'bipod' + | 'uniform' + | 'vest' + | 'backpack' + | 'helmet' + | 'facewear' + | 'nvg' + | 'radio' + | 'medical' + | 'tool' + | 'explosive' + | 'miscellaneous'; + 'Weapon Configuration'?: { + slotType?: ('primary' | 'secondary' | 'launcher') | null; + compatibleMagazines?: (number | Asset)[] | null; + compatibleAttachments?: (number | Asset)[] | null; + }; + 'Nightvision Configuration'?: { + nvgGeneration?: ('gen1' | 'gen2' | 'gen3') | null; + /** + * Whether or not these night-vision goggles possess a thermal vision mode. + */ + nvgHasThermals?: boolean | null; + }; + 'Container Configuration'?: { + capacityKg?: number | null; + weightKg?: number | null; + }; + 'Magazine Configuration'?: { + roundCapacity?: number | null; + ammoType?: string | null; + /** + * Whether or not this magazine is compatible with the GreenMag mod. + */ + greenmagCompatible?: boolean | null; + }; + 'Asset Metadata'?: { + mod?: string | null; + thumbnail?: (number | null) | Media; + gameDescription?: string | null; + }; + /** + * Whether or not this asset can be traded on the market. If unchecked, the asset can never be bought or sold, only crafted or collected. + */ + tradeable?: boolean | null; + marketTrading?: { + /** + * The rarity of this item in the world of the active campaign. Rarity will be accounted for in market valuation and trading calculations. + */ + Rarity?: ('basic' | 'common' | 'uncommon' | 'rare' | 'veryRare' | 'epic' | 'legendary' | 'unique') | null; + /** + * The asset's "perceived value" when considered in market and trading calculations. Note that the "base" values are the "resting" valuations. Later calculations will increase and decrease the valuation based on this resting valuation, ultimately tending to remain within some percentage margin of this base price. Always and only considers the primary campaign currency in regard to price. + */ + perceivedValue?: { + baseBuyPrice?: number | null; + baseSellPrice?: number | null; + }; + }; + crafting: { + craftable?: boolean | null; + /** + * This asset requires a blueprint to be crafted, otherwise it can only be obtained from scavenging and/or trading. + */ + blueprintRequired?: boolean | null; + /** + * One or more technologies required before this weapon can be crafted. + */ + technologyRequired?: (number | Technology)[] | null; + /** + * If checked, this asset requires the above technologies or else cannot be used at all. + */ + technologyRequiredToUse?: boolean | null; + craftingData: { + inputs?: + | { + resource: (number | Resource)[]; + quantity?: number | null; + /** + * If checked, this resource is "lended" to the crafting of this asset. Upon the crafting's completion, this resource is refunded to the unit. + */ + lended?: boolean | null; + id?: string | null; + }[] + | null; + requiresStructure?: (number | Structure)[] | null; + craftingTimePerUnit: number; + /** + * Byproducts of the crafting process. For each unit crafted of this Asset, these byproducts are also created and added to the unit's resources. + */ + byproducts?: + | { + resource: (number | Resource)[]; + quantity?: number | null; + id?: string | null; + }[] + | null; + /** + * An optional batch size. If provided, this number of this asset will be created upon crafting completion. + */ + batchSize: number; + }; + }; + /** + * Suggested complements to this asset (for example, if the asset was a Bandage, some related assets could include an IV, blood bag, epinephrine, etc.) + */ + relatedAssets?: (number | Asset)[] | null; + /** + * If checked, this item is approved and considered "live" (i.e. it will be considered in calculations and made available in-game.) + */ + isLive?: boolean | null; + notes?: { + root: { + type: string; + children: { + type: any; + version: number; + [k: string]: unknown; + }[]; + direction: ('ltr' | 'rtl') | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + [k: string]: unknown; + } | null; + updatedAt: string; + createdAt: string; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "vehicles". + */ +export interface Vehicle { + id: number; + name: string; + /** + * If not provided, this vehicle will ONLY be available as a virtual asset (i.e., cannot be used in Arma.) + */ + classname?: string | null; + /** + * A detailed description of the vehicle, including details like tactical usage, mechanical details, and/or world lore regarding this vehicle. + */ + Description?: { + root: { + type: string; + children: { + type: any; + version: number; + [k: string]: unknown; + }[]; + direction: ('ltr' | 'rtl') | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + [k: string]: unknown; + } | null; + image?: (number | null) | Media; + category?: ('civilian' | 'utility' | 'transport' | 'weaponized' | 'military')[] | null; + materials?: + | { + resource: number | Resource; + amount?: number | null; + id?: string | null; + }[] + | null; + /** + * The number of ticks it takes to complete construction of this vehicle. + */ + constructionTime?: number | null; + /** + * What research must be done before this can be built. + */ + requiredTech?: (number | Technology)[] | null; + requiredStructure?: { + docs?: (number | Structure)[]; + hasNextPage?: boolean; + totalDocs?: number; + }; + scrapReturns?: + | { + resource: number | Resource; + amount?: number | null; + /** + * An optional percent (%) chance to recover this specific resource (between 0 and 100). + */ + chance?: number | null; + id?: string | null; + }[] + | null; + /** + * Recurring costs to keep this vehicle operational. + */ + upkeepCosts?: + | { + resource: number | Resource; + /** + * How much of this resource is consumed per tick. + */ + amount?: number | null; + id?: string | null; + }[] + | null; + width?: number | null; + height?: number | null; + maxMass?: number | null; + /** + * The number of ACE cargo slots this vehicle has by default. + */ + aceCargoSlots?: number | null; + /** + * The cargo this vehicle is permitted to carry if used virtually (e.g., during deliveries, moving troops, etc.) + */ + allowedVirtualCargo?: + | { + item: + | { + relationTo: 'assets'; + value: number | Asset; + } + | { + relationTo: 'vehicles'; + value: number | Vehicle; + } + | { + relationTo: 'resources'; + value: number | Resource; + }; + amount?: number | null; + id?: string | null; + }[] + | null; + maxHealth?: number | null; + armorRating?: ('none' | 'light' | 'medium' | 'heavy' | 'composite') | null; + maxSpeedOnRoad?: number | null; + maxSpeedOffRoad?: number | null; + maxAirspeed?: number | null; + maxKnots?: number | null; + pylons?: + | { + pylonName: string; + pylonType: 'mountedWeapon' | 'pylon'; + amount?: number | null; + ammoType?: (number | null) | Asset; + id?: string | null; + }[] + | null; + updatedAt: string; + createdAt: string; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "users". @@ -650,595 +1298,6 @@ export interface Faction { updatedAt: string; createdAt: string; } -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "technologies". - */ -export interface Technology { - id: number; - name: string; - /** - * An "Asset" technology will unlock a new item, vehicle, gear piece, structure, etc. An "Upgrade" technology will unlock an improvement to an Asset, like faster build speed, more storage capacity, etc. - */ - type: 'asset' | 'upgrade'; - summary: string; - description?: { - root: { - type: string; - children: { - type: any; - version: number; - [k: string]: unknown; - }[]; - direction: ('ltr' | 'rtl') | null; - format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; - indent: number; - version: number; - }; - [k: string]: unknown; - } | null; - requirements?: { - intelligencePoints?: number | null; - prerequisiteTechnologies?: - | { - /** - * ALL of these technologies MUST be unlocked before this one can be. - */ - requiresAnd?: { - technology?: (number | Technology)[] | null; - }; - /** - * One or more of these technologies MUST be unlocked before this one can be. - */ - requiresOr?: { - technology?: (number | Technology)[] | null; - }; - id?: string | null; - }[] - | null; - }; - researchCosts: { - /** - * The minimum amount of time in minutes that it takes to research this technology. If no maximum duration is provided, this technology will be researched in an amount of time exactly equal to this number. - */ - minimumResearchDuration: number; - /** - * The maximum amount of time in minutes that it takes to research this technology. - */ - maximumResearchDuration?: number | null; - }; - updatedAt: string; - createdAt: string; -} -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "assets". - */ -export interface Asset { - id: number; - name: string; - className: string; - assetType: - | 'weapon' - | 'magazine' - | 'optic' - | 'muzzle' - | 'bipod' - | 'uniform' - | 'vest' - | 'backpack' - | 'helmet' - | 'facewear' - | 'nvg' - | 'radio' - | 'medical' - | 'tool' - | 'explosive' - | 'miscellaneous'; - 'Weapon Configuration'?: { - slotType?: ('primary' | 'secondary' | 'launcher') | null; - compatibleMagazines?: (number | Asset)[] | null; - compatibleAttachments?: (number | Asset)[] | null; - }; - 'Nightvision Configuration'?: { - nvgGeneration?: ('gen1' | 'gen2' | 'gen3') | null; - /** - * Whether or not these night-vision goggles possess a thermal vision mode. - */ - nvgHasThermals?: boolean | null; - }; - 'Container Configuration'?: { - capacityKg?: number | null; - weightKg?: number | null; - }; - 'Magazine Configuration'?: { - roundCapacity?: number | null; - ammoType?: string | null; - /** - * Whether or not this magazine is compatible with the GreenMag mod. - */ - greenmagCompatible?: boolean | null; - }; - 'Asset Metadata'?: { - mod?: string | null; - thumbnail?: (number | null) | Media; - gameDescription?: string | null; - }; - /** - * Whether or not this asset can be traded on the market. If unchecked, the asset can never be bought or sold, only crafted or collected. - */ - tradeable?: boolean | null; - marketTrading?: { - /** - * The rarity of this item in the world of the active campaign. Rarity will be accounted for in market valuation and trading calculations. - */ - Rarity?: ('basic' | 'common' | 'uncommon' | 'rare' | 'veryRare' | 'epic' | 'legendary' | 'unique') | null; - /** - * The asset's "perceived value" when considered in market and trading calculations. Note that the "base" values are the "resting" valuations. Later calculations will increase and decrease the valuation based on this resting valuation, ultimately tending to remain within some percentage margin of this base price. Always and only considers the primary campaign currency in regard to price. - */ - perceivedValue?: { - baseBuyPrice?: number | null; - baseSellPrice?: number | null; - }; - }; - crafting: { - craftable?: boolean | null; - /** - * This asset requires a blueprint to be crafted, otherwise it can only be obtained from scavenging and/or trading. - */ - blueprintRequired?: boolean | null; - /** - * One or more technologies required before this weapon can be crafted. - */ - technologyRequired?: (number | Technology)[] | null; - /** - * If checked, this asset requires the above technologies or else cannot be used at all. - */ - technologyRequiredToUse?: boolean | null; - craftingData: { - inputs?: - | { - resource: (number | Resource)[]; - quantity?: number | null; - /** - * If checked, this resource is "lended" to the crafting of this asset. Upon the crafting's completion, this resource is refunded to the unit. - */ - lended?: boolean | null; - id?: string | null; - }[] - | null; - requiresStructure?: (number | Structure)[] | null; - craftingTimePerUnit: number; - /** - * Byproducts of the crafting process. For each unit crafted of this Asset, these byproducts are also created and added to the unit's resources. - */ - byproducts?: - | { - resource: (number | Resource)[]; - quantity?: number | null; - id?: string | null; - }[] - | null; - /** - * An optional batch size. If provided, this number of this asset will be created upon crafting completion. - */ - batchSize: number; - }; - }; - /** - * Suggested complements to this asset (for example, if the asset was a Bandage, some related assets could include an IV, blood bag, epinephrine, etc.) - */ - relatedAssets?: (number | Asset)[] | null; - /** - * If checked, this item is approved and considered "live" (i.e. it will be considered in calculations and made available in-game.) - */ - isLive?: boolean | null; - notes?: { - root: { - type: string; - children: { - type: any; - version: number; - [k: string]: unknown; - }[]; - direction: ('ltr' | 'rtl') | null; - format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; - indent: number; - version: number; - }; - [k: string]: unknown; - } | null; - updatedAt: string; - createdAt: string; -} -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "resources". - */ -export interface Resource { - id: number; - showWarning?: boolean | null; - /** - * Human-readable name for resources (e.g., "Fuel"). - */ - name: string; - /** - * Internal ID for scripts (e.g., "res_fuel"). - */ - codeName: string; - type: 'physical' | 'fluid' | 'energy' | 'abstract'; - /** - * A description of the resource, including where it comes from, how it is used, and any lore-related details. - */ - description?: { - root: { - type: string; - children: { - type: any; - version: number; - [k: string]: unknown; - }[]; - direction: ('ltr' | 'rtl') | null; - format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; - indent: number; - version: number; - }; - [k: string]: unknown; - } | null; - /** - * While not required, an icon is absolutely recommended and will affect the way this resource is displayed in the application UI. - */ - icon?: (number | null) | Media; - /** - * A HEX color code used by this resource in the application UI (e.g., progress bars, text highlighting, etc.) - */ - color?: string | null; - /** - * The inherent "gold standard" value of one (1) unit of this resource. Used for calculating exchange rates and total asset worth. - */ - baseValue: number; - /** - * Can this be bought and/or sold on the market? - */ - tradeable?: boolean | null; - rarity: 'common' | 'uncommon' | 'rare' | 'epic' | 'legendary' | 'exotic' | 'unique'; - /** - * The unit of measure for this resource in singular form (no plurals, e.g., liter, kg, point, unit, megawatt, person, etc.) - */ - unitOfMeasure: string; - /** - * The weight of one (1) unit (can be "0" for abstract resources like "Intel", "Labor", etc.) Critical for logistics transport calculations. - */ - massPerUnit: number; - /** - * Maximum amount that fits in a single standard cargo slot (if applicable.) - */ - stackSize?: number | null; - updatedAt: string; - createdAt: string; -} -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "structures". - */ -export interface Structure { - id: number; - /** - * The display name (e.g., "Forward Operating Base"). - */ - name: string; - /** - * Internal ID for scripts (e.g., "fob_alpha_tier1"). - */ - codeName: string; - /** - * A detailed description of the structure, including details like tactical advantages, resource production, and/or world lore regarding this structure. - */ - description: { - root: { - type: string; - children: { - type: any; - version: number; - [k: string]: unknown; - }[]; - direction: ('ltr' | 'rtl') | null; - format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; - indent: number; - version: number; - }; - [k: string]: unknown; - }; - /** - * An image depicting the structure. While not required, it is highly recommended as the existence of an image will affect the layout of the structure's page on the front-end. - */ - image?: (number | null) | Media; - category: 'logistics' | 'military' | 'civilian'; - materials: { - resource: number | Resource; - amount?: number | null; - id?: string | null; - }[]; - constructionTime: number; - /** - * What research must be done before this can be built. - */ - requiredTech?: (number | Technology)[] | null; - /** - * If true, this structure can only be constructed as the result of an upgrade to an existing structure. If false, this structure can be constructed independently of any other structure's upgrade tree, even if another structure can upgrade into this one. - */ - requiresUpgrade?: boolean | null; - /** - * When this structure is upgraded, it becomes this type of structure. Note that the costs of the new structure apply - if this field is set, the new structure's build costs will still be consumed when this building is upgraded. To make it cheaper to upgrade, lower the "Upgrade Cost Multiplier". - */ - upgradesInto?: (number | null) | Structure; - /** - * The multiplier applied to the base build cost of the structure being upgraded into. A value of one (1) means the cost will exactly equal the structure's normal build cost, while a value of zero (0) would make the upgrade entirely free, only taking up construction time instead. - */ - upgradeCostMultiplier?: number | null; - terrainType: 'land' | 'water'; - /** - * What resources, if any, must be within a certain distance of this structure on placement. - */ - resourcesInRange?: - | { - resource?: (number | null) | Resource; - range?: number | null; - id?: string | null; - }[] - | null; - /** - * What structures, if any, must be within a certain distance of this structure on placement. - */ - structuresInRange?: - | { - structure?: (number | null) | Structure; - range?: number | null; - id?: string | null; - }[] - | null; - /** - * Recurring costs to keep this structure online. - */ - upkeep?: - | { - resource?: (number | null) | Resource; - /** - * How much of this resource is consumed per "game tick" (every hour.) - */ - amount?: number | null; - id?: string | null; - }[] - | null; - /** - * A base price that this structure could sell for when fully operational. Leaving this empty will make this structure unsellable. - */ - sellPrice?: number | null; - /** - * The app will use this type to determine what processes should run for this structure type. - */ - functionType?: ('producer' | 'storage' | 'sensor' | 'converter' | 'defense' | 'miscellaneous') | null; - canProduce?: - | ( - | { - relationTo: 'assets'; - value: number | Asset; - } - | { - relationTo: 'vehicles'; - value: number | Vehicle; - } - | { - relationTo: 'resources'; - value: number | Resource; - } - )[] - | null; - autoGeneratedResources?: - | { - resource: number | Resource; - /** - * The minimum amount of this resource produced each production cycle. - */ - minAmount: number; - /** - * The maximum amount of this resource produced each production cycle. If this field is left empty, will produce a static amount equal to the minimum value. - */ - maxAmount?: number | null; - /** - * The minimum number of ticks it takes to produce this resource (e.g., '5' would produce this resource, at minimum, every 5 ticks). - */ - minFrequency: string; - /** - * The maximum number of ticks it takes to produce this resource (e.g., '5' would produce this resource, at maximum, every 5 ticks, ignoring production chance). - */ - maxFrequency?: string | null; - /** - * The chance (1-100) that this resource will be produced each production cycle. - */ - produceChance?: number | null; - id?: string | null; - }[] - | null; - /** - * The maximum storage capacity of this structure as mass (retrieved from the mass defined for every asset stored here.) - */ - maxCapacityMass?: number | null; - xSize?: number | null; - ySize?: number | null; - allowedStorage?: - | { - item?: - | ({ - relationTo: 'assets'; - value: number | Asset; - } | null) - | ({ - relationTo: 'vehicles'; - value: number | Vehicle; - } | null) - | ({ - relationTo: 'resources'; - value: number | Resource; - } | null); - /** - * How many of this asset, vehicle, or resource can be stored here. - */ - amount?: number | null; - id?: string | null; - }[] - | null; - /** - * Any items from this list can never be stored here, even if they meet all other criteria. - */ - prohibitedStorage?: - | { - item?: - | ({ - relationTo: 'assets'; - value: number | Asset; - } | null) - | ({ - relationTo: 'vehicles'; - value: number | Vehicle; - } | null) - | ({ - relationTo: 'resources'; - value: number | Resource; - } | null); - id?: string | null; - }[] - | null; - maxHealth: number; - /** - * So long as the structure meets its operational requirements, this is the amount of health restored to the structure every game tick. - */ - repairSpeed?: number | null; - updatedAt: string; - createdAt: string; -} -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "vehicles". - */ -export interface Vehicle { - id: number; - name: string; - /** - * If not provided, this vehicle will ONLY be available as a virtual asset (i.e., cannot be used in Arma.) - */ - classname?: string | null; - /** - * A detailed description of the vehicle, including details like tactical usage, mechanical details, and/or world lore regarding this vehicle. - */ - Description?: { - root: { - type: string; - children: { - type: any; - version: number; - [k: string]: unknown; - }[]; - direction: ('ltr' | 'rtl') | null; - format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; - indent: number; - version: number; - }; - [k: string]: unknown; - } | null; - image?: (number | null) | Media; - category?: ('civilian' | 'utility' | 'transport' | 'weaponized' | 'military')[] | null; - materials?: - | { - resource: number | Resource; - amount?: number | null; - id?: string | null; - }[] - | null; - /** - * The number of ticks it takes to complete construction of this vehicle. - */ - constructionTime?: number | null; - /** - * What research must be done before this can be built. - */ - requiredTech?: (number | Technology)[] | null; - requiredStructure?: { - docs?: (number | Structure)[]; - hasNextPage?: boolean; - totalDocs?: number; - }; - scrapReturns?: - | { - resource: number | Resource; - amount?: number | null; - /** - * An optional percent (%) chance to recover this specific resource (between 0 and 100). - */ - chance?: number | null; - id?: string | null; - }[] - | null; - /** - * Recurring costs to keep this vehicle operational. - */ - upkeepCosts?: - | { - resource: number | Resource; - /** - * How much of this resource is consumed per tick. - */ - amount?: number | null; - id?: string | null; - }[] - | null; - width?: number | null; - height?: number | null; - maxMass?: number | null; - /** - * The number of ACE cargo slots this vehicle has by default. - */ - aceCargoSlots?: number | null; - /** - * The cargo this vehicle is permitted to carry if used virtually (e.g., during deliveries, moving troops, etc.) - */ - allowedVirtualCargo?: - | { - item: - | { - relationTo: 'assets'; - value: number | Asset; - } - | { - relationTo: 'vehicles'; - value: number | Vehicle; - } - | { - relationTo: 'resources'; - value: number | Resource; - }; - amount?: number | null; - id?: string | null; - }[] - | null; - maxHealth?: number | null; - armorRating?: ('none' | 'light' | 'medium' | 'heavy' | 'composite') | null; - maxSpeedOnRoad?: number | null; - maxSpeedOffRoad?: number | null; - maxAirspeed?: number | null; - maxKnots?: number | null; - pylons?: - | { - pylonName: string; - pylonType: 'mountedWeapon' | 'pylon'; - amount?: number | null; - ammoType?: (number | null) | Asset; - id?: string | null; - }[] - | null; - updatedAt: string; - createdAt: string; -} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "narrative-events". @@ -1250,35 +1309,6 @@ export interface NarrativeEvent { updatedAt: string; createdAt: string; } -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "game-structures". - */ -export interface GameStructure { - id: number; - /** - * The name of the structure as displayed in-game and on the frontend. - */ - name: string; - /** - * The type of structure this is. - */ - type: number | Structure; - /** - * @minItems 2 - * @maxItems 2 - */ - coordinates?: [number, number] | null; - storedResources?: - | { - resource: number | Resource; - amount: number; - id?: string | null; - }[] - | null; - updatedAt: string; - createdAt: string; -} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "payload-kv". @@ -1307,6 +1337,14 @@ export interface PayloadLockedDocument { relationTo: 'media'; value: number | Media; } | null) + | ({ + relationTo: 'game-hard-resources'; + value: number | GameHardResource; + } | null) + | ({ + relationTo: 'game-structures'; + value: number | GameStructure; + } | null) | ({ relationTo: 'users'; value: number | User; @@ -1378,10 +1416,6 @@ export interface PayloadLockedDocument { | ({ relationTo: 'mod-lists'; value: number | ModList; - } | null) - | ({ - relationTo: 'game-structures'; - value: number | GameStructure; } | null); globalSlug?: string | null; user: { @@ -1443,6 +1477,35 @@ export interface MediaSelect { focalX?: T; focalY?: T; } +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "game-hard-resources_select". + */ +export interface GameHardResourcesSelect { + name?: T; + amount?: T; + description?: T; + updatedAt?: T; + createdAt?: T; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "game-structures_select". + */ +export interface GameStructuresSelect { + name?: T; + type?: T; + coordinates?: T; + storedResources?: + | T + | { + resource?: T; + amount?: T; + id?: T; + }; + updatedAt?: T; + createdAt?: T; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "users_select". @@ -2046,24 +2109,6 @@ export interface ModListsSelect { focalX?: T; focalY?: T; } -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "game-structures_select". - */ -export interface GameStructuresSelect { - name?: T; - type?: T; - coordinates?: T; - storedResources?: - | T - | { - resource?: T; - amount?: T; - id?: T; - }; - updatedAt?: T; - createdAt?: T; -} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "payload-kv_select". diff --git a/src/payload.config.ts b/src/payload.config.ts index a128e26..af0f325 100644 --- a/src/payload.config.ts +++ b/src/payload.config.ts @@ -27,6 +27,7 @@ import { Assignments } from "@/collections/users/Assignments"; import { Technologies } from "@/collections/intelligence/Technologies"; import { GameRules } from "@/collections/game/GameRules"; import { GameStructures } from "@/collections/game/GameStructures"; +import { GameHardResources } from "@/collections/game/GameHardResources"; const filename = fileURLToPath(import.meta.url); const dirname = path.dirname(filename); @@ -60,6 +61,10 @@ export default buildConfig({ collections: [ Media, + // Game + GameHardResources, + GameStructures, + // Users Users, Ranks, @@ -87,9 +92,6 @@ export default buildConfig({ // Server MissionFiles, ModLists, - - // Game - GameStructures ], editor: lexicalEditor(), secret: process.env.PAYLOAD_SECRET || "",