RolemasterUnified: Token makers and Inventory updates

- First version of token markers
  - Sheet options
  - Random colours
  - Trigger on status change.
- Fix stacking of popups on main page
- Add helper to get total number of bleeds
- Add options for token makers
- Add options for FX (doesn't do much)
- Export to NPC now grabs spell lists.
- Inventory (Stuff)
  - Fix Helmet perception penalty trashing other modifiers
  - Apply Armor DB
  - Removed Armor DB & Perception penalties when you remove items
  - Fix for armor losing it's encumberance.
  - Add support for "Sets" Armor without a helmet
  - Add armor material to edit window
  - Correctly support mixed material encumberances (metal != non-metal)
This commit is contained in:
Brett Nash
2025-09-15 22:17:04 -07:00
parent caf9e77f03
commit 749d01663f
4 changed files with 296 additions and 60 deletions
+244 -52
View File
@@ -3399,7 +3399,7 @@ Lists Selected <input type='number' name='comp_cmbaselistcount'> / 6
<section id='stats' class='tab-panel'>
<div class="sheet-3colrow">
<div class="rmusheet-column">
<div class='pcframebox'>
<div class='pcframebox' style='z-index: 699'>
<h2>Statistics</h2>
<div id="stats_pane" class="stats_pane">
<div class='stat'><span class='stat_names'><span style='position:relative'><span class="icon" style="font-family: 'Pictos Custom'; color: rgb(150,150,150);">y</span><span name='attr_agility_info' class='stat_info'></span>
@@ -3442,13 +3442,13 @@ Lists Selected <input type='number' name='comp_cmbaselistcount'> / 6
<span class='stat_attr' name='attr_will'></span>
</div>
</div>
</div> <!-- pcframebox -->
</div> <!-- pcframebox zindex 699 -->
<div class='pcframebox'>
<div class='pcframebox' style='z-index: 698'>
<div class='prop_pair'><span class='prop_prop_plain'>Power Level</span><span class='prop_value' name='attr_powerlevel'></span><span class='prop_prop_plain'>Profession</span><span class='prop_value' name='attr_profession'></span><span class='prop_prop_plain'>Race</span><span class='prop_value' name='attr_race'></span><span class='prop_prop_plain'>Culture</span><span class='prop_value' name='attr_culture'></span><span class='prop_prop_plain'>Level</span><span class='prop_value' name='attr_display_level'></span><span class='prop_prop_plain'>EP</span><span class='prop_value'><input type='number' style='width:100%' name='attr_xp'></span><span class='prop_prop_plain'>Fate Points</span><span class='prop_value'><input type='number' style='width:100%' name='attr_fatepoints'></span><span class='prop_prop_plain'>Realm</span><span class='prop_value' name='attr_realm'></span><span class='prop_prop_plain'>Religion</span><span class='prop_value'><input type='text' style='width:100%' name='attr_religion'></span><span class='prop_prop_plain'>Age</span><span class='prop_value'><input type='text' style='width:100%' name='attr_age'></span><span class='prop_prop_plain'>Skin</span><span class='prop_value'><input type='text' style='width:100%' name='attr_skin'></span><span class='prop_prop_plain'>Eyes</span><span class='prop_value'><input type='text' style='width:100%' name='attr_eyes'></span><span class='prop_prop_plain'>Hair</span><span class='prop_value'><input type='text' style='width:100%' name='attr_hair'></span><span class='prop_prop_plain'>Gender</span><span class='prop_value'><input type='text' style='width:100%' name='attr_gender'></span><span class='prop_prop_plain'>Build</span><span class='prop_value'><input type='text' style='width:100%' name='attr_build'></span><span class='prop_prop_plain'>Height</span><span class='prop_value'><input type='text' style='width:100%' name='attr_height'></span><span class='prop_prop_plain'>Weight</span><span class='prop_value'><input type='text' style='width:100%' name='attr_weight'></span><span class='iconwrap'><span class='prop_prop_plain'><span class="icon" style="font-family: 'Pictos Custom'; color: rgb(150,150,150);">y</span><span class='skill_info' name='attr_weightallowance_info'></span>Weight allowance</span></span>
<span class='prop_value' name='attr_weightallowance'></span><span class='prop_prop_plain'>Carried</span><span class='prop_value' name='attr_total_weight'></span><span class='prop_prop_plain'>Load (%)</span><span class='prop_value' name='attr_load'></span><span class='prop_prop_plain'>Max Pace</span><span class='prop_value' name='attr_maxpace'></span><span class='prop_prop_plain'>Enc Penalty</span><span class='prop_value' name='attr_encumberance_penalty'></span><span class='iconwrap'><span class='prop_prop_plain'><span class="icon" style="font-family: 'Pictos Custom'; color: rgb(150,150,150);">y</span><span class='skill_info' name='attr_bmr_info'></span>Base Move (BMR)</span></span>
<span class='prop_value' name='attr_bmr'></span><span class='prop_prop_plain'>Size</span><span class='prop_value' name='attr_size'></span><span class='prop_prop_plain'>Recovery Multiplier</span><span class='prop_value' name='attr_recovery_multiplier'></span><span class='prop_pair_heading'>Feats of Strength</span><span class='prop_prop_plain'>Routine</span><span class='prop_value' name='attr_strfeat_routine'></span><span class='prop_prop_plain'>Easy</span><span class='prop_value' name='attr_strfeat_easy'></span><span class='prop_prop_plain'>Light</span><span class='prop_value' name='attr_strfeat_light'></span><span class='prop_prop_plain'>Medium</span><span class='prop_value' name='attr_strfeat_medium'></span><span class='prop_prop_plain'>Hard</span><span class='prop_value' name='attr_strfeat_hard'></span><span class='prop_prop_plain'>Very Hard</span><span class='prop_value' name='attr_strfeat_veryhard'></span><span class='prop_prop_plain'>Ext. Hard</span><span class='prop_value' name='attr_strfeat_exthard'></span><span class='prop_prop_plain'>Sheer Folly</span><span class='prop_value' name='attr_strfeat_sheerfolly'></span><span class='prop_prop_plain'>Absurd</span><span class='prop_value' name='attr_strfeat_absurd'></span><span class='prop_prop_plain'>Nigh Impossible</span><span class='prop_value' name='attr_strfeat_nighimpossible'></span>
</div> <!-- pcframebox -->
</div> <!-- pcframebox zindex 698 -->
</div>
@@ -3457,7 +3457,7 @@ Lists Selected <input type='number' name='comp_cmbaselistcount'> / 6
<div class="rmusheet-column">
<div class='pcframebox'>
<div class='pcframebox' style='z-index: 697'>
<h2>Actions</h2>
@@ -3565,9 +3565,9 @@ Lists Selected <input type='number' name='comp_cmbaselistcount'> / 6
</div> <!-- status frame box -->
</div> <!-- pcframebox zindex 697 -->
<div class='pcframebox'>
<div class='pcframebox' style='z-index: 696'>
<h2>Status</h2>
@@ -3741,9 +3741,9 @@ Lists Selected <input type='number' name='comp_cmbaselistcount'> / 6
<button type='action' name='act_statussleep'
class='rollbutton fancybutton fancybuttoninline'>Sleep</button>
</div> <!-- status frame box -->
<div class='pcframebox'>
</div> <!-- pcframebox zindex 696 -->
<div class='pcframebox' style='z-index: 695'>
<h2>Attacks</h2>
@@ -3880,12 +3880,12 @@ Lists Selected <input type='number' name='comp_cmbaselistcount'> / 6
<hr>
</div> <!-- pcframebox -->
</div> <!-- pcframebox zindex 695 -->
</div> <!-- rmusheet-column -->
<div class='rmusheet-column'>
<div class='pcframebox'>
<div class='pcframebox' style='z-index: 694'>
<h2>Defense</h2>
@@ -3966,9 +3966,10 @@ Lists Selected <input type='number' name='comp_cmbaselistcount'> / 6
<span name='attr_db_fullblock_info' class='stat_info'></span>
Full Block (4 AP)</span>
<span class='prop_value' name='attr_db_fullblock'></span>
</div> <!-- pcframebox -->
</div>
<div class='pcframebox'>
</div>
</div> <!-- pcframebox zindex 694 -->
<div class='pcframebox' style='z-index: 693'>
<h2>RRs</h2>
@@ -4010,11 +4011,11 @@ Lists Selected <input type='number' name='comp_cmbaselistcount'> / 6
</div> <!-- pcframebox -->
</div> <!-- pcframebox zindex 693 -->
<div class='pcframebox'>
<div class='pcframebox' style='z-index: 692'>
<h2>Favorite Skills</h2>
<div class='favorite_group'>
@@ -4036,8 +4037,11 @@ Lists Selected <input type='number' name='comp_cmbaselistcount'> / 6
</fieldset>
</div>
</div>
<div class='pcframebox'>
</div> <!-- pcframebox zindex 692 -->
<div class='pcframebox' style='z-index: 691'>
<h2>Talents</h2>
<input type='hidden' name='attr_edit_talent_toggle' class='edit_control_toggle'>
@@ -4053,7 +4057,7 @@ Lists Selected <input type='number' name='comp_cmbaselistcount'> / 6
</fieldset>
<button type='action' class='fancybutton' name='act_talentaddstart'>Add Talent</button>
</div> <!-- pcframebox -->
</div> <!-- pcframebox zindex 691 -->
</div> <!-- rmusheet-column -->
@@ -6234,13 +6238,14 @@ Uses (Lvl/Total) <span name='attr_traps_usecount'></span> / <span name='attr_tra
<span class='inventoryarmortoggle'><b>Armor Location</b></span>
<select name='attr_inventoryaddarmorsubtype' class='inventoryarmortoggle fancyselect'>
<option value='Full'>Full</option>
<option value='Set'>Set (All but Helmet)</option>
<option value='Vambraces'>Vambraces</option>
<option value='Greaves'>Greaves</option>
<option value='Torso'>Torso</option>
<option value='Helmet'>Helmet</option>
</select>
<span class='inventoryarmortoggle'><b>Armor Type (AT)</b></span>
<select name='attr_inventoryaddarmorat' class='inventoryarmortoggle fancyselect:'>
<select name='attr_inventoryaddarmorat' class='inventoryarmortoggle fancyselect'>
<option value='1'>1</option>
<option value='2'>2</option>
<option value='3'>3</option>
@@ -6254,6 +6259,13 @@ Uses (Lvl/Total) <span name='attr_traps_usecount'></span> / <span name='attr_tra
</select>
<span class='inventoryarmortoggle'><b>DB Bonus</b></span>
<input type='number' name='attr_inventoryaddarmordbbonus' class='inventoryarmortoggle'>
<span class='inventoryarmortoggle'><b>Material</b></span>
<select name='attr_inventoryaddarmormaterial' class='inventoryarmortoggle fancyselect'>
<option value='cloth'>Cloth</option>
<option value='leather'>Leather</option>
<option value='metal'>Metal</option>
</select>
<span class='inventoryshieldtoggle'><b>Number of Attacks</b></span>
@@ -6529,6 +6541,11 @@ This is not for usual use. </p>
<button type="action" style="-toby-display:none;" name="act_initiative-action">initiative</button>
<hr>
<input type='text' name='attr_chatcommand'>
<button type='action' name='act_sendtochat'>Send to Chat</button>
<hr>
@@ -6542,7 +6559,7 @@ Sheet Version: <span name='attr_version'></span>
<br>
Revision 52f9842a63e00550f91634a07b2ecb1db3a83f16
Revision d1fa0cd182ccb30369359a6669cdec2e5abfc14d
<hr>
@@ -7663,6 +7680,16 @@ function miscBonusRemove(miscstr, name) {
return JSON.stringify(misc);
}
// Any item that matches the filter will be remmoebed
function miscBonusRemoveFilter(miscstr, fn) {
if (typeof(miscstr) != 'string' || miscstr == '') {
return '';
}
let misc = JSON.parse(miscstr);
misc = misc.filter(fn);
return JSON.stringify(misc);
}
function miscBonusTotal(miscstr, prefix = '') {
if (typeof(miscstr) != 'string' || miscstr == '') {
return [0, ""];
@@ -9713,18 +9740,21 @@ function getSubtlePenalty(realm, type, trickery, subtle, voice, hands, enc, meta
// Armor and Transcendance
console.log("Enc and metalenc", enc, metalenc, pentable, pentable.armorenc != pentable.metalenc);
if (enc || metalenc) {
if ((enc || metalenc) && (pentable.armorenc || pentable.metalenc)) {
// Enc is all encumberance from armor, Metalenc is jsut hte metal bit.
RMUSkills.uses("transcendence");
let epen = 0;
if (enc > 0 && pentable.armorenc) {
epen += enc * pentable.armorenc;
log += ` -${epen}&nbsp;[${enc} Enc% * ${pentable.armorenc}]`;
}
if (pentable.armorenc != pentable.metalenc) {
// Channeling
// FIXME: This is wrong for realsm were metal and non-metal are different but not zero
if ((pentable.armorenc == pentable.metalenc) || (enc == metalenc)) {
// Easy case: Metal and non metal the same - Essence basically
// Ignore metal as enc encompases both
epen += enc * pentable.metalenc;
log += ` -${epen}&nbsp;[${enc} Enc% * ${pentable.metalenc}]`;
} else {
let encdelta = enc - metalenc;
epen += encdelta * pentable.armorenc;
log += ` -${epen}&nbsp;[(${enc} - ${metalenc} Non-metal Enc% * ${pentable.armorenc}]`;
epen += metalenc * pentable.metalenc;
log += ` -${epen}&nbsp;[${enc} Enc% * ${pentable.metalenc} (metal)]`;
log += ` -${epen}&nbsp;[${metalenc} Metal Enc% * ${pentable.metalenc}]`;
}
// Now apply transcendance
if (transcend && epen > 0) {
@@ -13734,6 +13764,7 @@ frontpage.updateDB = function () {
console.log("dooing update", ev);
var updates = {};
const db_extra = miscBonusTotal(ev.db_misc || "")[0];
console.log("db_extra", db_extra, ev.db_misc)
const dbmiscmod = parseIntDefault(ev.dbmod, 0);
const basedb = +ev.qu * 3 + db_extra + dbmiscmod;
updates.db_base = basedb;
@@ -14218,15 +14249,22 @@ RMUInventory.itemfields = [ "itemname", "itemweight", "itemcount", "itemsystem",
let percept = 0;
let rangedpenalty = 0;
let maneuver_penalty = 0;
let toget = ['base_at_misc', 'perception_misc']
let toget = ['base_at_misc', 'perception_misc', 'db_misc'];
ids.forEach(id => {
toget.push(`repeating_stuff_${id}_itemname`);
toget.push(`repeating_stuff_${id}_itemequipped`);
toget.push(`repeating_stuff_${id}_itemsystem`);
});
let armordb = 0;
let armordbname = "None";
getAttrsPending(toget, (stuff) => {
console.log("Fetch equipment", stuff);
let updates = {};
updates.db_misc = miscBonusRemoveFilter(stuff.db_misc, (item) => {
return item.name.startsWith("Armor DB Bonus");
});
let atinfo = miscBonusHighest(stuff.base_at_misc || "", 1);
// Set default AT to base_at everywhere.
updates.at_head = updates.at_torso = updates.at_legs =
@@ -14253,10 +14291,12 @@ RMUInventory.itemfields = [ "itemname", "itemweight", "itemcount", "itemsystem",
rmuerror("Parsing object JSON", e);
return;
}
console.log(json);
// Look for magic fields:
// - AT
// - attack table
if (json.AT) {
let db = parseIntDefault(json.DB, 0);
switch (json.Subtype) {
case 'Helmet':
updates.at_head = json.AT;
@@ -14265,6 +14305,10 @@ RMUInventory.itemfields = [ "itemname", "itemweight", "itemcount", "itemsystem",
case 'Torso':
updates.at_torso = json.AT;
updates.at_torso_info = name;
if (db && db > armordb) {
armordb = db;
armordbname = name;
}
break;
case 'Vambraces':
updates.at_arms = json.AT;
@@ -14273,12 +14317,26 @@ RMUInventory.itemfields = [ "itemname", "itemweight", "itemcount", "itemsystem",
case 'Greaves':
updates.at_legs = json.AT;
updates.at_legs_info = name;
break
break;
case 'Set':
updates.at_torso = json.AT;
updates.at_legs = json.AT;
updates.at_arms = json.AT;
if (db && db > armordb) {
armordb = db;
armordbname = name;
}
updates.at_torso_info = updates.at_legs_info = updates.at_arms_info = name;
break;
default:
updates.at_head = json.AT;
updates.at_torso = json.AT;
updates.at_legs = json.AT;
updates.at_arms = json.AT;
if (db && db > armordb) {
armordb = db;
armordbname = name;
}
updates.at_head_info = updates.at_torso_info = updates.at_legs_info =
updates.at_arms_info = name;
}
@@ -14296,7 +14354,8 @@ RMUInventory.itemfields = [ "itemname", "itemweight", "itemcount", "itemsystem",
});
// Adjust due to perception penalty
updates.perception_misc = miscBonusSet(updates.perception_misc,
updates.db_misc = miscBonusSet(stuff.db_misc, `Armor DB Bonus (${armordbname})`, armordb);
updates.perception_misc = miscBonusSet(stuff.perception_misc,
"Armor Perception Penalty", percept);
updates.perception_penalty = percept;
updates.ranged_penalty = rangedpenalty;
@@ -14305,6 +14364,7 @@ RMUInventory.itemfields = [ "itemname", "itemweight", "itemcount", "itemsystem",
console.log("equipment update", updates);
setAttrsPending(updates, () => {
RMUSkills.updateSingleCategory("Awareness");
frontpage.updateDB();
});
});
});
@@ -14563,7 +14623,7 @@ RMUInventory.itemfields = [ "itemname", "itemweight", "itemcount", "itemsystem",
"inventoryaddname", "inventoryaddweight", "inventoryaddnotes",
"inventoryaddtype",
"inventoryaddarmorsubtype", "inventoryaddarmorat",
"inventoryaddarmordbbonus",
"inventoryaddarmordbbonus", "inventoryaddarmormaterial",
"inventoryaddshieldattacks", "inventoryaddshielddb",
"inventoryaddweaponmaterialc", "inventoryaddweaponmaterialw",
"inventoryaddweaponmateriali", "inventoryaddweaponmaterialm",
@@ -14594,6 +14654,9 @@ RMUInventory.itemfields = [ "itemname", "itemweight", "itemcount", "itemsystem",
if (db != 0) {
system.DB = ia.inventoryaddarmordbbonus;
}
// If Armor need to make a %
update.weight = `${update.weight}%`;
update.itemmaterial = ia.inventoryaddarmormaterial;
} else if (update.type == 'Shield') {
system['Attacks Blocked'] = ia.inventoryaddshieldattacks;
system['DB Bonus'] = ia.inventoryaddshielddb;
@@ -14687,6 +14750,7 @@ RMUInventory.itemfields = [ "itemname", "itemweight", "itemcount", "itemsystem",
if (sys.AT) {
update.inventoryaddarmorat = sys.AT;
}
update.inventoryaddarmormaterial = item[`${basename}_itemmaterial`];
update.inventoryaddarmordbbonus = parseIntDefault(sys.DB, 0);
}
{//
@@ -14696,6 +14760,7 @@ RMUInventory.itemfields = [ "itemname", "itemweight", "itemcount", "itemsystem",
if (sys['DB Bonus']) {
update.inventoryaddshielddb = sys['DB Bonus'];
}
update.inventoryaddarmormaterial = item[`${basename}_itemmaterial`];
}
// Ideally this should be the same as the above function
@@ -16750,6 +16815,7 @@ onCheck("clicked:newcombat", () => {
sendMessage(`${ev.character_name} prepares for battle`);
});
actionsUpdateStatus();
updatetokenmarkers();
});
// So this is our per AP stuff
@@ -16763,6 +16829,7 @@ onCheck("clicked:deletebleed", () => {
getAttrs(["character_name"], (x) => {
sendMessage(`${x.character_name} is no longer bleeding`);
setAttrs({ bleeding: 0, bleedlist: []});
updatetokenmarkers();
});
});
@@ -16770,6 +16837,7 @@ onCheck("clicked:statusstun75delete", () => {
getAttrs(["character_name"], (x) => {
sendMessage(`${x.character_name} has stun-75 lifted.`);
setAttrs({ statusstun75: 0 }, actionsUpdateStatus);
updatetokenmarkers();
});
});
@@ -16777,6 +16845,7 @@ onCheck("clicked:statusstun50delete", () => {
getAttrs(["character_name"], (x) => {
sendMessage(`${x.character_name} has stun-50 lifted.`);
setAttrs({ statusstun50: 0 }, actionsUpdateStatus);
updatetokenmarkers();
});
});
@@ -16784,6 +16853,7 @@ onCheck("clicked:statusstun25delete", () => {
getAttrs(["character_name"], (x) => {
sendMessage(`${x.character_name} has stun-25 lifted.`);
setAttrs({ statusstun25: 0 }, actionsUpdateStatus);
updatetokenmarkers();
});
});
@@ -16801,7 +16871,7 @@ function handleFreeClicked() {
function handleBleed(currentphase, bleedlist) {
// Bleedlist is probably a string.
if (!bleedlist || typeof(bleedlist) != 'string') {
return;
return 0;
}
// Shift current phase to 1..4
@@ -16823,6 +16893,22 @@ function handleBleed(currentphase, bleedlist) {
return total;
}
function totalBleed(bleedlist) {
// Bleedlist is probably a string.
if (!bleedlist || typeof(bleedlist) != 'string') {
return 0;
}
const bleeds = JSON.parse(bleedlist);
let total = 0;
for (let bleed of bleeds) {
total += bleed.amount;
}
return total;
}
/**
* Generic handler for the status list updates. Called once per phase.
*
@@ -16910,7 +16996,7 @@ function actionsUpdateStatus() {
updates.statuspercent = Math.max(100 + updates.allpenalties, 0);
updates.statuspercent200 = Math.max(200 + updates.allpenalties, 0);
VrmuSetAttrs(updates)
rmuSetAttrs(updates);
});
}
@@ -17020,6 +17106,7 @@ function doTurn() {
VrmuSetAttrs(updates, ()=> {
sendEOTMessage(`${name} completes their turn (Round ${round} / Phase ${relativephase})`);
actionsUpdateStatus();
updatetokenmarkers();
});
});
}
@@ -17144,14 +17231,6 @@ onCheck("change:bodydevelopment_bonus change:powerdevelopment_bonus", (x) => {
pp = 0;
}
update.pp_max = pp
/*
update.bar1_max = ev.bodydevelopment_bonus;
update.bar1_value = ev.hp;
update.bar2_max = ev.powerdevelopment_bonus;
update.bar2_value = ev.pp;*/
// if (!ev.hasOwnProperty(hp)) {
// update.hp = ev.bodydevelopment_bonus;
// }
setAttrs(update, { silent:true });
});
});
@@ -17162,7 +17241,6 @@ onCheck("change:bodydevelopment_bonus change:endurance_misc change:mentalfocus_b
});
function updateEnduranceBonus() {
console.log("updateEnduranceBonus");
getAttrs(['bodydevelopment_bonus', 'endurance_misc', 'mentalfocus_bonus'], (ev) => {
let log = "Endurance\n" +
`${F(ev.bodydevelopment_bonus)} Body Development\n`;
@@ -17203,7 +17281,6 @@ onCheck("change:hp", (_) => {
} else {
update.hppenalty = -100;
}
update.bar1_value = values.hp; // FIXME: this doesn't do much
update.hp_percent = hppercent;
setAttrs(update, { slient: true});
});
@@ -17418,6 +17495,68 @@ function doRollInitiative() {
});
}
/**/
function updatetokenmarkers() {
console.log("updatetokenmarkers start");
getAttrs(['option_usetokenmarkers', 'sheetselect', 'character_name',
// Stuns & staggered
'statusstagger', 'statusstun75', 'statusstun50', 'statusstun25',
'bleedlist', 'grapplestatus'], (info) => {
console.log("Update token makers", info);
if (!info.option_usetokenmarkers || info.option_usetokenmarkers == 'no') {
return;
}
if (info.option_usetokenmarkers == 'pc' && info.sheettype != 'playersheet') {
return;
}
if (info.option_usetokenmarkers == 'npc' && info.sheettype != 'creaturesheet') {
return;
}
let statusstagger = parseIntDefault(info.statusstagger, 0);
let statusstun75 = parseIntDefault(info.statusstun75, 0);
let statusstun50 = parseIntDefault(info.statusstun50, 0);
let statusstun25 = parseIntDefault(info.statusstun25, 0);
let grapplestatus = parseIntDefault(info.statusstun25, 0);
let tokens = []
let bleeds = totalBleed(info.bleedlist);
if (bleeds > 9) bleeds = 9;
tokens.push((bleeds > 0) ? `red:${bleeds}` : "!red");
tokens.push((statusstagger > 0) ? "pink" : "!pink");
if (statusstun75 > 0) {
let rounds = Math.floor(statusstun75 / 4);
tokens.push(`blue:${rounds}`);
} else {
tokens.push("!blue");
}
if (statusstun50 > 0) {
let rounds = Math.floor(statusstun50 / 4);
tokens.push(`green:${rounds}`);
} else {
tokens.push("!green");
}
if (statusstun25 > 0) {
let rounds = Math.floor(statusstun25 / 4);
tokens.push(`purple:${rounds}`);
} else {
tokens.push("!purple");
}
let tstr = tokens.join('|')
// Join token string. with --set st
let message = `!token-mod --ids @{${info.character_name}|character_id} --set statusmarkers|${tstr}`
console.log(message)
startRoll(message, (t) => finishRoll(t.rollId, t));
});
}
@@ -18148,13 +18287,47 @@ function doNpcGeneate() {
});
});
// move
out['data-Spelllists'] = []
addPendingFunction("NPC Gen: Save Spells Base", () => {
let spellsToGet = []
let prefixs = [];
// Messing with the indentation here for sanity
getSectionIDsPending("repeating_spelllistspellownbase", (ids1) => {
let p1 = ids1.map((id) => "repeating_spelllistspellownbase_" + id);
getSectionIDsPending("repeating_spelllistspellopen", (ids2) => {
let p2 = ids2.map((id) => "repeating_spelllistspellopen_" + id);
getSectionIDsPending("repeating_spelllistspellclosed", (ids3) => {
let p3 = ids3.map((id) => "repeating_spelllistspellclosed_" + id);
let prefixes = p1.concat(p2, p3);
for (prefix of prefixes) {
spellsToGet.push(
`${prefix}_listname`,
`${prefix}_ranks`,
`${prefix}_scr`,
);
}
getAttrsPending(spellsToGet, (spells) => {
for (prefix of prefixes) {
let ranks = parseIntDefault(spells[`${prefix}_ranks`],0);
if (!ranks || ranks === 0) { continue; }
let name = spells[`${prefix}_listname`];
let scr = spells[`${prefix}_scr`];
out['data-Spelllists'].push({
name: name,
scr: scr,
ranks: ranks });
}
});
}); }); }); // Nest getSectionIds above
});
addPendingFunction("NPC Gen: Save NPC String", () => {
setAttrs({npcstring: JSON.stringify(out)})
});
// defenses
// move
// spells
checkPending();
}
@@ -18495,7 +18668,10 @@ injury.parseInjuryString = function (str) {
}
// Update the status as soon as the injury is applied.
VrmuSetAttrs(updates, actionsUpdateStatus);
VrmuSetAttrs(updates, () => {
actionsUpdateStatus();
updatetokenmarkers();
});
});
}
@@ -19292,6 +19468,22 @@ function dX(x) {
return Math.floor(Math.random() * x) + 1;
}
// Hacky send to chat
onCheck("clicked:sendtochat", () => {
console.log("Send to chat clicked");
getAttrs(["chatcommand"], (ev) => {
console.log("command", ev);
//macroscript = "!token-mod --set statusmarkers|red";
startRoll(ev.chatcommand, (results2) => {
console.log(results2);
// Holding the computed data in an object is a bit cleaner if your rolls get more complex
let rollData2 = results2.results;
finishRoll(results2.rollId, rollData2);
});
});
});
</script>
+25 -8
View File
@@ -10,14 +10,31 @@
"compendium": "RMU",
"useroptions": [
{
"attribute": "default_currency",
"displayname": "Currency for the Game",
"displaytranslationkey": "defaultcurrency",
"type": "text",
"default": "GP(Gold Pieces)=10SP;SP(Silver Pieces)*=10BP;BP(Bronze Pieces)=10CP;CP(Copper Pieces)",
"description": "Custom currency description",
"descriptiontranslationkey": "DefaultCurrency"
"attribute": "option_usetokenmarkers",
"displayname": "Use Token Markeres",
"displaytranslationkey": "option_usetokenmarkers",
"type": "select",
"options": [
"no|No token marker updates",
"pc|Token Markers on PC sheets",
"npc|Token Markers on NPC/Creature sheets",
"all|Token Markers on all sheets"
],
"default": "no",
"description": "Show token makers for character state on the token. Covers stun, bleed and similar states. Requires Token Mod to be installed (and hence a Pro account)."
},
{
"attribute": "option_usefx",
"displayname": "Use FX",
"displaytranslationkey": "option_usefx",
"type": "select",
"options": [
"no|No FX",
"all|All Supported FX"
],
"default": "all",
"description": "Show FX when available based on the sheets guess of the attack"
}
],
"version": "1757570905"
"version": "1757999780"
}
@@ -168,6 +168,8 @@
"noprofession": "No Profession",
"open": "Open",
"operation": "Operation",
"option_usefx": "Use FX",
"option_usetokenmarkers": "Use Token Markers",
"paladin": "Paladin",
"perception": "Perception",
"performanceart": "Performance Art",
+25
View File
@@ -1,3 +1,28 @@
# 2025-09-16
- First version of token markers
- Sheet options
- Random colours
- Trigger on status change.
- Fix stacking of popups on main page
- Add helper to get total number of bleeds
- Add options for token makers
- Add options for FX (doesn't do much)
- Export to NPC now grabs spell lists.
- Inventory (Stuff)
- Fix Helmet perception penalty trashing other modifiers
- Apply Armor DB
- Removed Armor DB & Perception penalties when you remove items
- Fix for armor losing it's encumberance.
- Add support for "Sets" Armor without a helmet
- Add armor material to edit window
- Correctly support mixed material encumberances (metal != non-metal)
# 2025-09-11
- Try and get the info popups working again.
- fix spurious --> from the rolltemplates page
# 2025-09-09
- Can now roll weapon fumbles direct from the sheet.