Checks V2 - League-of-Fabulous-Developers/FoundryVTT-Fabula-Ultima GitHub Wiki
The first version of the Checks system was written relatively close to the start of the project. While that version of Checks worked fine on its own, it became obvious that it lacked flexibility and extensibility. Checks V2 aims to rectify this lack by splitting the execution of a Check into multiple phases and by providing Hooks to interact with the Check in progress during each phase. Although ProjectFU is written in plain JavaScript, this document uses TypeScript type definitions for clarity.
Checks V2 splits the execution of a Check into three phases, each with a corresponding Hook (in parentheses):
- Preparation (
projectfu.prepareCheck
): Configuration of the Check before it is rolled - Processing (
projectfu.processCheck
): Processing the results after the dice are rolled - Rendering (
projectfu.renderCheck
): Assembling different templates and rendering HTML into the final ChatMessage
For an example of use see Checks V2 by Example
type CheckType = 'attribute' | 'accuracy' | 'magic' | 'open' | 'opposed' | 'group' | 'support' | 'initiative' | 'display';
type Attribute = 'dex' | 'ins' | 'mig' | 'wlp';
type CheckModifier = {
label: string;
value: number;
}
type Check = {
type: CheckType;
id: string;
primary: Attribute;
secondary: Attribute;
modifiers: CheckModifier[];
additionalData: Record<string, unknown>;
}
type CheckResult = {
type: CheckType;
id: string;
actorUuid: string;
itemUuid: string;
roll: Roll;
additionalRolls: Roll[];
primary: {
attribute: Attribute;
dice: number;
result: number;
}
secondary: {
attribute: Attribute;
dice: number;
result: number;
}
modifiers: CheckModifier[];
modifierTotal: number;
result: number;
fumble: boolean;
critical: boolean;
additionalData: Record<string, unknown>;
}
The ChecksV2
object is available both as projectfu.ChecksV2
and game.projectfu.ChecksV2
and provides the following functions:
type ChecksV2 = {
display: (actor: FUActor, item: FUItem) => Promise<void>;
accuracyCheck: (actor: FUActor, item: FUItem, initialConfigCallback?: ConfigCallback) => Promise<void>;
attributeCheck: (actor: FUActor, attributes: CheckAttributes, initialConfigCallback?: ConfigCallback) => Promise<void>;
magicCheck: (actor: FUActor, item: FUItem, initialConfigCallback?: ConfigCallback) => Promise<void>;
opposedCheck: (actor: FUActor, initialConfigCallback: ConfigCallback) => Promise<void>;
groupCheck: () => Promise<void>; // Not yet implemented
supportCheck: () => Promise<void>; // Not yet implemented
openCheck: () => Promise<void>; // Not yet implemented
modifyCheck: (checkId: string, callback: ModificationCallback) => Promise<void>;
isCheck: (message: ChatMessage | string) => boolean;
}
type ConfigCallback = (check: Check) => Promise<void>;
type CheckAttributes = {
primary: Attribute;
secondary: Attribute;
}
type ModificationCallback = (check: CheckResult, actor: FUActor, item: FUItem) => Promise<{roll: Roll, check: Check} | null>
Any of accuracyCheck
, attributeCheck
, magicCheck
, opposedCheck
, groupCheck
, supportCheck
or openCheck
trigger a Check to be rolled.
As of the time of writing
groupCheck
,supportCheck
andopenCheck
are not yet implemented and will throw an error.
modifyCheck
can be used to change an already rolled Check and evaluate it again.
This is used for invoking Traits and Bonds.
isCheck
checks if a ChatMessage was created through ChecksV2.
display
uses the rendering infrastructure of ChecksV2 to display information about an item.
type prepareCheck = (check: Check, actor: FUActor, item: FUItem, registerCallback: CallbackRegistration) => void
type CallbackRegistration = (callback: (check:Check) => Promise<void>, priority?: number) => void
This Hook can be used to configure the Check before it is rolled.
The check.additionalData
object can be used to store information required for later in the workflow.
Because Hooks are synchronous by design, any asynchronous actions (like waiting for user input) need to be registered through the registerCallback
parameter.
If you have specific timing requirements for your callback you can use the optional second parameter for registerCallback
to specify when it should be executed.
Callbacks are executed in natural number order, meaning negative number have a higher priority than positive numbers, with the default priority being 0.
type processCheck = (check: CheckResult, actor: FUActor, item: FUItem) => void
This Hook can be used to do post processing based on the results of the dice.
Any content of check.additionalData
added during projectfu.prepareCheck
is present and can be referenced.
type renderCheck = (sections: CheckRenderData[], check: CheckResult, actor: FUActor, item: FUItem, additionalFlags: Record<string, unknown>) => void;
type CheckRenderData = (CheckSection | Promise<CheckSection> | (() => CheckSection) | (() => Promise<CheckSection>));
type CheckSection = {
content: string;
order?: number;
partial?: never;
data?: never;
} | {
partial: string;
data: Object;
order?: number;
content?: never;
}
This Hook is used to add content to the final ChatMessage.
The ChatMessage will be composed of multiple CheckSection
s.
A new CheckSection
can be added simply by adding it to the first parameter, the sections
array.
CheckSections
are expected to either contain their content
directly or provide a partial
to be rendered together with the required data
.
The order
field of the CheckSection
influences where its content is inserted into the ChatMessage.
For the default order of CheckSection
s provided by the system please see default-section-order.mjs