Skip to content
Urikslargda edited this page Jan 23, 2025 · 64 revisions

Use this page to share any cool macros you've created for NaN's GURPS 4th Ed. Game Aid for Foundry.

Clear all Active Effects

Type: Script

Description: Clears all effects on the selected token.

Note: Updated for Foundry v12+.

let effect = async function() {
  const effect = _token.actor.effects.contents;

  for (let i = 0; i < effect.length; i++) {
    console.log("debug", effect[i]);
    let condition = effect[i].name;
    let status = effect[i].disabled;
    let effect_id = effect[i]._id;

    console.log(`condition: [${condition}] status: [${status}] effect_id: [${effect_id}]`)
    if (status === false) {
        await _token.actor.deleteEmbeddedDocuments("ActiveEffect", [effect_id]);
    }
  }
}

effect();

Last Targeted Roll demo (and Margin of Success)

Type: Script

Description: Shows the use of the GURPS.lastTargetedRoll variable and how to get information such as margin of success from it.

let lastactor = GURPS.LastActor;
let data = GURPS.lastTargetedRolls[lastactor.id];
console.log(`check: [${data.thing}], modified target: [${data.finaltarget}], roll total: [${data.rtotal}], margin of success: [${data.margin}]`)

Mass Fright Check

Type: script

Description: Make a Fright Check for every selected token, using the current Modifier Bucket modifier. If there is a failure, set the token to have the "Stunned" status effect.

Notes: Updated to be correct. The original script set every token to "Stunned" if any token failed its Fright Check roll.

async function check() {
  // Add Rule of 14 modifier
  await GURPS.executeOTF('[+0 Rule of 14 *Max:13]')

  // save the list of modifiers (including Rule of 14)
  let modifiers = GURPS.ModifierBucket.modifierStack.modifierList

  // get a list of all selected tokens
  let selected = canvas.tokens.controlled

  // save the original actor
  const originalActor = GURPS.LastActor

  const failedTokens = []

  // for each token
  for (let token of selected) {
    // select the current token
    GURPS.SetLastActor(token.actor)

    // execute the Fright Check
    if (!(await GURPS.executeOTF(`[Fright Check]`))) failedTokens.push(token)

    // reset the modifiers
    for (let mod of modifiers) {
      GURPS.ModifierBucket.addModifier(mod.modint, mod.desc)
    }
  }

  console.log("failedTokens", failedTokens)
  failedTokens.forEach(async it => await GURPS.executeOTF(`[/st + stun :${it.id}]`))

  // clear the modifier bucket
  GURPS.ModifierBucket.clear()

  //restore last actor
  GURPS.SetLastActor(originalActor)
}

check()

Fireball Damage

Type: Script

Description: Prompts the user for how much energy he is putting into the Fireball, then generates and rolls the OTF for damage. For example, if the user enters '5' into the dialog, the following OTF is rolled: /r [5d burn *Costs 5FP].

function fireballDamage(energy) {
 GURPS.executeOTF(`/r [${energy}d burn *Costs ${energy}FP]`)
}

new Dialog({
  title: `Fireball Damage`,
  content: `<div><label for="energy">Energy</label><input id="energy" type="text" /></div>`,
  buttons: {
    confirm: {
      label: "Confirm",
      callback: async (html) => fireballDamage(html.find('#energy').val())
    }
  }
}).render(true);

Scale Lighting

Type: Script

Contributor: Iconat20 (Discord)

Description: It scales all the lights' dim and bright values by dividing them by three. Which is the factor needed when you change the grid size from 140 to 84 and grid units from 5ft to 1yd. Specific I know but it's useful when downloading pre-made foundry scenes from someone who made it for D&D.

canvas.lighting.updateAll(light => ({"config.bright": light.data.config.bright / 3, "config.dim": light.data.config.dim / 3 }))

Updated for V12:

canvas.lighting.updateAll(light => ({"config.bright": light.document.config.bright / 3, "config.dim": light.document.config.dim / 3 }))

