feat(world): game map collections with basemaps, map permissions, and meter proximity
Add maps basemap config (world size, image/tiles mode, zoom defaults, isActive), map-roads, map-zones, and resource-nodes collections with maps:* permissions. Track game-structures map placement, meter coordinates, and label visibility/priority. Bump proximityThreshold to meters and adapt side-op map creation plus test fixtures to the new required fields. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
parent
28816b7084
commit
5f06039697
13 changed files with 518 additions and 10 deletions
|
|
@ -445,7 +445,12 @@ async function resolveSideOpMap(
|
||||||
if (existing) return existing.id;
|
if (existing) return existing.id;
|
||||||
const created = await payload.create({
|
const created = await payload.create({
|
||||||
collection: "maps",
|
collection: "maps",
|
||||||
data: { name: customName },
|
data: {
|
||||||
|
name: customName,
|
||||||
|
worldSizeWidth: 8192,
|
||||||
|
worldSizeHeight: 8192,
|
||||||
|
basemapMode: "image",
|
||||||
|
},
|
||||||
overrideAccess: true,
|
overrideAccess: true,
|
||||||
});
|
});
|
||||||
return created.id;
|
return created.id;
|
||||||
|
|
|
||||||
|
|
@ -89,10 +89,10 @@ export const GameRules: GlobalConfig = {
|
||||||
{
|
{
|
||||||
name: "proximityThreshold",
|
name: "proximityThreshold",
|
||||||
type: "number",
|
type: "number",
|
||||||
defaultValue: 10,
|
defaultValue: 1000,
|
||||||
admin: {
|
admin: {
|
||||||
description:
|
description:
|
||||||
"Distance threshold for instant transfers. Structures within this distance can transfer resources instantly. Beyond this, shipments are required.",
|
"Distance threshold in real meters for instant transfers. Structures within this distance can transfer resources instantly. Beyond this, shipments are required.",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,45 @@ export const GameStructures: CollectionConfig = {
|
||||||
description: "The faction that owns this structure.",
|
description: "The faction that owns this structure.",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "map",
|
||||||
|
type: "relationship",
|
||||||
|
relationTo: "maps",
|
||||||
|
admin: {
|
||||||
|
description: "The game map this structure sits on.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "constructionStatus",
|
||||||
|
type: "select",
|
||||||
|
label: "Construction Status",
|
||||||
|
defaultValue: "complete",
|
||||||
|
options: [
|
||||||
|
{ label: "Awaiting Materials", value: "awaiting_materials" },
|
||||||
|
{ label: "Building", value: "building" },
|
||||||
|
{ label: "Complete", value: "complete" },
|
||||||
|
],
|
||||||
|
admin: {
|
||||||
|
description:
|
||||||
|
"Placed sites start awaiting materials. Once all blueprint materials are delivered construction starts immediately (timestamps below); the game tick flips past-deadline building sites to complete.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "constructionStartedAt",
|
||||||
|
type: "date",
|
||||||
|
label: "Construction Started At",
|
||||||
|
admin: {
|
||||||
|
description: "When construction began (materials consumed). Empty while awaiting materials.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "constructionCompletesAt",
|
||||||
|
type: "date",
|
||||||
|
label: "Construction Completes At",
|
||||||
|
admin: {
|
||||||
|
description: "The exact deadline at which the site becomes operational.",
|
||||||
|
},
|
||||||
|
},
|
||||||
...createStaffingFields("instance"),
|
...createStaffingFields("instance"),
|
||||||
{
|
{
|
||||||
label: "Geolocation",
|
label: "Geolocation",
|
||||||
|
|
@ -107,9 +146,34 @@ export const GameStructures: CollectionConfig = {
|
||||||
{
|
{
|
||||||
name: "coordinates",
|
name: "coordinates",
|
||||||
type: "point",
|
type: "point",
|
||||||
|
validate: () => true,
|
||||||
|
admin: {
|
||||||
|
description: "Map meters [x, y] on the structure's game map (not geographic degrees).",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "labelVisible",
|
||||||
|
type: "checkbox",
|
||||||
|
defaultValue: true,
|
||||||
|
label: "Show Name Label",
|
||||||
|
admin: {
|
||||||
|
description:
|
||||||
|
"Renders the structure's name on the map. When several structures cluster together, the highest-priority label wins until you zoom in.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "labelPriority",
|
||||||
|
type: "number",
|
||||||
|
defaultValue: 0,
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
admin: {
|
||||||
|
description:
|
||||||
|
"Label priority for clustering (0-100). Higher wins when structures overlap at low zoom; ties break by stored mass.",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "storedResources",
|
name: "storedResources",
|
||||||
type: "array",
|
type: "array",
|
||||||
|
|
|
||||||
115
src/collections/world/MapRoads.ts
Normal file
115
src/collections/world/MapRoads.ts
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
import { CollectionConfig } from "payload";
|
||||||
|
import { requirePermission } from "@/utils/access-control/hasPermission";
|
||||||
|
import { validateHexColor } from "@/lib/map/style";
|
||||||
|
import { validatePolyline } from "@/lib/map/points";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Road network segments for a map, used by ground shipment routing.
|
||||||
|
* Points are [x, y] tuples in real meters on the map's grid. Roads connect
|
||||||
|
* where endpoints (or points) coincide within the routing epsilon; author
|
||||||
|
* them so shared endpoints use identical coordinates.
|
||||||
|
*/
|
||||||
|
export const MapRoads: CollectionConfig = {
|
||||||
|
slug: "map-roads",
|
||||||
|
admin: {
|
||||||
|
group: "World",
|
||||||
|
useAsTitle: "name",
|
||||||
|
},
|
||||||
|
access: {
|
||||||
|
read: ({ req }) => !!req.user,
|
||||||
|
create: requirePermission("maps:create"),
|
||||||
|
update: requirePermission("maps:update"),
|
||||||
|
delete: requirePermission("maps:delete"),
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: "name",
|
||||||
|
type: "text",
|
||||||
|
admin: {
|
||||||
|
description: 'Road or route label (e.g. "Highway 1", "Kavala ridge trail").',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "description",
|
||||||
|
type: "textarea",
|
||||||
|
admin: {
|
||||||
|
description: "Shown in the road's tooltip and popup on the map.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "map",
|
||||||
|
type: "relationship",
|
||||||
|
relationTo: "maps",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "surface",
|
||||||
|
type: "select",
|
||||||
|
required: true,
|
||||||
|
defaultValue: "paved",
|
||||||
|
options: [
|
||||||
|
{ label: "Paved", value: "paved" },
|
||||||
|
{ label: "Dirt", value: "dirt" },
|
||||||
|
{ label: "Trail", value: "trail" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "speedMultiplier",
|
||||||
|
type: "number",
|
||||||
|
required: true,
|
||||||
|
defaultValue: 1,
|
||||||
|
min: 0.1,
|
||||||
|
max: 5,
|
||||||
|
admin: {
|
||||||
|
description:
|
||||||
|
"Routing weight multiplier. Higher = faster/preferred (1 = neutral). Affects which route Dijkstra picks; fuel is charged on geometric length.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "strokeColor",
|
||||||
|
type: "text",
|
||||||
|
validate: validateHexColor,
|
||||||
|
admin: {
|
||||||
|
description: "Line color as hex (e.g. #cbd5e1). Empty = default color for the surface.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "strokeWeight",
|
||||||
|
type: "number",
|
||||||
|
min: 1,
|
||||||
|
max: 10,
|
||||||
|
defaultValue: 2,
|
||||||
|
admin: {
|
||||||
|
description: "Line thickness in pixels.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "strokeStyle",
|
||||||
|
type: "select",
|
||||||
|
defaultValue: "solid",
|
||||||
|
options: [
|
||||||
|
{ label: "Solid", value: "solid" },
|
||||||
|
{ label: "Dashed", value: "dashed" },
|
||||||
|
{ label: "Dotted", value: "dotted" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "labelVisible",
|
||||||
|
type: "checkbox",
|
||||||
|
defaultValue: true,
|
||||||
|
label: "Show Name Label",
|
||||||
|
admin: {
|
||||||
|
description: "Renders the road's name along the line at zoom-dependent intervals.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "points",
|
||||||
|
type: "json",
|
||||||
|
required: true,
|
||||||
|
validate: validatePolyline,
|
||||||
|
admin: {
|
||||||
|
description: "Array of [x, y] meter tuples in map order.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
121
src/collections/world/MapZones.ts
Normal file
121
src/collections/world/MapZones.ts
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
import { CollectionConfig } from "payload";
|
||||||
|
import { requirePermission } from "@/utils/access-control/hasPermission";
|
||||||
|
import { validateHexColor } from "@/lib/map/style";
|
||||||
|
import { validatePolygon } from "@/lib/map/points";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GM-drawn terrain zones. Land blueprints cannot be placed inside water
|
||||||
|
* polygons; water blueprints require one. Default terrain (no zones on the
|
||||||
|
* map) accepts everything.
|
||||||
|
*/
|
||||||
|
export const MapZones: CollectionConfig = {
|
||||||
|
slug: "map-zones",
|
||||||
|
admin: {
|
||||||
|
group: "World",
|
||||||
|
useAsTitle: "name",
|
||||||
|
},
|
||||||
|
access: {
|
||||||
|
read: ({ req }) => !!req.user,
|
||||||
|
create: requirePermission("maps:create"),
|
||||||
|
update: requirePermission("maps:update"),
|
||||||
|
delete: requirePermission("maps:delete"),
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: "name",
|
||||||
|
type: "text",
|
||||||
|
admin: {
|
||||||
|
description: 'Zone label (e.g. "Altis bay", "Flood plain").',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "description",
|
||||||
|
type: "textarea",
|
||||||
|
admin: {
|
||||||
|
description: "Shown in the zone's tooltip and popup on the map.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "map",
|
||||||
|
type: "relationship",
|
||||||
|
relationTo: "maps",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "type",
|
||||||
|
type: "select",
|
||||||
|
required: true,
|
||||||
|
options: [
|
||||||
|
{ label: "Water", value: "water" },
|
||||||
|
{ label: "Land", value: "land" },
|
||||||
|
],
|
||||||
|
admin: {
|
||||||
|
description:
|
||||||
|
"Water zones block land-blueprint placement. Land zones are informational; default terrain is buildable land.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "fillColor",
|
||||||
|
type: "text",
|
||||||
|
validate: validateHexColor,
|
||||||
|
admin: {
|
||||||
|
description: "Polygon fill as hex (e.g. #38bdf8). Empty = default color for the type.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "strokeColor",
|
||||||
|
type: "text",
|
||||||
|
validate: validateHexColor,
|
||||||
|
admin: {
|
||||||
|
description: "Outline color as hex. Empty = default color for the type.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "fillOpacity",
|
||||||
|
type: "number",
|
||||||
|
min: 0,
|
||||||
|
max: 1,
|
||||||
|
admin: {
|
||||||
|
description: "Fill opacity 0-1. Empty = default (0.25 water, 0.08 land).",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "strokeWeight",
|
||||||
|
type: "number",
|
||||||
|
min: 0,
|
||||||
|
max: 10,
|
||||||
|
defaultValue: 1,
|
||||||
|
admin: {
|
||||||
|
description: "Outline thickness in pixels. 0 hides the outline.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "resources",
|
||||||
|
type: "relationship",
|
||||||
|
relationTo: "resources",
|
||||||
|
hasMany: true,
|
||||||
|
admin: {
|
||||||
|
description:
|
||||||
|
"Resources contained in this zone (e.g. an oil field). A zone with resources satisfies blueprint resourcesInRange requirements for any site inside it.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "labelVisible",
|
||||||
|
type: "checkbox",
|
||||||
|
defaultValue: true,
|
||||||
|
label: "Show Name Label",
|
||||||
|
admin: {
|
||||||
|
description: "Renders the zone's name at the polygon's center.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "points",
|
||||||
|
type: "json",
|
||||||
|
required: true,
|
||||||
|
validate: validatePolygon,
|
||||||
|
admin: {
|
||||||
|
description: "Polygon ring as an array of [x, y] meter tuples (need not repeat the first point).",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
@ -21,9 +21,116 @@ export const Maps: CollectionConfig = {
|
||||||
unique: true,
|
unique: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "fileName",
|
name: "isActive",
|
||||||
type: "text",
|
type: "checkbox",
|
||||||
unique: true,
|
label: "Active",
|
||||||
|
defaultValue: true,
|
||||||
|
admin: {
|
||||||
|
description: "Inactive maps are hidden from the map page and cannot receive placements.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "World Size (meters)",
|
||||||
|
type: "group",
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: "worldSizeWidth",
|
||||||
|
type: "number",
|
||||||
|
label: "Width (m)",
|
||||||
|
required: true,
|
||||||
|
min: 1,
|
||||||
|
defaultValue: 8192,
|
||||||
|
admin: {
|
||||||
|
description: "Map width in real meters (e.g. Altis is 8192).",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "worldSizeHeight",
|
||||||
|
type: "number",
|
||||||
|
label: "Height (m)",
|
||||||
|
required: true,
|
||||||
|
min: 1,
|
||||||
|
defaultValue: 8192,
|
||||||
|
admin: {
|
||||||
|
description: "Map height in real meters (e.g. Altis is 8192).",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Basemap",
|
||||||
|
type: "group",
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: "basemapMode",
|
||||||
|
type: "select",
|
||||||
|
required: true,
|
||||||
|
defaultValue: "image",
|
||||||
|
options: [
|
||||||
|
{ label: "Image Upload", value: "image" },
|
||||||
|
{ label: "XYZ Tile Layer", value: "tiles" },
|
||||||
|
],
|
||||||
|
admin: {
|
||||||
|
description: "Image: a single uploaded map image stretched over the world bounds. Tiles: an external XYZ tile server.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "basemapImage",
|
||||||
|
type: "upload",
|
||||||
|
relationTo: "media",
|
||||||
|
label: "Basemap Image",
|
||||||
|
admin: {
|
||||||
|
condition: (_data, siblingData) => siblingData?.basemapMode !== "tiles",
|
||||||
|
description: "Top-down map image. Rendered stretched across the full world size.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "tileUrlTemplate",
|
||||||
|
type: "text",
|
||||||
|
label: "Tile URL Template",
|
||||||
|
admin: {
|
||||||
|
condition: (_data, siblingData) => siblingData?.basemapMode === "tiles",
|
||||||
|
description: "XYZ template, e.g. https://tiles.example.com/{z}/{x}/{y}.png",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "minZoom",
|
||||||
|
type: "number",
|
||||||
|
defaultValue: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "maxZoom",
|
||||||
|
type: "number",
|
||||||
|
defaultValue: 12,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Default View",
|
||||||
|
type: "group",
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: "defaultCenterX",
|
||||||
|
type: "number",
|
||||||
|
label: "Center X (m)",
|
||||||
|
admin: {
|
||||||
|
description: "Defaults to half the world width when empty.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "defaultCenterY",
|
||||||
|
type: "number",
|
||||||
|
label: "Center Y (m)",
|
||||||
|
admin: {
|
||||||
|
description: "Defaults to half the world height when empty.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "defaultZoom",
|
||||||
|
type: "number",
|
||||||
|
defaultValue: 2,
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
|
||||||
73
src/collections/world/ResourceNodes.ts
Normal file
73
src/collections/world/ResourceNodes.ts
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
import { CollectionConfig } from "payload";
|
||||||
|
import { requirePermission } from "@/utils/access-control/hasPermission";
|
||||||
|
import { validatePosition } from "@/lib/map/points";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map resource nodes (deposits, forests, wells, ...). Display + placement
|
||||||
|
* validation only: blueprints with resourcesInRange require a matching node
|
||||||
|
* within range. Nodes do not produce anything.
|
||||||
|
*/
|
||||||
|
export const ResourceNodes: CollectionConfig = {
|
||||||
|
slug: "resource-nodes",
|
||||||
|
admin: {
|
||||||
|
group: "World",
|
||||||
|
useAsTitle: "name",
|
||||||
|
},
|
||||||
|
access: {
|
||||||
|
read: ({ req }) => !!req.user,
|
||||||
|
create: requirePermission("maps:create"),
|
||||||
|
update: requirePermission("maps:update"),
|
||||||
|
delete: requirePermission("maps:delete"),
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: "name",
|
||||||
|
type: "text",
|
||||||
|
required: true,
|
||||||
|
admin: {
|
||||||
|
description: 'Node label (e.g. "North copper deposit").',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "description",
|
||||||
|
type: "textarea",
|
||||||
|
admin: {
|
||||||
|
description: "Shown in the node's tooltip and popup on the map.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "map",
|
||||||
|
type: "relationship",
|
||||||
|
relationTo: "maps",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "resources",
|
||||||
|
type: "relationship",
|
||||||
|
relationTo: "resources",
|
||||||
|
hasMany: true,
|
||||||
|
required: true,
|
||||||
|
admin: {
|
||||||
|
description: "Every resource this node yields (e.g. copper AND oil).",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "position",
|
||||||
|
type: "json",
|
||||||
|
required: true,
|
||||||
|
validate: validatePosition,
|
||||||
|
admin: {
|
||||||
|
description: "[x, y] tuple in meters on the map grid.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "richness",
|
||||||
|
type: "number",
|
||||||
|
defaultValue: 100,
|
||||||
|
min: 0,
|
||||||
|
admin: {
|
||||||
|
description: "Display-only abundance rating for the map UI.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
@ -17,6 +17,9 @@ import { Profiles } from "@/collections/users/Profiles";
|
||||||
import { Missions } from "@/collections/intelligence/Missions";
|
import { Missions } from "@/collections/intelligence/Missions";
|
||||||
import { MissionAttendances } from "@/collections/intelligence/MissionAttendances";
|
import { MissionAttendances } from "@/collections/intelligence/MissionAttendances";
|
||||||
import { Maps } from "@/collections/world/Maps";
|
import { Maps } from "@/collections/world/Maps";
|
||||||
|
import { MapRoads } from "@/collections/world/MapRoads";
|
||||||
|
import { MapZones } from "@/collections/world/MapZones";
|
||||||
|
import { ResourceNodes } from "@/collections/world/ResourceNodes";
|
||||||
import { Campaigns } from "@/collections/intelligence/Campaigns";
|
import { Campaigns } from "@/collections/intelligence/Campaigns";
|
||||||
import { Qualifications } from "@/collections/users/Qualifications";
|
import { Qualifications } from "@/collections/users/Qualifications";
|
||||||
import { MissionFiles } from "@/collections/server/MissionFiles";
|
import { MissionFiles } from "@/collections/server/MissionFiles";
|
||||||
|
|
@ -289,6 +292,9 @@ const collections = [
|
||||||
|
|
||||||
// World
|
// World
|
||||||
Maps,
|
Maps,
|
||||||
|
MapRoads,
|
||||||
|
MapZones,
|
||||||
|
ResourceNodes,
|
||||||
NarrativeEvents,
|
NarrativeEvents,
|
||||||
|
|
||||||
// Story
|
// Story
|
||||||
|
|
|
||||||
|
|
@ -483,6 +483,17 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "admin:maps:manage", label: "Manage Admin Page" },
|
{ value: "admin:maps:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
group: "Map Features",
|
||||||
|
permissions: [
|
||||||
|
{ value: "map-roads:read", label: "Read Roads" },
|
||||||
|
{ value: "admin:map-roads:manage", label: "Manage Roads Admin Page" },
|
||||||
|
{ value: "map-zones:read", label: "Read Zones" },
|
||||||
|
{ value: "admin:map-zones:manage", label: "Manage Zones Admin Page" },
|
||||||
|
{ value: "resource-nodes:read", label: "Read Resource Nodes" },
|
||||||
|
{ value: "admin:resource-nodes:manage", label: "Manage Resource Nodes Admin Page" },
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
group: "Narrative Events",
|
group: "Narrative Events",
|
||||||
permissions: [
|
permissions: [
|
||||||
|
|
@ -825,6 +836,12 @@ export const ALL_PERMISSION_VALUES = PERMISSION_GROUPS.flatMap((g) =>
|
||||||
"maps:update",
|
"maps:update",
|
||||||
"maps:delete",
|
"maps:delete",
|
||||||
"admin:maps:manage",
|
"admin:maps:manage",
|
||||||
|
"map-roads:read",
|
||||||
|
"admin:map-roads:manage",
|
||||||
|
"map-zones:read",
|
||||||
|
"admin:map-zones:manage",
|
||||||
|
"resource-nodes:read",
|
||||||
|
"admin:resource-nodes:manage",
|
||||||
"narrative-events:create",
|
"narrative-events:create",
|
||||||
"narrative-events:read",
|
"narrative-events:read",
|
||||||
"narrative-events:update",
|
"narrative-events:update",
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,7 @@ describe("Leadership evaluations", () => {
|
||||||
|
|
||||||
const map = (await payload.create({
|
const map = (await payload.create({
|
||||||
collection: "maps",
|
collection: "maps",
|
||||||
data: { name: `${RUN} Map` },
|
data: { name: `${RUN} Map`, worldSizeWidth: 8192, worldSizeHeight: 8192, basemapMode: "image" },
|
||||||
overrideAccess: true,
|
overrideAccess: true,
|
||||||
depth: 0,
|
depth: 0,
|
||||||
})) as unknown as MissionMap;
|
})) as unknown as MissionMap;
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ describe("Mission auto-complete", () => {
|
||||||
|
|
||||||
const map = (await payload.create({
|
const map = (await payload.create({
|
||||||
collection: "maps",
|
collection: "maps",
|
||||||
data: { name: `${RUN} Map` },
|
data: { name: `${RUN} Map`, worldSizeWidth: 8192, worldSizeHeight: 8192, basemapMode: "image" },
|
||||||
overrideAccess: true,
|
overrideAccess: true,
|
||||||
depth: 0,
|
depth: 0,
|
||||||
})) as unknown as MissionMap;
|
})) as unknown as MissionMap;
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@ describe("Mission feedback reminder", () => {
|
||||||
|
|
||||||
const map = (await payload.create({
|
const map = (await payload.create({
|
||||||
collection: "maps",
|
collection: "maps",
|
||||||
data: { name: `${RUN} Map` },
|
data: { name: `${RUN} Map`, worldSizeWidth: 8192, worldSizeHeight: 8192, basemapMode: "image" },
|
||||||
overrideAccess: true,
|
overrideAccess: true,
|
||||||
depth: 0,
|
depth: 0,
|
||||||
})) as unknown as MissionMap;
|
})) as unknown as MissionMap;
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ describe("Respawn ticket settlement", () => {
|
||||||
|
|
||||||
const map = (await payload.create({
|
const map = (await payload.create({
|
||||||
collection: "maps",
|
collection: "maps",
|
||||||
data: { name: `${RUN} Map` },
|
data: { name: `${RUN} Map`, worldSizeWidth: 8192, worldSizeHeight: 8192, basemapMode: "image" },
|
||||||
overrideAccess: true,
|
overrideAccess: true,
|
||||||
depth: 0,
|
depth: 0,
|
||||||
})) as unknown as MissionMap;
|
})) as unknown as MissionMap;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue