feat(factions): add player-faction type, reputation, relationships, and cover image
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
00919ceeb3
commit
b14e04c487
2 changed files with 146 additions and 1 deletions
|
|
@ -13,6 +13,30 @@ export const Factions: CollectionConfig = {
|
||||||
delete: isDeveloper,
|
delete: isDeveloper,
|
||||||
read: ({ req }) => !!req.user,
|
read: ({ req }) => !!req.user,
|
||||||
},
|
},
|
||||||
|
hooks: {
|
||||||
|
beforeValidate: [
|
||||||
|
async ({ data, operation, originalDoc, req }) => {
|
||||||
|
if (data?.type !== "playerFaction") return data;
|
||||||
|
|
||||||
|
const payload = req.payload;
|
||||||
|
const existing = await payload.find({
|
||||||
|
collection: "factions",
|
||||||
|
where: { type: { equals: "playerFaction" } },
|
||||||
|
limit: 1,
|
||||||
|
depth: 0,
|
||||||
|
overrideAccess: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const conflict = existing.docs.find((doc) => doc.id !== originalDoc?.id);
|
||||||
|
if (conflict) {
|
||||||
|
throw new Error(
|
||||||
|
`Only one faction can be the Player Faction at a time. "${conflict.name}" is already the Player Faction.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
name: "name",
|
name: "name",
|
||||||
|
|
@ -24,6 +48,10 @@ export const Factions: CollectionConfig = {
|
||||||
name: "type",
|
name: "type",
|
||||||
type: "select",
|
type: "select",
|
||||||
options: [
|
options: [
|
||||||
|
{
|
||||||
|
label: "Player Faction",
|
||||||
|
value: "playerFaction",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: "Major Faction",
|
label: "Major Faction",
|
||||||
value: "majorFaction",
|
value: "majorFaction",
|
||||||
|
|
@ -43,11 +71,68 @@ export const Factions: CollectionConfig = {
|
||||||
],
|
],
|
||||||
required: true,
|
required: true,
|
||||||
defaultValue: "minorFaction",
|
defaultValue: "minorFaction",
|
||||||
|
admin: {
|
||||||
|
description:
|
||||||
|
"Player Faction = the faction our unit is currently playing as. Only one faction can carry this type at a time.",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "description",
|
name: "description",
|
||||||
type: "richText",
|
type: "richText",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "reputation",
|
||||||
|
type: "number",
|
||||||
|
admin: {
|
||||||
|
description:
|
||||||
|
"This faction's standing in the world, from -100 (reviled) to 100 (revered). 0 is neutral. Adjusted manually by admins/GMs as campaign events unfold.",
|
||||||
|
},
|
||||||
|
defaultValue: 0,
|
||||||
|
min: -100,
|
||||||
|
max: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "relationships",
|
||||||
|
type: "array",
|
||||||
|
labels: {
|
||||||
|
singular: "Relationship",
|
||||||
|
plural: "Relationships",
|
||||||
|
},
|
||||||
|
admin: {
|
||||||
|
description:
|
||||||
|
"Pairwise standing between this faction and another faction, from -100 (hostile) to 100 (allied). 0 is neutral. One entry per counterpart faction.",
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: "targetFaction",
|
||||||
|
type: "relationship",
|
||||||
|
relationTo: "factions",
|
||||||
|
required: true,
|
||||||
|
admin: {
|
||||||
|
description: "The other faction in this pairwise relationship.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "value",
|
||||||
|
type: "number",
|
||||||
|
required: true,
|
||||||
|
defaultValue: 0,
|
||||||
|
min: -100,
|
||||||
|
max: 100,
|
||||||
|
admin: {
|
||||||
|
description:
|
||||||
|
"Standing from -100 (hostile) to 100 (allied). 0 is neutral.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "notes",
|
||||||
|
type: "textarea",
|
||||||
|
admin: {
|
||||||
|
description: "Optional context for why this relationship stands where it does.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "playableFaction",
|
name: "playableFaction",
|
||||||
type: "checkbox",
|
type: "checkbox",
|
||||||
|
|
@ -70,5 +155,14 @@ export const Factions: CollectionConfig = {
|
||||||
relationTo: "campaigns",
|
relationTo: "campaigns",
|
||||||
hasMany: true,
|
hasMany: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "coverImage",
|
||||||
|
type: "upload",
|
||||||
|
relationTo: "media",
|
||||||
|
admin: {
|
||||||
|
description:
|
||||||
|
"Background image for the faction detail page hero. Renders translucent with a top-to-bottom fade to black. Recommended aspect ratio: 16:9 or wider.",
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -986,7 +986,10 @@ export interface Vehicle {
|
||||||
export interface Faction {
|
export interface Faction {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
type: 'majorFaction' | 'minorFaction' | 'corporation' | 'special';
|
/**
|
||||||
|
* Player Faction = the faction our unit is currently playing as. Only one faction can carry this type at a time.
|
||||||
|
*/
|
||||||
|
type: 'playerFaction' | 'majorFaction' | 'minorFaction' | 'corporation' | 'special';
|
||||||
description?: {
|
description?: {
|
||||||
root: {
|
root: {
|
||||||
type: string;
|
type: string;
|
||||||
|
|
@ -1002,6 +1005,30 @@ export interface Faction {
|
||||||
};
|
};
|
||||||
[k: string]: unknown;
|
[k: string]: unknown;
|
||||||
} | null;
|
} | null;
|
||||||
|
/**
|
||||||
|
* This faction's standing in the world, from -100 (reviled) to 100 (revered). 0 is neutral. Adjusted manually by admins/GMs as campaign events unfold.
|
||||||
|
*/
|
||||||
|
reputation?: number | null;
|
||||||
|
/**
|
||||||
|
* Pairwise standing between this faction and another faction, from -100 (hostile) to 100 (allied). 0 is neutral. One entry per counterpart faction.
|
||||||
|
*/
|
||||||
|
relationships?:
|
||||||
|
| {
|
||||||
|
/**
|
||||||
|
* The other faction in this pairwise relationship.
|
||||||
|
*/
|
||||||
|
targetFaction: number | Faction;
|
||||||
|
/**
|
||||||
|
* Standing from -100 (hostile) to 100 (allied). 0 is neutral.
|
||||||
|
*/
|
||||||
|
value: number;
|
||||||
|
/**
|
||||||
|
* Optional context for why this relationship stands where it does.
|
||||||
|
*/
|
||||||
|
notes?: string | null;
|
||||||
|
id?: string | null;
|
||||||
|
}[]
|
||||||
|
| null;
|
||||||
/**
|
/**
|
||||||
* Whether or not this faction is playable. Some factions may be specifically intended to be unplayable based on narrative requirements.
|
* Whether or not this faction is playable. Some factions may be specifically intended to be unplayable based on narrative requirements.
|
||||||
*/
|
*/
|
||||||
|
|
@ -1011,6 +1038,10 @@ export interface Faction {
|
||||||
*/
|
*/
|
||||||
startingFaction?: boolean | null;
|
startingFaction?: boolean | null;
|
||||||
associatedCampaigns?: (number | Campaign)[] | null;
|
associatedCampaigns?: (number | Campaign)[] | null;
|
||||||
|
/**
|
||||||
|
* Background image for the faction detail page hero. Renders translucent with a top-to-bottom fade to black. Recommended aspect ratio: 16:9 or wider.
|
||||||
|
*/
|
||||||
|
coverImage?: (number | null) | Media;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
}
|
}
|
||||||
|
|
@ -1040,6 +1071,10 @@ export interface Campaign {
|
||||||
} | null;
|
} | null;
|
||||||
startDate?: string | null;
|
startDate?: string | null;
|
||||||
endDate?: string | null;
|
endDate?: string | null;
|
||||||
|
/**
|
||||||
|
* Background image for the campaign selector card on the campaigns page. Renders translucent with hover effects. Recommended aspect ratio: 16:9 or square.
|
||||||
|
*/
|
||||||
|
coverImage?: (number | null) | Media;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
}
|
}
|
||||||
|
|
@ -1519,6 +1554,10 @@ export interface Mission {
|
||||||
* Hash of the last attendance state rendered to Discord (managed by the bot).
|
* Hash of the last attendance state rendered to Discord (managed by the bot).
|
||||||
*/
|
*/
|
||||||
discordAttendanceHash?: string | null;
|
discordAttendanceHash?: string | null;
|
||||||
|
/**
|
||||||
|
* Background image for the mission page hero header. Renders translucent with a top-to-bottom fade to black. Recommended aspect ratio: 16:9 or wider.
|
||||||
|
*/
|
||||||
|
coverImage?: (number | null) | Media;
|
||||||
classification: {
|
classification: {
|
||||||
map: number | Map;
|
map: number | Map;
|
||||||
missionType: 'Zeus' | 'PvE' | 'PvP' | 'PvPvE' | 'Training' | 'Special';
|
missionType: 'Zeus' | 'PvE' | 'PvP' | 'PvPvE' | 'Training' | 'Special';
|
||||||
|
|
@ -2731,6 +2770,7 @@ export interface MissionsSelect<T extends boolean = true> {
|
||||||
operationType?: T;
|
operationType?: T;
|
||||||
discordMessageId?: T;
|
discordMessageId?: T;
|
||||||
discordAttendanceHash?: T;
|
discordAttendanceHash?: T;
|
||||||
|
coverImage?: T;
|
||||||
classification?:
|
classification?:
|
||||||
| T
|
| T
|
||||||
| {
|
| {
|
||||||
|
|
@ -2859,6 +2899,7 @@ export interface CampaignsSelect<T extends boolean = true> {
|
||||||
description?: T;
|
description?: T;
|
||||||
startDate?: T;
|
startDate?: T;
|
||||||
endDate?: T;
|
endDate?: T;
|
||||||
|
coverImage?: T;
|
||||||
updatedAt?: T;
|
updatedAt?: T;
|
||||||
createdAt?: T;
|
createdAt?: T;
|
||||||
}
|
}
|
||||||
|
|
@ -2870,9 +2911,19 @@ export interface FactionsSelect<T extends boolean = true> {
|
||||||
name?: T;
|
name?: T;
|
||||||
type?: T;
|
type?: T;
|
||||||
description?: T;
|
description?: T;
|
||||||
|
reputation?: T;
|
||||||
|
relationships?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
targetFaction?: T;
|
||||||
|
value?: T;
|
||||||
|
notes?: T;
|
||||||
|
id?: T;
|
||||||
|
};
|
||||||
playableFaction?: T;
|
playableFaction?: T;
|
||||||
startingFaction?: T;
|
startingFaction?: T;
|
||||||
associatedCampaigns?: T;
|
associatedCampaigns?: T;
|
||||||
|
coverImage?: T;
|
||||||
updatedAt?: T;
|
updatedAt?: T;
|
||||||
createdAt?: T;
|
createdAt?: T;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue