I used ai to write a script to put all arcane and divine spells into chat buttons for easy use without having to open the character sheet. This is especially useful for GMs that are managing multiple spell casting monsters in a combat. The script only lists the spells favored in the abilities section of the Mythras sheet.
Command
What it does
!cfspells
Lists all
Arcane & Divine spells as clickable buttons (select token first)
!cfcast
SpellName
Shows full
spell description, traits and rank in chat
!cfspells-debug
Lists all
repeating field names (troubleshooting)
!cfspells-row
Dumps a
spell row's raw data (troubleshooting)
Here is the script: var CFS={};
on('chat:message',function(msg){
if(msg.type!=='api')return;
var d=msg.content.startsWith('!cfspells-debug');
var r1=msg.content.startsWith('!cfspells-row');
var sc=msg.content.startsWith('!cfcast ');
if(!msg.content.startsWith('!cfspells')&&!sc)return;
var cid,tk,ch,cn,aa,rw={},sp=[],ar=[],dv=[],gn=[],out;
if(sc){
var sid=msg.content.replace('!cfcast ','').trim();
var s=CFS[sid];
if(!s){sendChat('CFS','/w gm Spell not found. Run !cfspells first.');return;}
out='&{template:default} {{name='+s.name+'}}';
if(s.rank)out+=' {{Rank='+s.rank+'}}';
if(s.traits)out+=' {{Traits='+s.traits+'}}';
if(s.desc)out+=' {{notes='+s.desc+'}}';
sendChat(msg.who,out);
return;
}
if(msg.selected&&msg.selected.length){tk=getObj('graphic',msg.selected[0]._id);if(tk)cid=tk.get('represents');}
if(!cid){sendChat('CFS','/w gm No token.');return;}
ch=getObj('character',cid);
if(!ch){sendChat('CFS','/w gm No character.');return;}
cn=ch.get('name');
aa=findObjs({type:'attribute',characterid:cid});
if(d){var fn=[];aa.forEach(function(a){var n=a.get('name'),m=n.match(/^repeating_[^_]+_[^_]+_(.+)$/);if(m&&fn.indexOf(m[1])===-1)fn.push(m[1]);});sendChat('CFS','/w gm '+fn.sort().join(', '));return;}
aa.forEach(function(a){var n=a.get('name'),m=n.match(/^(repeating_[^_]+_[^_]+)_(.+)$/);if(!m)return;var id=m[1],f=m[2],v=a.get('current')||'';if(!rw[id])rw[id]={name:'',type:'',rank:'',desc:'',traits:'',favored:''};if(f==='name')rw[id].name=v;if(f==='type')rw[id].type=v;if(f==='rank')rw[id].rank=v;if(f==='description')rw[id].desc=v;if(f==='traits')rw[id].traits=v;if(f==='favored')rw[id].favored=v;});
if(r1){var found=null;Object.keys(rw).forEach(function(id){if(!found&&(rw[id].type==='arcane_magic'||rw[id].type==='divine_magic'||rw[id].type==='magic'))found=id;});if(!found){sendChat('CFS','/w gm No spell row found.');return;}var dump=[];aa.forEach(function(a){var n=a.get('name');if(n.indexOf(found)!==-1)dump.push(n.replace(found+'_','')+'='+a.get('current'));});sendChat('CFS','/w gm '+dump.join(', '));return;}
Object.keys(rw).forEach(function(id){var r=rw[id];if(r.name&&(r.type==='arcane_magic'||r.type==='divine_magic'||r.type==='magic')&&r.favored==='1')sp.push(r);});
if(!sp.length){sendChat('CFS','/w gm No favored CF spells found.');return;}
sp.sort(function(a,b){var o={arcane_magic:0,magic:1,divine_magic:2},tA=o[a.type]||0,tB=o[b.type]||0;if(tA!==tB)return tA-tB;if((parseInt(a.rank)||0)!==(parseInt(b.rank)||0))return(parseInt(a.rank)||0)-(parseInt(b.rank)||0);return a.name.localeCompare(b.name);});
CFS={};
sp.forEach(function(s){
var sid=s.name.replace(/\s+/g,'_').replace(/[^a-zA-Z0-9_]/g,'');
CFS[sid]={name:s.name,rank:s.rank,desc:s.desc,traits:s.traits};
var label=s.name+(s.rank&&s.rank!='0'?' R'+s.rank:'');
var e='['+label+'](!cfcast '+sid+')';
if(s.type==='arcane_magic')ar.push(e);
else if(s.type==='divine_magic')dv.push(e);
else gn.push(e);
});
var wt=playerIsGM(msg.playerid)?'gm':msg.who.replace(/[\s.]/g,'');
var w='/w "'+wt+'" ';
sendChat('CFS',w+'**'+cn+' CF Spells**');
if(ar.length)sendChat('CFS',w+'**Arcane** '+ar.join(' '));
if(gn.length)sendChat('CFS',w+'**Magic** '+gn.join(' '));
if(dv.length)sendChat('CFS',w+'**Divine** '+dv.join(' '));
});