Roll20 uses cookies to improve your experience on our site. Cookies enable you to enjoy certain features, social sharing functionality, and tailor message and display ads to your interests on our site and others. They also help us understand how our site is being used. By continuing to use our site, you consent to our use of cookies. Update your cookie preferences .
×
Create a free account

[Script] Pathfinder Actions Chat Menu generator

May 18 (5 years ago)

Edited May 23 (5 years ago)
GiGs
Pro
Sheet Author
API Scripter

Pathfinder Action Menu.

For the Pathfinder by Roll20 Sheet.

This script generates a chat menu for a character of attacks, spells, spell-like abilities, or any combination of them, and whispers that chat menu so it doesnt clutter up the chat. You must be using the Pathfinder by Roll20 sheet (haven't tested with others, but i expect it won't work).

It can be used by players and GMs. 

Here's a good way to call it:

!pfactionmenu @{selected|character_id} ?{Menu Type?|Attacks|Spells|Spell-like|All}

You can also use any of the following to launch it:

!pfactionmenu @{selected|character_id} All
!pfactionmenu @{selected|character_id} Attacks Spell-like
!pfactionmenu @{selected|character_id} Spells
!pfactionmenu @{selected|character_id} Spell-like Spells

The chat menu will leave out any sections you dont have, so if you select All, but dont have any spell-like abilities, or spells above 4th level, those sections won't be printed.


Edit: Update

Sheet now has the option to show only prepared spells. Add the word 'prepared' anywhere after character_id, like so:

!pfactionmenu @{selected|character_id} Attacks Spells prepared
!pfactionmenu @{selected|character_id} prepared Spells Spell-like
!pfactionmenu @{selected|character_id} prepared Spells

It uses the pf rolltemplate and roll20's standard API menu buttoms, so its output looks like this



Paste the following into your scripts page:

/*
Pathfinder
!pfactionmenu @{selected|character_id} ?{Menu Type?|Attacks|Spells|Spell-like|All}
!pfactionmenu @{target|character_id} Attacks
!pfactionmenu @{name|character_id} Spells
!pfactionmenu @{selected|character_id} Attacks Spell-like
!pfactionmenu @{selected|character_id} All

if you want to show only prepared spells, add 'prepared' anywhere after character_id, like so:
!pfactionmenu @{selected|character_id} Attacks Spell-like prepared
!pfactionmenu @{selected|character_id} Attacks prepared Spell-like


*/

