diff --git a/templates/chat/roll-result/action-acquireasset.hbs b/templates/chat/roll-result/action-acquireasset.hbs
index 1459292f..27376d40 100644
--- a/templates/chat/roll-result/action-acquireasset.hbs
+++ b/templates/chat/roll-result/action-acquireasset.hbs
@@ -2,7 +2,7 @@
+
@@ -327,7 +327,7 @@
Waiting For GM ...
{{else if (test rollPhase "==" "AwaitingRoll")}}
-
+
{{/if}}
{{#if costData}}
diff --git a/templates/roll/partials/roll-collab-fortune-gm.hbs b/templates/roll/partials/roll-collab-fortune-gm.hbs
index 8a33d576..d84df68a 100644
--- a/templates/roll/partials/roll-collab-fortune-gm.hbs
+++ b/templates/roll/partials/roll-collab-fortune-gm.hbs
@@ -1,4 +1,4 @@
-
+
@@ -190,7 +190,7 @@
Waiting For GM ...
{{else if (test rollPhase "==" "AwaitingRoll")}}
-
+
{{/if}}
{{#if costData}}
diff --git a/templates/roll/partials/roll-collab-fortune.hbs b/templates/roll/partials/roll-collab-fortune.hbs
index 123c6dda..4e49c41e 100644
--- a/templates/roll/partials/roll-collab-fortune.hbs
+++ b/templates/roll/partials/roll-collab-fortune.hbs
@@ -1,4 +1,4 @@
-
+
@@ -323,7 +323,7 @@
Waiting For GM ...
{{else if (test rollPhase "==" "AwaitingRoll")}}
-
+
{{/if}}
{{#if costData}}
diff --git a/templates/roll/partials/roll-collab-indulgevice-gm.hbs b/templates/roll/partials/roll-collab-indulgevice-gm.hbs
index 8a33d576..d84df68a 100644
--- a/templates/roll/partials/roll-collab-indulgevice-gm.hbs
+++ b/templates/roll/partials/roll-collab-indulgevice-gm.hbs
@@ -1,4 +1,4 @@
-
+
@@ -190,7 +190,7 @@
Waiting For GM ...
{{else if (test rollPhase "==" "AwaitingRoll")}}
-
+
{{/if}}
{{#if costData}}
diff --git a/templates/roll/partials/roll-collab-indulgevice.hbs b/templates/roll/partials/roll-collab-indulgevice.hbs
index 123c6dda..4e49c41e 100644
--- a/templates/roll/partials/roll-collab-indulgevice.hbs
+++ b/templates/roll/partials/roll-collab-indulgevice.hbs
@@ -1,4 +1,4 @@
-
+
@@ -323,7 +323,7 @@
Waiting For GM ...
{{else if (test rollPhase "==" "AwaitingRoll")}}
-
+
{{/if}}
{{#if costData}}
diff --git a/templates/roll/partials/roll-collab-resistance.hbs b/templates/roll/partials/roll-collab-resistance.hbs
index 1b1e63bb..51f44fb5 100644
--- a/templates/roll/partials/roll-collab-resistance.hbs
+++ b/templates/roll/partials/roll-collab-resistance.hbs
@@ -156,7 +156,7 @@
Waiting For GM ...
{{else if (test rollPhase "==" "AwaitingRoll")}}
-
+
{{/if}}
{{#if costData}}
diff --git a/ts/@types/blades-general-types.d.ts b/ts/@types/blades-general-types.d.ts
index 46a9e1d9..774d5674 100644
--- a/ts/@types/blades-general-types.d.ts
+++ b/ts/@types/blades-general-types.d.ts
@@ -2,6 +2,9 @@ import {AttributeTrait, ActionTrait, District} from "../core/constants";
import BladesItem from "../BladesItem";
import BladesActor from "../BladesActor";
import BladesChat from "../classes/BladesChat";
+import BladesClockKey, {BladesClock} from "../classes/BladesClocks";
+import BladesConsequence from "../classes/BladesConsequence";
+import BladesRoll, {BladesRollMod} from "../classes/BladesRoll";
import {gsap} from "gsap/all";
@@ -132,10 +135,13 @@ declare global {
type gsapAnim = gsap.core.Tween | gsap.core.Timeline;
// Represents a generic Blades document
- type BladesDoc = BladesActor | BladesItem | BladesChat;
+ type BladesDoc = BladesActor | BladesItem;
// Represents any Blades document sheet
- type BladesSheet = BladesActorSheet | BladesItemSheet | BladesRoll;
+ type BladesSheet = BladesActorSheet | BladesItemSheet;
+
+ // Represents any document that can be the target of a BladesTargetLink subclass.
+ type BladesLinkDoc = BladesDoc | BladesChat | User;
// Represents a reference to a Blades document
type DocRef = string | BladesDoc;
diff --git a/ts/@types/blades-roll.d.ts b/ts/@types/blades-roll.d.ts
index 24e7a86e..516f9325 100644
--- a/ts/@types/blades-roll.d.ts
+++ b/ts/@types/blades-roll.d.ts
@@ -9,6 +9,8 @@ declare global {
namespace BladesRollMod {
+ // export type Value = string|number|string[];
+
export type Schema = {
key: string,
name: string,
@@ -43,22 +45,22 @@ declare global {
namespace BladesRoll {
- export interface Config extends BladesTargetLink.Config {
- rollType: RollType,
+ export interface Config extends Partial {
+ rollType?: RollType,
rollSubType?: RollSubType,
- rollUserID: IDString,
+ rollUserID?: IDString,
rollTrait?: RollTrait,
rollDowntimeAction?: DowntimeAction,
rollClockKey?: IDString|BladesClockKey,
rollClockKeyID?: IDString,
- rollPrimaryData: PrimaryDocData;
+ rollPrimaryData?: PrimaryDocData;
rollOppData?: OppositionDocData;
- rollParticipantData?: RollParticipantData,
+ rollParticipantData?: RollParticipantDataSet,
participantRollTo?: string,
resistanceRollTo?: {
- rollID: string,
+ id: string,
userID: string,
consequenceID: string
},
@@ -74,7 +76,9 @@ declare global {
>>,
resistanceData?: {
consequence: BladesConsequence.Data
- }
+ },
+
+ userPermissions?: Record
}
export interface Schema extends Omit {
@@ -99,7 +103,7 @@ declare global {
Partial>
>,
- userPermissions: Record,
+ userPermissions: Record,
template?: string,
finalPosition?: Position,
@@ -319,7 +323,7 @@ declare global {
export type ParticipantConstructorData = ParticipantSectionData & Partial;
- export interface RollParticipantData {
+ export interface RollParticipantDataSet {
[RollModSection.roll]?: {
Assist?: ParticipantDocData & ParticipantSectionData,
Group_1?: ParticipantDocData & ParticipantSectionData,
diff --git a/ts/@types/blades-target-link.d.ts b/ts/@types/blades-target-link.d.ts
index c08e0f6e..0feff9a0 100644
--- a/ts/@types/blades-target-link.d.ts
+++ b/ts/@types/blades-target-link.d.ts
@@ -5,6 +5,7 @@ import BladesNPC from "../documents/actors/BladesNPC";
import BladesFaction from "../documents/actors/BladesFaction";
import BladesCrew from "../documents/actors/BladesCrew";
import BladesTargetLink from "../classes/BladesTargetLink";
+import BladesChat from "../classes/BladesChat";
declare global {
@@ -13,13 +14,11 @@ declare global {
namespace BladesTargetLink {
- export type UnknownSchema = Record;
-
- export type StaticThisContext = typeof BladesTargetLink
+ export type StaticThisContext = typeof BladesTargetLink
& (new (data: Data & Schema) => BladesTargetLink & Subclass);
export type Config = {
- target?: IDString|UUIDString|BladesDoc,
+ target?: IDString|UUIDString|BladesDoc|BladesChat|User,
targetID?: IDString|UUIDString,
targetKey?: TargetKey,
targetFlagKey?: TargetFlagKey
@@ -33,10 +32,10 @@ declare global {
}
export type Instance = Data & {
- target: BladesDoc
+ target: BladesDoc|BladesChat|User
}
- export interface Subclass extends Instance {
+ export interface Subclass extends Instance {
data: Data & Schema,
diff --git a/ts/@types/index.d.ts b/ts/@types/index.d.ts
index 0ed98665..78f5616e 100644
--- a/ts/@types/index.d.ts
+++ b/ts/@types/index.d.ts
@@ -59,6 +59,7 @@ declare global {
ClockKeeper: BladesClockKeeper,
Director: BladesDirector,
Tracker: BladesGMTracker,
+ Rolls: Collection,
ClockKeys: Collection,
Consequences: Collection,
Tooltips: WeakMap
@@ -97,14 +98,6 @@ declare global {
}
interface LenientGlobalVariableTypes { game: never }
- interface FlagConfig {
- User: {
- [C.SYSTEM_ID]?: {
- rollCollab?: BladesRoll.FlagData
- }
- };
- }
-
// GreenSock Accessor Object
declare const gsap: gsap;
type BladesTweenTarget = JQuery | gsap.TweenTarget;
diff --git a/ts/blades.ts b/ts/blades.ts
index 88187a15..1f571ee6 100644
--- a/ts/blades.ts
+++ b/ts/blades.ts
@@ -585,6 +585,7 @@ class GlobalGetter {
Hooks.once("init", async () => {
// Initialize Game object
game.eunoblades = {
+ Rolls: new Collection(),
ClockKeys: new Collection(),
Consequences: new Collection(),
Director: BladesDirector.getInstance(),
diff --git a/ts/classes/BladesChat.ts b/ts/classes/BladesChat.ts
index 882ea279..aa311dd1 100644
--- a/ts/classes/BladesChat.ts
+++ b/ts/classes/BladesChat.ts
@@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
// #region IMPORTS ~
import {ApplyTooltipAnimations, ApplyConsequenceAnimations} from "../core/gsap";
-import {Position, Effect, RollResult} from "../core/constants";
+import C, {Position, Effect, RollResult} from "../core/constants";
import BladesRoll from "./BladesRoll";
import BladesConsequence from "./BladesConsequence";
@@ -14,36 +14,24 @@ namespace BladesChat {
export interface Flags {
template: string,
- rollData?: BladesRoll.FlagData
+ rollData?: BladesRoll.Schema
}
}
class BladesChat extends ChatMessage {
- // static override defineSchema() {
- // return Object.assign(super.defineSchema(), {
- // csqData: new foundry.data.fields.ObjectField()
- // });
- // }
-
static Initialize() {
- // let lastMessageID: string|false = Array.from(game.messages).pop()?.id ?? "";
+
Hooks.on("renderChatMessage", (msg: BladesChat, html: JQuery) => {
ApplyTooltipAnimations(html);
- if (msg.flags.rollData) {
+ const {rollData} = msg.flagData;
+ if (rollData) {
ApplyConsequenceAnimations(html);
BladesConsequence.ApplyChatListeners(html);
}
html.addClass("display-ok");
- // if (lastMessageID && _msg.id === lastMessageID) {
- // setTimeout(() => {
- // $(document).find("#chat .chat-message:not([class*='-roll'])")
- // .remove();
- // }, 500);
- // lastMessageID = false;
- // }
-
});
+
return loadTemplates([
"systems/eunos-blades/templates/chat/roll-result/action.hbs",
"systems/eunos-blades/templates/chat/roll-result/action-clock.hbs",
@@ -66,23 +54,29 @@ class BladesChat extends ChatMessage {
static async ConstructRollOutput(rollInst: BladesRoll): Promise {
const template = rollInst.resultChatTemplate;
- const rollFlags = {
- ...rollInst.flagData,
+ const rollData = {
+ ...rollInst.data,
rollTraitVerb: rollInst.rollTraitVerb ?? "",
rollTraitPastVerb: rollInst.rollTraitPastVerb ?? rollInst.rollTraitVerb ?? ""
};
return await BladesChat.create({
speaker: rollInst.getSpeaker(BladesChat.getSpeaker()),
- content: await renderTemplate(template, rollFlags),
+ content: await renderTemplate(template, rollData),
type: CONST.CHAT_MESSAGE_TYPES.ROLL,
flags: {
- template,
- rollData: rollFlags
+ "eunos-blades": {
+ template,
+ rollData
+ }
}
}) as BladesChat;
}
+ get flagData() {
+ return this.flags["eunos-blades"];
+ }
+
get allRollConsequences():
Record implements
} as Schema;
}
- static override async Create(
+ static override async Create(
config: BladesClockKey.Config & Partial,
clockConfigs: Array> = []
) {
@@ -132,7 +132,7 @@ class BladesClockKey extends BladesTargetLink implements
// Update the clock key with the new clock data
await clockKey.updateTarget("clocksData", clocksData, ClockKeyUpdateAction.RenderAll);
- return clockKey as BladesClockKey & BladesTargetLink.Subclass;
+ return clockKey as BladesClockKey & BladesTargetLink;
}
static GetFromElement(elem: HTMLElement | JQuery): BladesClockKey | undefined {
@@ -207,16 +207,16 @@ class BladesClockKey extends BladesTargetLink implements
}
get isClockKeeperKey(): boolean {
- return this.target.type === BladesItemType.clock_keeper;
+ return this.target instanceof BladesClockKeeper;
}
get isFactionKey(): boolean {
- return this.target.type === BladesActorType.faction;
+ return this.target instanceof BladesFaction;
}
get isProjectKey(): boolean {
- return this.target.type === BladesItemType.project;
+ return this.target instanceof BladesProject;
}
get isScoreKey(): boolean {
- return this.target.type === BladesItemType.score;
+ return this.target instanceof BladesScore;
}
get visibleClocks(): BladesClock[] {
return this.clocks.filter((clock) => clock.isVisible);
@@ -385,7 +385,7 @@ class BladesClockKey extends BladesTargetLink implements
config.index = indexOverride ?? this.size;
// Parse config to full data object
- const cData = BladesClock.ParseConfig(config as BladesClock.Config);
+ const cData = BladesClock.ParseConfig(config as BladesClock.Config);
return cData;
}
@@ -977,8 +977,8 @@ class BladesClockKey extends BladesTargetLink implements
this.postUpdateRender(postUpdateAction);
}
- override async updateTargetData(
- val: T | null,
+ override async updateTargetData(
+ val: unknown,
postUpdateAction: ClockKeyUpdateAction|boolean = false
) {
await super.updateTargetData(val, true);
@@ -1654,8 +1654,8 @@ class BladesClock extends BladesTargetLink implements Blades
this.postUpdateRender(postUpdateAction);
}
- override async updateTargetData(
- val: T | null,
+ override async updateTargetData(
+ val: Partial | null,
postUpdateAction: ClockKeyUpdateAction|boolean = false
) {
await super.updateTargetData(val, true);
diff --git a/ts/classes/BladesConsequence.ts b/ts/classes/BladesConsequence.ts
index fff009b8..621434b9 100644
--- a/ts/classes/BladesConsequence.ts
+++ b/ts/classes/BladesConsequence.ts
@@ -149,7 +149,7 @@ class BladesConsequence extends BladesTargetLink {
return new BladesConsequence({
...csqData,
type: type as ConsequenceType,
- rollID: msg.flags.rollID as IDString,
+ id: msg.flags.id as IDString,
userID: msg.flags.rollUserID,
primaryID: msg.flags.rollPrimaryData.rollPrimaryID as IDString,
primaryType: msg.flags.rollPrimaryData.rollPrimaryType,
@@ -164,7 +164,7 @@ class BladesConsequence extends BladesTargetLink {
chatID: IDString;
- rollID: IDString;
+ id: IDString;
userID: IDString;
@@ -203,7 +203,7 @@ class BladesConsequence extends BladesTargetLink {
id: this.resistTo.id,
chatID: this.chatMessage.id as IDString,
userID: this.user.id as IDString,
- rollID: this.rollID,
+ id: this.id,
primaryID: this.primaryID,
primaryType: this.primaryType,
position: this.position,
@@ -236,7 +236,7 @@ class BladesConsequence extends BladesTargetLink {
const {id, targetID, targetKey, targetFlagKey} = {...parentCsq ?? {}, ...data} as BladesTargetLink.Data;
super({id, targetID, targetKey, targetFlagKey});
const {
- chatID, userID, rollID, primaryID, primaryType,
+ chatID, userID, id, primaryID, primaryType,
position, effect, result
} = {...parentCsq ?? {}, ...data} as BladesConsequence.Data;
@@ -251,7 +251,7 @@ class BladesConsequence extends BladesTargetLink {
eLog.checkLog3("bladesConsequence", "[new BladesConsequence]", {
parentCsq,
- id, chatID, userID, rollID, primaryID, primaryType,
+ id, chatID, userID, id, primaryID, primaryType,
name,
type,
position, effect, result,
@@ -262,7 +262,7 @@ class BladesConsequence extends BladesTargetLink {
});
if (typeof id !== "string") { throw new Error("[new BladesConsequence] Missing 'id' in constructor data object."); }
- if (typeof rollID !== "string") { throw new Error("[new BladesConsequence] Missing 'rollID' in constructor data object."); }
+ if (typeof id !== "string") { throw new Error("[new BladesConsequence] Missing 'id' in constructor data object."); }
if (typeof chatID !== "string") { throw new Error("[new BladesConsequence] Missing 'chatID' in constructor data object."); }
const chatMessage = game.messages.get(chatID);
if (!(chatMessage instanceof BladesChat)) { throw new Error(`[new BladesConsequence] No chat message with id '${chatID}' found.`); }
@@ -281,7 +281,7 @@ class BladesConsequence extends BladesTargetLink {
if (!(typeof result === "string" && [RollResult.partial, RollResult.fail].includes(result))) { throw new Error("[new BladesConsequence] Missing 'result' in constructor data object."); }
this._id = id;
- this.rollID = rollID;
+ this.id = id;
this.chatMessage = chatMessage;
this.chatID = chatMessage.id;
this.user = user;
@@ -478,7 +478,7 @@ class BladesConsequence extends BladesTargetLink {
// get rollFlagData(): BladesRoll.FlagData {
// // Get rollPrimaryData from archived roll flags on user document.
// let rollFlagData = this._user.getFlag(C.SYSTEM_ID, "rollCollab") as BladesRoll.FlagData;
- // if (rollFlagData.rollID !== this._rollID) {
+ // if (rollFlagData.id !== this._rollID) {
// rollFlagData = this._user.getFlag(C.SYSTEM_ID, `rollCollabArchive.${this._rollID}`) as BladesRoll.FlagData;
// }
// if (!rollFlagData) { throw new Error(`Unable to locate flag data for roll id '${this._rollID}'`); }
diff --git a/ts/classes/BladesRoll.ts b/ts/classes/BladesRoll.ts
index f702f3f3..7eaa5ab1 100644
--- a/ts/classes/BladesRoll.ts
+++ b/ts/classes/BladesRoll.ts
@@ -61,8 +61,6 @@ function isModStatus(str: unknown): str is RollModStatus {
return typeof str === "string" && str in RollModStatus;
}
-
-
/**
* Checks if the given section can contain BladesRollParticipant documents.
* @param {RollModSection} section
@@ -87,15 +85,16 @@ function isParticipantSubSection(subSection: string): subSection is BladesRoll.R
// #endregion
// #region Utility Functions ~
+
// #endregion
class BladesRollMod extends BladesTargetLink {
static override ApplySchemaDefaults(
schemaData: Partial
- ) {
+ ): Schema {
// Ensure all properties of Schema are provided
- if (!schemaData.name) { throw new Error("name is required for BladesRollMod.Schema"); }
+ if (!schemaData.name) {throw new Error("name is required for BladesRollMod.Schema");}
return {
key: `${schemaData.name}-positive-roll`,
modType: RollModType.general,
@@ -112,7 +111,7 @@ class BladesRollMod extends BladesTargetLink {
return [RollModStatus.ForcedOn, RollModStatus.ForcedOff, RollModStatus.Hidden];
}
- private static getModData(mStrings: string[]): BladesRollMod.Schema {
+ private static getModData(mStrings: string[]): Partial {
const nameString = U.pullElement(mStrings, (v) => typeof v === "string" && /^na/i.test(v));
const nameVal = (typeof nameString === "string" && nameString.replace(/^.*:/, ""));
@@ -129,19 +128,18 @@ class BladesRollMod extends BladesTargetLink {
const posNegString = (U.pullElement(mStrings, (v) => typeof v === "string" && /^p/i.test(v)) || "posNeg:positive");
const posNegVal = posNegString.replace(/^.*:/, "") as "positive" | "negative";
- const partialData: Partial = {
+ return {
key: `${nameVal}-${posNegVal}-${catVal}`,
name: nameVal,
section: catVal,
posNeg: posNegVal
};
-
- return this.ApplySchemaDefaults(partialData);
}
- private static getModParameterKeyVal(mString: string): Partial<
- Record, string|number|string[]>
- > {
+ private static getModParameterKeyVal(mString: string): Partial,
+ string | number | string[]
+ >> {
const [keyString, valString] = mString.split(/:/) as [string, string];
let val: string | string[] = /\|/.test(valString) ? valString.split(/\|/) : valString;
@@ -193,7 +191,7 @@ class BladesRollMod extends BladesTargetLink {
return {[key]: valProcessed};
}
- static ParseDocRollMods(doc: BladesDoc): BladesRollMod.Schema[] {
+ static ParseDocRollMods(doc: BladesDoc): Array> {
if (doc instanceof BladesChat) {
throw new Error("BladesRollMod.ParseDocRollMods cannot be called on a BladesChat document.");
@@ -205,7 +203,7 @@ class BladesRollMod extends BladesTargetLink {
return roll_mods
.filter((elem) => elem && typeof elem === "string")
.map((modString) => {
- if (!modString) { return undefined; }
+ if (!modString) {return undefined;}
const mStrings = modString.split(/@/);
@@ -213,14 +211,14 @@ class BladesRollMod extends BladesTargetLink {
mStrings.forEach((mString) => {
Object.assign(
- this.ApplySchemaDefaults(rollModData),
+ rollModData,
this.getModParameterKeyVal(mString)
);
});
return rollModData;
})
- .filter((elem): elem is BladesRollMod.Schema => Boolean(elem));
+ .filter((elem): elem is Partial => Boolean(elem));
}
get status() {
@@ -319,10 +317,10 @@ class BladesRollMod extends BladesTargetLink {
.filter((rType): rType is BladesRoll.AnyRollType => Boolean(rType));
const typesApply = (!this.rollInstance.isParticipantRoll && types.length === 0)
- || rollTypes.some((rType) => types.includes(rType));
+ || rollTypes.some((rType) => types.includes(rType));
const traitsApply = (!this.rollInstance.isParticipantRoll && traits.length === 0)
- || (this.rollInstance.rollTrait && traits.includes(this.rollInstance.rollTrait));
+ || (this.rollInstance.rollTrait && traits.includes(this.rollInstance.rollTrait));
return Boolean(typesApply && traitsApply);
}
@@ -575,42 +573,46 @@ class BladesRollMod extends BladesTargetLink {
this._rollInstance = rollInstance;
}
- get rollInstance(): BladesRoll { return this._rollInstance; }
- get name(): string { return this.data.name; }
- get modType(): RollModType { return this.data.modType; }
- get sourceName(): string { return this.data.source_name ?? this.data.name; }
- get section(): RollModSection { return this.data.section; }
- get posNeg(): "positive"|"negative" { return this.data.posNeg; }
+ get rollInstance(): BladesRoll {return this._rollInstance;}
+ get name(): string {return this.data.name;}
+ get modType(): RollModType {return this.data.modType;}
+ get sourceName(): string {return this.data.source_name ?? this.data.name;}
+ get section(): RollModSection {return this.data.section;}
+ get posNeg(): "positive" | "negative" {return this.data.posNeg;}
- get userStatus(): RollModStatus|undefined { return this.data.user_status; }
+ get userStatus(): RollModStatus | undefined {return this.data.user_status;}
set userStatus(val: RollModStatus | undefined) {
if (val === this.userStatus) {return;}
if (!val || val === this.baseStatus) {
- this.updateTarget("user_status", null).then(this.rollInstance.renderForAll.bind(this));
+ this.updateTarget("user_status", null)
+ .then(this.rollInstance.renderRollCollab_SocketCall.bind(this.rollInstance));
} else {
if (!game.user.isGM
&& (BladesRollMod.GMOnlyModStatuses.includes(val)
- || (this.userStatus && BladesRollMod.GMOnlyModStatuses.includes(this.userStatus)))
+ || (this.userStatus && BladesRollMod.GMOnlyModStatuses.includes(this.userStatus)))
) {
return;
}
- this.updateTarget("user_status", val).then(this.rollInstance.renderForAll.bind(this));
+ this.updateTarget("user_status", val)
+ .then(this.rollInstance.renderRollCollab_SocketCall.bind(this.rollInstance));
}
}
- get baseStatus(): RollModStatus { return this.data.base_status; }
- get heldStatus(): RollModStatus|undefined { return this.data.held_status; }
+ get baseStatus(): RollModStatus {return this.data.base_status;}
+ get heldStatus(): RollModStatus | undefined {return this.data.held_status;}
set heldStatus(val: RollModStatus | undefined) {
if (val === this.heldStatus) {return;}
if (!val) {
- this.updateTarget("held_status", null).then(this.rollInstance.renderForAll.bind(this));
+ this.updateTarget("held_status", null)
+ .then(this.rollInstance.renderRollCollab_SocketCall.bind(this.rollInstance));
} else {
- this.updateTarget("held_status", val).then(this.rollInstance.renderForAll.bind(this));
+ this.updateTarget("held_status", val)
+ .then(this.rollInstance.renderRollCollab_SocketCall.bind(this.rollInstance));
}
}
- get value(): number { return this.data.value; }
- get effectKeys(): string[] { return this.data.effectKeys ?? []; }
+ get value(): number {return this.data.value;}
+ get effectKeys(): string[] {return this.data.effectKeys ?? [];}
get sideString(): string | undefined {
if (this.data.sideString) {return this.data.sideString;}
@@ -704,7 +706,7 @@ class BladesRollPrimary implements BladesRoll.PrimaryDocData {
return this._rollPrimaryDoc;
}
- get flagData(): BladesRoll.PrimaryDocData {
+ get data(): BladesRoll.PrimaryDocData {
return {
rollPrimaryID: this.rollPrimaryID,
rollPrimaryName: this.rollPrimaryName,
@@ -749,7 +751,7 @@ class BladesRollPrimary implements BladesRoll.PrimaryDocData {
}
get hasArmor() {
- if (!this.rollPrimaryDoc) { return false; }
+ if (!this.rollPrimaryDoc) {return false;}
if (this.rollPrimaryType === BladesActorType.pc) {
const rollPrimaryDoc = this.rollPrimaryDoc as BladesPC;
@@ -760,7 +762,7 @@ class BladesRollPrimary implements BladesRoll.PrimaryDocData {
rollPrimaryDoc.system.armor.active.light
|| rollPrimaryDoc.remainingLoad >= 2
)
- ) { return true; }
+ ) {return true;}
// Otherwise, can PC spend heavy armor?
if (
@@ -769,7 +771,7 @@ class BladesRollPrimary implements BladesRoll.PrimaryDocData {
rollPrimaryDoc.system.armor.active.heavy
|| rollPrimaryDoc.remainingLoad >= 3
)
- ) { return true; }
+ ) {return true;}
}
if (BladesItem.IsType(this.rollPrimaryDoc, BladesItemType.cohort_gang, BladesItemType.cohort_expert)) {
const {value, max} = this.rollPrimaryDoc.system.armor;
@@ -779,10 +781,10 @@ class BladesRollPrimary implements BladesRoll.PrimaryDocData {
}
get hasSpecialArmor() {
- if (!this.rollPrimaryDoc) { return false; }
- if (!BladesPC.IsType(this.rollPrimaryDoc)) { return false; }
- if (!this.rollPrimaryDoc.system.armor.active.special) { return false; }
- if (this.rollPrimaryDoc.system.armor.checked.special) { return false; }
+ if (!this.rollPrimaryDoc) {return false;}
+ if (!BladesPC.IsType(this.rollPrimaryDoc)) {return false;}
+ if (!this.rollPrimaryDoc.system.armor.active.special) {return false;}
+ if (this.rollPrimaryDoc.system.armor.checked.special) {return false;}
return true;
}
@@ -895,7 +897,7 @@ class BladesRollOpposition implements BladesRoll.OppositionDocData {
}
// #endregion
- rollInstance?: BladesRoll|undefined;
+ rollInstance?: BladesRoll | undefined;
_rollOppID: IDString | undefined;
@@ -996,7 +998,7 @@ class BladesRollOpposition implements BladesRoll.OppositionDocData {
return [C.SYSTEM_ID, "rollCollab.rollOppData"] as const;
}
- get flagData(): BladesRoll.OppositionDocData {
+ get data(): BladesRoll.OppositionDocData {
return {
rollOppID: this.rollOppID,
rollOppName: this.rollOppName,
@@ -1012,14 +1014,14 @@ class BladesRollOpposition implements BladesRoll.OppositionDocData {
}
async updateRollFlags() {
- if (!this.rollInstance) { return; }
- await this.rollInstance.document.setFlag(...this.flagParams, this.flagData);
- socketlib.system.executeForEveryone("renderRollCollab", this.rollInstance.rollID);
+ if (!this.rollInstance) {return;}
+ await this.rollInstance.document.setFlag(...this.flagParams, this.data);
+ socketlib.system.executeForEveryone("renderRollCollab", this.rollInstance.id);
}
refresh() {
- if (!this.rollInstance) { return; }
- const rollOppFlags = this.rollInstance.flagData.rollOppData;
+ if (!this.rollInstance) {return;}
+ const rollOppFlags = this.rollInstance.data.rollOppData;
if (rollOppFlags) {
this.rollOppID = rollOppFlags.rollOppID;
this.rollOppName = rollOppFlags.rollOppName;
@@ -1167,7 +1169,7 @@ class BladesRollParticipant implements BladesRoll.ParticipantDocData {
return [C.SYSTEM_ID, `rollCollab.rollParticipantData.${this.rollParticipantSection}.${this.rollParticipantSubSection}`] as const;
}
- get flagData(): BladesRoll.ParticipantDocData & BladesRoll.ParticipantSectionData {
+ get data(): BladesRoll.ParticipantDocData & BladesRoll.ParticipantSectionData {
return {
rollParticipantSection: this.rollParticipantSection,
rollParticipantSubSection: this.rollParticipantSubSection,
@@ -1183,12 +1185,12 @@ class BladesRollParticipant implements BladesRoll.ParticipantDocData {
}
async updateRollFlags() {
- await this.rollInstance.document.setFlag(...this.flagParams, this.flagData);
- socketlib.system.executeForEveryone("renderRollCollab", this.rollInstance.rollID);
+ await this.rollInstance.document.setFlag(...this.flagParams, this.data);
+ socketlib.system.executeForEveryone("renderRollCollab", this.rollInstance.id);
}
refresh() {
- const rollParticipantFlagData = this.rollInstance.flagData.rollParticipantData?.[this.rollParticipantSection];
+ const rollParticipantFlagData = this.rollInstance.data.rollParticipantData?.[this.rollParticipantSection];
if (rollParticipantFlagData) {
const rollParticipantFlags = rollParticipantFlagData[
this.rollParticipantSubSection as KeyOf
@@ -1214,13 +1216,13 @@ class BladesRollParticipant implements BladesRoll.ParticipantDocData {
class BladesRoll extends BladesTargetLink {
static _Debug: {
- modWatch: RegExp|false
+ modWatch: RegExp | false
} = {
modWatch: false
};
static Debug = {
- watchRollMod(name: string|false) {
+ watchRollMod(name: string | false) {
if (typeof name === "string") {
BladesRoll._Debug.modWatch = new RegExp(name, "g");
} else {
@@ -1249,61 +1251,68 @@ class BladesRoll extends BladesTargetLink {
}
static InitSockets() {
- socketlib.system.register("constructRollCollab", BladesRoll.ConstructRollCollab);
- socketlib.system.register("renderRollCollab", BladesRoll.RenderRollCollab);
- socketlib.system.register("closeRollCollab", BladesRoll.CloseRollCollab);
+
+ socketlib.system.register("constructRollCollab_SocketCall", BladesRoll.constructRollCollab_SocketResponse.bind(BladesRoll));
+ socketlib.system.register("renderRollCollab_SocketCall", BladesRoll.renderRollCollab_SocketResponse.bind(BladesRoll));
+ socketlib.system.register("closeRollCollab_SocketCall", BladesRoll.closeRollCollab_SocketResponse.bind(BladesRoll));
+
}
- static override ApplySchemaDefaults(
- schemaData: Partial & Partial
- ) {
- // Ensure all properties of Schema are provided
- const {rollPrimaryData, rollType, rollUserID} = schemaData;
- if (!rollPrimaryData) {
- throw new Error("Must include a rollPrimaryData when constructing a BladesRoll object.");
- }
- if (!rollType) {
- throw new Error("Must include a rollType when constructing a BladesRoll object.");
+ static override ParseConfig(
+ data: BladesTargetLink.Data & Partial
+ ): BladesTargetLink.Data & Partial {
+ if (data.rollPrimaryData instanceof BladesRollPrimary) {
+ data.rollPrimaryData = data.rollPrimaryData.data;
}
- if (!rollUserID) {
- throw new Error("Must include a rollUserID when constructing a BladesRoll object.");
+ if (data.rollOppData instanceof BladesRollOpposition) {
+ data.rollOppData = data.rollOppData.data;
}
-
- if (schemaData.rollParticipantData) {
- if (schemaData.rollParticipantData[RollModSection.roll]) {
- Object.keys(schemaData.rollParticipantData[RollModSection.roll]).forEach((key) => {
- const thisParticipant = schemaData.rollParticipantData?.[RollModSection.roll]?.[key as Exclude];
+ if (data.rollParticipantData) {
+ if (data.rollParticipantData[RollModSection.roll]) {
+ Object.keys(data.rollParticipantData[RollModSection.roll]).forEach((key) => {
+ const thisParticipant = data.rollParticipantData?.[RollModSection.roll]?.[key as Exclude];
if (thisParticipant instanceof BladesRollParticipant) {
- ((schemaData.rollParticipantData as NonNullable)[RollModSection.roll] as Record)[key as Exclude] = thisParticipant.flagData;
+ ((data.rollParticipantData as NonNullable)[RollModSection.roll] as Record)[key as Exclude] = thisParticipant.data;
}
});
}
- if (schemaData.rollParticipantData && schemaData.rollParticipantData[RollModSection.position]) {
- const participantPositionData = schemaData.rollParticipantData[RollModSection.position];
- Object.keys(participantPositionData)
- .forEach(() => {
- if (schemaData.rollParticipantData && participantPositionData) {
- if (participantPositionData.Setup instanceof BladesRollParticipant) {
- participantPositionData.Setup = participantPositionData.Setup.flagData;
- }
- }
- });
+ if (data.rollParticipantData[RollModSection.position]) {
+ Object.keys(data.rollParticipantData[RollModSection.position]).forEach((key) => {
+ const thisParticipant = data.rollParticipantData?.[RollModSection.position]?.[key as "Setup"];
+ if (thisParticipant instanceof BladesRollParticipant) {
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ data.rollParticipantData![RollModSection.position]![key as "Setup"] = thisParticipant.data;
+ }
+ });
}
- if (schemaData.rollParticipantData && schemaData.rollParticipantData[RollModSection.effect]) {
- const participantEffectData = schemaData.rollParticipantData[RollModSection.effect];
- Object.keys(participantEffectData)
- .forEach(() => {
- if (schemaData.rollParticipantData && participantEffectData) {
- if (participantEffectData.Setup instanceof BladesRollParticipant) {
- participantEffectData.Setup = participantEffectData.Setup.flagData;
- }
- }
- });
+ if (data.rollParticipantData[RollModSection.effect]) {
+ Object.keys(data.rollParticipantData[RollModSection.effect]).forEach((key) => {
+ const thisParticipant = data.rollParticipantData?.[RollModSection.effect]?.[key as "Setup"];
+ if (thisParticipant instanceof BladesRollParticipant) {
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ data.rollParticipantData![RollModSection.effect]![key as "Setup"] = thisParticipant.data;
+ }
+ });
}
}
+ return JSON.parse(JSON.stringify(data)) as BladesTargetLink.Data & Partial;
+ }
+
+ static override ApplySchemaDefaults(
+ schemaData: Partial,
+ linkData: BladesTargetLink.Data
+ ) {
+ // Ensure all properties of Schema are provided
+ const {rollPrimaryData, rollType} = schemaData;
+ if (!rollPrimaryData) {
+ throw new Error("Must include a rollPrimaryData when constructing a BladesRoll object.");
+ }
+ if (!rollType) {
+ throw new Error("Must include a rollType when constructing a BladesRoll object.");
+ }
return {
- rollModsData: {},
+ rollModsData: BladesRoll.GetDefaultRollModsDataSet(linkData),
rollPositionInitial: Position.risky,
rollEffectInitial: Effect.standard,
rollPosEffectTrade: false,
@@ -1386,12 +1395,11 @@ class BladesRoll extends BladesTargetLink {
userPermissions: {},
...schemaData,
rollType,
- rollUserID,
rollPrimaryData: rollPrimaryData instanceof BladesRollPrimary
- ? rollPrimaryData.flagData
+ ? rollPrimaryData.data
: rollPrimaryData,
rollOppData: schemaData.rollOppData instanceof BladesRollOpposition
- ? schemaData.rollOppData.flagData
+ ? schemaData.rollOppData.data
: schemaData.rollOppData
} as Schema;
}
@@ -1497,17 +1505,20 @@ class BladesRoll extends BladesTargetLink {
];
}
- get defaultRollModsDataSet(): BladesRollMod.Data[] {
- return BladesRoll.DefaultRollModSchemas.map((modSchema) => ({
- id: randomID() as IDString,
- targetID: this.targetID,
- targetKey: this.targetKey ? `${this.targetKey}.${this.id}.rollModsData` as TargetKey : undefined,
- targetFlagKey: this.targetFlagKey ? `${this.targetFlagKey}.${this.id}.rollModsData` as TargetFlagKey: undefined,
- ...modSchema
+ static GetDefaultRollModsDataSet(rollLinkData: BladesTargetLink.Data): Record {
+ return Object.fromEntries(BladesRoll.DefaultRollModSchemas.map((modSchema) => {
+ const modID = randomID() as IDString;
+ return [modID, {
+ id: modID,
+ targetID: rollLinkData.targetID,
+ targetKey: rollLinkData.targetKey ? `${rollLinkData.targetKey}.${rollLinkData.id}.rollModsData` as TargetKey : undefined,
+ targetFlagKey: rollLinkData.targetFlagKey ? `${rollLinkData.targetFlagKey}.${rollLinkData.id}.rollModsData` as TargetFlagKey : undefined,
+ ...modSchema
+ }];
}));
}
- static GetDieClass(rollType: RollType, rollResult: number|false|RollResult, dieVal: number, dieIndex: number) {
+ static GetDieClass(rollType: RollType, rollResult: number | false | RollResult, dieVal: number, dieIndex: number) {
switch (rollType) {
case RollType.Resistance: {
if (dieVal === 6 && dieIndex <= 1 && rollResult === -1) {
@@ -1544,7 +1555,7 @@ class BladesRoll extends BladesTargetLink {
static GetDieImage(
rollType: RollType,
- rollResult: number|false|RollResult,
+ rollResult: number | false | RollResult,
dieVal: number,
dieIndex: number,
isGhost = false,
@@ -1568,47 +1579,25 @@ class BladesRoll extends BladesTargetLink {
// #endregion
// #region STATIC METHODS: New Roll Creation ~
- static Current: Record = {};
-
- static _Active?: BladesRoll;
-
- static get Active(): BladesRoll | undefined {
- if (BladesRoll._Active) {return BladesRoll._Active;}
- if (U.objSize(BladesRoll.Current) > 0) {return U.getLast(Object.values(BladesRoll.Current));}
- return undefined;
- }
-
- static set Active(val: BladesRoll | undefined) {
- BladesRoll._Active = val;
- }
+ // static Current: Record = {};
- static async ConstructRollCollab({
- userID,
- rollID,
- rollPermission
- }: {userID: string, rollID: string, rollPermission: RollPermissions}) {
- const rollInst = new BladesRoll(userID, rollID, rollPermission);
- eLog.checkLog3("rollCollab", "ConstructRollCollab()", {params: {userID, rollID, rollPermission}, rollInst});
- BladesRoll._Active = rollInst;
- await rollInst.renderForAll();
- }
+ // static _Active?: BladesRoll;
- async renderForAll() {
- await socketlib.system.executeForEveryone("renderRollCollab", this.id);
- }
+ // static get Active(): BladesRoll | undefined {
+ // if (BladesRoll._Active) {return BladesRoll._Active;}
+ // if (U.objSize(BladesRoll.Current) > 0) {return U.getLast(Object.values(BladesRoll.Current));}
+ // return undefined;
+ // }
- static RenderRollCollab(rollID: string) {
- BladesRoll.Current[rollID]?.prepareRollParticipantData();
- BladesRoll.Current[rollID]?.render();
- }
+ // static set Active(val: BladesRoll | undefined) {
+ // BladesRoll._Active = val;
+ // }
- static async CloseRollCollab(rollID: string) {
- eLog.checkLog3("rollCollab", "CloseRollCollab()", {rollID});
- await BladesRoll.Current[rollID]?.close({rollID});
- // delete BladesRoll.Current[rollID];
- }
- static GetUserPermissions(config: BladesRoll.Config): Record {
+ static GetUserPermissions(config: BladesRoll.Config): Record {
+ if (!config.rollPrimaryData) {
+ throw new Error("[BladesRoll.GetUserPermissions()] Missing rollPrimaryData.");
+ }
// === ONE === GET USER IDS
@@ -1650,8 +1639,9 @@ class BladesRoll extends BladesTargetLink {
) {
if (config.rollUserID === GMUserID) {
userIDs[RollPermissions.Primary].push(...playerUserIDs);
- } else {
- userIDs[RollPermissions.Primary].push(config.rollUserID);
+ } else if (BladesPC.IsType(rollPrimaryDoc.parent)
+ && rollPrimaryDoc.parent.primaryUser?.id) {
+ userIDs[RollPermissions.Primary].push(rollPrimaryDoc.parent.primaryUser.id as IDString);
}
} else if (
BladesGMTracker.IsType(rollPrimaryDoc)
@@ -1660,24 +1650,32 @@ class BladesRoll extends BladesTargetLink {
}
// === THREE === DETERMINE ROLL PARTICIPANT USER(S)
-
-
// Check config.rollParticipantData to determine if roll starts with any participants
if (config.rollParticipantData) {
userIDs[RollPermissions.Participant].push(...getParticipantDocUserIDs(config.rollParticipantData, playerUserIDs));
}
- // Finally, add remaining players as observers.
+ // === FOUR === ASSIGN ROLL OBSERVERS
+ // Add remaining players as observers.
userIDs[RollPermissions.Observer] = playerUserIDs
.filter((uID) => !userIDs[RollPermissions.Participant].includes(uID));
- return userIDs;
+ // === FIVE === PARSE INTO {ID: PERMISSION} FORMAT
+ const userFlagData: Record = {};
+ (Object.entries(userIDs) as Array<[RollPermissions, IDString[]]>)
+ .forEach(([rollPermission, idsArray]) => {
+ for (const id of idsArray) {
+ userFlagData[id] = rollPermission;
+ }
+ });
+
+ return userFlagData;
/**
* Generates BladesRollParticipant documents from the provided schema data.
- * @param {BladesRoll.RollParticipantData} participantData
+ * @param {BladesRoll.RollParticipantDataSet} participantData
*/
- function getParticipantDocs(participantData: BladesRoll.RollParticipantData) {
+ function getParticipantDocs(participantData: BladesRoll.RollParticipantDataSet) {
return Object.values(flattenObject(participantData))
.map((pData) => {
if (BladesRollParticipant.IsDoc(pData)) {
@@ -1699,11 +1697,11 @@ class BladesRoll extends BladesTargetLink {
/**
* Returns the user ids of potential BladesRollParticipants defined in the provided data schema.
- * @param {BladesRoll.RollParticipantData} participantData
+ * @param {BladesRoll.RollParticipantDataSet} participantData
* @param {IDString[]} unassignedIDs
*/
function getParticipantDocUserIDs(
- participantData: BladesRoll.RollParticipantData,
+ participantData: BladesRoll.RollParticipantDataSet,
unassignedIDs: IDString[]
): IDString[] {
return getParticipantDocs(participantData)
@@ -1725,42 +1723,16 @@ class BladesRoll extends BladesTargetLink {
}
}
- static async PrepareActionRoll(rollID: string, config: BladesRoll.Config) {
+ static PrepareActionRollConfig(config: BladesRoll.Config) {
// Validate the rollTrait
if (!(config.rollTrait === "" || U.isInt(config.rollTrait) || U.lCase(config.rollTrait) in {...ActionTrait, ...Factor})) {
throw new Error(`[PrepareActionRoll()] Bad RollTrait for Action Roll: ${config.rollTrait}`);
}
- // Retrieve the roll users
- const userIDs = BladesRoll.GetUserPermissions(config);
-
- // Prepare roll user flag data
- const userFlagData: Record = {};
- (Object.entries(userIDs) as Array<[RollPermissions, IDString[]]>)
- .forEach(([rollPermission, idsArray]) => {
- for (const id of idsArray) {
- userFlagData[id] = rollPermission;
- }
- });
-
- // Prepare the flag data.
- const flagUpdateData: BladesRoll.Data = {
- ...BladesRoll.DefaultFlagData,
- ...pruneConfig(config as BladesRoll.Config & Record),
- userPermissions: userFlagData,
- rollID
- };
-
- // Return flagData and roll users
- return {
- flagUpdateData,
- userIDs
- };
-
}
- static async PrepareResistanceRoll(rollID: string, config: BladesRoll.Config) {
+ static PrepareResistanceRollConfig(config: BladesRoll.Config) {
// Validate consequenceData
if (!config.resistanceData || !BladesConsequence.IsValidConsequenceData(config.resistanceData?.consequence)) {
@@ -1771,103 +1743,35 @@ class BladesRoll extends BladesTargetLink {
// Set rollTrait
config.rollTrait = config.resistanceData.consequence.attribute;
- eLog.checkLog3("bladesRoll", "BladesRoll.PrepareResistanceRoll() [1]", {rollID, config});
-
- // Retrieve the roll users
- const userIDs = BladesRoll.GetUserPermissions(config);
- eLog.checkLog3("bladesRoll", "BladesRoll.PrepareResistanceRoll() [2]", {userIDs});
-
- // Prepare roll user flag data
- const userFlagData: Record = {};
- (Object.entries(userIDs) as Array<[RollPermissions, string[]]>)
- .forEach(([rollPermission, idsArray]) => {
- for (const id of idsArray) {
- userFlagData[id] = rollPermission;
- }
- });
- eLog.checkLog3("bladesRoll", "BladesRoll.PrepareResistanceRoll() [3]", {userFlagData});
-
- // Prepare the flag data.
- const flagUpdateData: BladesRoll.FlagData = {
- ...BladesRoll.DefaultFlagData,
- ...pruneConfig(config as BladesRoll.Config & Record),
- userPermissions: userFlagData,
- rollID
- };
- eLog.checkLog3("bladesRoll", "BladesRoll.PrepareResistanceRoll() [4]", {flagUpdateData});
+ eLog.checkLog3("bladesRoll", "BladesRoll.PrepareResistanceRoll() [1]", {config});
- // Return flagData and roll users
- return {
- flagUpdateData,
- userIDs
- };
}
- static async PrepareFortuneRoll(rollID: string, config: BladesRoll.Config) {
+ static PrepareFortuneRollConfig(config: BladesRoll.Config) {
// Validate the rollTrait
if (!(U.isInt(config.rollTrait) || U.lCase(config.rollTrait) in {...ActionTrait, ...AttributeTrait, ...Factor})) {
throw new Error(`[PrepareFortuneRoll()] Bad RollTrait for Fortune Roll: ${config.rollTrait}`);
}
- // Retrieve the roll users
- const userIDs = BladesRoll.GetUserPermissions(config);
-
- // Prepare roll user flag data
- const userFlagData: Record = {};
- (Object.entries(userIDs) as Array<[RollPermissions, string[]]>)
- .forEach(([rollPermission, idsArray]) => {
- for (const id of idsArray) {
- userFlagData[id] = rollPermission;
- }
- });
-
- // Prepare the flag data.
- const flagUpdateData: BladesRoll.FlagData = {
- ...BladesRoll.DefaultFlagData,
- ...pruneConfig(config as BladesRoll.Config & Record),
- userPermissions: userFlagData,
- rollID
- };
+ }
- // Return flagData and roll users
- return {
- flagUpdateData,
- userIDs
- };
+ static PrepareIndulgeViceRollConfig(config: BladesRoll.Config) {
- }
+ // Validate rollPrimary
+ if (!config.rollPrimaryData || !BladesPC.IsType(config.rollPrimaryData.rollPrimaryDoc)) {
+ throw new Error("[BladesRoll.PrepareIndulgeViceRollConfig] RollPrimary must be a PC for Indulge Vice rolls.");
+ }
- static async PrepareIndulgeViceRoll(rollID: string, config: BladesRoll.Config) {
+ // Set rollTrait
+ const {attributes} = config.rollPrimaryData.rollPrimaryDoc;
+ const minAttrVal = Math.min(...Object.values(attributes));
+ config.rollTrait = U.sample(
+ Object.values(AttributeTrait).filter((attr) => attributes[attr] === minAttrVal)
+ )[0];
// Set other known config values
config.rollDowntimeAction = DowntimeAction.IndulgeVice;
-
- // Retrieve the roll users
- const userIDs = BladesRoll.GetUserPermissions(config);
-
- // Prepare roll user flag data
- const userFlagData: Record = {};
- (Object.entries(userIDs) as Array<[RollPermissions, string[]]>)
- .forEach(([rollPermission, idsArray]) => {
- for (const id of idsArray) {
- userFlagData[id] = rollPermission;
- }
- });
-
- // Prepare the flag data.
- const flagUpdateData: BladesRoll.FlagData = {
- ...BladesRoll.DefaultFlagData,
- ...pruneConfig(config as BladesRoll.Config & Record),
- userPermissions: userFlagData,
- rollID
- };
-
- // Return flagData and roll users
- return {
- flagUpdateData,
- userIDs
- };
}
/**
@@ -1876,111 +1780,90 @@ class BladesRoll extends BladesTargetLink {
* document, then sends out a socket call to the relevant users to construct
* and display the roll instance.
*
- * @param {BladesRoll.ConstructorConfig} config The configuration object for the new roll.
+ * @param {BladesRoll.Config} config The configuration object for the new roll.
*/
- static async NewRoll(config: BladesRoll.ConstructorConfig) {
- // If no rollType is provided, throw an error.
- if (!isRollType(config.rollType)) {
- throw new Error("[BladesRoll.NewRoll()] You must provide a valid rollType in the config object.");
- }
+ static async NewRoll(config: BladesRoll.Config) {
+
+ const {targetKey, targetFlagKey} = config;
- // Get User document to serve as flag storage.
+ // Get User document.
const rollUser = game.users.get(config.rollUserID ?? game.user.id ?? "");
- if (!rollUser?.id) {
- throw new Error("[BladesRoll.NewRoll()] You must provide a valid rollUserID in the config object.");
- }
- eLog.checkLog3("bladesRoll", "BladesRoll.NewRoll() [1]", {config, rollUser});
-
- // If roll flag data is already on user.
- const flagData = rollUser.getFlag("eunos-blades", "rollCollab") as BladesRoll.FlagData | undefined;
- if (flagData) {
- const {rollID} = flagData;
- // If user is documenting a roll with a dialog window open, disallow starting a new roll.
- if ($(document).find(`.roll-collab-sheet .sheet-topper[data-roll-id='${rollID}']`)[0]) {
- throw new Error(`[BladesRoll.NewRoll()] User ${rollUser.name} already documenting live roll with ID '${rollID}'`);
- }
- // Otherwise, archive the existing roll and prepare for a new one by clearing the main rollCollab flag.
- await rollUser.setFlag("eunos-blades", `rollCollabArchive.${rollID}`, flagData);
- await rollUser.unsetFlag("eunos-blades", "rollCollab");
- eLog.checkLog3("bladesRoll", "BladesRoll.NewRoll() [2]", {userFlags: rollUser.flags});
- }
-
- let {rollPrimaryData} = config;
-
- if (!BladesRollPrimary.IsValidData(rollPrimaryData)) {
- // If no rollPrimaryData is provided, attempt to derive it from user data
- let rollPrimarySourceData: BladesRoll.PrimaryDocData;
- if (BladesPC.IsType(rollUser.character)) {
- rollPrimarySourceData = rollUser.character;
- rollPrimaryData = {
- rollPrimaryID: rollPrimarySourceData.rollPrimaryID,
- rollPrimaryName: rollPrimarySourceData.rollPrimaryName,
- rollPrimaryType: rollPrimarySourceData.rollPrimaryType,
- rollPrimaryImg: rollPrimarySourceData.rollPrimaryImg,
- rollModsData: rollPrimarySourceData.rollModsData,
- rollFactors: rollPrimarySourceData.rollFactors,
- applyHarm: rollPrimarySourceData.applyHarm,
- applyWorsePosition: rollPrimarySourceData.applyWorsePosition
- };
+ // If config is NOT a valid BladesTargetLink config object, link it to the user id
+ if (!BladesTargetLink.IsValidConfig(config)) {
+ if (!rollUser?.id) {
+ throw new Error("[BladesRoll.NewRoll()] You must provide a valid rollUserID or targetID (UUID) in the config object.");
}
- }
- if (!BladesRollPrimary.IsValidData(rollPrimaryData)) {
- throw new Error("[BladesRoll.NewRoll()] A valid source of PrimaryDocData must be provided to construct a roll.");
- }
+ eLog.checkLog3("bladesRoll", "BladesRoll.NewRoll() [1]", {config, rollUser});
- // Get the rollPrimary document, if an ID is provided.
- const rollPrimary = new BladesRollPrimary(undefined, rollPrimaryData);
- const {rollPrimaryDoc} = rollPrimary;
+ Object.assign(config, {
+ target: rollUser,
+ targetKey,
+ targetFlagKey: targetFlagKey ?? "rollCollab" as TargetFlagKey
+ });
+ }
- // Create a random ID for storing the roll instance
- const rID = randomID() as IDString;
+ // If no rollPrimaryData is provided, attempt to derive it from target or user
+ if (!BladesRollPrimary.IsValidData(config.rollPrimaryData)) {
+ let rollPrimary: BladesRoll.PrimaryDoc;
+ if (BladesRollPrimary.IsDoc(config.target)) {
+ rollPrimary = config.target;
+ } else if (BladesRollPrimary.IsDoc(rollUser?.character)) {
+ rollPrimary = rollUser.character;
+ } else {
+ throw new Error("[BladesRoll.NewRoll()] A valid source of PrimaryDocData must be provided to construct a roll.");
+ }
- // Derive user flag data depending on given roll type and subtype
- let userIDs: Record;
- let flagUpdateData: BladesRoll.FlagData;
+ config.rollPrimaryData = {
+ rollPrimaryID: rollPrimary.rollPrimaryID,
+ rollPrimaryName: rollPrimary.rollPrimaryName,
+ rollPrimaryType: rollPrimary.rollPrimaryType,
+ rollPrimaryImg: rollPrimary.rollPrimaryImg,
+ rollModsData: rollPrimary.rollModsData,
+ rollFactors: rollPrimary.rollFactors,
+ applyHarm: rollPrimary.applyHarm,
+ applyWorsePosition: rollPrimary.applyWorsePosition
+ };
+ }
- // Construct Config object
- const configData: BladesRoll.Config = {
- ...config,
- rollUserID: rollUser.id as IDString,
- rollPrimaryData
- };
+ // Acquire the roll primary document
+ const {rollPrimaryDoc} = new BladesRollPrimary(undefined, config.rollPrimaryData);
- // Modify Config object depending on subtype and downtime action where necessary.
- switch (configData.rollSubType) {
+ // Modify Config object depending on subtype where necessary.
+ switch (config.rollSubType) {
case RollSubType.Engagement:
case RollSubType.Incarceration: {
- configData.rollType = RollType.Fortune;
+ config.rollType = RollType.Fortune;
break;
}
default: break;
}
- switch (configData.rollDowntimeAction) { // Can be done outside of Downtime during Flashbacks!
+ // Modify Config object depending on downtime action where necessary.
+ switch (config.rollDowntimeAction) { // Remember: Can be done outside of Downtime during Flashbacks!
case DowntimeAction.AcquireAsset: {
- configData.rollType = RollType.Action;
- configData.rollTrait = Factor.tier;
+ config.rollType = RollType.Action;
+ config.rollTrait = Factor.tier;
break;
}
case DowntimeAction.IndulgeVice: {
- configData.rollType = RollType.IndulgeVice;
+ config.rollType = RollType.IndulgeVice;
if (!BladesPC.IsType(rollPrimaryDoc)) {
throw new Error("Only a PC character can roll to Indulge Vice.");
}
const minAttrVal = Math.min(...Object.values(rollPrimaryDoc.attributes));
- configData.rollTrait = U.sample(
+ config.rollTrait = U.sample(
Object.values(AttributeTrait).filter((attr) => rollPrimaryDoc.attributes[attr] === minAttrVal)
)[0];
break;
}
case DowntimeAction.LongTermProject: {
- configData.rollType = RollType.Action;
+ config.rollType = RollType.Action;
// Validate that rollOppData points to a project item
- if (!BladesRollOpposition.IsValidData(configData.rollOppData)) {
+ if (!BladesRollOpposition.IsValidData(config.rollOppData)) {
throw new Error("No rollOppData provided for LongTermProject roll.");
}
- const rollOpp = new BladesRollOpposition(undefined, configData.rollOppData);
+ const rollOpp = new BladesRollOpposition(undefined, config.rollOppData);
if (![
BladesItemType.project,
BladesItemType.design
@@ -1990,26 +1873,26 @@ class BladesRoll extends BladesTargetLink {
break;
}
case DowntimeAction.Recover: {
- configData.rollType = RollType.Action;
+ config.rollType = RollType.Action;
// Validate that rollPrimary is an NPC or a PC with Physiker.
if (BladesPC.IsType(rollPrimaryDoc)) {
if (!rollPrimaryDoc.abilities.find((ability) => ability.name === "Physiker")) {
throw new Error("A PC rollPrimary on a Recovery roll must have the Physiker ability.");
}
- configData.rollTrait = ActionTrait.tinker;
- } else if (rollPrimary.rollPrimaryType === BladesActorType.npc) {
- configData.rollTrait = Factor.quality;
+ config.rollTrait = ActionTrait.tinker;
+ } else if (rollPrimaryDoc?.rollPrimaryType === BladesActorType.npc) {
+ config.rollTrait = Factor.quality;
} else {
throw new Error("Only a PC with Physiker or an NPC can be rollPrimary on a Recover roll.");
}
break;
}
case DowntimeAction.ReduceHeat: {
- configData.rollType = RollType.Action;
+ config.rollType = RollType.Action;
// rollPrimary must be a cohort with a parent PC or Crew,
// and PC must be member of a crew
// and Crew must not have zero Heat.
- let parentCrew: BladesCrew|undefined = undefined;
+ let parentCrew: BladesCrew | undefined = undefined;
if (rollPrimaryDoc) {
const {parent} = rollPrimaryDoc;
if (BladesCrew.IsType(parent)) {
@@ -2020,7 +1903,7 @@ class BladesRoll extends BladesTargetLink {
}
if (!BladesCrew.IsType(parentCrew)) {
- throw new Error(`Could not find crew for rollPrimary ${rollPrimary.rollPrimaryName}`);
+ throw new Error(`Could not find crew for rollPrimary '${rollPrimaryDoc?.rollPrimaryName}'`);
}
if (parentCrew.system.heat.value === 0) {
throw new Error("Attempt to Reduce Heat for a Crew with no Heat.");
@@ -2028,57 +1911,86 @@ class BladesRoll extends BladesTargetLink {
break;
}
case undefined: break;
- default: throw new Error(`Unrecognized Roll Downtime Action: ${configData.rollDowntimeAction}`);
+ default: throw new Error(`Unrecognized Roll Downtime Action: ${config.rollDowntimeAction}`);
}
+ // Prepare roll user flag data
+ config.userPermissions = BladesRoll.GetUserPermissions(config);
+
switch (config.rollType) {
case RollType.Action: {
- ({userIDs, flagUpdateData} = await BladesRoll.PrepareActionRoll(rID, configData));
+ BladesRoll.PrepareActionRollConfig(config);
break;
}
case RollType.Resistance: {
- ({userIDs, flagUpdateData} = await BladesRoll.PrepareResistanceRoll(rID, configData));
+ BladesRoll.PrepareResistanceRollConfig(config);
break;
}
case RollType.Fortune: {
- ({userIDs, flagUpdateData} = await BladesRoll.PrepareFortuneRoll(rID, configData));
+ BladesRoll.PrepareFortuneRollConfig(config);
break;
}
case RollType.IndulgeVice: {
- ({userIDs, flagUpdateData} = await BladesRoll.PrepareIndulgeViceRoll(rID, configData));
+ BladesRoll.PrepareIndulgeViceRollConfig(config);
break;
}
+ default: throw new Error(`Unrecognized Roll Type: ${String(config.rollType)}`);
+ }
+
+ // Ensure rollType is defined
+ if (!config.rollType) {
+ throw new Error("rollType must be defined in config");
}
// Log the roll data
- eLog.checkLog3("bladesRoll", "BladesRoll.NewRoll()", {userIDs, flagUpdateData, rollPrimaryData: flagUpdateData.rollPrimaryData});
+ eLog.checkLog3("bladesRoll", "BladesRoll.NewRoll()", {config});
- // Store the roll data on the storage document
- await rollUser.setFlag(C.SYSTEM_ID, "rollCollab", flagUpdateData);
+ // Construct and initialize the BladesRoll/BladesTargetLink instance
+ const rollInst = await BladesRoll.Create(config as BladesTargetLink.Config & {rollType: RollType}) as BladesRoll;
// Send out socket calls to all users to see the roll.
- socketlib.system.executeForUsers("constructRollCollab",
- userIDs[RollPermissions.GM],
- {userID: rollUser.id, rollID: rID, rollPermission: RollPermissions.GM}
- );
- socketlib.system.executeForUsers("constructRollCollab",
- userIDs[RollPermissions.Primary],
- {userID: rollUser.id, rollID: rID, rollPermission: RollPermissions.Primary}
- );
- socketlib.system.executeForUsers("constructRollCollab",
- userIDs[RollPermissions.Observer],
- {userID: rollUser.id, rollID: rID, rollPermission: RollPermissions.Observer}
- );
- socketlib.system.executeForUsers("constructRollCollab",
- userIDs[RollPermissions.Participant],
- {userID: rollUser.id, rollID: rID, rollPermission: RollPermissions.Participant}
- );
+ rollInst.constructRollCollab_SocketCall(rollInst.linkData);
}
// #endregion
- // #region Constructor ~
- rollID: string;
+ // #region SOCKET CALLS & RESPONSES ~
+ constructRollCollab_SocketCall(linkData: BladesTargetLink.Data) {
+ socketlib.system.executeForEveryone("constructRollCollab_SocketCall", linkData);
+ }
+
+ static constructRollCollab_SocketResponse(
+ linkData: BladesTargetLink.Data
+ ) {
+ const rollInst = new BladesRoll(linkData);
+ eLog.checkLog3("rollCollab", "constructRollCollab_SocketResponse()", {params: {linkData}, rollInst});
+ this.renderRollCollab_SocketResponse(rollInst.id);
+ }
+ renderRollCollab_SocketCall() {
+ socketlib.system.executeForEveryone("renderRollCollab_SocketCall", this.id);
+ }
+
+ static renderRollCollab_SocketResponse(id: IDString) {
+ const rollInst = game.eunoblades.Rolls.get(id);
+ if (!rollInst) {
+ throw new Error(`[BladesRoll.renderRollCollab_SocketResponse] No roll found with id ${id}.`);
+ }
+ rollInst.prepareRollParticipantData();
+ rollInst.render();
+ }
+
+ closeRollCollab_SocketCall() {
+ socketlib.system.executeForEveryone("closeRollCollab_SocketCall", this.id);
+ }
+
+ static closeRollCollab_SocketResponse(id: IDString) {
+ const rollInst = game.eunoblades.Rolls.get(id);
+ if (!rollInst) {return;}
+ rollInst.close();
+ }
+ // #endregion
+
+ // #region Constructor ~
rollPermission: RollPermissions;
_rollPrimary: BladesRollPrimary;
@@ -2089,26 +2001,22 @@ class BladesRoll extends BladesTargetLink {
projectSelectOptions?: Array>;
- constructor(userID: string, rollID: string, rollPermission: RollPermissions) {
- const rollUser = game.users.get(userID);
- if (!rollUser) {
- throw new Error("[new BladesRoll()] Must provide a valid rollUser to roll.");
- }
- super(rollUser);
- this.rollID = rollID;
- this.rollPermission = rollPermission;
- const rollFlagData = rollUser.getFlag(C.SYSTEM_ID, "rollCollab") as BladesRoll.FlagData;
- this._rollPrimary = new BladesRollPrimary(this, rollFlagData.rollPrimaryData);
- if (rollFlagData.rollOppData) {
- this._rollOpposition = new BladesRollOpposition(this, rollFlagData.rollOppData);
- } else if (rollFlagData.rollDowntimeAction === DowntimeAction.LongTermProject) {
+ constructor(config: BladesRoll.Config & Partial)
+ constructor(data: BladesTargetLink.Data & Partial)
+ constructor(dataOrConfig: (BladesRoll.Config | BladesTargetLink.Data) & Partial) {
+ super(dataOrConfig);
+ this.rollPermission = this.data.userPermissions[game.user.id as IDString];
+ this._rollPrimary = new BladesRollPrimary(this, this.data.rollPrimaryData);
+ if (this.data.rollOppData) {
+ this._rollOpposition = new BladesRollOpposition(this, this.data.rollOppData);
+ } else if (this.data.rollDowntimeAction === DowntimeAction.LongTermProject) {
this.projectSelectOptions = Array.from(game.items)
.filter((item) => BladesItem.IsType(item, BladesItemType.project))
.map((project) => ({value: project.id ?? "", display: project.name}));
}
- if (rollFlagData.rollParticipantData) {
+ if (this.data.rollParticipantData) {
this._rollParticipants = {};
- for (const [rollSection, rollParticipantList] of Object.entries(rollFlagData.rollParticipantData)) {
+ for (const [rollSection, rollParticipantList] of Object.entries(this.data.rollParticipantData)) {
if ([RollModSection.roll, RollModSection.position, RollModSection.effect]
.includes(rollSection as RollModSection) && !U.isEmpty(rollParticipantList)) {
const sectionParticipants: Record = {};
@@ -2122,7 +2030,7 @@ class BladesRoll extends BladesTargetLink {
}
}
}
- BladesRoll.Current[this.rollID] = this;
+ game.eunoblades.Rolls.set(this.id, this);
}
// #endregion
@@ -2154,14 +2062,14 @@ class BladesRoll extends BladesTargetLink {
});
await rollParticipant.updateRollFlags();
- socketlib.system.executeForEveryone("renderRollCollab", this.rollID);
+ socketlib.system.executeForEveryone("renderRollCollab", this.id);
}
async removeRollParticipant(
rollSection: BladesRoll.RollParticipantSection,
rollSubSection: BladesRoll.RollParticipantSubSection
) {
- await this.clearFlagVal(`rollParticipantData.${rollSection}.${rollSubSection}`);
+ await this.updateTarget(`rollParticipantData.${rollSection}.${rollSubSection}`, null);
}
async updateUserPermission(
@@ -2172,12 +2080,12 @@ class BladesRoll extends BladesTargetLink {
}
// #region Basic User Flag Getters/Setters ~
- get flagData(): BladesRoll.FlagData {
- if (!this.document.getFlag(C.SYSTEM_ID, "rollCollab")) {
- throw new Error("[get flags()] No RollCollab Flags Found on User Document");
- }
- return this.document.getFlag(C.SYSTEM_ID, "rollCollab") as BladesRoll.FlagData;
- }
+ // get data(): BladesRoll.FlagData {
+ // if (!this.document.getFlag(C.SYSTEM_ID, "rollCollab")) {
+ // throw new Error("[get flags()] No RollCollab Flags Found on User Document");
+ // }
+ // return this.document.getFlag(C.SYSTEM_ID, "rollCollab") as BladesRoll.FlagData;
+ // }
get rollPrimary(): BladesRollPrimary {
return this._rollPrimary;
@@ -2194,8 +2102,8 @@ class BladesRoll extends BladesTargetLink {
}
get rollOpposition(): BladesRollOpposition | undefined {
- if (!this._rollOpposition && BladesRollOpposition.IsValidData(this.flagData.rollOppData)) {
- this._rollOpposition = new BladesRollOpposition(this, this.flagData.rollOppData);
+ if (!this._rollOpposition && BladesRollOpposition.IsValidData(this.data.rollOppData)) {
+ this._rollOpposition = new BladesRollOpposition(this, this.data.rollOppData);
}
return this._rollOpposition?.refresh();
}
@@ -2214,17 +2122,13 @@ class BladesRoll extends BladesTargetLink {
}
get rollClockKey(): BladesClockKey | undefined {
- return this.flagData.rollClockKeyID
- ? game.eunoblades.ClockKeys.get(this.flagData.rollClockKeyID)
+ return this.data.rollClockKeyID
+ ? game.eunoblades.ClockKeys.get(this.data.rollClockKeyID)
: undefined;
}
set rollClockKey(val: BladesClockKey | undefined) {
- if (val) {
- this.setFlagVal("rollClockKeyID", val.id);
- } else {
- this.clearFlagVal("rollClockKeyID");
- }
+ this.updateTarget("rollClockKeyID", val ?? null);
}
/**
@@ -2234,7 +2138,7 @@ class BladesRoll extends BladesTargetLink {
* The created instances are stored in the rollParticipants object.
*/
private prepareRollParticipantData(): void {
- const participantFlagData = this.flagData.rollParticipantData;
+ const participantFlagData = this.data.rollParticipantData;
if (!participantFlagData) {return;}
const rollParticipants: BladesRoll.RollParticipantDocs = {};
@@ -2291,7 +2195,7 @@ class BladesRoll extends BladesTargetLink {
get rollParticipantSelectOptions(): Record<
"Assist" | "Setup" | "Group",
Array>
- > {
+ > {
const nonPrimaryPCs = BladesPC.All
.filter((actor) => actor.hasTag(Tag.PC.ActivePC) && actor.id !== this.rollPrimary.rollPrimaryID)
.map((actor) => ({value: actor.id, display: actor.name}));
@@ -2302,35 +2206,35 @@ class BladesRoll extends BladesTargetLink {
};
}
- get rollType(): RollType {return this.flagData.rollType;}
+ get rollType(): RollType {return this.data.rollType as RollType;}
- get rollSubType(): RollSubType | undefined {return this.flagData.rollSubType;}
+ get rollSubType(): RollSubType | undefined {return this.data.rollSubType;}
set rollSubType(val: RollSubType | undefined) {
- this.setFlagVal("rollSubType", val);
+ this.updateTarget("rollSubType", val ?? null);
}
get rollPhase(): RollPhase {
- return this.getFlagVal("rollPhase") ?? RollPhase.Collaboration;
+ return this.data.rollPhase ?? RollPhase.Collaboration;
}
async setRollPhase(rollPhase: RollPhase) {
- await this.setFlagVal("rollPhase", rollPhase);
+ await this.updateTarget("rollPhase", rollPhase);
}
- get rollDowntimeAction(): DowntimeAction | undefined {return this.flagData.rollDowntimeAction;}
+ get rollDowntimeAction(): DowntimeAction | undefined {return this.data.rollDowntimeAction;}
- get rollTrait(): BladesRoll.RollTrait | undefined {return this.flagData.rollTrait;}
+ get rollTrait(): BladesRoll.RollTrait | undefined {return this.data.rollTrait;}
get rollTraitVerb(): ValueOf | undefined {
- if (!this.rollTrait) { return undefined; }
- if (!(this.rollTrait in C.ActionVerbs)) { return undefined; }
+ if (!this.rollTrait) {return undefined;}
+ if (!(this.rollTrait in C.ActionVerbs)) {return undefined;}
return C.ActionVerbs[this.rollTrait as ActionTrait] as ValueOf | undefined;
}
get rollTraitPastVerb(): ValueOf | undefined {
- if (!this.rollTrait) { return undefined; }
- if (!(this.rollTrait in C.ActionPastVerbs)) { return undefined; }
+ if (!this.rollTrait) {return undefined;}
+ if (!(this.rollTrait in C.ActionPastVerbs)) {return undefined;}
return C.ActionPastVerbs[this.rollTrait as ActionTrait] as ValueOf | undefined;
}
@@ -2410,7 +2314,7 @@ class BladesRoll extends BladesTargetLink {
}
get posEffectTrade(): "position" | "effect" | false {
- return this.flagData?.rollPosEffectTrade ?? false;
+ return this.data?.rollPosEffectTrade ?? false;
}
// getFlagVal(flagKey?: string): T | undefined {
@@ -2423,32 +2327,32 @@ class BladesRoll extends BladesTargetLink {
// async setFlagVal(flagKey: string, flagVal: unknown, isRerendering = true) {
// await this.document.setFlag(C.SYSTEM_ID, `rollCollab.${flagKey}`.replace(/(rollCollab\.)+/g, "rollCollab."), flagVal);
// if (isRerendering) {
- // socketlib.system.executeForEveryone("renderRollCollab", this.rollID);
+ // socketlib.system.executeForEveryone("renderRollCollab", this.id);
// }
// }
// async clearFlagVal(flagKey: string, isRerendering = true) {
// await this.document.unsetFlag(C.SYSTEM_ID, `rollCollab.${flagKey}`.replace(/(rollCollab\.)+/g, "rollCollab."));
// if (isRerendering) {
- // socketlib.system.executeForEveryone("renderRollCollab", this.rollID);
+ // socketlib.system.executeForEveryone("renderRollCollab", this.id);
// }
// }
get initialPosition(): Position {
- return this.getFlagVal("rollPositionInitial") ?? Position.risky;
+ return this.data.rollPositionInitial ?? Position.risky;
}
set initialPosition(val: Position) {
- this.setFlagVal("rollPositionInitial", val ?? Position.risky);
+ this.updateTarget("rollPositionInitial", val ?? Position.risky);
}
get initialEffect(): Effect {
- return this.getFlagVal("rollEffectInitial") ?? Effect.standard;
+ return this.data.rollEffectInitial ?? Effect.standard;
}
set initialEffect(val: Effect) {
- this.setFlagVal("rollEffectInitial", val);
+ this.updateTarget("rollEffectInitial", val ?? Effect.standard);
}
get isApplyingConsequences(): boolean {
@@ -2490,21 +2394,21 @@ class BladesRoll extends BladesTargetLink {
get finalResult(): number {
return this.getModsDelta(RollModSection.result)
- + (this.flagData?.GMBoosts.Result ?? 0)
+ + (this.data?.GMBoosts.Result ?? 0)
+ (this.tempGMBoosts.Result ?? 0);
}
get finalDicePool(): number {
return Math.max(0, this.rollTraitData.value
+ this.getModsDelta(RollModSection.roll)
- + (this.flagData.GMBoosts.Dice ?? 0)
+ + (this.data.GMBoosts.Dice ?? 0)
+ (this.tempGMBoosts.Dice ?? 0));
}
get isRollingZero(): boolean {
return Math.max(0, this.rollTraitData.value
+ this.getModsDelta(RollModSection.roll)
- + (this.flagData.GMBoosts.Dice ?? 0)
+ + (this.data.GMBoosts.Dice ?? 0)
+ (this.tempGMBoosts.Dice ?? 0)) <= 0;
}
@@ -2576,7 +2480,7 @@ class BladesRoll extends BladesTargetLink {
this.rollPrimary.rollFactors,
{isMutatingOk: false}
),
- this.flagData.rollFactorToggles.source,
+ this.data.rollFactorToggles.source,
{isMutatingOk: false}
) as Record;
@@ -2587,7 +2491,7 @@ class BladesRoll extends BladesTargetLink {
this.rollOpposition.rollFactors,
{isMutatingOk: false}
),
- this.flagData.rollFactorToggles.opposition,
+ this.data.rollFactorToggles.opposition,
{isMutatingOk: false}
) as Record
: {};
@@ -2597,7 +2501,7 @@ class BladesRoll extends BladesTargetLink {
(Object.entries(mergedSourceFactors) as Array<[Factor, BladesRoll.FactorData]>)
.map(([factor, factorData]) => {
factorData.value +=
- (this.flagData.GMBoosts[factor] ?? 0)
+ (this.data.GMBoosts[factor] ?? 0)
+ (this.tempGMBoosts[factor] ?? 0);
if (factor === Factor.tier) {
factorData.display = U.romanizeNum(factorData.value);
@@ -2610,7 +2514,7 @@ class BladesRoll extends BladesTargetLink {
opposition: Object.fromEntries(
(Object.entries(mergedOppFactors) as Array<[Factor, BladesRoll.FactorData]>)
.map(([factor, factorData]) => {
- factorData.value += this.flagData.GMOppBoosts[factor] ?? 0;
+ factorData.value += this.data.GMOppBoosts[factor] ?? 0;
if (factor === Factor.tier) {
factorData.display = U.romanizeNum(factorData.value);
} else {
@@ -2639,7 +2543,7 @@ class BladesRoll extends BladesTargetLink {
let initReportCount = 0;
const watchMod = (label: string) => {
- if (BladesRoll._Debug.modWatch === false) { return; }
+ if (BladesRoll._Debug.modWatch === false) {return;}
const reportLabel = `(${initReportCount}) == ${label}`;
const rollMod = this.rollMods
.find((mod) => BladesRoll._Debug.modWatch && BladesRoll._Debug.modWatch.exec(mod.name));
@@ -2688,7 +2592,7 @@ class BladesRoll extends BladesTargetLink {
watchMod("DISABLE - AUTO-REVEAL/ENABLE");
// ... Payable Pass
- autoRevealDisablePass.forEach((rollMod) => { rollMod.setPayableStatus(); });
+ autoRevealDisablePass.forEach((rollMod) => {rollMod.setPayableStatus();});
watchMod("DISABLE - PAYABLE");
/* *** PASS TWO: FORCE-ON PASS *** */
@@ -2961,7 +2865,7 @@ class BladesRoll extends BladesTargetLink {
set rollMods(val: BladesRollMod[]) {this._rollMods = val;}
canResistWithArmor(csqData: BladesRoll.ConsequenceData) {
- if (!this.rollPrimary.hasArmor) { return false; }
+ if (!this.rollPrimary.hasArmor) {return false;}
return csqData.attribute === AttributeTrait.prowess;
}
@@ -2977,16 +2881,16 @@ class BladesRoll extends BladesTargetLink {
// #region CONSEQUENCES: Getting, Accepting, Resisting
private get _csqData(): BladesRoll.ConsequenceData[] {
- const csqData = this.flagData.consequenceData?.
- [this.finalPosition]?.
- [this.rollResult as RollResult.partial|RollResult.fail];
+ const csqData = this.data.consequenceData?.
+ [this.finalPosition]?.
+ [this.rollResult as RollResult.partial | RollResult.fail];
if (csqData) {
return Object.values(csqData);
}
return [];
}
- getConsequenceByID(csqID: string): BladesRoll.ConsequenceData|false {
+ getConsequenceByID(csqID: string): BladesRoll.ConsequenceData | false {
return this._csqData.find((cData) => cData.id === csqID) ?? false;
}
@@ -3298,7 +3202,7 @@ class BladesRoll extends BladesTargetLink {
rollCosts: BladesRoll.CostData[]
): BladesRoll.Context {
const {
- flagData: rData,
+ data: rData,
rollPrimary,
rollTraitData,
rollTraitOptions,
@@ -3315,7 +3219,7 @@ class BladesRoll extends BladesTargetLink {
throw new Error("A primary roll source is required for BladesRoll.");
}
const baseData = {
- ...this.flagData,
+ ...this.data,
cssClass: "roll-collab",
editable: this.options.editable,
isGM,
@@ -3427,21 +3331,21 @@ class BladesRoll extends BladesTargetLink {
};
}
- private calculateGMBoostsData(flagData: BladesRoll.FlagData) {
+ private calculateGMBoostsData(data: BladesRoll.FlagData) {
return {
GMBoosts: {
- Dice: flagData.GMBoosts.Dice ?? 0,
- [Factor.tier]: flagData.GMBoosts[Factor.tier] ?? 0,
- [Factor.quality]: flagData.GMBoosts[Factor.quality] ?? 0,
- [Factor.scale]: flagData.GMBoosts[Factor.scale] ?? 0,
- [Factor.magnitude]: flagData.GMBoosts[Factor.magnitude] ?? 0,
- Result: flagData.GMBoosts.Result ?? 0
+ Dice: data.GMBoosts.Dice ?? 0,
+ [Factor.tier]: data.GMBoosts[Factor.tier] ?? 0,
+ [Factor.quality]: data.GMBoosts[Factor.quality] ?? 0,
+ [Factor.scale]: data.GMBoosts[Factor.scale] ?? 0,
+ [Factor.magnitude]: data.GMBoosts[Factor.magnitude] ?? 0,
+ Result: data.GMBoosts.Result ?? 0
},
GMOppBoosts: {
- [Factor.tier]: flagData.GMOppBoosts[Factor.tier] ?? 0,
- [Factor.quality]: flagData.GMOppBoosts[Factor.quality] ?? 0,
- [Factor.scale]: flagData.GMOppBoosts[Factor.scale] ?? 0,
- [Factor.magnitude]: flagData.GMOppBoosts[Factor.magnitude] ?? 0
+ [Factor.tier]: data.GMOppBoosts[Factor.tier] ?? 0,
+ [Factor.quality]: data.GMOppBoosts[Factor.quality] ?? 0,
+ [Factor.scale]: data.GMOppBoosts[Factor.scale] ?? 0,
+ [Factor.magnitude]: data.GMOppBoosts[Factor.magnitude] ?? 0
}
};
}
@@ -3956,10 +3860,10 @@ class BladesRoll extends BladesTargetLink {
const chatMessage$ = chatElem$.closest(".chat-message");
const chatID = chatMessage$.data("messageId") as IDString;
const chatMessage = game.messages.get(chatID);
- if (!chatMessage) { return; }
+ if (!chatMessage) {return;}
const csqs = await BladesConsequence.GetFromChatMessage(chatMessage);
const thisCsq = csqs.find((csq) => csq.id === csqID);
- if (!thisCsq) { return; }
+ if (!thisCsq) {return;}
switch (clickTarget$.data("action")) {
case "accept-consequence": return thisCsq.accept();
case "resist-consequence": return thisCsq.resistConsequence();
@@ -4018,7 +3922,7 @@ class BladesRoll extends BladesTargetLink {
const target = elem$.data("target").replace(/flags\.eunos-blades\./, "");
const value = elem$.data("value");
- await this.document.setFlag(C.SYSTEM_ID, target, value).then(() => socketlib.system.executeForEveryone("renderRollCollab", this.rollID));
+ await this.document.setFlag(C.SYSTEM_ID, target, value).then(() => socketlib.system.executeForEveryone("renderRollCollab", this.id));
}
async _gmControlCycleTarget(event: ClickEvent) {
@@ -4054,7 +3958,7 @@ class BladesRoll extends BladesTargetLink {
const elem$ = $(event.currentTarget);
const target = elem$.data("target").replace(/flags\.eunos-blades\./, "");
- await this.document.unsetFlag(C.SYSTEM_ID, target).then(() => socketlib.system.executeForEveryone("renderRollCollab", this.rollID));
+ await this.document.unsetFlag(C.SYSTEM_ID, target).then(() => socketlib.system.executeForEveryone("renderRollCollab", this.id));
}
/**
@@ -4101,7 +4005,7 @@ class BladesRoll extends BladesTargetLink {
// If thisToggle is unrecognized, just toggle whatever value target points at
if (!["isActive", "isPrimary", "isDominant", "highFavorsPC"].includes(thisToggle)) {
await this.document.setFlag(C.SYSTEM_ID, `rollCollab.${target}`, value)
- .then(() => socketlib.system.executeForEveryone("renderRollCollab", this.rollID));
+ .then(() => socketlib.system.executeForEveryone("renderRollCollab", this.id));
}
// Otherwise, first toggle targeted factor to new value
@@ -4145,7 +4049,7 @@ class BladesRoll extends BladesTargetLink {
}
await this.document.setFlag(C.SYSTEM_ID, "rollCollab.rollFactorToggles", factorToggleData)
- .then(() => socketlib.system.executeForEveryone("renderRollCollab", this.rollID));
+ .then(() => socketlib.system.executeForEveryone("renderRollCollab", this.id));
}
async _onSelectChange(event: SelectChangeEvent) {
@@ -4161,13 +4065,13 @@ class BladesRoll extends BladesTargetLink {
);
} else {
await U.EventHandlers.onSelectChange(this, event);
- socketlib.system.executeForEveryone("renderRollCollab", this.rollID);
+ socketlib.system.executeForEveryone("renderRollCollab", this.id);
}
}
async _onTextInputBlur(event: InputChangeEvent) {
await U.EventHandlers.onTextInputBlur(this, event);
- socketlib.system.executeForEveryone("renderRollCollab", this.rollID);
+ socketlib.system.executeForEveryone("renderRollCollab", this.id);
}
async _onGMPopupClick(event: ClickEvent) {
@@ -4176,7 +4080,7 @@ class BladesRoll extends BladesTargetLink {
*
*
* */
@@ -4232,9 +4136,9 @@ class BladesRoll extends BladesTargetLink {
click: (event) => {
const curVal = `${$(event.currentTarget).data("value")}`;
if (curVal === "false") {
- this.document.setFlag(C.SYSTEM_ID, "rollCollab.rollPosEffectTrade", "effect").then(() => socketlib.system.executeForEveryone("renderRollCollab", this.rollID));
+ this.document.setFlag(C.SYSTEM_ID, "rollCollab.rollPosEffectTrade", "effect").then(() => socketlib.system.executeForEveryone("renderRollCollab", this.id));
} else {
- this.document.setFlag(C.SYSTEM_ID, "rollCollab.rollPosEffectTrade", false).then(() => socketlib.system.executeForEveryone("renderRollCollab", this.rollID));
+ this.document.setFlag(C.SYSTEM_ID, "rollCollab.rollPosEffectTrade", false).then(() => socketlib.system.executeForEveryone("renderRollCollab", this.id));
}
}
});
@@ -4242,9 +4146,9 @@ class BladesRoll extends BladesTargetLink {
click: (event) => {
const curVal = `${$(event.currentTarget).data("value")}`;
if (curVal === "false") {
- this.document.setFlag(C.SYSTEM_ID, "rollCollab.rollPosEffectTrade", "position").then(() => socketlib.system.executeForEveryone("renderRollCollab", this.rollID));
+ this.document.setFlag(C.SYSTEM_ID, "rollCollab.rollPosEffectTrade", "position").then(() => socketlib.system.executeForEveryone("renderRollCollab", this.id));
} else {
- this.document.setFlag(C.SYSTEM_ID, "rollCollab.rollPosEffectTrade", false).then(() => socketlib.system.executeForEveryone("renderRollCollab", this.rollID));
+ this.document.setFlag(C.SYSTEM_ID, "rollCollab.rollPosEffectTrade", false).then(() => socketlib.system.executeForEveryone("renderRollCollab", this.id));
}
}
});
@@ -4347,15 +4251,15 @@ class BladesRoll extends BladesTargetLink {
override async _onSubmit(event: Event, {updateData}: FormApplication.OnSubmitOptions = {}) {
const returnVal = await super._onSubmit(event, {updateData, preventClose: true});
- await socketlib.system.executeForEveryone("renderRollCollab", this.rollID);
+ await socketlib.system.executeForEveryone("renderRollCollab", this.id);
return returnVal;
}
- override async close(options: FormApplication.CloseOptions & {rollID?: string} = {}) {
+ override async close(options: FormApplication.CloseOptions & {id?: string} = {}) {
- if (options.rollID) {return super.close({});}
+ if (options.id) {return super.close({});}
// await this.document.setFlag(C.SYSTEM_ID, "rollCollab", null);
- socketlib.system.executeForEveryone("closeRollCollab", this.rollID);
+ socketlib.system.executeForEveryone("closeRollCollab", this.id);
return undefined;
}
diff --git a/ts/classes/BladesTargetLink.ts b/ts/classes/BladesTargetLink.ts
index 816133e2..a4b307e6 100644
--- a/ts/classes/BladesTargetLink.ts
+++ b/ts/classes/BladesTargetLink.ts
@@ -3,33 +3,52 @@ import U from "../core/utilities";
import C from "../core/constants";
import {BladesActor} from "../documents/BladesActorProxy";
import {BladesItem} from "../documents/BladesItemProxy";
+import BladesChat from "./BladesChat";
+import type {AnyDocumentData} from "@league-of-foundry-developers/foundry-vtt-types/src/foundry/common/abstract/data.mjs.d.ts";
-
-class BladesTargetLink {
+class BladesTargetLink {
// #region STATIC METHODS ~
+ static IsValidConfig(ref: unknown): ref is BladesTargetLink.Config {
+ return U.isSimpleObj(ref)
+ && (U.isDocID(ref.target)
+ || U.isDocUUID(ref.target)
+ || U.isDocUUID(ref.targetID)
+ || ref.target instanceof BladesActor
+ || ref.target instanceof BladesItem
+ || ref.target instanceof BladesChat
+ || ref.target instanceof User
+ )
+ && (U.isTargetKey(ref.targetKey) || U.isTargetFlagKey(ref.targetFlagKey))
+ && !(U.isTargetKey(ref.targetKey) && U.isTargetFlagKey(ref.targetFlagKey));
+ }
+
+ static IsValidData(ref: unknown): ref is BladesTargetLink.Data {
+ return U.isSimpleObj(ref)
+ && U.isDocID(ref.id)
+ && U.isDocUUID(ref.targetID)
+ && (U.isTargetKey(ref.targetKey) || U.isTargetFlagKey(ref.targetFlagKey))
+ && !(U.isTargetKey(ref.targetKey) && U.isTargetFlagKey(ref.targetFlagKey));
+ }
+
/**
- * This static method applies defaults to any values missing from the class' data Schema.
- * 'Schema' is defined by subclasses to BladesTargetLink.
- * Subclasses must override this method to apply their own defaults.
+ * Parses the configuration object to construct a data object for BladesTargetLink.
+ * It validates the config object, isolates BladesTargetLink.Config entries from unknown PartialSchema entries,
+ * determines the targetUUID from target/targetID, confirms the existence of 'targetKey' or 'targetFlagKey',
+ * applies schema defaults, and constructs the final data object to be returned.
*
* @template Schema - The data schema required by the subclass.
- * @param {Partial} schemaData - Schema data overriding the defaults.
- * @returns {Schema} - The schema data with defaults applied.
- * @throws {Error} - Throws an error if this method is not overridden in a subclass.
+ * @param {BladesTargetLink.Config & Partial} config - The configuration object containing potential BladesTargetLink properties and any subclass-specific schema data.
+ * @returns {BladesTargetLink.Data & Schema} - The constructed data object with defaults applied and necessary properties for BladesTargetLink.
+ * @throws {Error} - Throws an error if the config object is not a simple object, if it lacks a target reference, if it lacks a valid 'targetKey' or 'targetFlagKey', or if both 'targetKey' and 'targetFlagKey' are provided.
*/
- static ApplySchemaDefaults(
+ static #ParseConfig(
this: BladesTargetLink.StaticThisContext,
- schemaData: Partial
- ): Schema {
- throw new Error("[BladesTargetLink.ApplySchemaDefaults] Static Method ApplySchemaDefaults must be overridden in subclass");
- }
+ config: (BladesTargetLink.Config | BladesTargetLink.Data) & Partial
+ ): BladesTargetLink.Data & Partial {
- static ParseConfig(
- this: BladesTargetLink.StaticThisContext,
- config: BladesTargetLink.Config & Partial
- ): BladesTargetLink.Data & Schema {
+ if (this.IsValidData(config)) { return this.ParseConfig(config); }
// === VALIDATE CONFIG ===
// - Confirm 'config' is proper primitive type
@@ -39,6 +58,7 @@ class BladesTargetLink {
// - Isolate BladesTargetLink.Config entries from unknown PartialSchema entries
const {target, targetID, targetKey, targetFlagKey, ...schemaData} = config;
+ const partialSchema = schemaData as Partial;
// - Attempt to determine targetUUID from target/targetID
const targetRef = target ?? targetID;
@@ -71,45 +91,84 @@ class BladesTargetLink {
throw new Error(`[BladesTargetLink.ResolveConfigToData()] Data has BOTH 'targetKey' and 'targetFlagKey': '${JSON.stringify(config)}'`);
}
- // === APPLY SCHEMA DEFAULTS ===
- const schema = this.ApplySchemaDefaults(schemaData as Partial) as Schema;
-
// === RETURN CONSTRUCTED DATA OBJECT ===
- return {
- ...schema,
+ // Pass it through public ParseConfigData static method,
+ // So subclasses can override it to incorporate their own logic.
+
+ return this.ParseConfig({
+ ...partialSchema,
id: randomID() as IDString,
targetID: targetUUID,
targetKey,
targetFlagKey
- };
+ }) as BladesTargetLink.Data & Partial;
}
- static async InitTargetLink(
- this: BladesTargetLink.StaticThisContext,
- data: BladesTargetLink.Data & Schema
- ): Promise {
- // Validate target.
- const target = fromUuidSync(data.targetID);
- if (!target) {throw new Error(`[BladesTargetLink.InitTargetLink] No target found with UUID '${data.targetID}'`);}
+ /** Subclasses can override this method to include their own parse logic */
+ static ParseConfig(
+ data: BladesTargetLink.Data & Partial
+ ): BladesTargetLink.Data & Partial {
+ /* Subclasses can override with custom logic here;
+ if they don't, the default parsed config is returned */
+ return data;
+ }
- // Initialize server-side data on target.
- if (data.targetKey) {
- await target.update({[`${data.targetKey}.${data.id}`]: data});
- } else if (data.targetFlagKey) {
- await (target as BladesItem).setFlag(C.SYSTEM_ID, `${data.targetFlagKey}.${data.id}`, data);
+ static PartitionSchemaData(
+ data: BladesTargetLink.Data & Partial
+ ): BladesTargetLink.Data & {partialSchema: Partial} {
+ const {id, targetID, targetKey, targetFlagKey, ...schemaData} = data;
+ if (!id || !targetID || !(targetKey || targetFlagKey)) {
+ eLog.error("BladesTargetLink", "Bad Constructor Data", {data});
+ throw new Error("[new BladesTargetLink()] Bad Constructor Data (see log)");
}
- return target;
+ return {id, targetID, targetKey, targetFlagKey, partialSchema: schemaData as Partial};
}
- static async Create(
+ static #ApplySchemaDefaults(
this: BladesTargetLink.StaticThisContext,
+ schemaData: Partial,
+ linkData: BladesTargetLink.Data
+ ): Schema {
+ return this.ApplySchemaDefaults(schemaData, linkData);
+ }
+
+ /**
+ * This static method applies defaults to any values missing from the class' data Schema.
+ * 'Schema' is defined by subclasses to BladesTargetLink.
+ * Subclasses must override this method to apply their own defaults.
+ *
+ * @template Schema - The data schema required by the subclass.
+ * @param {Partial} schemaData - Schema data overriding the defaults.
+ * @returns {Schema} - The schema data with defaults applied.
+ * @throws {Error} - Throws an error if this method is not overridden in a subclass.
+ */
+ static ApplySchemaDefaults(
+ this: BladesTargetLink.StaticThisContext,
+ schemaData: Partial,
+ linkData: BladesTargetLink.Data
+ ): Schema {
+ throw new Error("[BladesTargetLink.ApplySchemaDefaults] Static Method ApplySchemaDefaults must be overridden in subclass");
+ }
+
+ /**
+ * Creates a new instance of BladesTargetLink and initializes it with the provided configuration.
+ * The configuration is parsed into a data object which is then used to initialize the target link.
+ * The function logs the parsed data for debugging purposes.
+ *
+ * @template Schema - The schema type parameter that extends the data structure.
+ * @param {BladesTargetLink.Config & Partial} config - The configuration object containing both the target link configuration and the schema configuration.
+ *
+ * @returns {Promise & BladesTargetLink.Subclass>} - A promise that resolves to a new instance of BladesTargetLink, initialized with the provided data.
+ *
+ * @throws {Error} - Throws an error if the initialization of the target link fails.
+ */
+ static async Create(
+ this: new (config: BladesTargetLink.Config & Partial) => BladesTargetLink,
config: BladesTargetLink.Config & Partial
- ): Promise & BladesTargetLink.Subclass> {
- const data = this.ParseConfig(config);
- eLog.checkLog2("BladesTargetLink.Create", "Config Parsed to Data", {config: U.objClone(config), data: U.objClone(data)});
- await this.InitTargetLink(data);
- eLog.checkLog3("BladesTargetLink.Create", "After Init Target Link", {data: U.objClone(data)});
- return new this(data) as BladesTargetLink & BladesTargetLink.Subclass;
+ ) {
+ const tLink = new this(config);
+ await tLink.initTargetLink();
+ return tLink;
}
// #endregion
@@ -120,7 +179,7 @@ class BladesTargetLink {
private _targetID: UUIDString;
private _targetKey?: TargetKey;
private _targetFlagKey?: TargetFlagKey;
- private _initialData: BladesTargetLink.Data & Schema;
+ private _initialSchema: Schema;
get id() {return this._id;}
get targetID() {return this._targetID;}
@@ -136,19 +195,27 @@ class BladesTargetLink {
? `${this.targetFlagKey}.${this.id}` as TargetFlagKey
: undefined;
}
- get initialData(): BladesTargetLink.Data & Schema { return this._initialData; }
+ get linkData(): BladesTargetLink.Data {
+ return {
+ id: this.id,
+ targetID: this.targetID,
+ targetKey: this.targetKey,
+ targetFlagKey: this.targetFlagKey
+ };
+ }
+ get initialSchema(): Schema { return this._initialSchema; }
- private _target: BladesDoc;
- get target(): BladesDoc {
+ private _target: BladesLinkDoc;
+ get target(): BladesLinkDoc {
return this._target;
}
protected get localData(): BladesTargetLink.Data & Schema {
if (this._target) {
- return this.linkData;
+ return this.data;
}
return {
- ...this.initialData,
+ ...this.initialSchema,
id: this.id,
targetID: this.targetID,
targetKey: this.targetKey,
@@ -156,84 +223,180 @@ class BladesTargetLink {
};
}
- protected get linkData() {
- let linkData: (BladesTargetLink.Data & Schema) | undefined;
- if (this.targetFlagKeyPrefix) {
- linkData = this.target.getFlag(
- C.SYSTEM_ID,
- this.targetFlagKeyPrefix
- ) as (BladesTargetLink.Data & Schema)
- ?? undefined;
- } else if (this.targetKeyPrefix) {
- linkData = getProperty(this.target, this.targetKeyPrefix);
- }
+ get data(): BladesTargetLink.Data & Schema {
+ type TargetData = BladesTargetLink.Data & Schema;
+ if (this._target) {
+ let data: TargetData | undefined;
+ if (this.targetFlagKeyPrefix) {
+ data = this.target.getFlag(C.SYSTEM_ID, this.targetFlagKeyPrefix) as TargetData | undefined;
+ } else if (this.targetKeyPrefix) {
+ data = getProperty(this.target, this.targetKeyPrefix);
+ }
- if (!linkData) {
- throw new Error("[BladesTargetLink.linkData] Error retrieving linkData.");
- }
+ if (!data) {
+ throw new Error("[BladesTargetLink.data] Error retrieving data.");
+ }
- return linkData;
+ return data;
+ } else {
+ eLog.warn("BladesTargetLink", "Attempt to access data of uninitiated BladesTargetLink: Returning local data only.", {bladesTargetLink: this, localData: this.localData});
+ return this.localData;
+ }
}
-
- get data() {return this.linkData;}
// #endregion
// #region CONSTRUCTOR ~
- constructor(
- data: BladesTargetLink.Data & BladesTargetLink.UnknownSchema
- ) {
- const {id, targetID, targetKey, targetFlagKey} = data;
- if (!id || !targetID || !(targetKey || targetFlagKey)) {
- eLog.error("BladesTargetLink", "Bad Constructor Data", {data});
- throw new Error("[new BladesTargetLink()] Bad Constructor Data (see log)");
- }
- this._id = id;
- this._targetID = targetID;
- if (U.isTargetKey(targetKey)) {
- this._targetKey = targetKey;
- } else if (U.isTargetFlagKey(targetFlagKey)) {
- this._targetFlagKey = targetFlagKey;
+ constructor(config: BladesTargetLink.Config & Partial)
+ constructor(data: BladesTargetLink.Data)
+ constructor(dataOrConfig: BladesTargetLink.Config & Partial | BladesTargetLink.Data) {
+
+ let linkData: BladesTargetLink.Data;
+ let schema: Schema;
+
+ // First, we construct the link data from the config or data object.
+ if (BladesTargetLink.IsValidData(dataOrConfig)) {
+
+ // If a simple link data object was provided, acquire the schema from the source document
+ linkData = {
+ id: dataOrConfig.id,
+ targetID: dataOrConfig.targetID,
+ targetKey: dataOrConfig.targetKey,
+ targetFlagKey: dataOrConfig.targetFlagKey
+ };
+ const target = fromUuidSync(dataOrConfig.targetID);
+ if (!target) {
+ throw new Error(`[new BladesTargetLink()] Unable to resolve target from uuid '${dataOrConfig.targetID}'`);
+ }
+
+ if (dataOrConfig.targetKey) {
+ schema = getProperty(target, `${dataOrConfig.targetKey}.${dataOrConfig.id}`);
+ } else {
+ schema = target.getFlag(C.SYSTEM_ID, `${dataOrConfig.targetFlagKey}.${dataOrConfig.id}`) as Schema;
+ }
+ } else {
+ // Otherwise, we have to parse the config into a data object, and extract any schema data
+ // First we convert the config object to a BladesTargetLink.Data & Partial object.
+ const partialData: BladesTargetLink.Data & Partial = BladesTargetLink.#ParseConfig(dataOrConfig);
+
+ // Next, we partition the data into the target link data and the schema data.
+ const {id, targetID, targetKey, targetFlagKey, partialSchema} = BladesTargetLink.PartitionSchemaData(partialData);
+
+ // Now we construct the data object
+ linkData = {id, targetID, targetKey, targetFlagKey};
+
+ // And apply any schema defaults to the provided schema data.
+ schema = BladesTargetLink.#ApplySchemaDefaults(partialSchema, linkData);
}
+
+ this._id = linkData.id;
+ this._targetID = linkData.targetID;
+ this._targetKey = linkData.targetKey;
+ this._targetFlagKey = linkData.targetFlagKey;
+
const target = fromUuidSync(this.targetID);
if (!target) {
throw new Error(`[new BladesTargetLink()] Unable to resolve target from uuid '${this._targetID}'`);
}
this._target = target;
- this._initialData = JSON.parse(JSON.stringify(data));
+
+ this._initialSchema = schema;
}
// #endregion
// #region ASYNC UPDATE & DELETE METHODS ~
- async updateTarget(prop: string, val: unknown, isSilent = false) {
- if (this.targetFlagKeyPrefix && (this.target as BladesItem).getFlag(C.SYSTEM_ID, `${this.targetFlagKeyPrefix}.${prop}`) !== val) {
- await (this.target as BladesItem).setFlag(C.SYSTEM_ID, `${this.targetFlagKeyPrefix}.${prop}`, val);
- } else if (this.targetKeyPrefix && (this.target as BladesItem)[`${this.targetKeyPrefix}.${prop}` as KeyOf] !== val) {
- await this.target.update({[`${this.targetKeyPrefix}.${prop}`]: val}, {render: false});
+ private getDotKeyToProp(prop: string|number|undefined, isNullifying = false): string {
+ if (this.targetKeyPrefix) {
+ if (prop === undefined) {
+ return isNullifying ? `${this.targetKey}.-=${this.id}` : this.targetKeyPrefix;
+ }
+ return `${this.targetKeyPrefix}.${isNullifying ? "-=" : ""}${prop}`;
+ }
+ if (this.targetFlagKeyPrefix) {
+ if (prop === undefined) {
+ return this.targetFlagKeyPrefix;
+ }
+ return `${this.targetFlagKeyPrefix}.${prop}`;
}
+ throw new Error("[BladesTargetLink.getDotKeyToProp()] Missing 'targetKeyPrefix' and 'targetFlagKeyPrefix'");
+ }
+
+ private getFlagParamsToProp(prop: string|number|undefined) {
+ return [C.SYSTEM_ID, this.getDotKeyToProp(prop)] as const;
}
- async updateTargetData(val: T | null, isSilent = false) {
+ private async updateTargetFlag(prop: string|number|undefined, val: unknown) {
+ if (!this.targetFlagKeyPrefix) { return; }
if (val === null) {
- if (this.targetFlagKeyPrefix) {
- await (this.target as BladesItem).unsetFlag(C.SYSTEM_ID, `${this.targetFlagKeyPrefix}`);
- } else {
- await this.target.update({[`${this.targetKey}.-=${this.id}`]: null});
- }
- } else {
+ await this.target.unsetFlag(...this.getFlagParamsToProp(prop));
+ } else if (this.target instanceof BladesActor) {
+ await this.target.setFlag(...this.getFlagParamsToProp(prop), val);
+ } else if (this.target instanceof BladesItem) {
+ await this.target.setFlag(...this.getFlagParamsToProp(prop), val);
+ } else if (this.target instanceof User) {
+ await this.target.setFlag(...this.getFlagParamsToProp(prop), val);
+ } else if (this.target instanceof BladesChat) {
+ await this.target.setFlag(...this.getFlagParamsToProp(prop), val);
+ }
+ }
+
+ private async updateTargetKey(prop: string|undefined, val: unknown) {
+ if (!this.targetKeyPrefix) { return; }
+ await this.target.update({[this.getDotKeyToProp(prop, val === null)]: val}, {render: false});
+ }
+
+ /**
+ * Initializes a target link by updating the target's data with the provided data object.
+ * If a targetKey is provided, the data is updated directly on the target.
+ * If a targetFlagKey is provided, the data is set as a flag on the target.
+ *
+ * This method need only be run once, when the document is first created and its data must be written to server storage.
+ * TargetLink documents whose data already exists in server storage can be constructed directly (i.e. new BladesTargetLink(data))
+ *
+ * @param {BladesTargetLink.Data & Schema} data - The combined data object containing both the target link data and the schema data.
+ * @returns {Promise} - A promise that resolves when the server update is complete.
+ */
+ async initTargetLink() {
+ // Construct data object
+ const data: BladesTargetLink.Data & Schema = {
+ id: this.id,
+ targetID: this.targetID,
+ targetKey: this.targetKey,
+ targetFlagKey: this.targetFlagKey,
+ ...this.initialSchema
+ };
+
+ // Initialize server-side data on target.
+ if (data.targetKey) {
+ await this.target.update({[`${data.targetKey}.${data.id}`]: data});
+ } else if (data.targetFlagKey) {
+ await (this.target as BladesItem).setFlag(C.SYSTEM_ID, `${data.targetFlagKey}.${data.id}`, data);
+ }
+ }
+
+ async updateTarget(prop: string, val: unknown, isSilent = false) {
+ if (getProperty(this.data, prop) === val) { return; }
+ if (this.targetFlagKeyPrefix) {
+ await this.updateTargetFlag(prop, val);
+ } else if (this.targetKeyPrefix) {
+ await this.updateTargetKey(prop, val);
+ }
+ }
+
+ async updateTargetData(val: Partial | null, isSilent = false) {
+ if (val) {
// Add BladesTargetLink.Data to provided schema
- const linkData: BladesTargetLink.Data & T = {
+ val = {
...val,
id: this.id,
targetID: this.targetID,
targetKey: this.targetKey,
targetFlagKey: this.targetFlagKey
};
- // Update target
- if (this.targetFlagKeyPrefix) {
- await (this.target as BladesItem).setFlag(C.SYSTEM_ID, this.targetFlagKeyPrefix, linkData);
- } else if (this.targetKeyPrefix) {
- await this.target.update({[this.targetKeyPrefix]: linkData}, {render: !isSilent});
- }
+ }
+ if (this.targetFlagKeyPrefix) {
+ await this.updateTargetFlag(undefined, val);
+ } else {
+ await this.updateTargetKey(undefined, val);
}
}
diff --git a/ts/core/constants.ts b/ts/core/constants.ts
index 6a5a780f..caba5736 100644
--- a/ts/core/constants.ts
+++ b/ts/core/constants.ts
@@ -1348,7 +1348,7 @@ const C = {
Vice.Life_Essence,
Vice.Electroplasmic_Power
]
-};
+} as const;
// #endregion
// #region RANDOMIZER DATA
diff --git a/ts/core/logger.ts b/ts/core/logger.ts
index 774ef223..32d9bbf2 100644
--- a/ts/core/logger.ts
+++ b/ts/core/logger.ts
@@ -51,6 +51,11 @@ const STYLES = {
"margin-left": "-100px",
padding: "0 100px"
},
+ warn: {
+ color: C.Colors.dBLACK,
+ background: C.Colors.dGOLD,
+ "font-weight": 500
+ },
error: {
color: C.Colors.bRED,
background: C.Colors.ddRED,
@@ -182,6 +187,7 @@ const logger = {
checkLog3: (...content: eLogParams) => eLogger("checkLog", ...content, 3),
checkLog4: (...content: eLogParams) => eLogger("checkLog", ...content, 4),
checkLog5: (...content: eLogParams) => eLogger("checkLog", ...content, 5),
+ warn: (...content: eLogParams) => eLogger("warn", ...content),
error: (...content: eLogParams) => eLogger("error", ...content),
hbsLog: (...content: eLogParams) => eLogger("handlebars", ...content)
};