Skip to content

Commit 43c7bbb

Browse files
authored
Merge branch 'Roll20:master' into add-faerun-calendar
2 parents f985bb7 + 74dcdec commit 43c7bbb

19 files changed

+44555
-188
lines changed

AttackMaster/3.2.1/attackMaster.js

+7,255
Large diffs are not rendered by default.

AttackMaster/attackMaster.js

+34-40
Original file line numberDiff line numberDiff line change
@@ -181,14 +181,18 @@ API_Meta.AttackMaster={offset:Number.MAX_SAFE_INTEGER,lineCount:-1};
181181
* which are uncharged but allow c: charge comparisons to support enable
182182
* and disable of weapon attack rows. Reintroduce support for effects on
183183
* '-inhand' in such a way as not to clash with new dancing weapons type.
184+
* v3.2.1 11/02/2024 Link weapon tables to InHand table by storing the row number. Use this
185+
* to reveal hidden weapons on an attack if "reveal on use" set. Improve
186+
* parseStr() to handle undefined or empty strings without erroring. Fixed
187+
* ad-hoc dancing weapons.
184188
*/
185189

186190
var attackMaster = (function() {
187191
'use strict';
188-
var version = '3.2.0',
192+
var version = '3.2.1',
189193
author = 'Richard @ Damery',
190194
pending = null;
191-
const lastUpdate = 1707473376;
195+
const lastUpdate = 1708765145;
192196

193197
/*
194198
* Define redirections for functions moved to the RPGMaster library
@@ -750,7 +754,7 @@ var attackMaster = (function() {
750754
const reRangeData = /}}\s*?rangedata\s*?=.*?(?:\n.*?)*{{/im;
751755
const reACData = /}}\s*acdata\s*=(.*?){{/im;
752756
const reRingData = /(?:ring|ac)data\s*?=\s*?(\[.*?\])/im;
753-
const reItemData = /}}[\s\w\-]*?(?<!tohit|dmg|ammo|range)data\s*?=\s*?\[[^{]*?\]/im;
757+
const reItemData = /}}[\s\w\-]*?(?<!tohit|dmg|ammo|range)data\s*?=\s*?\[.*?\]{{/im;
754758

755759
const reRangeMods = Object.freeze ({
756760
near: {field:'N',def:'-5',re:/[\[,\s]N:([-\+\d]+?)[,\]]/i},
@@ -932,7 +936,6 @@ var attackMaster = (function() {
932936
setTimeout( () => updateHandouts(handouts,true,findTheGM()), 30);
933937
setTimeout( cmdMasterRegister, 40 );
934938
setTimeout( () => updateDBindex(false), 90); // checking the DB indexing
935-
setTimeout( rebuildDancers, 100 );
936939
} catch (e) {
937940
log('AttackMaster Initialisation: JavaScript '+e.name+': '+e.message+' while initialising the API');
938941
sendDebug('AttackMaster Initialisation: JavaScript '+e.name+': '+e.message+' while initialising the API');
@@ -1125,7 +1128,7 @@ var attackMaster = (function() {
11251128
* Function to replace special characters in a string
11261129
*/
11271130

1128-
var parseStr=function(str,replaced=replacers){
1131+
var parseStr=function(str='',replaced=replacers){
11291132
return replaced.reduce((m, rep) => m.replace(rep[0], rep[1]), str);
11301133
}
11311134

@@ -1804,34 +1807,6 @@ var attackMaster = (function() {
18041807
return true;
18051808
}
18061809

1807-
/*
1808-
* Check for any "in-hand" dancing weapons and prompt
1809-
* RoundMaster to rebuild their dancing effects after a
1810-
* campaign restart.
1811-
*/
1812-
1813-
var rebuildDancers = function() {
1814-
1815-
var charID, dancer, weapon;
1816-
1817-
filterObjs( obj => {
1818-
if (obj.get('_type') !== 'graphic') return false;
1819-
if (!(charID = obj.get('represents'))) return false;
1820-
let charCS = getObj('character',charID);
1821-
if (!charCS) return false;
1822-
let Inhand = getTable( charCS, fieldGroups.INHAND );
1823-
for (let i=0; !_.isUndefined(weapon = Inhand.tableLookup( fields.InHand_name, i, false )); i++) {
1824-
if (weapon === '-') continue;
1825-
dancer = Inhand.tableLookup( fields.InHand_dancer, i );
1826-
if (dancer.length) {
1827-
let weapon = Inhand.tableLookup( fields.InHand_trueName, i ),
1828-
type = Inhand.tableLookup( fields.InHand_type, i );
1829-
sendAPI( fields.roundMaster+' --dancer rebuild|'+obj.id+'|'+weapon+'|'+type+'|'+dancer );
1830-
};
1831-
};
1832-
});
1833-
};
1834-
18351810
/* ----------------------------------------------- Weapon Management Functions ----------------------------------------
18361811
18371812
/*
@@ -2092,7 +2067,7 @@ var attackMaster = (function() {
20922067
if (weapName && weapName.length && !sheathed.includes(weapName)) {
20932068
sheathed.push(weapName);
20942069
sendAPImacro(curToken,'',weapName,'-sheath');
2095-
let weapData = resolveData( weapName, fields.MagicItemDB, /weapdata\s*?=\s*?(\[.*?\])/im, charCS ).parsed;
2070+
let weapData = resolveData( weapName, fields.MagicItemDB, reItemData, charCS ).parsed;
20962071
if (weapData && weapData.off) {
20972072
sendAPI( parseStr(weapData.off).replace(/@{\s*selected\s*\|\s*token_id\s*}/ig,tokenID)
20982073
.replace(/{\s*selected\s*\|/ig,'{'+charCS.get('name')+'|'), null, 'attk filterWeapons');
@@ -2198,8 +2173,10 @@ var attackMaster = (function() {
21982173
values[fields.Ammo_range[0]][fields.Ammo_range[1]]=((rangeDataArray[w] || rangeDataArray[0])[0].match(/[\[,\s]r:(=?[+-]?[\s\w\+\-\d\/]+)[,\]]/i) || ['',''])[1];
21992174
values[fields.Ammo_type[0]][fields.Ammo_type[1]]=ammoType;
22002175
values[fields.Ammo_miName[0]][fields.Ammo_miName[1]]=ammoTrueName;
2176+
values[fields.Ammo_miIndex[0]][fields.Ammo_miIndex[1]]=miIndex;
22012177

2202-
tableInfo.AMMO = tableInfo.AMMO.addTableRow( tableInfo.AMMO.tableFind( fields.Ammo_name, [values[fields.Ammo_name[0]][fields.Ammo_name[1]],'-'] ), values );
2178+
ammoRow = tableInfo.AMMO.tableFind( fields.Ammo_miName, ammoTrueName ) || tableInfo.AMMO.tableFind( fields.Ammo_name, '-' );
2179+
tableInfo.AMMO = tableInfo.AMMO.addTableRow( ammoRow, values );
22032180

22042181
if (!_.isUndefined(tableInfo.WEAP)) {
22052182
dispValues[fields.Weap_dmgBonus[0]][fields.Weap_dmgBonus[1]] = dispValues[fields.Weap_adj[0]][fields.Weap_adj[1]];
@@ -2350,8 +2327,8 @@ var attackMaster = (function() {
23502327
innate = weapon[2].toLowerCase().includes('innate'),
23512328
weapData = parseData( toHit, reWeapSpecs ),
23522329
attk2H = noOfHands == 2 ? 1 : 0,
2353-
weapType = toHit.type || weapon[1],
2354-
weapSuperType = toHit.superType || weapon[4],
2330+
weapType = weapData.type || weapon[1],
2331+
weapSuperType = weapData.superType || weapon[4],
23552332
proficiency = innate ? 0 : proficient( charCS, weaponTrueName, weapType, weapSuperType );
23562333

23572334
if (!levelTest( charCS, weapData )) continue;
@@ -2376,6 +2353,7 @@ var attackMaster = (function() {
23762353
values[fields.MW_superType[0]][fields.MW_superType[1]]=weapSuperType;
23772354
values[fields.MW_dancing[0]][fields.MW_dancing[1]]=(dancing?1:0);
23782355
values[fields.MW_attkCount[0]][fields.MW_attkCount[1]]=weaponAttkCount;
2356+
values[fields.MW_hand[0]][fields.MW_hand[1]]=handIndex;
23792357
if (!values[fields.MW_message[0]][fields.MW_message[1]]) values[fields.MW_message[0]][fields.MW_message[1]] = weapParsed.message;
23802358
if (!values[fields.MW_cmd[0]][fields.MW_cmd[1]]) values[fields.MW_cmd[0]][fields.MW_cmd[1]] = weapParsed.cmd;
23812359
dancingProf = parseInt(values[fields.MW_dancingProf[0]][fields.MW_dancingProf[1]]);
@@ -2422,6 +2400,7 @@ var attackMaster = (function() {
24222400
values[fields.RW_superType[0]][fields.RW_superType[1]]=weapSuperType;
24232401
values[fields.RW_dancing[0]][fields.RW_dancing[1]]=(dancing?1:0);
24242402
values[fields.RW_attkCount[0]][fields.RW_attkCount[1]]=weaponAttkCount;
2403+
values[fields.RW_hand[0]][fields.RW_hand[1]]=handIndex;
24252404
if (!values[fields.RW_message[0]][fields.RW_message[1]]) values[fields.RW_message[0]][fields.RW_message[1]] = weapParsed.message;
24262405
if (!values[fields.RW_cmd[0]][fields.RW_cmd[1]]) values[fields.RW_cmd[0]][fields.RW_cmd[1]] = weapParsed.cmd;
24272406
dancingProf = parseInt(values[fields.RW_dancingProf[0]][fields.RW_dancingProf[1]]);
@@ -3017,7 +2996,6 @@ var attackMaster = (function() {
30172996
return new Promise(resolve => {
30182997

30192998
try {
3020-
30212999
var tokenID = args[1],
30223000
attkType = args[2],
30233001
dmgMsg = parseStr(args[5] || attrLookup( charCS, fields.Dmg_specials ) || ''),
@@ -3032,6 +3010,7 @@ var attackMaster = (function() {
30323010
mwNumber = mwIndex + (fields.MW_table[1]==0 ? 1 : 2),
30333011
weaponName = tableInfo.MELEE.tableLookup( fields.MW_name, mwIndex ),
30343012
miName = tableInfo.MELEE.tableLookup( fields.MW_miName, mwIndex ),
3013+
miRowref = attrLookup( charCS, fields.InHand_index, fields.InHand_table, tableInfo.MELEE.tableLookup( fields.MW_hand, mwIndex ) ),
30353014
dancing = tableInfo.MELEE.tableLookup( fields.MW_dancing, mwIndex ),
30363015
attkAdj = (tableInfo.MELEE.tableLookup( fields.MW_adj, mwIndex ) || '').replace(/^[+-]([+-])/,'$1'),
30373016
attkStyleAdj= tableInfo.MELEE.tableLookup( fields.MW_styleAdj, mwIndex ),
@@ -3246,6 +3225,7 @@ var attackMaster = (function() {
32463225
setAbility( charCS, 'Do-not-use-DmgL-MW'+mwNumber, (parseMWattkMacro(args, charCS, attkType, addDmgMsg( dmgMacroDef.obj[1].body, dmgMsg, weapDmgMsg ))+attkMacro));
32473226
hitCharges = (hitCharges == '' ? 1 : hitCharges);
32483227
attkMacro = weapCharged && hitCharges ? ('\n!magic --mi-charges '+tokenID+'|-'+hitCharges+'|'+miName+'||'+weapCharged) : '';
3228+
attkMacro += ((weaponName !== miName) && attrLookup( charCS, fields.Items_reveal, fields.Items_table, miRowref ).toLowerCase() == 'use') ? ('\n!magic --button GM-ResetSingleMI|'+tokenID+'|'+miRowref+'|silent') : '';
32493229
attkMacro += weapCmd ? ('\n' + weapCmd) : '';
32503230
attkMacro += touchWeap ? ('\n!attk --blank-weapon '+tokenID+'|'+miName+'|silent') : '';
32513231
if (abilityType == Attk.TARGET) {
@@ -3298,6 +3278,7 @@ var attackMaster = (function() {
32983278
rwNumber = rwIndex + (fields.RW_table[1]==0 ? 1 : 2),
32993279
weaponName = tableInfo.RANGED.tableLookup( fields.RW_name, rwIndex ),
33003280
miName = tableInfo.RANGED.tableLookup( fields.RW_miName, rwIndex ),
3281+
miRowref = attrLookup( charCS, fields.InHand_index, fields.InHand_table, tableInfo.RANGED.tableLookup( fields.RW_hand, rwIndex ) ),
33013282
dancing = tableInfo.RANGED.tableLookup( fields.RW_dancing, rwIndex ),
33023283
attkAdj = tableInfo.RANGED.tableLookup( fields.RW_adj, rwIndex ),
33033284
weapDmgAdj = /^[+-][+-]/.test(attkAdj),
@@ -3319,6 +3300,7 @@ var attackMaster = (function() {
33193300
weapTypeTxt = (slashWeap?'S':'')+(pierceWeap?'P':'')+(bludgeonWeap?'B':''),
33203301
ammoName = tableInfo.AMMO.tableLookup( fields.Ammo_name, ammoIndex ),
33213302
ammoMIname = tableInfo.AMMO.tableLookup( fields.Ammo_miName, ammoIndex ),
3303+
ammoRowref = tableInfo.AMMO.tableLookup( fields.Ammo_miIndex, ammoIndex ),
33223304
dmgAdj = tableInfo.AMMO.tableLookup( fields.Ammo_adj, ammoIndex ),
33233305
dmgStyleAdj = tableInfo.AMMO.tableLookup( fields.Ammo_styleAdj, ammoIndex ),
33243306
dmgSM = tableInfo.AMMO.tableLookup( fields.Ammo_dmgSM, ammoIndex ),
@@ -3569,6 +3551,8 @@ var attackMaster = (function() {
35693551
};
35703552
attkMacro += ((weapCharged && hitCharges) ? ('\n!magic --mi-charges '+tokenID+'|-'+hitCharges+'|'+miName+'|'+weapChgType) : '');
35713553
if (abilityType === Attk.TARGET) weapCmd = weapCmd.replace(/@{target\|.*?\|?token_id}/igm,'@{target|Select Target|token_id}');
3554+
attkMacro += ((weaponName !== miName) && attrLookup( charCS, fields.Items_reveal, fields.Items_table, miRowref ).toLowerCase() == 'use') ? ('\n!magic --button GM-ResetSingleMI|'+tokenID+'|'+miRowref+'|silent') : '';
3555+
attkMacro += ((ammoName !== ammoMIname) && attrLookup( charCS, fields.Items_reveal, fields.Items_table, ammoRowref ).toLowerCase() == 'use') ? ('\n!magic --button GM-ResetSingleMI|'+tokenID+'|'+ammoRowref+'|silent') : '';
35723556
attkMacro += weapCmd ? ('\n'+weapCmd) : '';
35733557
attkMacro += touchWeap ? ('\n!attk --blank-weapon '+tokenID+'|'+miName+'|silent') : '';
35743558
setAbility( charCS, 'Do-not-use-Attk-RW'+rwNumber+'-'+dist, attkMacro );
@@ -3943,7 +3927,8 @@ var attackMaster = (function() {
39433927
weapCharged = !(['uncharged','cursed','cursed+uncharged','single-uncharged'].includes(weapCharge)),
39443928
enabling = ['enable','disable'].includes(weapCharge),
39453929
miQty = Items.tableLookup( fields.Items_qty, itemIndex );
3946-
if (miQty <= 0 || (!_.isUndefined(itemIndex) && weapCharged && (miQty - (!charges ? 1 : charges)) < 0)) {
3930+
// if (miQty <= 0 || (!_.isUndefined(itemIndex) && weapCharged && (miQty - (!charges ? 1 : charges)) < 0)) {
3931+
if (!_.isUndefined(itemIndex) && weapCharged && (miQty - (!charges ? 1 : charges)) < 0) {
39473932
meleeWeaps += '<span style=' + design.grey_button + '>' + (enabling ? '' : miQty) + ' ' + weaponName + '</span>';
39483933
} else {
39493934
if (errFlag = await buildMWattkMacros( args, senderId, charCS, tableInfo, weaponIndex, backstab )) return;
@@ -4999,7 +4984,7 @@ var attackMaster = (function() {
49994984

50004985
// Check if this is a dancing weapon
50014986

5002-
weapData = resolveData( trueWeapon, weaponDB, reWeapData, charCS, {on:reWeapSpecs.on,dancer:reWeapSpecs.dancer}, r ).parsed;
4987+
weapData = resolveData( trueWeapon, weaponDB, reItemData, charCS, {on:reWeapSpecs.on,dancer:reWeapSpecs.dancer}, r ).parsed;
50034988

50044989
// Then add the weapon to the InHand table
50054990

@@ -6105,6 +6090,15 @@ var attackMaster = (function() {
61056090

61066091
silent = silent || _.isUndefined(weapTable.INHAND.tableFind( fields.InHand_name, weapon )) || _.isUndefined(weapTable.INHAND.tableFind( fields.InHand_trueName, weapon ));
61076092

6093+
let inhandRow = weapTable.INHAND.tableFind( fields.InHand_trueName, weapon ) || weapTable.INHAND.tableFind( fields.InHand_name, weapon ),
6094+
itemRow = _.isUndefined(inhandRow) ? undefined : weapTable.INHAND.tableLookup( fields.InHand_index, inhandRow ),
6095+
cmd = resolveData( weapon, fields.MagicItemDB, reItemData, charCS, {off:reWeapSpecs.off}, itemRow ).parsed.off;
6096+
6097+
silent = silent || _.isUndefined(inhandRow);
6098+
if (!_.isUndefined(inhandRow) && cmd && cmd.length) {
6099+
sendAPI( parseStr(cmd).replace(/@{\s*selected\s*\|\s*token_id\s*}/ig,tokenID)
6100+
.replace(/{\s*selected\s*\|/ig,'{'+charCS.get('name')+'|'), null, 'attk doBlankWeapon');
6101+
};
61086102
blankWeapon( charCS, weapTable, _.keys(weapTable), weapon );
61096103

61106104
if (!silent) {

AttackMaster/script.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
"$schema": "https://github.com/DameryDad/roll20-api-scripts/blob/AttackMaster/AttackMaster/Script.json",
33
"name": "AttackMaster",
44
"script": "attackMaster.js",
5-
"version": "3.2.0",
6-
"previousversions": ["1.036","1.038","1.039","1.041","1.042","2.046","1.3.00","1.3.01","1.3.02","1.3.03","1.4.01","1.4.02","1.4.03","1.4.04","1.4.05","1.4.06","1.4.07","1.5.01","1.5.02","1.5.03","2.1.0","2.2.0","2.3.0","2.3.1","2.3.2","2.3.3","3.0.0","3.1.2"],
5+
"version": "3.2.1",
6+
"previousversions": ["1.036","1.038","1.039","1.041","1.042","2.046","1.3.00","1.3.01","1.3.02","1.3.03","1.4.01","1.4.02","1.4.03","1.4.04","1.4.05","1.4.06","1.4.07","1.5.01","1.5.02","1.5.03","2.1.0","2.2.0","2.3.0","2.3.1","2.3.2","2.3.3","3.0.0","3.1.2","3.2.0"],
77
"description": "AttackMaster API for AD&D 2E provides functions to manage weapons, armour & shields, including taking weapons in hand and using them to attack, ranged weapon range and ammo management; penalties & bonuses for non-proficiency, proficiency, specialisation & mastery; 1-handed, 2-handed or many-handed weapons, and multi-weapon attacks.\n[AttackMaster Documentation](https://wiki.roll20.net/Script:AttackMaster) \n\n### Related APIs\nThis API works best with the RPGMaster series of APIs\n[RPGMaster Documentation](https://wiki.roll20.net/RPGMaster) \n\n### Getting Started\n* After installation, add the commands `!attk --menu` and `!attk --other-menu` as 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.",
88
"authors": "Richard E.",
99
"roll20userid": "6497708",

0 commit comments

Comments
 (0)