I have a script that creates attributes on characters that represents the status markers on their token. Attributes are named sm_<NAME> and will have a value of 0-9 representing the number on the status marker. A status marker that is present without a number will have the value 1. A status marker with the number 0 will have the number zero, as will a status marker that isn't present. Some example attribute names: sm_blue sm_all-for-one sm_archery-target Here's the RealizeStatusmarkers script. on('ready',() => {
const markerOnValue = 1;
const markers=[
'red', 'blue', 'green', 'brown', 'purple', 'pink', 'yellow', 'dead',
'skull', 'sleepy', 'half-heart', 'half-haze', 'interdiction', 'snail',
'lightning-helix', 'spanner', 'chained-heart', 'chemical-bolt',
'death-zone', 'drink-me', 'edge-crack', 'ninja-mask', 'stopwatch',
'fishing-net', 'overdrive', 'strong', 'fist', 'padlock',
'three-leaves', 'fluffy-wing', 'pummeled', 'tread', 'arrowed', 'aura',
'back-pain', 'black-flag', 'bleeding-eye', 'bolt-shield',
'broken-heart', 'cobweb', 'broken-shield', 'flying-flag',
'radioactive', 'trophy', 'broken-skull', 'frozen-orb', 'rolling-bomb',
'white-tower', 'grab', 'screaming', 'grenade', 'sentry-gun',
'all-for-one', 'angel-outfit', 'archery-target'
];
const markerAttrRegexp=/^sm_(?:red|blue|green|brown|purple|pink|yellow|dead|skull|sleepy|half-heart|half-haze|interdiction|snail|lightning-helix|spanner|chained-heart|chemical-bolt|death-zone|drink-me|edge-crack|ninja-mask|stopwatch|fishing-net|overdrive|strong|fist|padlock|three-leaves|fluffy-wing|pummeled|tread|arrowed|aura|back-pain|black-flag|bleeding-eye|bolt-shield|broken-heart|cobweb|broken-shield|flying-flag|radioactive|trophy|broken-skull|frozen-orb|rolling-bomb|white-tower|grab|screaming|grenade|sentry-gun|all-for-one|angel-outfit|archery-target)$/;
const getOrCreateAttr = (()=>{
let cache = findObjs({ type: 'attribute'})
.filter( a => markerAttrRegexp.test(a.get('name')))
.reduce( (m,a) => {
const cid=a.get('characterid');
m[cid]=m[cid]||{};
m[cid][a.get('name')]=a;
return m;
},{});
return (p)=>{
if(!cache.hasOwnProperty(p.characterid) || !cache[p.characterid].hasOwnProperty(p.name)){
cache[p.characterid]=cache[p.characterid]||{};
cache[p.characterid][p.name] = createObj('attribute',p);
}
return cache[p.characterid][p.name];
};
})();
const setStatus = (cid, s, v) => getOrCreateAttr({
type: 'attribute',
name: `sm_${s}`,
characterid: cid
}).set('current',parseInt(v,10)||0);
const handleStatusMarkerChange = _.debounce((obj,prev) => {
if(obj.get('represents').length) {
let character = getObj('character',obj.get('represents'));
if(character && character.get('controlledby').length){
let sm = obj.get('statusmarkers');
if(sm!==prev.statusmarkers){
prev.statusmarkers.split(/,/)
.map((s)=>s.split(/@/))
.forEach( s => setStatus(character.id,s[0]))
;
sm.split(/,/)
.filter(s=>s.length>0)
.map((s)=>s.split(/@/))
.forEach( s => setStatus(character.id,s[0],parseInt(s[1],10)||markerOnValue))
;
}
}
}
},10);
const markersFor = (c) => markers.map( s => ({characterid: c.id, name: `sm_${s}`, current: 0}) );
const preCreateMarkersOn = (c) => {
let queue = markersFor(c);
const dequeue = () => {
let prop = queue.shift();
if(prop){
getOrCreateAttr(prop);
setTimeout(dequeue,0);
}
};
dequeue();
};
const handleControlledbyChange = (obj,prev) => {
if( 0 === prev.controlledby.length) {
preCreateMarkersOn(obj);
}
};
on('change:graphic',handleStatusMarkerChange);
on('change:character:controlledby',handleControlledbyChange);
/* global TokenMod */
if('undefined' !== typeof TokenMod && TokenMod.ObserveTokenChange){
TokenMod.ObserveTokenChange(handleStatusMarkerChange);
}
const workQueue = findObjs({ type: 'character' })
.filter( c => c.get('controlledby').length )
.reduce( (m,c) => [...m, ...markersFor(c)], []);
log(`RealizeStatusmarkers: Initializing attributes: ${workQueue.length}`);
const drainQueue = () => {
let prop = workQueue.shift();
if(prop){
getOrCreateAttr(prop);
setTimeout(drainQueue,0);
} else {
log(`RealizeStatusmarkers: Finished initializing attributes.`);
}
};
drainQueue();
});