Quick Combat

Type: Script

Contributor: danielrab (Discord)

Description: add selected tokens to the combat, roll initiative for each of them, then start the combat.

async function quickCombat() {
    await _token.toggleCombat();
    await game.combat.rollAll();
    await game.combat.startCombat();
}
quickCombat();

Manual Damage

Type: Script

Contributor: danielrab (Discord)

Description: opens a dialog that asks for damage value (for example 2d6+2) and damage type (for example cr) and rolls the damage.

function manualDamage(damage, type) {
  GURPS.executeOTF(`/r [${damage} ${type}]`);
}
new Dialog({
  title: `Manual Damage`,
  content: `<div><label for="damageAmount">damage</label><input id="damageAmount" type="text" /></div>
<div><label for="damageType">type</label><input id="damageType" type="text" /></div>`,
  buttons: {
    confirm: {
      label: "Confirm",
      callback: async (html) => {
        manualDamage(html.find('#damageAmount').val(), html.find('#damageType').val());
      }
    }
  }
}, {
  width: 400
}).render(true);

Quick-Draw and Shoot

Type: Script

Contributor: Nick Coffin, PI

Description: Roll for a Quick Draw and if successful, roll a ranged attack applying the current Modifier Bucket modifiers.

async function fastDrawAndShoot() {
  // Save the current list of modifiers.
  let modifiers = GURPS.ModifierBucket.modifierStack.modifierList

  // Clear the Modifier Bucket.
  GURPS.ModifierBucket.clear()

  // Execute the fast-draw portion and give the user feedback.
  await GURPS.executeOTF(`[S:Fast*Draw*Arrow ? "You rapidly draw an arrow and shoot!", "You fiddle with the bow for a second."]`)

  // Get success or failure of the Fast-Draw roll.
  let roll = GURPS.lastTargetedRolls[GURPS.LastActor.id]

  // If Fast-Draw roll is successful:
  if (!roll.failure) {
    
    // Restore the list of modifiers to the Modifier Bucket.
    for (let mod of modifiers) {
      GURPS.ModifierBucket.addModifier(mod.modint, mod.desc)
    }

    // Roll the attack.
    await GURPS.executeOTF(`[A:Longbow*]`)
  }
}

fastDrawAndShoot()

Toggle Reeling (or Exhausted)

Deprecated -- just select the tokens and run the appropriate chat command: /st t reeling or /st t exhausted.

Type: Script

Contributor: Nick Coffin, PI

Description: For all selected tokens, flip the status of Reeling -- i.e., if reeling is currently on, turn it off; if off, turn it on.

let effect = 'reeling' // change 'reeling' to 'exhausted' if needed

// for each selected token
for (let token of canvas.tokens.controlled) {
 let flag = token.actor.data.data.conditions[effect]

 // flip the effect
 token.actor.toggleEffectByName(effect, !flag)
}

Roll an Attack and Automatically Roll on Critical Hit Table

Type: Script

Contributor: Nick Coffin, PI (for Nose)

Description: Just what the title says.

const roll = async function () {
  // Attack with Great Axe
  await GURPS.executeOTF('A:Great*Axe')

  // if the attack roll was a Critical Success...
  console.log('LastTargetedRoll', GURPS.lastTargetedRoll)
  if (GURPS.lastTargetedRoll.isCritSuccess) {
    // Find a roll table with name 'Critical Hit'
    let tname = 'Critical Hit'
    let tables = game.tables.contents.filter(t => t.name.match(tname))

    console.log('tables', game.tables)
    console.log('crit table', tables)

    // if found, roll on the table and display to the chat
    if (tables.length == 1) {
      let table = tables[0]
      let r = await table.roll()
      table.draw(r)
    }
  }
}

roll()

Create and Toggle an Active Effect

Type: Script

Contributor: Nick Coffin, PI (for Nose)

