mrkwnzl / cyphersystem-foundryvtt

The Cypher System for Foundry VTT
Other
21 stars 14 forks source link

Support `@Embed` in Foundry V12 for Items #362

Closed farling42 closed 1 month ago

farling42 commented 3 months ago

The default @Embed functionality in Foundry 12 only supports Document and Roll Tables.

The following code will allow the description of any cypher item to included inline in other documents.

This will be particularly useful for Types and Foci where a series of abilities are listed, so that the text of the abilities can appear within the same displayed document page (so that the user doesn't have to open each ability to see the wording of it).

It only requires implementing the _buildEmbedHTML function as follows (which is based on core _embedTextPage function):

export class CypherItem extends Item {
  static LOG_V10_COMPATIBILITY_WARNINGS = true;

  async _buildEmbedHTML(config, options) {
    const embed = await super._buildEmbedHTML(config, options);
    if (embed) return embed;
    // As per foundry.js: JournalEntryPage#_embedTextPage
    options = { ...options, _embedDepth: options._embedDepth + 1, relativeTo: this };
    const {
      secrets=options.secrets,
      documents=options.documents,
      links=options.links,
      rolls=options.rolls,
      embeds=options.embeds
    } = config;
    foundry.utils.mergeObject(options, { secrets, documents, links, rolls, embeds });

    let toenrich;
    const key = "CYPHERSYSTEM.Embed." + this.type + (this.system.basic.cost && this.system.basic.cost != "0" ? ".cost" : ".noCost");
    if (game.i18n.has(key)) {
      toenrich = game.i18n.format(key, {
        uuid: this.uuid, 
        pool: this.system.basic.pool, 
        cost: this.system.basic.cost, 
        description: this.system.description.replace(/^<p>/,"").replace(/<\/p>$/,"")  // strip leading <p> and trailing </p>
      });
    } else {
      toenrich = this.system.description;
    }
    const container = document.createElement("div");
    container.innerHTML = await TextEditor.enrichHTML(toenrich);
    return container.children;
  }
}

with new entries in en.json:

  "CYPHERSYSTEM.Embed.ability.cost": "<p><strong>@UUID[{uuid}] ({cost} {pool}):</strong> {description}</p>",
  "CYPHERSYSTEM.Embed.ability.noCost": "<p><strong>@UUID[{uuid}]</strong> {description}</p>"
mrkwnzl commented 3 months ago

Do you have a specific example of where this would be used? I haven’t touched V12, yet, as I want to put out a last update for V11 (with roll scripting) before moving on to V12.

farling42 commented 3 months ago

For a focus, the page can be written as:

Your flesh is made of hard mineral, making you a hulking, difficult-to-harm humanoid.

**Tier 1**

@Embed[Compendium.my-cyphersystem.abilities.HUhcmzDU3zN63h9f caption=false]
@Embed[Compendium.my-cyphersystem.abilities.dCeQCLqfm0PQa2eu caption=false]

**Tier 2**

@Embed[Compendium.my-cyphersystem.abilities.vmJc3t2ycp6ehlZr caption=false]{Golem Grip}

**Tier 3**

@Embed[Compendium.my-cyphersystem.abilities.Zk6CUwLK86R95F0a caption=false]{Trained Basher}

Choose one of:

@Embed[Compendium.my-cyphersystem.abilities.P6npXZ3lBL8E7vQ1 caption=false]{Golem Stomp}
@Embed[Compendium.my-cyphersystem.abilities.NUpg05HotcwzUjAO caption=false]{Weaponization}

**Tier 4**

@Embed[Compendium.my-cyphersystem.abilities.vL4CnjLqvf2LLrMc caption=false]{Deep Reserves}

**Tier 5**

@Embed[Compendium.my-cyphersystem.abilities.F1iNboBYijaAUHHq caption=false]{Specialized Basher}
@Embed[Compendium.my-cyphersystem.abilities.AzclaWBpaQFb8d5U caption=false]{Still As a Statue}

**Tier 6**

Choose one of:

@Embed[Compendium.my-cyphersystem.abilities.p1DQTNoGm3E3kWFz caption=false]{Ultra Enhancement}
@Embed[Compendium.my-cyphersystem.abilities.E9yXH0coCP6hji39 caption=false]{Mind Surge}

GM Intrusions: Creatures of stone sometimes forget their own strength or weight. A walking statue can terrify common folk.

The above provides a page that looks like: image

The default layout has the "cite" link AFTER the description which isn't ideal, but simply the following would put a link on the line before each embed.

@UUID[Compendium.my-cyphersystem.abilities.HUhcmzDU3zN63h9f]
@Embed[Compendium.my-cyphersystem.abilities.HUhcmzDU3zN63h9f caption=false cite=false]
@UUID[Compendium.my-cyphersystem.abilities.dCeQCLqfm0PQa2eu]
@Embed[Compendium.my-cyphersystem.abilities.dCeQCLqfm0PQa2eu caption=false cite=false]

image

farling42 commented 3 months ago

This layout is the specific reason that I wrote the "inline linktext" module in the first place :-)

mrkwnzl commented 3 months ago

Just for clarification, neither of the examples in the images can be done with vanilla V12?

farling42 commented 3 months ago

Vanilla V12 only supports using @Embed with Journal Entries and Roll Tables. They specifically don't include Actors or Items because those document types are always system-dependent (so core Foundry wouldn't know which field to use for the embedding).

mrkwnzl commented 3 months ago

All right! Thank you for this! I’ll take a closer look when I prepare the V12 update.

farling42 commented 2 months ago

New improved version to get closer to the printed books layout for Abilities:

en.json

  "CYPHERSYSTEM.Embed.ability.cost": "<p><strong>@UUID[{uuid}] ({cost} {pool}):</strong> {description}</p>",
  "CYPHERSYSTEM.Embed.ability.noCost": "<p>@UUID[{uuid}]: {description}</p>"

item.js

  async _buildEmbedHTML(config, options) {
    const embed = await super._buildEmbedHTML(config, options);
    if (embed) return embed;
    // As per foundry.js: JournalEntryPage#_embedTextPage
    options = { ...options, _embedDepth: options._embedDepth + 1, relativeTo: this };
    const {
      secrets=options.secrets,
      documents=options.documents,
      links=options.links,
      rolls=options.rolls,
      embeds=options.embeds
    } = config;
    foundry.utils.mergeObject(options, { secrets, documents, links, rolls, embeds });

    let toenrich;
    const key = "CYPHERSYSTEM.Embed." + this.type + (this.system.basic.cost && this.system.basic.cost != "0" ? ".cost" : ".noCost");
    if (game.i18n.has(key)) {
      toenrich = game.i18n.format(key, {
        uuid: this.uuid, 
        pool: this.system.basic.pool, 
        cost: this.system.basic.cost, 
        description: this.system.description.replace(/^<p>/,"").replace(/<\/p>$/,"")  // strip leading <p> and trailing </p>
      });
    } else {
      toenrich = this.system.description;
    }
    const container = document.createElement("div");
    container.innerHTML = await TextEditor.enrichHTML(toenrich);
    return container.children;
  }
mrkwnzl commented 1 month ago

Implemented in v3.0.0. Thanks, @farling42!