on('ready',function(){
    "use strict";
    const ch = function (c) {
        var entities = {
			'@' : '#64',
			'{' : '#123',
			'|' : '#124',
			'}' : '#125',
			'#' : '#13'
		};

		if(_.has(entities,c) ){
			return ('&'+entities[c]+';');
		}
		return '';
	};
	const field = (section, id, field) => `repeating_${section}_${id}_${field}`;
	const repIDs = (cid, section, display) => {
	    const repeating = findObjs({                              
            _type: "attribute",
            _characterid: cid
        })
        .filter((obj)=> {
            return obj.get('name').startsWith('repeating_' + section) && obj.get('name').endsWith(display); 
        })
        .map(obj => obj.get('name').replace(`repeating_${section}_`,'').replace('_' + display,'').trim());
        return repeating;
	};
	const getPrepared = (cid, section, spellid, suffix = '_spellprepared') => {
	    const found = findObjs({                              
            _type: "attribute",
            _characterid: cid
        })
        .filter((obj)=> {
            return obj.get('name').startsWith(`repeating_${section}_${spellid}`) && obj.get('name').endsWith(suffix); 
        })[0];
        let prepared = 0;
        if(found) {
            prepared = found.get('current');
        }
        return prepared;
	};
    on('chat:message',function(msg){
        if('api' === msg.type && msg.content.startsWith('!pfactionmenu') ){
            let args = msg.content.split(/\s+/);;
            const parameters = {
                'attacks': {section: 'attacks', display: 'atkname', macro: 'rollbase', title: 'Attacks'},
                'spell-like': {section: 'spell-like', display: 'spelldisplay', macro: 'rollcontent', title: 'Spell-Like Abilities'},
                'spell-0': {section: 'spell-0', display: 'spelldisplay', macro: 'rollcontent', title: 'Level 0 Spells'},
                'spell-1': {section: 'spell-1', display: 'spelldisplay', macro: 'rollcontent', title: 'Level 1 Spells'},
                'spell-2': {section: 'spell-2', display: 'spelldisplay', macro: 'rollcontent', title: 'Level 2 Spells'},
                'spell-3': {section: 'spell-3', display: 'spelldisplay', macro: 'rollcontent', title: 'Level 3 Spells'},
                'spell-4': {section: 'spell-4', display: 'spelldisplay', macro: 'rollcontent', title: 'Level 4 Spells'},
                'spell-5': {section: 'spell-5', display: 'spelldisplay', macro: 'rollcontent', title: 'Level 5 Spells'},
                'spell-6': {section: 'spell-6', display: 'spelldisplay', macro: 'rollcontent', title: 'Level 6 Spells'},
                'spell-7': {section: 'spell-7', display: 'spelldisplay', macro: 'rollcontent', title: 'Level 7 Spells'},
                'spell-8': {section: 'spell-8', display: 'spelldisplay', macro: 'rollcontent', title: 'Level 8 Spells'},
                'spell-9': {section: 'spell-9', display: 'spelldisplay', macro: 'rollcontent', title: 'Level 9 Spells'},
            }
            const cid = args[1] || 'N/A';
            const who = getAttrByName(cid,'character_name');
            if(cid === 'N/A' || who === undefined) {
                sendChat("Action Menu","You need to include a valid character ID.");
                return;
            }

            let controllers = getObj("character", cid).get('controlledby');
            if(controllers !== '') controllers = controllers.split(',');
            
            
            // build a list of repeating sections we want to use
            const allowed = ['all', 'attacks', 'spells', 'spell-like', 'prepared'];
            let sections = []; // sections is the list of repeating sections this menu will be built from
            let showPrepOnly = 0;
            
            for(let i = 2; i < args.length; i++) {
                if(allowed.includes(args[i].toLowerCase())) {
                    if(args[i].toLowerCase() === 'all' || args[i].toLowerCase() === 'attacks' ) {
                        sections.push('attacks');
                    }
                    if(args[i].toLowerCase() === 'all' || args[i].toLowerCase() === 'spell-like' ) {
                        sections.push('spell-like');
                    }
                    if(args[i].toLowerCase() === 'all' || args[i].toLowerCase() === 'spells' ) {
                        sections.push('spell-0', 'spell-1', 'spell-2', 'spell-3', 'spell-4', 'spell-5', 'spell-6', 'spell-7', 'spell-8', 'spell-9');
                    }
                    if(args[i].toLowerCase() === 'prepared') {
                        showPrepOnly = 1;
                    }
                }
            }
            if(sections.length === 0) {
                sendChat("Action Menu","Type not recognised. Enter some combination of Attacks, Spells, Spell-like, or All.");
                return;
            }
            log("prep: " + showPrepOnly);
            
            // now we have an array of every section on the sheet we can build the menu.
            let menuname = (args.length === 3 && args[2].toLowerCase() !== 'all') ? args[2].toUpperCase() : 'ACTIONS';
            let icon = (sections.length ===1 && sections[0] === 'attacks') ? 'attack' : 
                (sections.length > 1 && sections.includes('attacks')) ? 'ability' : 'spell';
            let output = `&{template:pc} {{showchar=[[1]]}} {{charname=${who}}} {{type=${icon}}} {{smallname=${menuname} MENU}} {{descflag=1}} {{desc=`;
            
            sections.forEach(s => {
                let repeating = repIDs(cid, parameters[s].section, parameters[s].display);
                let founditems = [];
                if(repeating.length > 0) {
                    repeating.forEach(id => {
                        let print = true;
                        if(showPrepOnly && s.startsWith('spell-') && !s.endsWith('-like')) {
                            // check if spell is prepared
                            let found = getPrepared(cid, s, id) *1||0;
                            if(found === 0) print = false;
                        }
                        if(print) founditems.push(id);
                    });
                    if(founditems.length > 0) {
                        if(menuname !== 'ATTACKS' && menuname !== 'SPELL-LIKE') output += `**${parameters[s].title}**\n`;
                        founditems.forEach(id => {
                            output += `[@{${who}|${field(parameters[s].section,id,parameters[s].display)}}]`;
                            output += `(!${ch('#')}${ch('@')}{${who}|${field(parameters[s].section,id,parameters[s].macro)}}) `;
                        })                        
                        output += `\n`;
                    }
                    
                }
            });
            output += `}}`;
            
            // time to print it out, after sorting out who needs to see it.
            const sender = 'character|' + cid;
            const caller = (getObj('player',msg.playerid)||{get:()=>'API'}).get('_displayname');
            sendChat(sender,'/w ' + caller + ' ' + output);
        };
    });
});
May 18 (5 years ago)
vÍnce
Pro
Sheet Author

