I need Foundry to pretty much be as good as our Roll20 environment across the board. Foundry Macros appear to be much more powerful and robust than those offered in Roll20...but we have a couple of years of patching over warts and automating our game system.
This posting focuses on my learning the Foundry macro ropes, largely indexing resources that I expect will make my journey easier.
FoundryVTT Macros 101
My first surge of understanding resulted from watching a YouTube video posted by spacemandev. The YouTube video is here: FoundryVTT Macros 101.
Spacemandev posted the five macros he intended to highlight in the video, though he only actually covered one (turns out things go slower when explaining or if you don't understand). The source code is on https://github.com/spacemandev-git/fvtt-tutorials.
His video uses Visual Studio as a development environment, which seemed really useful. That software is currently offered at a very reasonable price of free on code.visualstudio.com. I have grabbed and installed it on my apple silicon mac. I'll come back later and read their page on Getting Started.
Launching the Console
One key learning is how to open the console from the keyboard of a Mac: Option-Command-I (It's just F12 on a PC -- ok, PC implementation wins this one. Side note, the video gives the incorrect hotkey for the Mac.
The console can also be launched from the application menu (top of Mac screen), FoundryVTT, Toggle Developer Tools.
There is an amazing wealth of options in those tools. The console itself being the place the info/warning/error messages end up as well as informational bits created by commands like: console.log("Hello World").
Macros 102
Spacemandev posted a followup video that goes into how to roll results in a macro in another YouTube video: Macros 102.
In this video, he delves into his attackMacro.js. I've not watched the 50-minute long vid, yet, but I suspect it will be well worth the time.
How to Act on One or More Selected Tokens
(async () => {var tokens = canvas.tokens.controlled;if (tokens.length > 0 ){for (let token of tokens ){let actor = token.actor// some code which modifies the actor or token}} else {ui.notifications.error("No token selected.");}})();
As I look at that snippet, several equations pop into my non-JavaScript brain.
What's that async?
Apparently. the async more or less declares that this block can be executed asynchronously (out of order). When ordering is important, use that await prefix to force execution before the following code. That's trippy to me, I've never created code that could run out of order.
What's up with var and let?
They both look like variable declarations with the assigned initial values. I found a page that delves into these Javascript constructs on freecodecamp. I need to read that page. I think the net is that var is an old implementation, the snippet likely could have just used let.
Here is a summary of the three declarations from Love2Dev.
- var - has function level scoping and can change the value reference
- let - has block level scoping and can change the value reference
- const - has block level scoping but cannot change the value reference
What's that funky wrapper?
My watching of spacemandev's videos makes sense of the opening and closing lines of bizarre-looking characters. They are what seems to be one of two schools of formatting. Spacemandev advocates the following wrapper style"
main()
async function main(){
// Code goes here.
}
My Wrapper for the Task
main()
async function main() {let tokens = canvas.tokens.controlled;if (tokens.length > 0 ){for (let token of tokens ){let actor = token.actor// some code which modifies the actor or token}} else {ui.notifications.error("No token selected.");}}
I'll be trying that out in the not too distant future.
Some Handy Websites
- Foundry VTT Community Wiki - Variety of resources, including many links to other resources.
- Foundry API Documentation - Official (I think) docs on the API
Drink Health Potion Source Code
main()async function main(){console.log("Hello World")//Is a token selected? If not, errorconsole.log("Tokens: ", canvas.tokens.controlled)if(canvas.tokens.controlled.length == 0 || canvas.tokens.controlled.length > 1){ui.notifications.error("Please select a single token");return;}let actor = canvas.tokens.controlled[0].actor//Does the token have a health potion? Otherwise errorconsole.log("Actor: ", actor);let healthpotion = actor.items.find(item => item.data.name == "Health Potion")if(healthpotion == null || healthpotion == undefined){ui.notifications.error("No Health Potions left");return;}//If token is max health if so, don't do anythingif(actor.data.data.health.value == actor.data.data.health.max){ui.notifications.error("Actor already at max health");return;}//Subtract a health potionawait healthpotion.update({"data.quantity": healthpotion.data.data.quantity - 1})if(healthpotion.data.data.quantity < 1){healthpotion.delete();}//Increase token health//// New Health is going to be grater the max health////// If so, we want the new health to maxlet newHealth = actor.data.data.health.value + healthpotion.data.data.attributes.hp_restore.valueif(newHealth > actor.data.data.health.max){newHealth = actor.data.data.health.max}//update the actor healthawait actor.update({"data.health.value": newHealth});ui.notifications.info(`${actor.data.name} drank a health potion`)}
No comments:
Post a Comment