Description: Demonstrates creating a new Active Effect and applying it to selected tokens. This example only adds effect modifiers. Toggling turns the effect on if it is currently off, or turning it off if currently on.

Notes: Needs to be updated for Foundry V12.

// get selected tokens
let tokens = canvas.tokens.controlled

// If any tokens are selected ...
if (tokens && tokens.length > 0) {

  // create a new effect
  let effect = {
      icon: 'systems/gurps/icons/statuses/number-3.webp',
      id: 'distraction',
      label: 'Distraction',
      changes: [
        {
          key: 'system.conditions.target.modifiers',
          value: '-3 for Distraction',
          mode: CONST.ACTIVE_EFFECT_MODES.ADD,
        },
      ],
      duration: {
        // If a non-zero duration, show the icon on the token.
        rounds: Number.MAX_VALUE
      },
  }

  // toggle the effect for all selected tokens
  for (let t of tokens) {
    const existing = t.actor.effects.contents.filter(e => e.name === effect.label)

    if (existing && existing.length) {
      console.log("★ delete effects")
      existing.forEach(async e => t.actor.deleteEmbeddedDocuments('ActiveEffect', [e.id]))
    } else { 
      console.log("✪ create effects")
      t.actor.createEmbeddedDocuments('ActiveEffect', [effect])
    }
  }
}

Bad Footing Active Effect

Type: Script

