This API supports initiative for RPGs using the Turn Order and the Tracker window. The InitiativeMaster API provides functions dealing with all aspects of: managing how initiative is done; rolling for initiative; for "group" and "individual" initiative types providing Character action selection to determine the speed and number of attacks of weapons, the casting time of spells & the usage speed of magic items; supporting initiative for multiple attacks with one or multiple weapons per round; supporting and tracking actions that take multiple rounds; managing the resulting Turn Order; as well as performing the "End of Day" activity. It works very closely with the RoundMaster API to the extent that InitiativeMaster cannot work without RoundMaster (though the reverse is possible). InitiativeMaster also works closely with AttackMaster API and MagicMaster API and uses the data configured on the Character Sheet by these other APIs, although it can use manually completed Character Sheets once correctly configured. As with all RPGMaster series APIs (other than RoundMaster), the correct RPGMaster Library for the D&D game version and Roll20 character sheet type you are using must also be loaded, to provide the correct rule set, parameters and databases for your campaign.'
+'Syntax of InitiativeMaster calls
'
@@ -247,7 +255,7 @@ var initMaster = (function() {
+'--thief [token-id]
'
+'--other [token-id]
'
+'--check-init [token-id]
'
- +'Updated:--set-mods [token-id]|cmd|name|[=][+/-]mod|[=][+/-]mult|[SILENT]'
+ +'--set-mods [token-id]|cmd|name|[=][+/-]mod|[=][+/-]mult|[SILENT]'
+'4. Maintain the Turn Order and Rounds
'
+'--maint
'
+'--check-tracker
'
@@ -351,7 +359,7 @@ var initMaster = (function() {
+'--check-init [token-id]
'
+'Takes an optional token ID.
'
+'Displays a dialog showing all currently in-play initiative modifiers for the character represented by the identified or selected token. Also provides buttons to add, change or remove modifiers - one for mods that add or subtract to the initiative roll, and one for mods that multiply or reduce the number of attack actions the character can undertake. See the --set-mods command for more information.
'
- +'3.11 Updated: Add, change or remove modifiers
'
+ +'3.11 Add, change or remove modifiers
'
+'--set-mods [token-id] | (DEL / FIX / MOD / MULT / BOTH) | name | [[=][+/-]mod] | [[=][+/-]mult] | [SILENT]
'
+'Takes an optional token ID, a command specifying the action, the name of the mod, the optional value of an addative modifier optionally preceeded with = or - or +, the optional value of a multiplying modifier optionally preceeded with = or - or +, and an optional "silent" qualifier.
'
+'Sets, fixes, changes, or deletes a named initiative modifier that can have one or both of additive and multiplicative elements. Each of these modifiers can include maths to be evaluated using standard maths operators +, -, *, /, (, ), and ^(#,#,...) for max, and v(#,#,...) for min (commas can be replaced by semi-colons ;). Preceeding a mod or mult value by = will set that value (e.g. =-2 will set the value to -2), or preceeding by + or - without the = will amend the current value by that amount. The FIX command will cause the named modifier to override all other modifiers and the initiative dice roll and set future initiative rolls to the value of mod (until DEL or a different command is used for the same name of modifier). Including the SILENT argument will not display any outcome, while ommitting it will display the result in a --check-init dialog.
'
@@ -518,6 +526,7 @@ var initMaster = (function() {
CARRY: 'CARRY',
SUBMIT: 'SUBMIT',
SETMODS: 'SETMODS',
+ SECTIONS_OPTION:'SECTIONS_OPTION',
});
var Caster = Object.freeze({
@@ -887,6 +896,13 @@ var initMaster = (function() {
return replacers.reduce((m, rep) => m.replace(rep[0], rep[1]), str);
}
+ /*
+ * Function to return the msVersion of the Character Sheet
+ * i.e. which versions of MagicMaster it is matched to
+ */
+
+ var csVer = (charCS) => parseFloat(((attrLookup( charCS, fields.msVersion ) || '1.5').match(/^\d+\.?\d*/) || ['1.5'])[0]) || 1.5;
+
/**
* Handle Pending Requests
*/
@@ -1154,8 +1170,6 @@ var initMaster = (function() {
var buildWeaponArgs = function( charCS, cmd, weaponRef ) {
-// log('buildWeaponArgs: cmd = '+cmd+', weaponRef = '+weaponRef);
-
var fieldObj = {
melee: {name:fields.MW_name,miName:fields.MW_miName,styleSpeed:fields.MW_styleSpeed,adj:fields.MW_adj,speed:fields.MW_speed,styleAttks:fields.MW_styleAttks,noAttks:fields.MW_noAttks,preInit:fields.MW_preInit,attkRound:fields.MW_attkRound,attkCount:fields.MW_attkCount,curCount:fields.MW_curCount,twoHanded:fields.MW_twoHanded,type:fields.MW_type},
ranged:{name:fields.RW_name,miName:fields.RW_miName,styleSpeed:fields.RW_styleSpeed,adj:fields.RW_adj,speed:fields.RW_speed,styleAttks:fields.RW_styleAttks,noAttks:fields.RW_noAttks,preInit:fields.RW_preInit,attkRound:fields.RW_attkRound,attkCount:fields.RW_attkCount,curCount:fields.RW_curCount,twoHanded:fields.RW_twoHanded,type:fields.RW_type}
@@ -1176,10 +1190,13 @@ var initMaster = (function() {
weapSpecial = (proficient( charCS, weaponName, weaponType, '' ) > 0) ? 1 : preInit,
twoHanded = (WeaponTables.tableLookup( attrs.twoHanded, weaponRef ) || 0),
curRound = WeaponTables.tableLookup( attrs.attkRound, weaponRef ) || 0;
+
+ if (_.isUndefined(WeaponTables.tableLookup( attrs.name, weaponRef, false )))
+ WeaponTables = WeaponTables.addTableRow( weaponRef );
if (curRound != state.initMaster.round) {
attackCount = WeaponTables.tableLookup( attrs.attkCount, weaponRef ) || 0;
- WeaponTables = WeaponTables.tableSet( attrs.curCount, weaponRef, attackCount );
WeaponTables = WeaponTables.tableSet( attrs.attkRound, weaponRef, state.initMaster.round );
+ WeaponTables = WeaponTables.tableSet( attrs.curCount, weaponRef, attackCount );
} else {
attackCount = WeaponTables.tableLookup( attrs.curCount, weaponRef ) || 0;
}
@@ -1255,8 +1272,6 @@ var initMaster = (function() {
throw new Error( 'Invalid button while handling a melee attack initiative selection' );
}
-// log('handleInitMW: rowIndex = '+rowIndex+', refIndex = '+refIndex);
-
var weapArgs = buildWeaponArgs( charCS, BT.MELEE, refIndex ),
buildCall = '!init --buildMenu ' + senderId
+ '|' + (charType == CharSheet.MONSTER ? MenuType.COMPLEX : MenuType.WEAPON)
@@ -1295,13 +1310,13 @@ var initMaster = (function() {
{return;}
let weapArgs = buildWeaponArgs( charCS, ((rowIndex%2) > 0 ? BT.MELEE : BT.RANGED), refIndex );
- setInitVars( charCS, [MenuType.MW_PRIME,tokenID,refIndex,('with their '+weapArgs.name),evalAttr(weapArgs.speed),(weapArgs.mult+'*'+weapArgs.attkNum),weapArgs.preInit,'1','0',weapArgs.attks], 'current');
+ setInitVars( charCS, [MenuType.MW_PRIME,tokenID,rowIndex,('with their '+weapArgs.name),evalAttr(weapArgs.speed,charCS),(weapArgs.mult+'*'+weapArgs.attkNum),weapArgs.preInit,'1','0',weapArgs.attks], 'current');
args[0] = ((rowIndex2%2) > 0 ? BT.MELEE : BT.RANGED);
handleSecondWeapon( charCS, args, senderId );
}
/**
- * Handle the selection of the Two Weapons button on the Weapon menu
+ * Handle the selection of the Prime Weapon button on the Weapon menu
**/
var handlePrimeWeapon = function( charCS, args, senderId ) {
@@ -1510,8 +1525,6 @@ var initMaster = (function() {
var handleInitMIBag = function( charCS, args, senderId ) {
var repItemField,
- itemName,
- itemSpeed,
tokenID = args[1],
charButton = args[2],
rowIndex = args[3],
@@ -1520,8 +1533,10 @@ var initMaster = (function() {
if (_.isUndefined(rowIndex)) {
throw new Error( 'Invalid button while handling a initiative selection to use a magic item' );
}
- itemName = attrLookup( charCS, fields.Items_name, fields.Items_table, rowIndex );
- itemSpeed = (attrLookup( charCS, fields.Items_trueSpeed, fields.Items_table, rowIndex ) || attrLookup( charCS, fields.Items_speed, fields.Items_table, rowIndex ) || 0);
+ var Items = getTableGroupField( charCS, {}, fieldGroups.MI, 'name' );
+ Items = getTableGroupField( charCS, Items, fieldGroups.MI, 'trueSpeed' );
+ var itemName = tableGroupLookup( Items, 'name', rowIndex ),
+ itemSpeed = tableGroupLookup( Items, 'trueSpeed', rowIndex );
buildCall = '!init --buildMenu ' + senderId
+ '|' + MenuType.MIBAG
@@ -1768,7 +1783,7 @@ var initMaster = (function() {
actions = handleAllWeapons( senderId, charCS, args, base, (rowIndex != -2) );
- if (rowIndex == 0 && (initMenu == MenuType.COMPLEX || initMenu == MenuType.SIMPLE || initMenu == MenuType.WEAPON)) {
+ if ((rowIndex == 0 || rowIndex == -2) && (initMenu == MenuType.COMPLEX || initMenu == MenuType.SIMPLE || initMenu == MenuType.WEAPON)) {
var monAttk1 = (attrLookup( charCS, fields.Monster_dmg1 ) || '').split(','),
monAttk2 = (attrLookup( charCS, fields.Monster_dmg2 ) || '').split(','),
@@ -1782,7 +1797,8 @@ var initMaster = (function() {
monDmg2 = reDiceRollSpec.test(monAttk2[0]) ? monAttk2[1] : (monAttk2[0] || ''),
monDmg3 = reDiceRollSpec.test(monAttk3[0]) ? monAttk3[1] : (monAttk3[0] || '');
- actions = [new Set()];
+ if (rowIndex != -2) actions = [new Set()];
+
setAttr( charCS, fields.Prev_round, 0 );
setAttr( charCS, [fields.Prev_round[0] + tokenID, fields.Prev_round[1]], state.initMaster.round, null, null, null, true );
setAttr( charCS, fields.Init_chosen, 0 );
@@ -1797,15 +1813,15 @@ var initMaster = (function() {
setAttr( charCS, fields.Init_carryPreInit, 0 );
setAttr( charCS, fields.Init_carry2H, 0 );
- if (monAttk1[0].length && (rowIndex2 == 0 || rowIndex2 == 1)) {
+ if (monAttk1[0].length && (rowIndex == -2 || rowIndex2 == 0 || rowIndex2 == 1)) {
actions.push({init:(base+Math.round(monSpeed1)+monMod),ignore:0,action:('with their '+monDmg1),msg:(' rate 1, speed '+Math.round(monSpeed1)+'/'+init_Mult+', modifier '+monMod)});
for (let i=2; i<=init_Mult; i++) {actions.push({init:(base+Math.round(i * monSpeed1)+monMod),ignore:0,action:('with their '+monDmg1),msg:''})};
}
- if (monAttk2[0].length && (rowIndex2 == 0 || rowIndex2 == 2)) {
+ if (monAttk2[0].length && (rowIndex == -2 || rowIndex2 == 0 || rowIndex2 == 2)) {
actions.push({init:(base+Math.round(monSpeed2)+monMod),ignore:0,action:('with their '+monDmg2),msg:(' rate 1, speed '+Math.round(monSpeed2)+'/'+init_Mult+', modifier '+monMod)});
for (let i=2; i<=init_Mult; i++) {actions.push({init:(base+Math.round(i * monSpeed2)+monMod),ignore:0,action:('with their '+monDmg2),msg:''})};
}
- if (monAttk3[0].length && (rowIndex2 == 0 || rowIndex2 == 3)) {
+ if (monAttk3[0].length && (rowIndex == -2 || rowIndex2 == 0 || rowIndex2 == 3)) {
actions.push({init:(base+Math.round(monSpeed3)+monMod),ignore:0,action:('with their '+monDmg3),msg:(' rate 1, speed '+Math.round(monSpeed3)+'/'+init_Mult+', modifier '+monMod)});
for (let i=2; i<=init_Mult; i++) {actions.push({init:(base+Math.round(i * monSpeed3)+monMod),ignore:0,action:('with their '+monDmg3),msg:''})};
}
@@ -2018,14 +2034,14 @@ var initMaster = (function() {
/*
* Checks for the existence of magic items in the MI bag
*/
-
+
var checkForMIs = function( charCS ) {
- var MagicItems = getTableField( charCS, {}, fields.Items_table, fields.Items_name ),
- i = fields.Items_table[1],
+ var MagicItems = getTableGroupField( charCS, {}, fieldGroups.MI, 'name' ),
+ i = 0,
item;
- while (!_.isUndefined(item = MagicItems.tableLookup( fields.Items_name, i++, false ))) {
+ while (!_.isUndefined(item = tableGroupLookup( MagicItems, 'name', i++, false ))) {
if (item.length && item != '-') {return true;}
}
return false;
@@ -2240,19 +2256,20 @@ var initMaster = (function() {
var content = '',
inHandTitle = false,
MagicTable = getTableField( charCS, {}, fields.Magic_table, fields.Magic_name ),
- ItemsTable = getTableField( charCS, {}, fields.Items_table, fields.Items_name ),
+ ItemsTable = getTableGroupField( charCS, {}, fieldGroups.MI, 'name' ),
powerList = {},
magicName, miName, miQty;
MagicTable = getTableField( charCS, MagicTable, fields.Magic_table, fields.Magic_miName );
- ItemsTable = getTableField( charCS, ItemsTable, fields.Items_table, fields.Items_trueName );
- ItemsTable = getTableField( charCS, ItemsTable, fields.Items_table, fields.Items_qty );
+ ItemsTable = getTableGroupField( charCS, ItemsTable, fieldGroups.MI, 'trueName' );
+ ItemsTable = getTableGroupField( charCS, ItemsTable, fieldGroups.MI, 'qty' );
for (let r = MagicTable.table[1]; !_.isUndefined(magicName = MagicTable.tableLookup( fields.Magic_name, r, false )); r++) {
if (magicName != '-') {
miName = MagicTable.tableLookup( fields.Magic_miName, r );
- let itemRow = ItemsTable.tableFind( fields.Items_trueName, miName );
- miQty = ItemsTable.tableLookup( fields.Items_qty, itemRow );
- miName = ItemsTable.tableLookup( fields.Items_name, itemRow ) || 'Miagic Item';
+ let itemRow, itemTable;
+ [itemRow,itemTable] = tableGroupFind( ItemsTable, 'trueName', miName );
+ miQty = ItemsTable[itemTable].tableLookup( fields[fieldGroups[itemTable].prefix+'qty'], itemRow );
+ miName = ItemsTable[itemTable].tableLookup( fields[fieldGroups[itemTable].prefix+'name'], itemRow ) || 'Magic Item';
if (!powerList[miName]) powerList[miName] = {};
powerList[miName][magicName] = [r,(isNaN(parseInt(miQty)) ? 1 : miQty)];
}
@@ -2340,10 +2357,6 @@ var initMaster = (function() {
async function makeWeaponButtons( tokenID, senderId, charButton, submitted, MWcmd, RWcmd, show2H=true, showDancing=true, showInHand=true, showWeapons=false, MWtable, RWtable ) {
try {
-// if (_.isUndefined(show2H) || _.isNull(show2H)) {show2H = true};
-// if (_.isUndefined(showDancing) || _.isNull(showDancing)) {showDancing = true};
-// if (_.isUndefined(showInHand) || _.isNull(showInHand)) {showInHand = true};
-
var charCS = getCharacter( tokenID,false ),
weapName,
ammoRowAdj,
@@ -2356,9 +2369,9 @@ var initMaster = (function() {
content = '',
weapList = [],
dancingWeapons = '',
- ItemsTable = getTableField( charCS, {}, fields.Items_table, fields.Items_name );
+ ItemsTable = getTableGroupField( charCS, {}, fieldGroups.MI, 'name' );
- ItemsTable = getTableField( charCS, ItemsTable, fields.Items_table, fields.Items_qty );
+ ItemsTable = getTableGroupField( charCS, ItemsTable, fieldGroups.MI, 'qty' );
// build the character Melee Weapon list
@@ -2388,20 +2401,22 @@ var initMaster = (function() {
content += '**Melee Weapons**\n';
header = false;
}
- let miName = WeaponTable.tableLookup( fields.MW_miName, i ) || '';
+ let miName = WeaponTable.tableLookup( fields.MW_miName, i ) || '',
+ itemIndex = attrLookup( charCS, fields.InHand_index, fields.InHand_table, WeaponTable.tableLookup( fields.MW_hand, i )),
+ tableIndex, tableRowID, table;
+ [tableIndex,table,tableRowID] = tableGroupIndex( ItemsTable, itemIndex );
if (showWeapons && miName && miName.length) {
if (weapList.includes(miName.dbName())) continue;
weapList.push(miName.dbName());
weapName = miName;
}
- let weapData = resolveData( miName, fields.WeaponDB, reNotAttackData, charCS, {chargeType:reWeapSpecs.chargeType} ),
+ let weapData = resolveData( miName, fields.WeaponDB, reNotAttackData, charCS, {chargeType:reWeapSpecs.chargeType}, tableIndex, tableRowID ),
weapCharged = weapData.chargeType && !(['uncharged','cursed','single-uncharged'].includes(weapData.chargeType.toLowerCase())),
charges = weapCharged ? (WeaponTable.tableLookup( fields.MW_charges, i ) || 1) : 0,
exhausted = submitted,
qty = '';
if (charges) {
- let itemIndex = attrLookup( charCS, fields.InHand_index, fields.InHand_table, WeaponTable.tableLookup( fields.MW_hand, i ));
- qty = _.isUndefined(itemIndex) ? 0 : ItemsTable.tableLookup( fields.Items_qty, itemIndex ) || 0;
+ qty = _.isUndefined(itemIndex) ? 0 : tableGroupLookup( ItemsTable, 'qty', itemIndex ) || 0;
exhausted = qty < charges;
qty = String(qty) + ' ';
}
@@ -2454,14 +2469,15 @@ var initMaster = (function() {
header = false;
}
let miName = WeaponTable.tableLookup( fields.RW_miName, i ) || '',
- weapData = resolveData( miName, fields.WeaponDB, reNotAttackData, charCS, {chargeType:reWeapSpecs.chargeType} ),
+ itemIndex,itemRowID,table;
+ [itemIndex,table,itemRowID] = tableGroupFind( ItemsTable, 'name', miName );
+ let weapData = resolveData( miName, fields.WeaponDB, reNotAttackData, charCS, {chargeType:reWeapSpecs.chargeType}, itemIndex, itemRowID ),
weapCharged = weapData.chargeType && !(['uncharged','cursed','single-uncharged'].includes(weapData.chargeType.toLowerCase())),
charges = weapCharged ? WeaponTable.tableLookup( fields.RW_charges, i ) : 0,
exhausted = submitted,
qty = '';
if (charges) {
- let itemIndex = ItemsTable.tableFind( fields.Items_name, miName );
- qty = _.isUndefined(itemIndex) ? 0 : ItemsTable.tableLookup( fields.Items_qty, itemIndex ) || 0;
+ qty = _.isUndefined(itemIndex) ? 0 : ItemsTable[table].tableLookup( fields[fieldGroups[table].prefix+'qty'], itemIndex ) || 0;
exhausted = qty < charges;
qty = String(qty) + ' ';
}
@@ -2573,15 +2589,15 @@ var initMaster = (function() {
weaponButtons,buttonID,content,
primeHand, primeRef, offHand, offRef, bothHands, bothRef;
- var MW_handFields = getTableField( charCS, {}, fields.MW_table, fields.MW_hand ),
+ var MW_handFields = getTableField( charCS, {}, fields.MW_table, fields.MW_name ),
+ MW_handFields = getTableField( charCS, MW_handFields, fields.MW_table, fields.MW_hand ),
MW_handFields = getTableField( charCS, MW_handFields, fields.MW_table, fields.MW_miName ),
- MW_handFields = getTableField( charCS, MW_handFields, fields.MW_table, fields.MW_name ),
MW_handFields = getTableField( charCS, MW_handFields, fields.MW_table, fields.MW_twoHanded ),
MW_handFields = getTableField( charCS, MW_handFields, fields.MW_table, fields.MW_dancing ),
MW_handFields = getTableField( charCS, MW_handFields, fields.MW_table, fields.MW_charges ),
- RW_handFields = getTableField( charCS, {}, fields.RW_table, fields.RW_hand ),
+ RW_handFields = getTableField( charCS, {}, fields.RW_table, fields.RW_name ),
+ RW_handFields = getTableField( charCS, RW_handFields, fields.RW_table, fields.RW_hand ),
RW_handFields = getTableField( charCS, RW_handFields, fields.RW_table, fields.RW_miName ),
- RW_handFields = getTableField( charCS, RW_handFields, fields.RW_table, fields.RW_name ),
RW_handFields = getTableField( charCS, RW_handFields, fields.RW_table, fields.RW_charges, '', 1 ),
RW_handFields = getTableField( charCS, RW_handFields, fields.RW_table, fields.RW_twoHanded, '', 1 ),
RW_handFields = getTableField( charCS, RW_handFields, fields.RW_table, fields.RW_dancing, '', 0 ),
@@ -2595,26 +2611,25 @@ var initMaster = (function() {
let miName = (InHandField.tableLookup( fields.InHand_miName, hand ) || InHandField.tableLookup( fields.InHand_name, hand ) || '');
- if (!_.isUndefined(ref) && !!miName && miName.dbName().length) {
+/* if (!_.isUndefined(ref) && !!miName && miName.dbName().length) {
index = ((ref%2) > 0 ? ((ref-(1+(fields.MW_table[1]*2)))/2) : ((ref-(2+(fields.RW_table[1]*2)))/2));
+ log('getRef: early exit: miName = '+miName+', hand = '+hand+', ref defined = '+ref+', index calculates to '+index);
return [index,ref];
}
-
+*/
if (!_.isUndefined(index = MW_handFields.tableFind( fields.MW_hand, hand ))) {
ref = (1 - (fields.MW_table[1] * 2)) + (index * 2);
} else if (!_.isUndefined(index = RW_handFields.tableFind( fields.RW_hand, hand ))) {
ref = (2 - (fields.RW_table[1] * 2)) + (index * 2);
- } else {
- if (!!miName && miName.dbName().length && miName !== '-') {
- if (!_.isUndefined(index = MW_handFields.tableFind( fields.MW_miName, miName ))) {
- ref = (1 - (fields.MW_table[1] * 2)) + (index * 2);
- } else if (!_.isUndefined(index = RW_handFields.tableFind( fields.RW_miName, miName ))) {
- ref = (2 - (fields.RW_table[1] * 2)) + (index * 2);
- } else if (!_.isUndefined(index = MW_handFields.tableFind( fields.MW_name, miName ))) {
- ref = (1 - (fields.MW_table[1] * 2)) + (index * 2);
- } else if (!_.isUndefined(index = RW_handFields.tableFind( fields.RW_name, miName ))) {
- ref = (2 - (fields.RW_table[1] * 2)) + (index * 2);
- };
+ } else if (!!miName && miName.dbName().length && miName !== '-') {
+ if (!_.isUndefined(index = MW_handFields.tableFind( fields.MW_miName, miName ))) {
+ ref = (1 - (fields.MW_table[1] * 2)) + (index * 2);
+ } else if (!_.isUndefined(index = RW_handFields.tableFind( fields.RW_miName, miName ))) {
+ ref = (2 - (fields.RW_table[1] * 2)) + (index * 2);
+ } else if (!_.isUndefined(index = MW_handFields.tableFind( fields.MW_name, miName ))) {
+ ref = (1 - (fields.MW_table[1] * 2)) + (index * 2);
+ } else if (!_.isUndefined(index = RW_handFields.tableFind( fields.RW_name, miName ))) {
+ ref = (2 - (fields.RW_table[1] * 2)) + (index * 2);
};
};
if (!forceFind) rememberWeapRef(charCS,hand,ref);
@@ -2641,7 +2656,7 @@ var initMaster = (function() {
if ((charButton%2)>0) {
handleInitMW( CharSheet.CHARACTER, charCS, [BT.MELEE,tokenID,charButton,charHand], senderId );
} else {
- handleInitRW( CharSheet.CHARACTER, charCS, [BT.MELEE,tokenID,charButton,charHand], senderId );
+ handleInitRW( CharSheet.CHARACTER, charCS, [BT.RANGED,tokenID,charButton,charHand], senderId );
}
return;
}
@@ -2673,7 +2688,7 @@ var initMaster = (function() {
+ (submitted ? '' : ('](!init --button ' + BT.TWOWEAPONS + '|' + tokenID + '|' + offRef + '|' + primeRef + '|' + offHand + '|' + primeHand + ')'))
+ '}}';
}
- if (hands > 2 || weapCount.monster > 1) {
+ if (hands > 2 || (weapCount.monster > 0 && weapCount.melee > 0)) {
content += '{{Many Hands Option='
+ (-2 == charButton ? '' : (submitted ? '' : '['))
+ 'All Weapons'
@@ -2970,7 +2985,17 @@ var initMaster = (function() {
inHandTitle = false,
inBagTitle = false,
inHandMIs = '',
- buttonID = 0;
+ buttonID = 0,
+ sheetVer = csVer(charCS),
+ sections = {POTIONS:{n:2,name:'**Potions**'},
+ DUSTS:{n:3,name:'**Dusts**'},
+ WANDS:{n:4,name:'**Rods, Staves & Wands**'},
+ SCROLLS:{n:5,name:'**Scrolls & Books**'},
+ GEAR:{n:6,name:'**Weapons, Armour & Gear**'},
+ MISC:{n:7,name:'**Miscellaneous**'},
+ TREASURE:{n:8,name:'**Treasure**'},
+ COINS:{n:9,name:'**Coins**'}},
+ config = getSetPlayerConfig(senderId);
tokenName = getObj( 'graphic', tokenID ).get('name');
@@ -2984,24 +3009,32 @@ var initMaster = (function() {
// build the Magic Item list
- miTable = getTableField( charCS, {}, fields.Items_table, fields.Items_name );
- for (r = miTable.table[1]; !_.isUndefined(miName = miTable.tableLookup( fields.Items_name, r, false )); r++) {
- if (miName != '-') {
- if (!inBagTitle) {
- content += '{{Section2=Magic Items in Bag\n';
+ let sectType = (_.isUndefined(config) || _.isUndefined(config.sections) || !!config.sections) ? 'Hide' : 'Show';
+ content += '{{ =Items on your person *'+sectType+' sections*}}';
+
+ var sectHeader = '{{Section2=Magic Items in Bag\n';
+ miTable = getTableGroupField( charCS, {}, fieldGroups.MI, 'name' );
+ for (const table in miTable) {
+ if (sheetVer > 3.9 && config.sections !== false) sectHeader = '{{Section'+sections[table].n+'='+sections[table].name+'\n';
+ for (r = 0; !_.isUndefined(miName = miTable[table].tableLookup( fields[fieldGroups[table].prefix+'name'], r, false )); r++) {
+ if (miName != '-') {
+ content += sectHeader
+ + (buttonID == charButton ? ('') : (submitted ? '' : '['))
+ + miName
+ + (((buttonID == charButton) || submitted) ? '' : '](!init --button ' + BT.MI_BAG + '|' + tokenID + '|' + buttonID + '|' + r + ')');
+ sectHeader = '';
inBagTitle = true;
}
- content += (buttonID == charButton ? '' : (submitted ? '' : '['));
- content += miName;
- content += (((buttonID == charButton) || submitted) ? '' : '](!init --button ' + BT.MI_BAG + '|' + tokenID + '|' + buttonID + '|' + r + ')');
+ buttonID++;
}
- buttonID++;
- }
- if (inBagTitle) {
- content += '}}';
- }
+ if (inBagTitle && sheetVer > 3.9 && config.sections !== false) {
+ content += '}}';
+ inBagTitle = false;
+ };
+ };
+ if (inBagTitle) content += '}}';
- if (!inHandTitle && !inBagTitle) {
+ if (!!sectHeader.length) {
sendParsedMsg( tokenID, Init_Messages.noMIBag, null, flags.feedbackName );
return;
}
@@ -3260,8 +3293,8 @@ var initMaster = (function() {
classes = classObjects( charCS, senderId, parseTable ),
race = (attrLookup( charCS, fields.Race ) || 'human').dbName(),
tokenName = getObj('graphic',tokenID).get('name');
- var ItemNames = getTableField( charCS, {}, fields.Items_table, fields.Items_name ),
- ItemNames = getTableField( charCS, ItemNames, fields.Items_table, fields.Items_trueName ),
+ var ItemNames = getTableGroupField( charCS, {}, fieldGroups.MI, 'name' ),
+ ItemNames = getTableGroupField( charCS, ItemNames, fieldGroups.MI, 'trueName' ),
Inits = getTable( charCS, fieldGroups.INIT ),
totalMod = 0,
eqModFlag = false,
@@ -3313,11 +3346,13 @@ var initMaster = (function() {
itemClasses = [],
itemSuperTypes = [],
nameArray = [],
- item = '';
- for (let r = 0; !_.isUndefined(item = ItemNames.tableLookup( fields.Items_name, r, false )); r++) {
+ item = '',
+ tableIndex, tableRowID, table;
+ for (let r = 0; !_.isUndefined(item = tableGroupLookup( ItemNames, 'name', r, false )); r++) {
if (item === '-') continue;
- let trueItem = ItemNames.tableLookup( fields.Items_trueName, r ),
- itemData = resolveData( trueItem, fields.MagicItemDB, reNotAttackData, charCS, parseTable, r ).parsed,
+ let trueItem = tableGroupLookup( ItemNames, 'trueName', r );
+ [tableIndex,table,tableRowID] = tableGroupIndex( ItemNames, r );
+ let itemData = resolveData( trueItem, fields.MagicItemDB, reNotAttackData, charCS, parseTable, tableIndex, tableRowID ).parsed,
itemObj = abilityLookup( fields.MagicItemDB, trueItem ),
addRules = itemData.rules.split('|').map( r => (r[0] === '-' ? '-' : '')+r.dbName() ),
specsArray = (!!itemObj.obj ? itemObj.specs() : ['-','-','magic','1H','-']);
@@ -3534,6 +3569,7 @@ var initMaster = (function() {
return;
}
setInitVars( charCS, args, 'current');
+// log('doBuildMenu: senderId = '+senderId+', menu = '+menu+', args = '+args);
buildMenu( menu, charCS, MenuState.ENABLED, args, senderId );
return;
}
@@ -3697,27 +3733,30 @@ var initMaster = (function() {
modEq = (args[3] || ' ')[0] === '=' ? '=' : '',
multOp = '=+-*/'.includes((args[4] || ' ')[0]),
multEq = (args[4] || ' ')[0] === '=' ? '=' : '',
- modVal = (modOp ? args[3].slice(1) : args[3]) || 0,
- multVal = (multOp ? args[4].slice(1) : args[4]) || 1,
+ modVal = parseFloat(evalAttr((modOp ? args[3].slice(1) : args[3]),charCS)) || 0,
+ multVal = parseFloat(evalAttr((multOp ? args[4].slice(1) : args[4]),charCS)) || 1,
silent = silent || ((args[5] || '').toLowerCase() === 'silent'),
values = initValues( InitMagic.fieldGroup ),
msg = '';
+ log('doMagicInitEffect: modOp:'+modOp+', modVal:'+modVal+', multOp:'+multOp+', multVal:'+multVal+', args = '+args);
+
if (_.isUndefined(initIndex) && (modVal !== 0 || multVal !== 1)) {
- modVal = evalAttr( args[3] || modVal );
- multVal = evalAttr( args[4] || multVal );
msg = 'Added new magic initiative effect *'+name+'* with mod '+(modVal>0 ? '+' : '')+modVal+' and mult '+'x'+multVal;
values[fields.InitMagic_cmd[0]][fields.InitMagic_cmd[1]] = cmd;
values[fields.InitMagic_name[0]][fields.InitMagic_name[1]] = name;
values[fields.InitMagic_mod[0]][fields.InitMagic_mod[1]] = modVal;
values[fields.InitMagic_mult[0]][fields.InitMagic_mult[1]] = multVal;
InitMagic = InitMagic.addTableRow( initIndex, values );
+ log('doMagicInitEffect: set row '+(InitMagic.sortKeys.length-1)+', with msg '+msg);
+ log('doMagicInitEffect: stored modVal = '+InitMagic.tableLookup( fields.InitMagic_mod, (InitMagic.sortKeys.length-1)));
} else if (!_.isUndefined(initIndex)) {
+ log('doMagicInitEffect: updating an existing line, e.g. to delete it');
let curMod = InitMagic.tableLookup( fields.InitMagic_mod, initIndex ),
curMult = InitMagic.tableLookup( fields.InitMagic_mult, initIndex );
- modVal = evalAttr( modEq ? args[3] : (modOp ? curMod+args[3] : modVal) );
- multVal = evalAttr( multEq ? args[3] : (multOp ? curMult+args[3] : multVal) );
+ modVal = modEq || !modOp ? modVal : (parseFloat(evalAttr(curMod+args[3],charCS )) || 0);
+ multVal = multEq || !multOp ? multVal : (parseFloat(evalAttr(curMult+args[3],charCS )) || 1);
if (cmd === 'del' || (modVal === 0 && multVal === 1)) {
InitMagic = InitMagic.delTableRow( initIndex );
@@ -3741,7 +3780,9 @@ var initMaster = (function() {
break;
};
};
- }
+ } else {
+ log('doMagicInitEffect: oops! not doing anything');
+ };
makeCheckInitMenu( tokenID, charCS, senderId, silent, msg );
return;
}
@@ -4061,10 +4102,10 @@ var initMaster = (function() {
names = _.without(names,tokenName);
setAttr( charObj, fields.Timespent, '1' );
setAttr( charObj, fields.CharDay, state.moneyMaster.inGameDay );
- if (cost == 0) return true;
- setAttr( charObj, fields.Money_copper, ((parseInt(attrLookup( charObj, fields.Money_copper )||0)||0) - Math.floor((cost*100)%10)) );
- setAttr( charObj, fields.Money_silver, ((parseInt(attrLookup( charObj, fields.Money_silver )||0)||0) - Math.floor((cost*10)%10)) );
- setAttr( charObj, fields.Money_gold, ((parseInt(attrLookup( charObj, fields.Money_gold )||0)||0) - Math.floor(cost)) );
+ if (cost != 0) spendMoney( charObj, cost );
+// setAttr( charObj, fields.Money_copper, ((parseInt(attrLookup( charObj, fields.Money_copper )||0)||0) - Math.floor((cost*100)%10)) );
+// setAttr( charObj, fields.Money_silver, ((parseInt(attrLookup( charObj, fields.Money_silver )||0)||0) - Math.floor((cost*10)%10)) );
+// setAttr( charObj, fields.Money_gold, ((parseInt(attrLookup( charObj, fields.Money_gold )||0)||0) - Math.floor(cost)) );
return true;
});
content += '}}';
@@ -4124,7 +4165,7 @@ var initMaster = (function() {
if (!args)
{return;}
- if (args.length < 1 || args.length > 10) {
+ if (args.length < 1) {
throw new Error('Invalid initMaster button command syntax');
}
@@ -4289,6 +4330,18 @@ var initMaster = (function() {
handleInitSubmit( senderId, charCS, args );
break;
+ case BT.SECTIONS_OPTION:
+
+ // Handle swapping the display of items in the items bag into sections
+
+ let config = getSetPlayerConfig( senderId );
+ config.sections = _.isUndefined(config.sections) ? false : !!!config.sections;
+ getSetPlayerConfig( senderId, config );
+ args.splice(0,2);
+ let cmd = args.shift()
+ sendAPI( '!init --'+cmd+' '+args.join('|') );
+ break;
+
default:
throw new Error( 'doButton: invalid action name for switch - "' + handler + '"' );
@@ -4544,7 +4597,7 @@ var initMaster = (function() {
if (senderMod.length > 1) senderId = fixSenderId( [senderMod[1]], selected, senderId );
if (!flags.noWaitMsg) {
- sendWait(senderId,50,'initMaster');
+ sendWait(senderId,1,'initMaster');
}
_.each(args, function(e) {
diff --git a/InitMaster/script.json b/InitMaster/script.json
index ddbb876ba..3a1dcf13d 100644
--- a/InitMaster/script.json
+++ b/InitMaster/script.json
@@ -2,8 +2,8 @@
"$schema": "https://github.com/DameryDad/roll20-api-scripts/blob/InitMaster/InitMaster/Script.json",
"name": "InitMaster",
"script": "initMaster.js",
- "version": "4.0.2",
- "previousversions": ["1.037","1.039","1.041","1.043","1.045","1.046","1.3.00","1.3.01","1.3.02","1.3.03","1.4.01","1.4.02","1.4.05","1.4.06","1.4.07","1.5.01","2.1.0","2.2.0","2.3.0","2.3.1","2.3.3","3.0.0","3.1.2","3.2.0","3.3.0","3.3.1","3.4.0","3.5.0","4.0.1"],
+ "version": "5.0.0",
+ "previousversions": ["1.037","1.039","1.041","1.043","1.045","1.046","1.3.00","1.3.01","1.3.02","1.3.03","1.4.01","1.4.02","1.4.05","1.4.06","1.4.07","1.5.01","2.1.0","2.2.0","2.3.0","2.3.1","2.3.3","3.0.0","3.1.2","3.2.0","3.3.0","3.3.1","3.4.0","3.5.0","4.0.1","4.0.2"],
"description": "The InitMaster API supports initiative for RPGs using the Turn Order and the Tracker window. It provides functions dealing with all aspects of: managing how initiative is done; rolling for initiative; for 'group' and 'individual' initiative types providing Character action selection to determine the speed and number of attacks of weapons, the casting time of spells & the usage speed of magic items; supporting initiative for multiple attacks with one or multiple weapons per round; supporting and tracking actions that take multiple rounds; managing the resulting Turn Order; as well as performing the 'End of Day' activity. It works very closely with the RoundMaster API to the extent that InitiativeMaster cannot work without RoundMaster (though the reverse is possible). InitiativeMaster also works closely with AttackMaster API and MagicMaster API and uses the data configured on the Character Sheet by these other APIs, although it can use manually completed Character Sheets once correctly configured.\n[InitMaster Documentation](https://wiki.roll20.net/Script:InitMaster) \n\n### Related APIs\nThis API works best with the RPGMaster series of APIs, and requires RoundMaster API to work (loaded automatically on One-Click install)\n[RPGMaster Documentation](https://wiki.roll20.net/RPGMaster)\n\n### Getting Started\n* If using with CommandMaster API, use the `!cmd --initialise` command to install the DMs Macro Quick Bar buttons, or\n* If not using CommandMaster API, as a Macro in the DM's macro quick bar, add the command `!init --maint` to manage RoundMaster functions\n* Add the command `!init --menu` as an Ability Macros on Character Sheets of Characters, NPCs & Monsters that will use the API, and tick 'Show as Token Action'. These menus will then be available to Players controlling those sheets and give access to all common commands used in game-play.",
"authors": "Richard E.",
"roll20userid": "6497708",