Very cool addition for the PF by Roll20 sheet GiGs. Should get lot's of use for this.

FYI: The PF Community sheet has chat menus(attacks, skills, spells, abilities, etc.) built into the sheet.

May 18 (5 years ago)
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator

Nice GiGs, you do quick work.

May 18 (5 years ago)
GiGs
Pro
Sheet Author
API Scripter

Thanks, both of you.

Vince, that's interesting about the community sheet. While writing this, it did occur to me that it would be pretty easy for sheet designers to include such a feature and wondered if any had. 

Turned out incredible, GiGs! Thanks again for building this for me!

May 18 (5 years ago)
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator

GiGs said:

Thanks, both of you.

Vince, that's interesting about the community sheet. While writing this, it did occur to me that it would be pretty easy for sheet designers to include such a feature and wondered if any had. 

Yep, and similarly the Starfinder by Roll20 and 5e Shaped sheets also have chat menu macros.

May 18 (5 years ago)
GiGs
Pro
Sheet Author
API Scripter

You're welcome, Seph. Thanks for giving me the idea!

May 23 (5 years ago)
vÍnce
Pro
Sheet Author
How hard would it be to add an option to only show the prepared spells? ;-)
May 23 (5 years ago)
GiGs
Pro
Sheet Author
API Scripter

I'm not familiar with the pathfinder sheet, actually. If it uses a checkbox or some other way to mark each spell as prepared or not, on that spell's row, it should be pretty straightforward. I'll check it out later.

May 23 (5 years ago)
GiGs
Pro
Sheet Author
API Scripter

Just updated to only show prepared spells, if you add the word 'prepared' to the script command.

May 23 (5 years ago)
vÍnce
Pro
Sheet Author


GiGs said:

Just updated to only show prepared spells, if you add the word 'prepared' to the script command.

I think that will make a lot of spell caster's very happy.  Thanks GiGs


May 23 (5 years ago)
Natha
KS Backer
Sheet Author
API Scripter

Great work, Gigs!

May 23 (5 years ago)
GiGs
Pro
Sheet Author
API Scripter

Thanks, Natha!

June 28 (5 years ago)
Marc
Pro

This Script is amazing, Thanks GiGs!

I have a question if I may;

is it possible to adjust the script so when a prepared spell is cast it removes one from the prepared amount? so, for example, when a caster uses all 3 of their prepared magic missiles it stops showing as a prepared spell?


that would be a great help.

June 30 (5 years ago)
GiGs
Pro
Sheet Author
API Scripter

I'm glad you like it. I think it is be possible - IIRC I added the feature to my other menu script. I probably wont be able to work on this one for a week or so (probably), so it might be worth checking out my other menu script to see if that does the job in the meantime: https://app.roll20.net/forum/permalink/7474530/ . It takes a little more setup, but is more powerful.