Contributor: devakm (based heavily on Nick's example above)

Notes: Foundry V12+ and GGA 17.17+

Description:

You can run this script on one or more selected tokens, or trigger it automatically using Monk's Active Tile Triggers when a player enters difficult terrain. A typical MATT config might look like this, where StatusBadFooting is the name of the macro:

image

The script applies an Active Effect with two effects: one with tag #hit so it applies -2 to attack rolls and one with tag #defense so it applies a penalty of -1 to defense rolls. Feel free to adjust these numbers for even worse footing scenarios.

/* Usage: /:StatusBadFooting 
*/
// setup: 
let effectName ='BadFooting'; //   effect name, i.e. [M:BadFooting]
let effectDescription = 'Bad Footing'; // Text description of the effect
let effectPenalty = -2;
let secondaryEffectPenalty = -1;

let tokens = canvas.tokens.controlled

// If any tokens are selected ...
if (tokens && tokens.length > 0) {
    // create a new effect
    let effect = {
        icon: `systems/gurps/icons/statuses/BAD${effectPenalty}.webp`,
        id: 'badfooting',
        label: `${effectName}`,
        changes: [
            {
                key: 'system.conditions.self.modifiers',
                value: `${effectPenalty} ${effectDescription} #hit`,
                mode: CONST.ACTIVE_EFFECT_MODES.ADD,
            },
            {
                key: 'system.conditions.self.modifiers',
                value: `${secondaryEffectPenalty} ${effectDescription} #defense`,
                mode: CONST.ACTIVE_EFFECT_MODES.ADD,
            },
        ],
        duration: {
            // If a non-zero duration, show the icon on the token.
            rounds: Number.MAX_VALUE
        },
    }

    // toggle the effect for all selected tokens
    for (let t of tokens) {
        const existing = t.actor.effects.contents.filter(e => e.name === effect.label)

        if (existing && existing.length) {
        console.log("★ delete effects")
        existing.forEach(async e => t.actor.deleteEmbeddedDocuments('ActiveEffect', [e.id]))
        } else { 
        console.log("✪ create effects")
        t.actor.createEmbeddedDocuments('ActiveEffect', [effect])
        }
    }

} else {
    console.log("No tokens selected");
    ui.notifications.warn("No tokens selected");
}

Spell Damage

Type: Script

Contributor: Nick Coffin, PI

Description: GURPS Magic (the standard system) usually handles damaging spells by making the dice of damage directly proportional to the amount of mana/energy that the mage puts into it -- Fireballs doing 1d burn per point of energy is an example. Import this macro, but don't call it directly -- instead create another macro or OTF to call this one with the necessary parameters filled in. Look below this macro's source code for examples of how to call it from OTF.

Calling the macro pops up a dialog asking the player where the energy should come from, FP or another source (in this case, a resource tracker named 'EnergyReserve'). To add a different source, find where 'Energy Reserve' and 'EnergyReserve' are used in the macro and change accordingly.

NEW VERSION: Since Foundry v11 updated the way macros work, this script had to be updated. The original version appears below this updated one.

// Usage: SpellDamage title=<title> dice=<dice> adds=<adds> type=<type> adjust=<adjust>
//   Title - name to display on dialog
//   Dice - dice of damage per energy point; may be a fraction
//   Adds - modifier to each die of damage, zero if none
//   Type - Damage type (cr, cut, burn, etc...)
//   Adjust - Adjust the energy cost by this number
// Examples:
//   title=Fireball dice=1 adds=1 type=burn -- 1d+1 burn damage per energy point
//   title=Concussion dice=0.5 type=cr adjust=-1 -- 1d cr per 2 points of energy; reduce total cost by 1

console.log(scope)
let title = scope.title
let dice = scope.dice ? parseFloat(scope.dice) : undefined
let adds = scope.adds ? parseInt(scope.adds) : 0
let type = scope.type ?? 'dmg'
let adjust = scope.adjust ? parseInt(scope.adjust) : 0
console.log(`title:${title}; dice:${dice}; adds:${adds}; type:${type}; adjust:${adjust}`)

function damage(energy, source) {
 let limit = 0 - energy; // cost cannot be less than 0
 if (adjust < 0 && adjust < limit ) adjust = 0 - energy;
 let cost = `${energy + adjust}FP`
 if (source === 'HP') cost = `${energy + adjust}HP`
 if (source === 'Energy Reserve') cost = `${energy + adjust} tr(EnergyReserve)`

 let d = dice * energy
 let a = adds * energy
 if (a === 0) a = ''
 else if (a > 0) a = `+${a}`

 let formula = `[${d}d${a} ${type} *Costs ${cost}]`
 console.log(formula)
 GURPS.executeOTF(`/r ` + formula)
}

new Dialog({
 title: title,
 content: `
<div style='padding: 4px;'>
 <label for="energy">Energy</label>
 <input id="energy" type="text" />
</div>
<div style='padding: 4px;'>
 <label for="source">Source</label>
 <select id="source">
  <option value='Energy Reserve'>Energy&nbsp;Reserve</option>
  <option value='FP'>FP</option>
  <option value='HP' style='color: darkred;'>HP</option>
 </select>
</div>
`,
 buttons: {
  confirm: {
   label: "Confirm",
   callback: async (html) => damage(
      parseInt(html.find('#energy').val()), 
      html.find('#source').val())
  }
 }
}).render(true)

Example of use:

["Fireball Damage" /:SpellDamage title=Fireball dice=1 type=burn adjust=-1]
["Concussion Damage" /:SpellDamage title=Concussion dice=0.5 type=cr]

The first one creates an OTF button that does 1d burn per point of energy, and costs the caster 1 energy point less -- i.e., a 1d burn fireball would not subtract any energy from the caster's reserves, while a 2d burn would cost the caster 1 point.

The second creates an OTF button that does 1d cr damage per 2 points of energy.

=============

ORIGINAL Version of the Spell Damage macro. Does not work with later Foundry v11 updates. This version of the macro is intended to be used with the Advanced Macros module in Foundry to generically handle passing arguments to script macros.

// Usage: SpellDamage <title> <dice> <adds> <type> <adjust>
//   Title - name to display on dialog
//   Dice - dice of damage per energy point; may be a fraction
//   Adds - modifier to each die of damage, zero if none
//   Type - Damage type (cr, cut, burn, etc...)
//   Adjust - Adjust the energy cost by this number
// Examples:
//   Fireball 1 1 burn 0 -- 1d+1 burn damage per energy point
//   Concussion 0.5 0 cr -1 -- 1d cr per 2 points of energy; reduce total cost by 1

console.log(args[0])
let title = args[0][1]
let dice = parseFloat(args[0][2])
let adds = args[0].length > 3 ? parseInt(args[0][3]) : 0
let type = args[0].length > 4 ? args[0][4] : 'dmg'
let adjust = args[0].length > 5 ? parseInt(args[0][5]) : 0
console.log(`title:${title}; dice:${dice}; adds:${adds}; type:${type}; adjust:${adjust}`)

function damage(energy, source) {
 let limit = 0 - energy; // cost cannot be less than 0
 if (adjust < 0 && adjust < limit ) adjust = 0 - energy;
 let cost = `${energy + adjust}FP`
 if (source === 'HP') cost = `${energy + adjust}HP`
 if (source === 'Energy Reserve') cost = `${energy + adjust} tr(EnergyReserve)`

 let d = dice * energy
 let a = adds * energy
 if (a === 0) a = ''
 else if (a > 0) a = `+${a}`

 let formula = `[${d}d${a} ${type} *Costs ${cost}]`
 console.log(formula)
 GURPS.executeOTF(`/r ` + formula)
}

new Dialog({
 title: title,
 content: `
<div style='padding: 4px;'>
 <label for="energy">Energy</label>
 <input id="energy" type="text" />
</div>
<div style='padding: 4px;'>
 <label for="source">Source</label>
 <select id="source">
  <option value='Energy Reserve'>Energy&nbsp;Reserve</option>
  <option value='FP'>FP</option>
  <option value='HP' style='color: darkred;'>HP</option>
 </select>
</div>
`,
 buttons: {
  confirm: {
   label: "Confirm",
   callback: async (html) => damage(
      parseInt(html.find('#energy').val()), 
      html.find('#source').val())
  }
 }
}).render(true)

Example of use:

["Damage" /:SpellDamage Fireball 1 0 burn -1]
["Damage" /:SpellDamage Concussion 0.5 0 cr]

The first one creates an OTF button that does 1d burn per point of energy, and costs the caster 1 energy point less -- i.e., a 1d burn fireball would not subtract any energy from the caster's reserves, while a 2d burn would cost the caster 1 point.

The second creates an OTF button that does 1d cr damage per 2 points of energy.

Major Healing

Type: Script

Contributor: devakm

OtF to trigger it: ["Major Healing"/:majorHealing]

This takes inspiration from SpellDamage to let you specify the energy source. It also handles crits, which don't cost any energy, and gives a bonus on the heal when you crit. You can easily alter the crit bonus formula by editing critHeal. You can change which animation to play by altering anim, or remove the animation altogether by changing anim=''.

// Usage: /:majorHealing

function majorHealing(energy, source) {
	let cost = `/fp -${energy}`;
	if (source === 'HP') cost = `/hp -${energy}`;
	if (source === 'EnergyReserve') cost = `/tr(EnergyReserve) -${energy}`;
	let heal=energy*2;
	let critHeal=(energy*2)+energy;
	let anim = '\\\\!/anim HealingAbility*Blue c *0.2';
	let OtF = '[/if [Sp:"Major Healing"] cs:{/hp +'+critHeal+' @target '+anim+'} s:{/hp +'+heal+' @target '+anim+' \\\\'+cost+' }]'; 
	console.log(OtF);
	GURPS.executeOTF(OtF);
 }
 
new Dialog({
 title: `Major Healing`,
 content: `
<div style='padding: 4px;'>
 <label for="energy">Energy</label>
 <input id="energy" type="text" />
</div>
<div style='padding: 4px;'>
 <label for="source">Source</label>
 <select id="source">
  <option value='FP'>FP</option>
  <option value='EnergyReserve'>Energy&nbsp;Reserve</option>
  <option value='HP' style='color: darkred;'>HP</option>
 </select>
</div>
`,
 buttons: {
  confirm: {
   label: "Confirm",
   callback: async (html) => majorHealing(
      parseInt(html.find('#energy').val()), 
      html.find('#source').val())
  }
 }
}).render(true)

Missile Spell Handler Template

Type: Script

Requirements: Foundry V12 and GGA 17.17+

Contributor: devakm

The objective for this macro is to provide a template for reducing complex OtF commands into easy-to-use macros, such as /:fireball This example uses the SpellDamage macro (from this wiki page) to calculate attack damage, so be sure you also have that macro installed.

OtF to trigger it: ["Fireball"/:fireball]

/* Usage: /:fireball
 * copy this macro and rename it to match whatever missile spell you want to cast, 
 * then reference it in an OtF like this: [/:spellname]
 * Fill in the values below to match the name of the spell, the ranged attack, the animations, and (optionally) outcome formulas.
 * The example success formulas use the SpellDamage macro
*/

// OtF action setup:
// change the next two lines to match your spell
let SpellName ='Fireball'; //  Spell Name, i.e. [Sp:Fireball]
let RangedAttackName ='Fireball'; //  Ranged Attack Name, i.e. [R:Fireball]
// change next two lines to match your critical result tables
let critHitTable = 'CritHit'; // name of the rolltable for critical hits; set to space ' ' to disable 
let critMissTable = 'CritMiss'; // name of the rolltable for critical misses; set to space ' ' to disable 

// Outcomes animations for critical success, critical failure, regular success, and regular failure
// each of these can be tested individually as a chat command. Requires JB2A.
// outcome animations:
let sanim = '!/anim FireBolt*Regular_Orange*15ft -0.2 +0.6'; // success animation 
let csanim = '!/anim FireBolt*Regular_Orange*15ft -0.2 +0.6 \\\\!/wait 500 \\\\!/anim FireballExplosion*Orange c *0.2'; // critical success animation 
let fanim = '!/anim FireBolt*Regular_Orange*15ft -0.2 -3'; // failure animation 
let cfanim = '!/anim FireballExplosion*Orange c *0.1 @self'; // critical failure animation 

// Outcome formulas for critical success, critical failure, regular success, and regular failure
// outcome formulas:
let csformula ='/:SpellDamage title=Fireball dice=1 adds=+2 type=burn adjust=-4'; // critical success formula 
let cfformula ='/hp -1d-3!'; // critical failure formula 
let sformula ='/:SpellDamage title=Fireball dice=1 adds=0 type=burn adjust=0'; //success formula 
let fformula ='/fp -1'; // failure formula 

if (critHitTable.length > 1) {
    csformula += ' \\\\/rolltable '+critHitTable;
}
if (critMissTable.length > 1) {
    cfformula += ' \\\\/rolltable '+critMissTable;
}
// OtF rolls for spell and ranged attack
let PrimaryCheck = '[Sp:'+SpellName+']'; // alter the OtF type used for primary check
let SecondaryCheck = '[R:'+RangedAttackName+']'; // alter the OtF type used for secondary check

// generate OtF string
let OtF = '[/if '+PrimaryCheck+' cs:{/if '+SecondaryCheck+' {'+csanim+' \\\\'+csformula+' }} cf:{'+cfanim+' \\\\'+cfformula+'} s:{/if '+SecondaryCheck+' {'+sanim+' \\\\'+sformula+'}} f:{'+fanim+' \\\\'+fformula+'} ]'; 
console.log(OtF);
GURPS.executeOTF(OtF);

Melee Spell Strike Handler Template

Type: Script

Requirements: Foundry V12 and GGA 17.17+

Contributor: devakm

This is a variant of the Spell Handler Template designed to handle melee attacks with a Wizard's Staff or similar attack combined with Burning Touch, Shocking Touch, Deathtouch, etc. Modify as needed.

OtF to trigger it: ["Burning Smash"/:BurningSmash]

/* Usage: /:BurningSmash
 * Copy this macro and rename it to match whatever melee spell and attack combo you want to use, 
 * then reference it in an OtF like this: ["Burning Smash"/:BurningSmash]
 * Fill in the values below to match the name of the spell, the melee attack, the animations, and (optionally) outcome formulas.
 * The example success formulas roll melee strike damage first and then use the SpellDamage macro.
*/

// OtF action setup:
// change the next two lines to match your spell and meleee attack
let SpellName ='Burning*Touch'; //  Spell Name, i.e. [Sp:Burning*Touch]
let AttackName ='Wizard*Swung'; //  Attack Name, i.e. [R:Burning*Touch]
// change next two lines to match your critical result tables
let critHitTable = 'CritHit'; // name of the rolltable for critical hits; set to space ' ' to disable 
let critMissTable = 'CritMiss'; // name of the rolltable for critical misses; set to space ' ' to disable 

// Outcomes for critical success, critical failure, regular success, and regular failure
// each of these settings can be tested individually as an OtF or chat command.
// outcome animations:
let sanim = '!/anim Quarterstaff03*Regular_Purple *0.3 -0.2 +1 \\\\!/wait 1500 \\\\!/anim Impact_01_Regular_Yellow c *0.7 \\\\!/anim Flames_01_Regular_Orange c *0.5'; // success animation 
let csanim = '!/anim Quarterstaff03*Regular_Purple *0.3 -0.2 +1 \\\\!/wait 1500 \\\\!/anim Impact_01_Regular_Yellow c \\\\!/anim Flames_01_Regular_Orange c *0.8'; // critical success animation 
let fanim = '!/anim Quarterstaff03*Regular_Purple *0.3 -0.2 -0.1'; // failure animation 
let cfanim = '!/anim Impact_01_Regular_Yellow c *0.8 @self \\\\!/anim Flames_01_Regular_Orange c *0.4 @self'; // critical failure animation 

// OtF rolls for spell and melee attack
let PrimaryCheck = '[Sp:'+SpellName+']'; // alter the OtF type used for primary check
let SecondaryCheck = '[M:'+AttackName+']'; // alter the OtF type used for secondary check
let StrikeDamage = '/r [D:'+AttackName+']'; // strike damage OtF. If this penetrates DR, then apply SpellDamage
// outcome formulas:
let csformula = StrikeDamage+' \\\\/:SpellDamage title=BurningTouch dice=1 adds=+2 type=burn adjust=-8'; // critical success formula 
let cfformula ='/hp -1d-3!'; // critical failure formula 
let sformula = StrikeDamage+' \\\\/:SpellDamage title=BurningTouch dice=1 adds=0 type=burn adjust=0'; //success formula 
let fformula ='/fp -1'; // failure formula 

// add critHitTable and critMissTable to the critical success and critical failure formulas if they are not empty
if (critHitTable.length > 1) {
    csformula += ' \\\\/rolltable '+critHitTable;
}
if (critMissTable.length > 1) {
    cfformula += ' \\\\/rolltable '+critMissTable;
}
// generate OtF string
let OtF = '[/if '+PrimaryCheck+' cs:{/if '+SecondaryCheck+' {'+csanim+' \\\\'+csformula+' }} cf:{'+cfanim+' \\\\'+cfformula+'} s:{/if '+SecondaryCheck+' {'+sanim+' \\\\'+sformula+'}} f:{'+fanim+' \\\\'+fformula+'} ]'; 
console.log(OtF);
GURPS.executeOTF(OtF);

Alternate Form

Type: Script

Contributor: LordHelmet (Discord)

Description: Replaces a selected Token completely with a different Token, from different Actors, while playing an animation. Suitable for characters with Alternate Form, such as werewolves or superheroes who transform from Normals to Supers (such as DC's Shazam).

const {x,y} = token.document;

// in the quotes the path for the animation to be played, easy to get via Sequencer Database
// const file = "modules/jb2a_patreon/Library/Generic/Particles/ParticlesSwirl01_01_Regular_Blue_400x400.webm";

// in the quotes the ID of the actor that should be there at the end of the transformation.
// get it in the console via "game.actors.getName('name of the actor').id
const tokenData = await game.actors.get("ID of the actor").getTokenDocument({x,y});

await new Sequence()
    .effect()
        .file("path for the first animation")
        .atLocation(token)
        .scale(0.3, 0.3)
        .playbackRate(2)
        .waitUntilFinished(-1000)
    .effect()
        .file("path for the second animation")
        .atLocation(token)
        .scale(0.6, 0.6)
        .scaleIn(0.2, 2000)
        .randomRotation()
        .fadeIn(2000)
        .scaleOut(3, 1500, { ease: "easeInExpo", delay: -400})
        .fadeOut(1500)
    .wait(2000)
    
    .thenDo(async function(){
        await canvas.scene.createEmbeddedDocuments("Token", [tokenData]);
        token.document.delete();
    })

    .play()

Quick Contest between a single Actor and any number of other actors.

Type: Script

Contributor: Nick Coffin, PI

Requires one actor selected -- this is the attacker -- and any number of other tokens "targeted".

The attacker rolls a Will check; then each other targeted token rolls a Will check and the results are interpreted as a Quick Contest. The margin of success of the attacker is modified by the range in yards between the attacker and a target.

async function quickContest() {
  // Find the last actor ... assume this is the originator of the effect.
  const original = GURPS.LastActor

  // Quit if no last actor.
  if (!original) {
    ui.notifications.warn('No actor selected!')
    return
  }

  // Find the list of targeted tokens.
  const targets = Array.from(game.user.targets)

  // Quit if no targets.
  if (targets.length === 0) {
    ui.notifications.warn('No targets selected!')
    return
  }

  // Get the selected token... this assumes that the controlled (selected) token is the
  // token for the GURPS.LastActor.
  const selected = canvas.tokens.controlled[0]

  // Perform Will check on original actor and get the roll result and margin of success.
  await GURPS.executeOTF(`[Will]`)
  const margin = GURPS.lastTargetedRoll.margin

  // For each target...
  for (const target of targets) {
    // Get the actor of the target.
    const actor = target.actor

    if (actor) {
      // Get the distance between this token and the original.
      const distance = Math.floor(canvas.grid.measureDistance(selected.center, target.center) + 0.5)

      // Set effective target number to margin - distance.
      const targetNumber = margin - distance

      console.log(`Actor name: ${actor.name}, distance ${distance}`)

      GURPS.LastActor = actor
      await GURPS.executeOTF(`[Will]`)
      const thisMargin = GURPS.lastTargetedRoll.margin

      if (thisMargin >= margin) {
        // Defender wins.
      } else {
        // Originator wins. Do something bad to the actor.
        await GURPS.executeOTF(`/fp -2`)
      }
    }
  }

  // Reset Last Actor
  GURPS.LastActor = original
}

quickContest()

Open/Close Helmet Visor

Type: Script

Contributor: devakm

Notes: Foundry V12+

If you use the GURPS Arc of Vision rules (B74), you can roughly approximate this in Foundry as sight angle 240. This prevents that token from seeing anything in the blue hexes here:

image

Those who wear helmets with full-face protection such as a Greathelm, however, suffer the No Peripheral Vision disadvantage (B151), which means they also can't see the melee side red hexes (Example A). This is roughly sight angle 120 in Foundry.

If the helm has a visor, or you take it off and need to put it back on again for battle, then changing the angle setting on those tokens becomes very tedious. This script toggles between the two modes: 240 and 120.

let tokenId = _token.id;
let token = canvas.tokens.get(tokenId); // Get the token object
let tokenDoc = await token.document; // wake up lazy document
if (token.document.sight.angle === 120) {
    console.log(`Visor is closed; opening it; face unprotected`);
    await tokenDoc.update({"sight.angle": 240});
} else {
    console.log(`Visor is open; closing it; face protected`);
    await tokenDoc.update({"sight.angle": 120});
}
Clone this wiki locally