Skip to content
Volksters edited this page Aug 20, 2023 · 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.

let effect = async function() {
  const effect = _token.actor.effects.contents;
  for (let i = 0; i < effect.length; i++) {
    let condition = effect[i].data.label;
    let status = effect[i].data.disabled;
    let effect_id = effect[i].data._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.

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

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

  // execute the Fright Check
  await GURPS.executeOTF(`[/if ![Fright Check] [/st + stun]]`)

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

 // 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 }))

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.

// Attack with Rapier
GURPS.executeOTF('A:Rapier')

// if the attack roll was a Critical Success...
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))

  // 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)
  }
}

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.

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

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: 'data.conditions.target.modifiers',
    value: '-3 for Distraction',
    mode: 2 // ADD
   }]
 }

 // toggle the effect for all selected tokens
 for (let token of tokens) {
  token.toggleEffect(effect)
 }
}

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. This macro is intended to be used with the Advanced Macros module in Foundry to generically handle this type of spell. 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.

// 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)

Spell Handler Template

Type: Script

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

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

/* Usage: /:fireball
 * copy this macro and rename it to match other spells, then reference it in an OtF like this: [/:spellname]
 * then fill in the values below to match the name of the spell, the ranged attack, the animations, and outcomes.
 * the example success formulas use the SpellDamage macro
*/

// OtF action setup:
let SpellName ='Fireball'; //  Spell Name, i.e. [Sp:Fireball]
let RangedAttackName ='Fireball'; //  Ranged Attack Name, i.e. [R:Fireball]
let PrimaryCheck = '[Sp:'+SpellName+']'; // alter the OtF type used for primary check
let SecondaryCheck = '[R:'+RangedAttackName+']'; // alter the OtF type used for secondary check

// 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 FireBolt*Regular_Orange -0.3 +1.5'; // success animation 
let csanim = '!/anim FireBolt*Regular_Orange -0.3 +1.5'; // critical success animation 
let fanim = '!/anim FireBolt*Regular_Orange -0.3 -3'; // failure animation 
let cfanim = '!/anim FireballExplosion*Orange c *0.1 @self'; // critical failure animation 
// outcome formulas:
let csformula ='/:SpellDamage Fireball 1 +2 burn -4'; // critical success formula 
let cfformula ='/hp -1d-3!'; // critical failure formula 
let sformula ='/:SpellDamage Fireball 1 0 burn 0'; //success formula 
let fformula ='/fp -1'; // failure formula 

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

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 other spell attacks, then reference it in an OtF like this: [/:spellname]
 * Fill in the values below to match the name of the spell, the melee attack, the animations, and outcomes.
 * the example success formulas roll melee strike damage first and then use the SpellDamage macro
*/

// OtF action setup:
let SpellName ='Burning*Touch'; //  Spell Name, i.e. [Sp:Burning*Touch]
let AttackName ='Wizard*Swung'; //  Attack Name, i.e. [R:Burning*Touch]
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

// 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 
// outcome formulas:
let csformula = StrikeDamage+' \\\\/:SpellDamage BurningTouch 1 +2 burn -8'; // critical success formula 
let cfformula ='/hp -1d-3!'; // critical failure formula 
let sformula = StrikeDamage+' \\\\/:SpellDamage BurningTouch 1 0 burn 0'; //success formula 
let fformula ='/fp -1'; // failure formula 

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()