Skip to content

Commit

Permalink
Add schemas for ChatMessage, Combat, JournalEntry, RollTable,…
Browse files Browse the repository at this point in the history
… and `TableResult` (foundryvtt#10415)
  • Loading branch information
stwlam committed Sep 29, 2023
1 parent 13b38da commit fde3dd7
Show file tree
Hide file tree
Showing 17 changed files with 232 additions and 273 deletions.
12 changes: 7 additions & 5 deletions build/lib/extractor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ class PackExtractor {
} else if ("details" in docSource.system && "publicNotes" in docSource.system.details) {
docSource.system.details.publicNotes = cleanDescription(docSource.system.details.publicNotes);
}
} else if ("content" in docSource) {
} else if ("content" in docSource && typeof docSource.content === "string") {
docSource.content = cleanDescription(docSource.content);
}

Expand All @@ -409,10 +409,12 @@ class PackExtractor {
}
delete (docSource as { _stats?: unknown })._stats;

docSource.img &&= docSource.img.replace(
"https://assets.forge-vtt.com/bazaar/systems/pf2e/assets/",
"systems/pf2e/"
) as ImageFilePath;
if ("img" in docSource && typeof docSource.img === "string") {
docSource.img = docSource.img.replace(
"https://assets.forge-vtt.com/bazaar/systems/pf2e/assets/",
"systems/pf2e/"
) as ImageFilePath;
}

if (isObject(docSource.flags?.pf2e) && Object.keys(docSource.flags.pf2e).length === 0) {
delete docSource.flags.pf2e;
Expand Down
2 changes: 1 addition & 1 deletion src/module/actor/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ function createEncounterRollOptions(actor: ActorPF2e): Record<string, boolean> {
[`encounter:threat:${numericThreat}`, !!threat],
[`encounter:threat:${threat}`, !!threat],
[`encounter:round:${encounter.round}`, true],
[`encounter:turn:${encounter.turn + 1}`, true],
[`encounter:turn:${Number(encounter.turn) + 1}`, true],
["self:participant:own-turn", encounter.combatant?.actor === actor],
[`self:participant:initiative:roll:${initiativeRoll}`, true],
[`self:participant:initiative:rank:${initiativeRank}`, true],
Expand Down
2 changes: 1 addition & 1 deletion src/module/apps/sidebar/chat-log.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ class ChatLogPF2e extends ChatLog<ChatMessagePF2e> {
protected override async _processDiceCommand(
command: string,
matches: RegExpMatchArray[],
chatData: DeepPartial<foundry.documents.ChatMessageSource>,
chatData: PreCreate<Omit<ChatMessagePF2e["_source"], "rolls"> & { rolls: (string | RollJSON)[] }>,
createOptions: ChatMessageModificationContext
): Promise<void> {
const actor = ChatMessage.getSpeakerActor(chatData.speaker ?? {}) || game.user.character;
Expand Down
2 changes: 2 additions & 0 deletions src/module/encounter/document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,8 @@ class EncounterPF2e extends Combat {
interface EncounterPF2e extends Combat {
readonly combatants: foundry.abstract.EmbeddedCollection<CombatantPF2e<this, TokenDocumentPF2e | null>>;

scene: ScenePF2e;

rollNPC(options: RollInitiativeOptionsPF2e): Promise<this>;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ export class Migration850FlatFootedToOffGuard extends MigrationBase {
if (source.name === "Remaster Changes") return;

source.name = source.name.replace(this.#oldNamePattern, this.#newName);
source.img &&= source.img.replace(this.#imgPattern, "off-guard.webp") as ImageFilePath;
if ("img" in source && typeof source.img === "string") {
source.img = source.img.replace(this.#imgPattern, "off-guard.webp");
}
source.pages = recursiveReplaceString(source.pages, (s) => this.#replace(s));
if ("content" in source && typeof source.content === "string") {
source.content = this.#replace(source.content);
Expand Down
31 changes: 16 additions & 15 deletions src/module/system/damage/damage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,23 +212,24 @@ export class DamagePF2e {
unadjustedOutcome: context.unadjustedOutcome ?? null,
};

const messageData = await roll.toMessage(
{
speaker: ChatMessagePF2e.getSpeaker({ actor: self?.actor, token: self?.token }),
flavor,
flags: {
pf2e: {
context: contextFlag,
target: targetFlag,
modifiers: data.modifiers?.map((m) => m.toObject()) ?? [],
origin: item?.getOriginData(),
strike,
preformatted: "both",
const messageData: Omit<foundry.documents.ChatMessageSource, "rolls"> & { rolls: (string | RollJSON)[] } =
await roll.toMessage(
{
speaker: ChatMessagePF2e.getSpeaker({ actor: self?.actor, token: self?.token }),
flavor,
flags: {
pf2e: {
context: contextFlag,
target: targetFlag,
modifiers: data.modifiers?.map((m) => m.toObject()) ?? [],
origin: item?.getOriginData(),
strike,
preformatted: "both",
},
},
},
},
{ create: false }
);
{ create: false }
);

// If there is splash damage, include it as an additional roll for separate application
const splashRolls = await (async (): Promise<RollJSON[]> => {
Expand Down
2 changes: 1 addition & 1 deletion src/scripts/macros/hotbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export async function rollActionMacro(

const content = await renderTemplate("systems/pf2e/templates/chat/strike-card.hbs", templateData);
const token = actor.token ?? actor.getActiveTokens(true, true).shift() ?? null;
const chatData: Partial<foundry.documents.ChatMessageSource> = {
const chatData: PreCreate<foundry.documents.ChatMessageSource> = {
speaker: ChatMessagePF2e.getSpeaker({ actor, token }),
content,
type: CONST.CHAT_MESSAGE_TYPES.OTHER,
Expand Down
17 changes: 9 additions & 8 deletions types/foundry/client/data/documents/chat-message.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ declare global {
class ChatMessage extends ClientBaseChatMessage {
constructor(data: PreCreate<foundry.documents.ChatMessageSource>, context?: DocumentConstructionContext<null>);

flavor: string;

_rollExpanded: boolean;

/**
Expand Down Expand Up @@ -39,9 +37,6 @@ declare global {
*/
override get visible(): boolean;

/** The User who created the chat message. */
get user(): User | undefined;

override prepareData(): void;

/**
Expand Down Expand Up @@ -173,20 +168,26 @@ declare global {
export(): string;
}

interface ChatMessage extends ClientBaseChatMessage {
user: User;
}

namespace ChatMessage {
function create<TDocument extends ChatMessage>(
this: ConstructorOf<TDocument>,
data: PreCreate<TDocument["_source"]>[],
data: DeepPartial<Omit<TDocument["_source"], "rolls"> & { rolls: (string | RollJSON)[] }>[],
context?: ChatMessageModificationContext
): Promise<TDocument[]>;
function create<T extends ChatMessage>(
this: ConstructorOf<T>,
data: PreCreate<T["_source"]>,
data: DeepPartial<Omit<T["_source"], "rolls"> & { rolls: (string | RollJSON)[] }>,
context?: ChatMessageModificationContext
): Promise<T | undefined>;
function create<T extends ChatMessage>(
this: ConstructorOf<T>,
data: PreCreate<T["_source"]>[] | PreCreate<T["_source"]>,
data:
| DeepPartial<Omit<T["_source"], "rolls"> & { rolls: (string | RollJSON)[] }>[]
| DeepPartial<Omit<T["_source"], "rolls"> & { rolls: (string | RollJSON)[] }>,
context?: ChatMessageModificationContext
): Promise<T[] | T | undefined>;
}
Expand Down
11 changes: 0 additions & 11 deletions types/foundry/client/data/documents/combat.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ declare global {
class Combat extends ClientBaseCombat {
constructor(data: PreCreate<foundry.documents.CombatSource>, context?: DocumentConstructionContext<null>);

active: boolean;

/** Track the sorted turn order of this combat encounter */
turns: CollectionValue<this["combatants"]>[];

Expand All @@ -30,21 +28,12 @@ declare global {
/** Get the Combatant who has the current turn. */
get combatant(): CollectionValue<this["combatants"]> | undefined;

/** The numeric round of the Combat encounter */
get round(): number;

/** A reference to the Scene document within which this Combat encounter occurs */
get scene(): NonNullable<NonNullable<CollectionValue<this["combatants"]>["actor"]>["parent"]>["parent"];

/** Return the object of settings which modify the Combat Tracker behavior */
get settings(): Record<string, unknown>;

/** Has this combat encounter been started? */
get started(): boolean;

/** The numeric turn of the combat round in the Combat encounter */
get turn(): number;

override get visible(): true;

/** Is this combat active in the current scene? */
Expand Down
10 changes: 1 addition & 9 deletions types/foundry/common/abstract/data.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,15 +204,7 @@ export default abstract class DataModel<
static migrateDataSafe(source: object): object;
}

export type RawObject<TModel extends _DataModel> = {
[P in keyof TModel]: TModel[P] extends EmbeddedCollection<infer U>
? RawObject<U>[]
: TModel[P] extends DataModel
? RawObject<TModel[P]>
: TModel[P] extends DataModel[]
? RawObject<TModel[P][number]>[]
: TModel[P];
};
export type RawObject<TModel extends _DataModel> = TModel["_source"];

export interface DataModelValidationOptions {
changes?: object;
Expand Down
111 changes: 49 additions & 62 deletions types/foundry/common/documents/chat-message.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Document, DocumentMetadata } from "../abstract/module.d.ts";
import type * as fields from "../data/fields.d.ts";
import type { BaseUser } from "./module.d.ts";

/**
Expand All @@ -8,54 +9,52 @@ import type { BaseUser } from "./module.d.ts";
* @param data Initial data from which to construct the document.
* @property data The constructed data object for the document.
*/
export default class BaseChatMessage extends Document<null> {
blind: boolean;
content: string;
flags: ChatMessageFlags;
export default class BaseChatMessage extends Document<null, ChatMessageSchema> {
rolls: Rolled<Roll>[];
speaker: ChatSpeakerData;
type: ChatMessageType;
whisper: string[];

static override get metadata(): ChatMessageMetadata;

/** Is a user able to create a new chat message? */
protected static _canCreate(user: BaseUser, doc: BaseChatMessage): boolean;

/** Is a user able to update an existing chat message? */
protected static _canUpdate(user: BaseUser, doc: BaseChatMessage, data: ChatMessageSource): boolean;

/** Is a user able to delete an existing chat message? */
protected static _canDelete(user: BaseUser, doc: BaseChatMessage): boolean;

static override createDocuments<TDocument extends Document<null>>(
this: ConstructorOf<TDocument>,
data?: (TDocument | PreCreate<TDocument["_source"]>)[],
context?: ChatMessageModificationContext
): Promise<TDocument[]>;
static override defineSchema(): ChatMessageSchema;
}

export default interface BaseChatMessage extends Document<null> {
export default interface BaseChatMessage
extends Document<null, ChatMessageSchema>,
ModelPropsFromSchema<ChatMessageSchema> {
readonly _source: ChatMessageSource;

get documentName(): "ChatMessage";
}

export interface ChatMessageSource {
_id: string;
type: ChatMessageType;
user: string;
timestamp: string;
flavor?: string;
content: string;
speaker: ChatSpeakerData;
whisper: string[];
blind: boolean;
rolls: (string | RollJSON)[];
sound: AudioFilePath;
emote?: boolean;
flags: ChatMessageFlags;
}
export type ChatMessageSchema = {
/** The _id which uniquely identifies this ChatMessage document */
_id: fields.DocumentIdField;
/** The message type from CONST.CHAT_MESSAGE_TYPES */
type: fields.NumberField<ChatMessageType, ChatMessageType, true, true, true>;
/** The _id of the User document who generated this message */
user: fields.ForeignDocumentField<BaseUser, true, false, true>;
/** The timestamp at which point this message was generated */
timestamp: fields.NumberField<number, number, true, false, true>;
/** An optional flavor text message which summarizes this message */
flavor: fields.HTMLField;
/** The HTML content of this chat message */
content: fields.HTMLField;
/** A ChatSpeakerData object which describes the origin of the ChatMessage */
speaker: fields.SchemaField<ChatSpeakerSchema>;
/** An array of User _id values to whom this message is privately whispered */
whisper: fields.ArrayField<fields.ForeignDocumentField<string>>;
/** Is this message sent blindly where the creating User cannot see it? */
blind: fields.BooleanField;
/** Serialized content of any Roll instances attached to the ChatMessage */
rolls: fields.ArrayField<fields.JSONField<Roll, true>>;
/** The URL of an audio file which plays when this message is received */
sound: fields.FilePathField<AudioFilePath>;
/** Is this message styled as an emote? */
emote: fields.BooleanField;
/** An object of optional key/value flags */
flags: fields.ObjectField<ChatMessageFlags>;
};

export type ChatMessageSource = SourceFromSchema<ChatMessageSchema>;

export interface ChatMessageFlags extends DocumentFlags {
core?: {
Expand All @@ -65,37 +64,25 @@ export interface ChatMessageFlags extends DocumentFlags {
};
}

/**
* The data schema for an embedded Chat Speaker object.
* @extends DocumentData
* @memberof data
* @see ChatMessageData
*
* @param data Initial data used to construct the data object
* @param [document] The document to which this data object belongs
*
* @property [scene] The _id of the Scene where this message was created
* @property [actor] The _id of the Actor who generated this message
* @property [token] The _id of the Token who generated this message
* @property [alias] An overridden alias name used instead of the Actor or Token name
*/
export interface ChatSpeakerData {
scene?: string | null;
actor?: string | null;
token?: string | null;
alias: string;
}
type ChatSpeakerSchema = {
/** The _id of the Scene where this message was created */
scene: fields.ForeignDocumentField<string>;
/** The _id of the Actor who generated this message */
actor: fields.ForeignDocumentField<string>;
/** The _id of the Token who generated this message */
token: fields.ForeignDocumentField<string>;
/** An overridden alias name used instead of the Actor or Token name */
alias: fields.StringField<string, string, false, false, true>;
};

type ChatSpeakerData = SourceFromSchema<ChatSpeakerSchema>;

interface ChatMessageMetadata extends DocumentMetadata {
name: "ChatMessage";
collection: "messages";
label: "DOCUMENT.ChatMessage";
labelPlural: "DOCUMENT.ChatMessages";
isPrimary: true;
permissions: {
create: (typeof BaseChatMessage)["_canCreate"];
update: (typeof BaseChatMessage)["_canUpdate"];
delete: (typeof BaseChatMessage)["_canDelete"];
};
}

declare global {
Expand Down
Loading

0 comments on commit fde3dd7

Please sign in to comment.