From a424a5d1286869236aae4b3968c091576a30d61e Mon Sep 17 00:00:00 2001 From: DameryDad <74715860+DameryDad@users.noreply.github.com> Date: Tue, 26 Mar 2024 18:37:51 +0100 Subject: [PATCH] Weaponised powers, cursed changing items & fixes * Allow re-usable (-1) powers to be weaponised in the same way that other spells and powers are. * For spells & powers stored on items with a casting level, set the MU- and PR- casting levels to the stored level as well as the overall casting level. * Extend "changing" items to allow cursed types. * Define the store: attribute for bag-type objects which can be used with "nostore" to define a bag from which can be taken from but not stored. * Extend GM's add-items dialog to cater for equipment. * Fixed spell-storing items displaying "ghost" spells. * Fixed issue with removing memorised spells. --- MagicMaster/3.3.0/MagicMaster.js | 9624 ++++++++++++++++++++++++++++++ MagicMaster/MagicMaster.js | 193 +- MagicMaster/magicMaster.js | 193 +- MagicMaster/script.json | 4 +- 4 files changed, 9864 insertions(+), 150 deletions(-) create mode 100644 MagicMaster/3.3.0/MagicMaster.js diff --git a/MagicMaster/3.3.0/MagicMaster.js b/MagicMaster/3.3.0/MagicMaster.js new file mode 100644 index 000000000..5f9a953c4 --- /dev/null +++ b/MagicMaster/3.3.0/MagicMaster.js @@ -0,0 +1,9624 @@ +// Github: https://github.com/Roll20/roll20-api-scripts/tree/master/MagicMaster +// Beta: https://github.com/DameryDad/roll20-api-scripts/tree/MagicMaster/MagicMaster +// By: Richard @ Damery +// Contact: https://app.roll20.net/users/6497708/richard-at-damery + +var API_Meta = API_Meta||{}; // eslint-disable-line no-var +API_Meta.MagicMaster={offset:Number.MAX_SAFE_INTEGER,lineCount:-1}; +{try{throw new Error('');}catch(e){API_Meta.MagicMaster.offset=(parseInt(e.stack.split(/\n/)[1].replace(/^.*:(\d+):.*$/,'$1'),10)-8);}} + +/** + * MagicMaster.js + * + * * Copyright 2020: Richard @ Damery. + * Licensed under the GPL Version 3 license. + * http://www.gnu.org/licenses/gpl.html + * + * This script is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This script is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * + * The goal of this script is to create and automate aspects of magic spell & item + * discovery, storage & use, initially for the ADnD 2e game in Roll20 + * + * v0.001-v2.042 Removed previous change history - see v2.048 for detail + * v2.043 01/01/2022 Prioritise user-defined database items over provided ones + * Add spell weapons to the provided weapons database (e.g. Ice Knife) + * Add all remaining spells from the PHB to the databases + * v2.044 08/01/2022 Added index of database ability objects to improve performance. + * v2.045 14/01/2022 Fixes to multiclass spell level calculation and non-MU/non-Priest + * spellcaster spell numbers, and force container type when adding items. + * v2.046 23/01/2022 Fixed illegal characters not rendered by One-Click installation + * Corrected definitions of Spear and Lightning-Bolt + * Added menu for adding Spells & Powers to magic items + * v2.047 02/02/2022 Added --config command and menu for DM to alter some API behavior + * Fixed Shocking Bracers LB error & enhanced Rage power + * Multiple weapon and magic item definition updates + * v2.048 24/02/2022 Fixed bug with prioritising user-defined database entries + * v3.050 27/02/2022 Added Class-DB as a standard database. Added Class Powers to + * Powers-DB. Updated abilityLookup() + * to cope with missing databases and abilities that were previously indexed. + * Fixed multiple Magic Item issues, esp. Spell Storing MIs. + * Restricted spellcasters to allowed spells (configurable via --config menu). + * Split Weapon & Armour DBs to AttackMaster. + * v3.051 10/03/2022 Make database load an asynchronous process to eliminate invalid + * "infinite loop" errors. Introduced "Creature" character class. + * Fixed issues with handling manual updates to Character Sheet tables. + * Added support for Attacks-DB. Synchronised DB Indexing between APIs + * to ensure all DBs loaded before indexing. + * v3.060 28/03/2022 Moved all Table Mgt, Ability Mgt, Chat Mgt, Database Mgt to a + * shared library + * v3.061 25/04/2022 Fixed all errors found in 3.060, and moved all game-specific and + * character sheet specific data structures to RPG-specific shared library + * v3.062 16/05/2022 Added function to configure output by player so players can choose their + * own output type. + * v3.063 18/05/2022 Updated menu & item display to use RPGM roll templates. Improved error + * message for character classes that cast spells at levels they've not + * yet attained. + * v3.064 14/07/2022 Removed whispers from database entries and changed to direct to + * appropriate player programmatically so that GM can test locally + * v0.3.65 19/07/2022 Converted to use revised internal database structures + * v0.3.66 17/09/2022 Optionally allow semi-colon as an escape character terminator. Updated dB + * functions to copy items used by a character to their sheet so can be moved + * to new campaigns easily. Added more RPGMaster common functions to library. + * Moved help handouts to Library. Support editing martial MIs and other MIs + * separately. Improved handling of MIs with spells & powers. Support only + * showing item types when searching containers. Allow spell name as parameter + * for --cast-again. Improved menu processing for viewing & using MIs. Added + * renaming, auto-reveal, and type-only containers to --GM-edit-MI menu. Added + * --tidy command to remove attributes and ability macros no longer needed on + * specified Character Sheets. Updated --help command to display handouts. + * Added --extract-db command to generate a Character Sheet version of a database + * if GM wants it. + * v1.3.00 17/09/2022 Brought version numbering in line with other APIs. First release using + * RPGMaster RPG-version-specific Library. + * v1.3.01 07/10/2022 Allow spells to be set as powers, and fix clashes between MIs that use the + * same powers and spells, and improve CS --tidy function + * v1.3.02 09/10/2022 Support spell level absorbing magic items, and spells as weapons. Fixed + * regular expression for database Specs parsing + * v1.3.03 11/11/2022 Add a button to the Memorise Powers menu to memorise all currently valid powers. + * Fixed some error messages. Added response to --index-db command. Added + * config option for restricting powers to valid level or not. + * v1.3.04 24/11/2022 Skipped, to keep in line with versions of other APIs + * v1.3.05 24/11/2022 Added new --level-change command to allow level-drain by creatures. + * v1.4.01 28/11/2022 Support for the extended Creatures database & fighting styles. Extended String + * prototype with dbName() method. + * v1.4.02 13/11/2022 Added ability to specify "cast as level" in Class/Race/Creature powers + * v1.4.03 24/01/2023 Fixed bugs in getting class/race default power uses per day, and in + * finding blank/extended rows in the Items table to store items in. + * v1.4.04 24/01/2023 Added support for configurable default token bar mapping. Added separate + * Ammo list to Change MI menu (appears if AttackMaster is not loaded). + * v1.4.05 02/03/2023 Moved character level parsing to library. Added charge type override to + * --mi-charges command. If casting level of MI powers/spells not specified + * default to that of character. Fix removeMIpowers() to deal with spaces. + * Added 'perm-' charge type qualifier to prevent any MI from disappearing at + * zero charges. Add destination command to --message syntax. Add --mi-rest + * command to reset single MI or single MI power. + * v1.4.06 08/04/2023 Fixed bug with item search introduced by v1.4.05. Added magic item bag + * creation to support Bags of Holding, and MIs that contain other MIs. Fixed + * inconsistencies with hyphenated item names and spell / power storing items. + * v1.4.07 15/04/2023 Added 'discharging' and 'cursed-uncharged' item types which do not divide. Added + * ability for items to store spells as powers using --store-spells command. + * Added current qty of currently selected MI to MIct|max attribute (itemQty field) + * on the character sheet. + * v1.4.08 16/04/2023 Fixed issues with hiding and revealing cursed items. Added alphabeticised lists + * and item review to the GM Add Items menu. + * v1.5.01 17/05/2023 Added 'qty:' data tag to specify initial quantity of any item. Added support + * for 'Looks Like' template tag supporting auto-hiding of items. Added 'st:' + * data tag to specify displayed type of a hidden item. Added RPGM config options + * for 'Allow Any Power', 'alpha lists' & 'auto-hide items'. Added the + * 'single-uncharged' item charge type to designate uncursed uncharged items that + * do not stack or divide when looted or stored, and perhaps discharge + * unconventionally. Moved getShownType() to library so can be used from + * other APIs. Improved tidying of MI ability macros on character sheets. Added + * auto-hiding of items with the 'Looks Like' template tag. Return to GM Add Items + * dialog if GM is reviewing MI spells. Use a GM whisper when GM is viewing the + * description of a spell or item a character has just used. Default casting level + * of a power to that of the caster if level is not specified in definition. Extended + * --mi-power and --mi-rest commands to support powers able to drain multiple charges + * and set new maximums per day. Fixed --tidy command and auto-tidying of character + * sheets. Prevented --tidy from accidentally tidying any db character sheet that + * had been dropped as a token. Allow --message to take a character ID or a token ID + * to send the message to + * v1.5.02 24/05/2023 Fixed tableLookup() reference for storing spells in spell-storing MIs. Fixed use + * of alphabetical lists in the GM's Add Items dialog. + * v1.5.03 08/06/2023 Fixed error where a self-hidden item name does not have a db definition. + * v2.1.0 21/07/2023 Extend the --mem-spell command with -ADD and -ANY command qualifiers that give + * players the ability to add additional spells to a spell storing MI, and/or + * change the existing spells. Weaponised spells cast from spell-storing MIs now + * restrict the "Change Weapon" lists to only include weponised spells from the MI. + * Made many more functions asynchronous to multi-thread. Fixed bug setting non-MU + * and non-PR spell-casters to incorrectly have MU or PR spell level. Fixed issue + * of trying to display non-hyphenated abilities from char sheet. Fixed container + * type not updating when items are stored in it to become searchable. + * v2.2.0 21/07/2023 Implemented The Aaron's API_Meta error handling. Added senderId override capability + * as id immediately after !magic & before 1st --cmd. Added –learn command to run a + * “learn a spell” process for wizards. Added –find-traps command to support finding + * traps separately from –search command, and allow it on non-type 4 containers. + * Moved reACspecs, reSpellSpecs & reClassSpecs to library. Added Skill-based roll + * configuration option switching GM-rolled thieving actions (Find Traps) with player + * rolled. Converted some messages to use new sendMsgToWho() function to improve + * message addressing. Several bug fixes. Fixed checkValidSpell() and changeMIspells() + * to support new –learn command. Support better management of Magic Item spells, + * using similar approach as to Powers. Removed potential setTimeout() issues with + * asynchronous use of variable values – passed as parameters instead. Improved + * --search processing to ensure works for both search & store. Fixed button case error. + * v2.3.0 19/09/2023 Fixed spell-storing magic item issue with '-ADD' and '-ANY' capabilities. Added + * '-CHANGE' capability to spell-storing MIs. Added 'store' and 'lvl' attributes to + * MI data definitions to set the 'add', 'any' and 'change' status (defaults to 'none'), + * and to restrict the number of spell levels stored (defaults to 99).Extend + * handleAddAllPowers() to also deal with MU & PR spells at all levels, and made it + * asynchronous. Fixed checking valid spells & valid powers. Use stored spell + * row/column data to build item-specific spell lists. Ensure that --change-attr + * command rounds correctly for odd/even scores. + * v2.3.1 19/10/2023 For --extract-db if multiple databases start with the supplied name, ask which to extract + * v2.3.2 25/10/2023 Fixed hyphenation of reviewed weapons, items, spells & powers. Changed behavior of + * menu to memorise spells & powers so more intuitive. + * v2.3.3 05/11/2023 Fixed issue with case mismatch on looking up SpellsPerLevel. + * v2.3.4 12/11/2023 Fixed issue with level at which Powers are cast. + * v3.0.0 31/10/2023 Added support for other character sheets and game systems. Corrected how the + * casting level of spells used as powers are calculated. Fixed storing items in + * type 6 or 7 containers. Added query: attribute to magic items to add Roll Queries + * to adding a new MI to a sheet to drive variable data. Moved parseData() to library. + * Fixed Long Rest for MI spells & powers. Added config option for magical weapon plus + * affecting weapon speeds, and corrected --config command documentation. + * v3.1.0 17/12/2023 Added additional support for other character sheets. Moved configuation menu to + * library. Added support for an "item carried" flag as used on the AD&D1e sheet. + * v3.1.2 15/01/2024 Implemented inheritance for magic item database objects. Implemented magic item + * query: and variables. Implemented "change-last" and "cursed+change-last" magic item + * classes for items that change to a different item when reaching zero charges. Added + * "Remove Curse" option to GM-only MI menu. Use evalAttr() when specifying qty for + * storing an MI + * v3.2.0 08/02/2024 New recharge types 'enable' and 'disable' which are uncharged but allow c: + * charge comparisons to support enabling and disabling of weapon attack rows. Fix + * moving hidden db item variables to always move with the trueName of the weapon + * (not the name). Added swordType as a shorthand query tag. Fixes to do with handling + * hidden equipment items. Add optional API command as a 5th parameter to --message + * command. + * v3.2.1 11/02/2024 Improvements to management of hidden items. Config item to set default reveal type + * to on use or manually. Better support for data attribute hide: - force hiding with + * 'hide', default to auto-hide state with no definition, or force no hiding with + * anything else. Improve parseStr() handling of undefined or empty strings. + * v3.3.0 26/02/2024 Allow re-usable (-1) powers to be weaponised in the same way that other spells and + * powers are. For spells & powers stored on items with a casting level, set the MU- + * and PR- casting levels to the stored level as well as the overall casting level. + * Extend "changing" items to allow cursed types. Define the store: attribute for + * bag-type objects which can be used with "nostore" to define a bag from which can + * be taken from but not stored. Extend GM's add-items dialog to cater for equipment. + * Fixed spell-storing items displaying "ghost" spells. Fixed issue with removing + * memorised spells. + */ + +var MagicMaster = (function() { + 'use strict'; + var version = '3.3.0', + author = 'RED', + pending = null; + const lastUpdate = 1711471190; + + /* + * Define redirections for functions moved to the RPGMaster library + */ + + const getRPGMap = (...a) => libRPGMaster.getRPGMap(...a); + const getHandoutIDs = (...a) => libRPGMaster.getHandoutIDs(...a); + const setAttr = (...a) => libRPGMaster.setAttr(...a); + const attrLookup = (...a) => libRPGMaster.attrLookup(...a); + const setAbility = (...a) => libRPGMaster.setAbility(...a); + const abilityLookup = (...a) => libRPGMaster.abilityLookup(...a); + const doDisplayAbility = (...a) => libRPGMaster.doDisplayAbility(...a); + const getAbility = (...a) => libRPGMaster.getAbility(...a); + const getTableField = (...t) => libRPGMaster.getTableField(...t); + const getTable = (...t) => libRPGMaster.getTable(...t); + const getLvlTable = (...t) => libRPGMaster.getLvlTable(...t); + const initValues = (...v) => libRPGMaster.initValues(...v); + const checkDBver = (...a) => libRPGMaster.checkDBver(...a); + const saveDBtoHandout = (...a) => libRPGMaster.saveDBtoHandout(...a); + const buildCSdb = (...a) => libRPGMaster.buildCSdb(...a); + const checkCSdb = (...a) => libRPGMaster.checkCSdb(...a); + const getDBindex = (...a) => libRPGMaster.getDBindex(...a); + const updateHandouts = (...a) => libRPGMaster.updateHandouts(...a); + const findThePlayer = (...a) => libRPGMaster.findThePlayer(...a); + const findCharacter = (...a) => libRPGMaster.findCharacter(...a); + const fixSenderId = (...a) => libRPGMaster.fixSenderId(...a); + const evalAttr = (...a) => libRPGMaster.evalAttr(...a); + const getCharacter = (...a) => libRPGMaster.getCharacter(...a); + const characterLevel = (...a) => libRPGMaster.characterLevel(...a); + const caster = (...a) => libRPGMaster.caster(...a); + const getTokenValue = (...a) => libRPGMaster.getTokenValue(...a); + const classObjects = (...a) => libRPGMaster.classObjects(...a); + const redisplayOutput = (...a) => libRPGMaster.redisplayOutput(...a); + const getMagicList = (...a) => libRPGMaster.getMagicList(...a); + const getShownType = (...a) => libRPGMaster.getShownType(...a); + const addMIspells = (...a) => libRPGMaster.addMIspells(...a); + const handleCheckWeapons = (...a) => libRPGMaster.handleCheckWeapons(...a); + const handleCheckSaves = (...a) => libRPGMaster.handleCheckSaves(...a); + const parseClassDB = (...a) => libRPGMaster.parseClassDB(...a); + const parseData = (...a) => libRPGMaster.parseData(...a); + const resolveData = (...a) => libRPGMaster.resolveData(...a); + const getSetPlayerConfig = (...a) => libRPGMaster.getSetPlayerConfig(...a); + const makeConfigMenu = (...a) => libRPGMaster.makeConfigMenu(...a); + const sendToWho = (...m) => libRPGMaster.sendToWho(...m); + const sendMsgToWho = (...m) => libRPGMaster.sendMsgToWho(...m); + const sendPublic = (...m) => libRPGMaster.sendPublic(...m); + const sendAPI = (...m) => libRPGMaster.sendAPI(...m); + const sendFeedback = (...m) => libRPGMaster.sendFeedback(...m); + const sendResponse = (...m) => libRPGMaster.sendResponse(...m); + const sendResponsePlayer = (...p) => libRPGMaster.sendResponsePlayer(...p); + const sendResponseError = (...e) => libRPGMaster.sendResponseError(...e); + const sendError = (...e) => libRPGMaster.sendError(...e); + const sendCatchError = (...e) => libRPGMaster.sendCatchError(...e); + const sendParsedMsg = (...m) => libRPGMaster.sendParsedMsg(...m); + const sendGMquery = (...m) => libRPGMaster.sendGMquery(...m); + const sendWait = (...m) => libRPGMaster.sendWait(...m); + + /* + * Handle for reference to character sheet field mapping table. + * See RPG library for your RPG/character sheet combination for + * full details of this mapping. See also the help handout on + * RPGMaster character sheet setup. + */ + + var fields = { + defaultTemplate: 'RPGMdefault', + spellTemplate: 'RPGMspell', + potionTemplate: 'RPGMpotion', + menuTemplate: 'RPGMmenu', + warningTemplate: 'RPGMwarning', + }; + + /* + * List of the "standard" RPGMaster databases to support identification of + * custom user databases and db entries to give priority to. + */ + +// const stdDB = ['MU_Spells_DB','PR_Spells_DB','Powers_DB','MI_DB','MI_DB_Ammo','MI_DB_Armour','MI_DB_Equipment','MI_DB_Potions','MI_DB_Rings','MI_DB_Scrolls_Books','MI_DB_Wands_Staves_Rods','MI_DB_Weapons','Attacks_DB','Class_DB']; + + /* + * Handle for reference to database data relevant to MagicMaster. + * Actual data is held in the relevant RPG-specific library. Refer + * to the library for full details. See also the help handout for + * each database. + */ + + var dbNames; + + /* + * Handle for the Database Index, used for rapid access to the character + * sheet ability fields used to hold database items. + */ + + var DBindex = {}; + + /* + * Handle for the library object used to pass back RPG & character sheet + * specific data tables. + */ + + var RPGMap = {}; + + /* + * MagicMaster related help handout information. + */ + + const handouts = Object.freeze({ + MagicMaster_Help: {name:'MagicMaster Help', + version:3.05, + avatar:'https://s3.amazonaws.com/files.d20.io/images/257656656/ckSHhNht7v3u60CRKonRTg/thumb.png?1638050703', + bio:'
' + + '<%= confirm_button %>' + + ' | ' + + '' + + '<%= reject_button %>' + + ' | ' + + '
'
+ + selectableSlot+'Rename '+slotName+(chosenSlot ? ('](!magic --button GM-RenameMI|'+tokenID+'|'+MIrowref+'|'+MItoStore+'|?{What name should '+slotName+' now have?}) ') : ' ')+' ' + + selectableSlot+(!slotCursed ? 'Change Type' : 'Remove Curse')+(chosenSlot ? ('](!magic --button GM-ChangeMItype|'+tokenID+'|'+MIrowref+'|'+MItoStore+'|'+(slotCursed ? 'removeCurse' : ('?{Currently '+slotType+'. What type should '+slotName+' now be?|charged|uncharged|recharging|rechargeable|selfchargeable|absorbing|discharging|cursed|cursed+charged|cursed+recharging|cursed+rechargeable|cursed+selfchargeable|cursed+absorbing|cursed+discharging}'))+') ') : ' ')+' ' + + selectableSlot+'Change displayed charges'+(chosenSlot ? ('](!magic --button GM-ChangeDispCharges|'+tokenID+'|'+MIrowref+'|'+MItoStore+'|?{How many displayed charges should '+slotName+' now have (currently '+slotQty+')?|'+slotQty+'}) ') : ' ')+' ' + + selectableSlot+'Change actual charges'+(chosenSlot ? ('](!magic --button GM-ChangeActCharges|'+tokenID+'|'+MIrowref+'|'+MItoStore+'|?{How many actual charges should '+slotActualName+' now have (currently '+slotActualQty+')?|'+slotActualQty+'}) ') : ' ')+' ' + + storableSlot+'Store Spells/Powers in MI'+((spellStoring && chosenSlot) ? ('](!magic --store-spells '+tokenID+'|'+slotActualName+'|||GM-EDIT-MI) ') : ' ')+' | '
+ + ''+hiddenSlot+'Reveal '+revealType+((hiddenMI && chosenSlot) ? ('](!magic --set-reveal '+tokenID+'|'+slotActualName+'|?{Currently '+revealType+'. How should '+slotActualName+' be revealed?|Manually by DM,|When viewed,View|When used,Use|On Long Rest,Rest}|'+MIrowref+'|MENU) ') : ' ')+' ' + + selectableSlot+(hiddenMI ? 'Reveal Now' : 'Reset Qty to Max')+(chosenSlot ? ('](!magic --button GM-ResetSingleMI|'+tokenID+'|'+MIrowref+') ') : ' ')+' ' + + selectableSlot+'Change Cost'+(chosenSlot ? ('](!magic --button GM-SetMIcost|'+tokenID+'|'+MIrowref+'|'+MItoStore+'|?{How much should '+slotName+' now cost (currently '+slotCost+'GP)?|'+slotCost+'})') : '')+' ' + + selectableSlot+'REMOVE MI'+(chosenSlot ? '](!magic --button GM-DelMI|'+tokenID+'|'+MIrowref+'|'+slotActualName+') ' : ' ')+' |
Menu images | '+(config.menuImages ? '\u2705' : '\u2B1C')+' |
Menu plain | '+(config.menuPlain ? '\u2705' : '\u2B1C')+' |
Menu dark | '+(config.menuDark ? '\u2705' : '\u2B1C')+' |
Magic Weapon | 5ft | '+weaponSwitch+'
Torch | 15ft | '+torchSwitch+'
Hooded Lantern | 30ft | '+hoodedSwitch+'
Bullseye Lantern | 60ft beam | '+bullseyeSwitch+'
Cont-Light gem | 60ft | '+contLightSwitch+'
Beacon Lantern | 240ft beam | '+beaconSwitch+'
'
+ selectableSlot+'Rename '+slotName+(chosenSlot ? ('](!magic --button GM-RenameMI|'+tokenID+'|'+MIrowref+'|'+MItoStore+'|?{What name should '+slotName+' now have?}) ') : ' ')+' ' - + selectableSlot+(!slotCursed ? 'Change Type' : 'Remove Curse')+(chosenSlot ? ('](!magic --button GM-ChangeMItype|'+tokenID+'|'+MIrowref+'|'+MItoStore+'|'+(slotCursed ? 'removeCurse' : ('?{Currently '+slotType+'. What type should '+slotName+' now be?|charged|uncharged|recharging|rechargeable|selfchargeable|absorbing|discharging|cursed|cursed+charged|cursed+recharging|cursed+rechargeable|cursed+selfchargeable|cursed+absorbing}'))+') ') : ' ')+' ' + + selectableSlot+(!slotCursed ? 'Change Type' : 'Remove Curse')+(chosenSlot ? ('](!magic --button GM-ChangeMItype|'+tokenID+'|'+MIrowref+'|'+MItoStore+'|'+(slotCursed ? 'removeCurse' : ('?{Currently '+slotType+'. What type should '+slotName+' now be?|charged|uncharged|recharging|rechargeable|selfchargeable|absorbing|discharging|cursed|cursed+charged|cursed+recharging|cursed+rechargeable|cursed+selfchargeable|cursed+absorbing|cursed+discharging}'))+') ') : ' ')+' ' + selectableSlot+'Change displayed charges'+(chosenSlot ? ('](!magic --button GM-ChangeDispCharges|'+tokenID+'|'+MIrowref+'|'+MItoStore+'|?{How many displayed charges should '+slotName+' now have (currently '+slotQty+')?|'+slotQty+'}) ') : ' ')+' ' + selectableSlot+'Change actual charges'+(chosenSlot ? ('](!magic --button GM-ChangeActCharges|'+tokenID+'|'+MIrowref+'|'+MItoStore+'|?{How many actual charges should '+slotActualName+' now have (currently '+slotActualQty+')?|'+slotActualQty+'}) ') : ' ')+' ' + storableSlot+'Store Spells/Powers in MI'+((spellStoring && chosenSlot) ? ('](!magic --store-spells '+tokenID+'|'+slotActualName+'|||GM-EDIT-MI) ') : ' ')+' | '
@@ -4509,6 +4547,8 @@ var MagicMaster = (function() {
storedLevel = attrLookup( charCS, fields.Spells_storedLevel, fields.Spells_table, args[3], args[4] );
if (storedLevel && storedLevel > 0) {
setAttr( charCS, fields.CastingLevel, storedLevel );
+ setAttr( charCS, fields.MU_CastingLevel, storedLevel );
+ setAttr( charCS, fields.PR_CastingLevel, storedLevel );
}
}
@@ -4524,7 +4564,7 @@ var MagicMaster = (function() {
var handleCastSpell = function( args, senderId ) {
const setValue = (...a) => libRPGMaster.setAttr(...a);
-
+
var tokenID = args[1],
rowIndex = args[3],
colIndex = args[4],
@@ -4618,12 +4658,12 @@ var MagicMaster = (function() {
Items = Items.tableSet( fields.Items_qty, itemRow, Math.max(parseInt(Items.tableLookup( fields.Items_qty, itemRow ) || 0)-level,0) );
Items = Items.tableSet( fields.Items_trueQty, itemRow, Math.max(parseInt(Items.tableLookup( fields.Items_trueQty, itemRow ) || 0)-level,0) );
}
- } else if (spellValue > 0) {
+ } else if (spellValue != 0) {
if (apiCommands.attk && apiCommands.attk.exists && spell.obj[1].body.match(/}}\s*tohitdata\s*=\s*\[.*?\]/im)) {
- sendAPI(fields.attackMaster+' --weapon '+tokenID+'|||'+miName);
+ sendAPI(fields.attackMaster+' '+senderId+' --weapon '+tokenID+'|Take '+spellName+' in-hand as a weapon and then Attack with it||'+miName);
} else {
- spellValue--;
+ if (spellValue > 0) spellValue--;
spellTables.tableSet( fields.Spells_castValue, rowIndex, spellValue );
}
}
@@ -5258,6 +5298,7 @@ var MagicMaster = (function() {
var MItables = getTable( charCS, fieldGroups.MI ),
MIname = MItables.tableLookup( fields.Items_name, MIrowref ),
+ MItrueName = MItables.tableLookup( fields.Items_trueName, MIrowref ),
MIreveal = MItables.tableLookup( fields.Items_reveal, MIrowref ).toLowerCase();
setAttr( charCS, fields.ItemChosen, MIname );
@@ -5272,7 +5313,7 @@ var MagicMaster = (function() {
}
content = '[Return to menu](!magic --button '+BT.CHOOSE_VIEW_MI+'|'+args[1]+'|'+args[2]+')';
setTimeout(() => sendResponse( charCS, content, senderId, flags.feedbackName, flags.feedbackImg, tokenID ),500);
- checkForBag( charCS, MIname );
+ checkForBag( charCS, MItrueName, MIrowref );
return;
}
if (isNaN(MIrowref) || (fields.Items_table[1] == 0 && MIrowref < 0)) {
@@ -5296,7 +5337,6 @@ var MagicMaster = (function() {
MIchangeTo = miData.changeTo;
}
MIcVal = parseInt(MIcVal);
-
if (!(_.isUndefined(MIcVal) || isNaN(MIcVal)) && (_.isUndefined(charges) || _.isNull(charges))) {
charges = MIcVal;
}
@@ -5315,7 +5355,8 @@ var MagicMaster = (function() {
switch (MItype.toLowerCase()) {
case 'change-each':
- if (MIchangeTo) {
+ case 'cursed+change-each':
+ if (MIchangeTo && charges > 0) {
let changeRow = MItables.tableFind( fields.Items_trueName, MIchangeTo );
if (isNaN(changeRow)) {
handleStoreMI( ['', tokenID, changeRow, MIchangeTo, charges, 'silent' ], false, senderId );
@@ -5326,12 +5367,13 @@ var MagicMaster = (function() {
};
case 'charged':
case 'perm-charged':
+ case 'cursed+charged':
case 'changing':
case 'change-last':
case 'cursed+change-last':
case 'discharging':
case 'perm-discharging':
- case 'cursed+charged':
+ case 'cursed+discharging':
case 'rechargeable':
case 'perm-rechargeable':
case 'cursed+rechargeable':
@@ -5371,7 +5413,7 @@ var MagicMaster = (function() {
setAttr( charCS, fields.ItemQty, MIqtyObj.get('current') );
- if (MIqty > charges) checkForBag( charCS, MIname );
+ if (MIqty > charges) checkForBag( charCS, MItrueName, MIrowref );
if (action.includes('USE') && (MIreveal == 'view' || MIreveal == 'use')) {
MIname = MItables.tableLookup( fields.Items_trueName, MIrowref );
@@ -5606,7 +5648,7 @@ var MagicMaster = (function() {
* spells into a spell-storing magic item.
*/
- var handleStoreMIspell = function( args, senderId ) {
+ var handleStoreMIspell = function( args, senderId ) { //split
var tokenID = args[1],
charCS = getCharacter(tokenID);
@@ -5686,15 +5728,17 @@ var MagicMaster = (function() {
muLevels = attrLookup( charCS, [fields.ItemMUspellValues[0]+item,fields.ItemMUspellValues[1]] ),
prLevels = attrLookup( charCS, [fields.ItemPRspellValues[0]+item,fields.ItemPRspellValues[1]] );
- if (!!muRows && !!muCols) {
- muRows = muRows.split(',');
- muCols = muCols.split(',');
- muSpells = muSpells.split(',');
- muLevels = muLevels.split(',');
- prRows = prRows.split(',');
- prCols = prCols.split(',');
- prSpells = prSpells.split(',');
- prLevels = prLevels.split(',');
+ log('handleStoreMIspell: muRows='+muRows+', muCols='+muCols+', muSpells='+muSpells+', muLevels='+muLevels+', prRows='+prRows+', prCols='+prCols+', prSpells='+prSpells+', prLevels='+prLevels);
+
+ if ((!!muRows && !!muCols) || (!!prRows && !!prCols)) {
+ muRows = (muRows || '').split(',');
+ muCols = (muCols || '').split(',');
+ muSpells = (muSpells || '').split(',');
+ muLevels = (muLevels || '').split(',');
+ prRows = (prRows || '').split(',');
+ prCols = (prCols || '').split(',');
+ prSpells = (prSpells || '').split(',');
+ prLevels = (prLevels || '').split(',');
let index = muRows.findIndex( (e,i) => e == MIrow && muCols[i] == MIcol ),
muSave = false, prSave = false;
if (index > -1 && muCols[index] === MIcol) {
@@ -6016,7 +6060,7 @@ var MagicMaster = (function() {
MIreveal = fromMIbag.tableLookup( fields.Items_reveal, fromRowRef ),
MItrueType = fromMIbag.tableLookup( fields.Items_trueType, fromRowRef ),
MItext = MIname,
- unrechargeable = ['charged','uncharged','cursed','cursed+change-last'],
+ splitable = ['charged','uncharged','cursed','change-each','cursed+change-each'],
recharging = ['recharging','cursed+recharging','absorbing','cursed+absorbing'],
slotInc = 1,
finalQty, finalCharges, pickQty, charges, content;
@@ -6033,7 +6077,7 @@ var MagicMaster = (function() {
switch (MIqty) {
case 0:
- if (!unrechargeable.includes(fromSlotType)) {
+ if (!splitable.includes(fromSlotType)) {
qty = pickQty = 0;
charges = MItrueQty;
} else {
@@ -6049,7 +6093,7 @@ var MagicMaster = (function() {
break;
default:
- if (!unrechargeable.includes(fromSlotType)) {
+ if (!splitable.includes(fromSlotType)) {
qty = MIqty;
pickQty = (recharging.includes(fromSlotType)) ? MIqty : MItrueQty;
charges = MItrueQty;
@@ -6071,7 +6115,7 @@ var MagicMaster = (function() {
finalQty = pickQty;
finalCharges = charges;
- if (unrechargeable.includes(fromSlotType) && stdEqual( toSlotName, MIname ) && stdEqual( toSlotType, MItype ) && stdEqual( toSlotTrueName, MItrueName )) {
+ if (splitable.includes(fromSlotType) && stdEqual( toSlotName, MIname ) && stdEqual( toSlotType, MItype ) && stdEqual( toSlotTrueName, MItrueName )) {
finalQty = (parseInt(finalQty)||0) + (parseInt(toSlotQty)||0);
finalCharges = (parseInt(finalCharges)||0) + (parseInt(toSlotCharges)||0);
slotInc = 0;
@@ -6131,11 +6175,11 @@ var MagicMaster = (function() {
if (!stdEqual(MIname,MItrueName)) {
await moveMIspells( senderId, fromCS, toCS, MItrueName );
}
- checkForBag( toCS, MItrueName );
+ checkForBag( toCS, MItrueName, toRowRef );
if (MIqty == 0) {
fromMIbag.addTableRow( fromRowRef ); // Blanks this row of the table
- removeMIability( fromCS, MIname, fromMIbag ); // Only removes ability if does not exist elsewhere in the equipment list
- removeMIability( fromCS, MItrueName, fromMIbag );
+ setTimeout(() => removeMIability( fromCS, MIname, fromMIbag ), 100); // Only removes ability if does not exist elsewhere in the equipment list
+ setTimeout(() => removeMIability( fromCS, MItrueName, fromMIbag ), 100);
} else {
fromMIbag.tableSet( fields.Items_trueQty, fromRowRef, (MItrueQty - charges) );
fromMIbag.tableSet( fields.Items_qty, fromRowRef, (MIqty - qty) );
@@ -6228,13 +6272,14 @@ var MagicMaster = (function() {
/*
* Handle storing an MI in a Magic Item bag.
+ * Can take either a tokenID or a Character ID.
* A flag parameter determines if this is a GM-only action
*/
async function handleStoreMI( args, GMonly, senderId ) {
var tokenID = args[1],
- MIrowref = args[2],
+ MIrowref = args[2] || '',
MIchosen = args[3],
MIqty = args[4] || 1,
silent = (args[5] || '').toUpperCase() === 'SILENT',
@@ -6242,6 +6287,11 @@ var MagicMaster = (function() {
charCS = getCharacter( tokenID ),
queries = args.slice(6);
+ if (!getObj( 'graphic', tokenID )) {
+ tokenID = undefined;
+ silent = true;
+ }
+
if (!charCS) {
sendDebug('handleStoreMI: invalid tokenID passed');
sendError('Internal MagicMaster error');
@@ -6269,9 +6319,9 @@ var MagicMaster = (function() {
MImaxQty = 0;
if (MIreplace) MIrowref = MItables.tableFind( fields.Items_trueName, MIreplace );
- if (inHand && isNaN(MIrowref)) MIrowref = MItables.tableFind( fields.Items_trueName, MIchosen );
- if (isNaN(MIrowref) || MIrowref<0) MIrowref = MItables.tableFind( fields.Items_name, '-' );
- if (isNaN(MIrowref)) {
+ if (inHand && isNaN(parseInt(MIrowref))) MIrowref = MItables.tableFind( fields.Items_trueName, MIchosen );
+ if (isNaN(parseInt(MIrowref)) || MIrowref<0) MIrowref = MItables.tableFind( fields.Items_name, '-' );
+ if (isNaN(parseInt(MIrowref))) {
MItables = MItables.addTableRow();
MIrowref = MItables.sortKeys.length-1;
}
@@ -6297,7 +6347,7 @@ var MagicMaster = (function() {
var midbCS, MIdisplayName;
if (!GMonly && slotType.toLowerCase().includes('cursed')) {
- sendParsedMsg( tokenID, messages.cursedSlot + '{{desc1=[Return to menu](!magic --edit-mi '+tokenID+')}}', senderId );
+ if (!silent) sendParsedMsg( tokenID, messages.cursedSlot + '{{desc1=[Return to menu](!magic --edit-mi '+tokenID+')}}', senderId );
return;
}
await moveMIspells( senderId, charCS, null, slotName );
@@ -6333,11 +6383,12 @@ var MagicMaster = (function() {
}
await moveMIspells( senderId, null, charCS, MIchosen );
+ checkForBag( charCS, MIchosen, MIrowref );
- sendAPI( (fields.attackMaster + ' --checkac ' + tokenID + '|Silent||' + senderId), senderId );
+ if (!!tokenID) sendAPI( (fields.attackMaster + ' --checkac ' + tokenID + '|Silent||' + senderId), senderId );
if (silent) {
- sendAPI( (fields.attackMaster + ' --button ' + (['PRIMARY','OFFHAND','BOTH','HAND'][Math.min(hand,3)]) + '-NOCURSE|' + tokenID + '|' + MIchosen + '|' + hand + '||Silent'), senderId );
+ if (tokenID && inHand) sendAPI( (fields.attackMaster + ' --button ' + (['PRIMARY','OFFHAND','BOTH','HAND'][Math.min(hand,3)]) + '-NOCURSE|' + tokenID + '|' + MIchosen + '|' + hand + '||Silent'), senderId );
sendWait(senderId,0);
return;
}
@@ -7837,6 +7888,9 @@ var MagicMaster = (function() {
log('doSearchForMIs: Not found trapMacro');
MIBagSecurity = 1;
}
+ } else if (!search && MIBagSecurity < 0) {
+ sendParsedMsg( putID, messages.noStoring, senderId );
+ return
} else if (!search || MIBagSecurity < 2 || hp <= 0 || intelligence <= 0) {
doPickOrPut( msg, senderId );
} else {
@@ -8013,20 +8067,12 @@ var MagicMaster = (function() {
return;
}
- var tokenID = args[0],
- miRowRef = args[1],
+ var miRowRef = args[1],
miChosen = args[2],
miQty = args[3] || 0,
miInHand = args[4],
- isGM = (args[5] || '').toUpperCase() === 'NOCURSE',
- charCS = getCharacter( tokenID );
+ isGM = (args[5] || '').toUpperCase() === 'NOCURSE';
- if (!charCS) {
- sendDebug('doAddItem: invalid token ID argument');
- sendResponseError(senderId,'Invalid token specified');
- return;
- };
-
args.unshift('');
args[2] = !isNaN(miInHand) ? 'inhand/'+miInHand : (!isNaN(miRowRef) ? miRowRef : '');
args[3] = !isNaN(miRowRef) ? miRowRef : miChosen+'/'+miRowRef;
@@ -8870,7 +8916,6 @@ var MagicMaster = (function() {
case BT.VIEW_MI:
case BT.USE_MI:
-
handleViewUseMI( args, playerIsGM(senderId), senderId );
break;
diff --git a/MagicMaster/magicMaster.js b/MagicMaster/magicMaster.js
index 6e28f6c9d..5f9a953c4 100644
--- a/MagicMaster/magicMaster.js
+++ b/MagicMaster/magicMaster.js
@@ -188,14 +188,22 @@ API_Meta.MagicMaster={offset:Number.MAX_SAFE_INTEGER,lineCount:-1};
* to on use or manually. Better support for data attribute hide: - force hiding with
* 'hide', default to auto-hide state with no definition, or force no hiding with
* anything else. Improve parseStr() handling of undefined or empty strings.
+ * v3.3.0 26/02/2024 Allow re-usable (-1) powers to be weaponised in the same way that other spells and
+ * powers are. For spells & powers stored on items with a casting level, set the MU-
+ * and PR- casting levels to the stored level as well as the overall casting level.
+ * Extend "changing" items to allow cursed types. Define the store: attribute for
+ * bag-type objects which can be used with "nostore" to define a bag from which can
+ * be taken from but not stored. Extend GM's add-items dialog to cater for equipment.
+ * Fixed spell-storing items displaying "ghost" spells. Fixed issue with removing
+ * memorised spells.
*/
var MagicMaster = (function() {
'use strict';
- var version = '3.2.1',
+ var version = '3.3.0',
author = 'RED',
pending = null;
- const lastUpdate = 1708765145;
+ const lastUpdate = 1711471190;
/*
* Define redirections for functions moved to the RPGMaster library
@@ -237,6 +245,7 @@ var MagicMaster = (function() {
const parseClassDB = (...a) => libRPGMaster.parseClassDB(...a);
const parseData = (...a) => libRPGMaster.parseData(...a);
const resolveData = (...a) => libRPGMaster.resolveData(...a);
+ const getSetPlayerConfig = (...a) => libRPGMaster.getSetPlayerConfig(...a);
const makeConfigMenu = (...a) => libRPGMaster.makeConfigMenu(...a);
const sendToWho = (...m) => libRPGMaster.sendToWho(...m);
const sendMsgToWho = (...m) => libRPGMaster.sendMsgToWho(...m);
@@ -272,7 +281,7 @@ var MagicMaster = (function() {
* custom user databases and db entries to give priority to.
*/
- const stdDB = ['MU_Spells_DB','PR_Spells_DB','Powers_DB','MI_DB','MI_DB_Ammo','MI_DB_Armour','MI_DB_Light','MI_DB_Potions','MI_DB_Rings','MI_DB_Scrolls_Books','MI_DB_Wands_Staves_Rods','MI_DB_Weapons','Attacks_DB','Class_DB'];
+// const stdDB = ['MU_Spells_DB','PR_Spells_DB','Powers_DB','MI_DB','MI_DB_Ammo','MI_DB_Armour','MI_DB_Equipment','MI_DB_Potions','MI_DB_Rings','MI_DB_Scrolls_Books','MI_DB_Wands_Staves_Rods','MI_DB_Weapons','Attacks_DB','Class_DB'];
/*
* Handle for reference to database data relevant to MagicMaster.
@@ -766,6 +775,7 @@ var MagicMaster = (function() {
castSpellClass: '&{template:'+fields.menuTemplate+'} {{name=Spellbooks}}{{desc=^^cname^^ has both Wizard and Priest spellbooks. Which do you want to use?}}{{desc1=[Wizard](!magic --cast-spell MU|^^tid^^) or [Priest](!magic --cast-spell PR|^^tid^^)}}',
memSpellClass: '&{template:'+fields.menuTemplate+'} {{name=Spellbooks}}{{desc=^^cname^^ has both Wizard and Priest spellbooks. Which do you want to use?}}{{desc1=[Wizard](!magic --mem-spell MU|^^tid^^) or [Priest](!magic --mem-spell PR|^^tid^^)}}',
viewSpellClass: '&{template:'+fields.menuTemplate+'} {{name=Spellbooks}}{{desc=^^cname^^ has both Wizard and Priest spellbooks. Which do you want to view?}}{{desc1=[Wizard](!magic --view-spell MU|^^tid^^) or [Priest](!magic --view-spell PR|^^tid^^)}}',
+ noStoring: '&{template:'+fields.warningTemplate+'} {{name=Can\'t Store Items Here}}{{desc=You can\'t store items in the selected container. Perhaps try somewhere else?}}',
});
const BT = Object.freeze({
@@ -922,7 +932,7 @@ var MagicMaster = (function() {
const reRaceData = /}}\s*?(?:Class|Race)Data\s*?=(.*?){{/im;
const reSpellData = /}}\s*?SpellData\s*?=(.*?){{/im;
const reRepeatingTable = /^(repeating_.*)_\$(\d+)_.*$/;
- const reItemData = /}}[\s\w\-]*?(? 0) {
let Items = getTable( bagCS, fieldGroups.MI );
- setAttr( bagCS, fields.ItemContainerType, '1' );
+ setAttr( bagCS, fields.ItemContainerType, (bag.parsed.store !== 'nostore' ? '1' : '-1') );
setAttr( bagCS, fields.ItemContainerSize, Math.max( fields.MIRowsStandard, bagData ));
- bagData = miObj.data(/}}[^{]*?data\s*?=\s*?(\[[^{]+?bag\:[^{]+?\]){{/im);
- _.each( bagData, item => {
- let itemData = parseData( item[0], reSpellSpecs, false, charCS, miName );
+// bagData = miObj.data(/}}[^{]*?data\s*?=\s*?(\[[^{]+?bag\:[^{]+?\]){{/im);
+ _.each( bag.raw, item => {
+ let itemData = parseData( item[0], itemSpecs, false, charCS, miName );
if ((itemData.spell || '').toUpperCase() != 'MI') return;
let itemObj = abilityLookup( fields.MagicItemDB, (itemData.trueName || itemData.name), charCS );
if (itemObj.obj) {
@@ -2573,7 +2601,7 @@ var MagicMaster = (function() {
if (!Items.tableFind( fields.Items_name, itemName ) && !Items.tableFind( fields.Items_trueName, itemName )) {
let MIobjs = filterObjs( obj => {
if (obj.type !== 'ability' && obj.type !== 'attribute') return false;
- return (obj.name === itemName || obj.name.startsWith(fields.ItemVar[0]+itemName.hyphened())); // Needs row reference
+ return (obj.name === itemName || obj.name.startsWith(fields.ItemVar[0]+itemName.hyphened()));
});
if (MIobjs) _.each(MIobjs,MIobj => MIobj.remove());
}
@@ -2737,15 +2765,16 @@ var MagicMaster = (function() {
makeGrey = (!type.includes('selfchargeable') && !type.includes('absorbing') && disable0 && qty == 0);
if (mi.length > 0 && (includeEmpty || mi != '-')) {
let miObj = abilityLookup( fields.MagicItemDB, mi, charCS, true );
+ log('makeMIbuttons: item = '+miText+', showMagic = '+showMagic+', obj.type = '+(miObj.obj ? miObj.obj[1].type : 'undefined'));
makeGrey = makeGrey || (!showMagic && (!miObj.obj || miObj.obj[1].type.toLowerCase().includes('magic')));
if (showTypes && miObj.obj) {
miText = getShownType( miObj, i, resolveData( trueMI, fields.MagicItemDB, reItemData, charCS, {itemType:reSpellSpecs.itemType}, i ).parsed.itemType );
- if (!['charged','uncharged','cursed','change-last','change-each','changing','cursed+change-list'].includes(type)) {
+ if (!['charged','uncharged','cursed','change-last','change-each','changing','cursed+change-last','discharging','cursed+discharging'].includes(type)) {
qty = Math.min(qty,1);
}
}
content += (i == MIrowref || makeGrey) ? ('') : '[';
- content += (mi != '-' ? (qty + ((qty != maxQty && isGM) ? '/'+maxQty : '') + ' ' + miText.replace(/\-/g,' ')) : '-');
+ content += (mi !== '-' ? (qty + ((qty != maxQty && isGM) ? '/'+maxQty : '') + ' ' + miText.replace(/\-/g,' ')) : '-');
if (mi != '-') slotsUsed++;
if (isView && mi.replace(reIgnore,'').length) {
if (Items.tableLookup( fields.Items_reveal, i ) == 'view') mi = trueMI;
@@ -2874,7 +2903,6 @@ var MagicMaster = (function() {
/*
* Make a list of spells in the specified memorised/stored list
*/
-
var makeSpellList = function( senderId, tokenID, command, selectedButton, noDash = false, submitted = false, extension = '', maxLevel = 13 ) {
var isMU = command.toUpperCase().includes('MU'),
@@ -2912,7 +2940,9 @@ var MagicMaster = (function() {
} else if (isMI) {
spellType = 'MI';
buttonList = 'EmptyList,' + attrLookup( charCS, [fields.ItemMUspellsList[0]+miName, fields.ItemMUspellsList[1]] ) || '';
+ log('makeSpellList: initial MU buttonList = '+buttonList);
buttonList += ',' + attrLookup( charCS, [fields.ItemPRspellsList[0]+miName, fields.ItemPRspellsList[1]]) || '';
+ log('makeSpellList: next PR buttonList = '+buttonList);
buttonList = buttonList.dbName().split(',');
let miObj = abilityLookup( fields.MagicItemDB, miName, charCS );
if (miObj.obj) {
@@ -2921,9 +2951,10 @@ var MagicMaster = (function() {
// see if can build an item-specific spell list...
rows.push((attrLookup( charCS, [fields.MIspellRows[0]+miName+'-mu',fields.MIspellRows[1]] ) || ''),(attrLookup( charCS, [fields.MIspellRows[0]+miName+'-pr',fields.MIspellRows[1]] ) || ''));
- rows = rows.join().split(',');
+ rows = rows.join().split(',').filter(r=>!!r);
cols.push((attrLookup( charCS, [fields.MIspellCols[0]+miName+'-mu',fields.MIspellCols[1]] ) || ''),(attrLookup( charCS, [fields.MIspellCols[0]+miName+'-pr',fields.MIspellCols[1]] ) || ''));
- cols = cols.join().split(',');
+ cols = cols.join().split(',').filter(c=>!!c);
+ log('makeSpellList: rows = '+rows+', cols = '+cols);
if (rows.length && cols.length) {
_.each( cols, (c,k) => {
let r = rows[k];
@@ -2932,6 +2963,7 @@ var MagicMaster = (function() {
if (miStore) spellName = spellMsg; else spellName = spellTables[c].tableLookup( fields.Spells_name, r );
let spellValue = parseInt((spellTables[c].tableLookup( fields.Spells_castValue, r )),10),
disabled = (miStore ? (spellValue != 0) : (spellValue == 0));
+ log('makeSpellList: r='+r+', c='+c+', spellMsg='+spellMsg+', spellName='+spellName+', spellValue='+spellValue);
if (!disabled) spellLevels = spellLevels + (parseInt(spellTables[c].tableLookup( fields.Spells_spellLevel, r )) || 1);
if (!noDash || spellName != '-') {
content += (buttonID == selectedButton ? '' : ((submitted || disabled) ? '' : '['));
@@ -2971,11 +3003,12 @@ var MagicMaster = (function() {
let spellMsg = spellTables[w].tableLookup( (oldVer ? fields.Spells_macro : fields.Spells_msg), r );
if (miStore) spellName = spellMsg.hyphened();
else spellName = spellTables[w].tableLookup( fields.Spells_name, r ).hyphened();
+ log('makeSpellList: r = '+r+', c = '+c+', w = '+w+', spellMsg = '+spellMsg+', spellName = '+spellName);
if (_.isUndefined(spellName)) {
levelSpells[lv].spells = 0;
break;
}
- if (!buttonList.length || (buttonIndex = buttonList.indexOf(spellMsg.dbName())) != -1) {
+ if (spellName.trim().length && (!buttonList.length || (buttonIndex = buttonList.indexOf(spellMsg.dbName())) != -1)) {
if (buttonList.length) buttonList.splice(buttonIndex,1);
spellLevels = spellLevels + (parseInt(spellTables[w].tableLookup( fields.Spells_spellLevel, r )) || 1);
let spellValue = parseInt((spellTables[w].tableLookup( fields.Spells_castValue, r )),10),
@@ -3003,6 +3036,7 @@ var MagicMaster = (function() {
}
spellTables = [];
}
+ log('makeSpellList: content = '+content+', spellLevels = '+spellLevels);
return [content,spellLevels];
}
@@ -3626,7 +3660,7 @@ var MagicMaster = (function() {
MIrowref = args[2],
itemName = args[3] || '',
charges = args[4],
- selectedMI = itemName.replace(/\s/g,'-'),
+ selectedMI = itemName.hyphened(),
alphaLists = state.MagicMaster.alphaLists,
charCS = getCharacter( tokenID );
@@ -3683,6 +3717,7 @@ var MagicMaster = (function() {
ammo = getMagicList(fields.MagicItemDB,miTypeLists,'ammo',senderId,'',false,'',alphaLists),
armour = getMagicList(fields.MagicItemDB,miTypeLists,'armour',senderId,'',false,'',alphaLists),
rings = getMagicList(fields.MagicItemDB,miTypeLists,'ring',senderId,'',false,'',alphaLists),
+ equip = getMagicList(fields.MagicItemDB,miTypeLists,'equipment',senderId,'',false,'',alphaLists),
misc = getMagicList(fields.MagicItemDB,miTypeLists,'miscellaneous',senderId,'',false,'',alphaLists);
content += '{{Section1=[Use '+(alphaLists ? 'full' : 'alphabeticised')+' lists](!magic --button '+BT.ALPHALIST_OPTION+'|'+tokenID+'|'+(alphaLists ? 'full' : 'alpha')+'|'+cmd+') to select items from}}'
@@ -3694,6 +3729,7 @@ var MagicMaster = (function() {
+ (!editMartial && !editAll ? '' : '[Ammo](!magic --button '+chooseCmd+'|'+tokenID+'|'+MIrowref+'|?{Ammunition to store|'+ammo+'}|'+charges+')')
+ (!editMartial && !editAll ? '' : '[Armour](!magic --button '+chooseCmd+'|'+tokenID+'|'+MIrowref+'|?{Armour to store|'+armour+'}|'+charges+')')
+ (editMartial ? '' : '[Ring](!magic --button '+chooseCmd+'|'+tokenID+'|'+MIrowref+'|?{Ring to store|'+rings+'}|'+charges+')')
+ + (editMartial ? '' : '[Equipment](!magic --button '+chooseCmd+'|'+tokenID+'|'+MIrowref+'|?{Equipment to store|'+equip+'}|'+charges+')')
+ (editMartial ? '' : '[Miscellaneous](!magic --button '+chooseCmd+'|'+tokenID+'|'+MIrowref+'|?{Misc Item to store|'+misc+'}|'+charges+')');
if (shortMenu) {
content += '\n**OR**\n'
@@ -3754,7 +3790,7 @@ var MagicMaster = (function() {
qty = String(qty)+'+1';
if (selected) {
let chosenData = resolveData( selectedMI, fields.MagicItemDB, reItemData, charCS, {qty:reSpellSpecs.qty,query:reSpellSpecs.query}, MIrowref ).parsed;
- qty = chosenData.qty || qty;
+ qty = chosenData.qty || (selectedMI.trueCompare(removeMI) ? qty : 1);
queries = parseQuery( chosenData.query );
}
@@ -3822,6 +3858,7 @@ var MagicMaster = (function() {
armour = getMagicList(fields.MagicItemDB,miTypeLists,'armour',senderId,'',false,'',!!alphaLists),
rings = getMagicList(fields.MagicItemDB,miTypeLists,'ring',senderId,'',false,'',!!alphaLists),
misc = getMagicList(fields.MagicItemDB,miTypeLists,'miscellaneous',senderId,'',false,'',!!alphaLists),
+ equip = getMagicList(fields.MagicItemDB,miTypeLists,['equipment','light'],senderId,'',false,'',!!alphaLists),
dmitems = getMagicList(fields.MagicItemDB,miTypeLists,'dmitem',senderId,'',false,'',false),
content = '&{template:'+fields.defaultTemplate+'}{{name=Edit '+charCS.get('name')+'\'s Magic Item Bag}}'
+ (msg && msg.length ? '{{section='+msg+'}}' : '')
@@ -3835,6 +3872,7 @@ var MagicMaster = (function() {
+ '[Armour](!magic --button GM-MItoStore|'+tokenID+'|'+MIrowref+'|?{Which piece of Armour?|'+armour+'})'
+ '[Ring](!magic --button GM-MItoStore|'+tokenID+'|'+MIrowref+'|?{Which Ring?|'+rings+'})'
+ '[Miscellaneous MI](!magic --button GM-MItoStore|'+tokenID+'|'+MIrowref+'|?{Which Misc MI?|'+misc+'})'
+ + '[Equipment](!magic --button GM-MItoStore|'+tokenID+'|'+MIrowref+'|?{What Equipment?|'+equip+'})'
+ '[DM only list](!magic --button GM-MItoStore|'+tokenID+'|'+MIrowref+'|?{Which DM only item?|'+dmitems+'})}}';
content += '{{desc1=**2. Choose slot to edit or store in**\n';
@@ -3935,7 +3973,7 @@ var MagicMaster = (function() {
itemObj = getAbility( fields.MagicItemDB, MItoStore, charCS, false, true, MItoStore, chosenSlot );
if (itemObj.obj) {
chosenData = resolveData( MItoStore, fields.MagicItemDB, reItemData, charCS, {qty:reSpellSpecs.qty,query:reSpellSpecs.query}, chosenSlot );
- initQty = chosenData.parsed.qty || initQty;
+ initQty = chosenData.parsed.qty || (itemName.trueCompare(slotName) ? initQty : 1);
queries = parseQuery( chosenData.parsed.query );
}
};
@@ -3949,7 +3987,7 @@ var MagicMaster = (function() {
+ '{{desc4=1. Or select MI from above ^\n'
+ '
'
+ selectableSlot+'Rename '+slotName+(chosenSlot ? ('](!magic --button GM-RenameMI|'+tokenID+'|'+MIrowref+'|'+MItoStore+'|?{What name should '+slotName+' now have?}) ') : ' ')+' ' - + selectableSlot+(!slotCursed ? 'Change Type' : 'Remove Curse')+(chosenSlot ? ('](!magic --button GM-ChangeMItype|'+tokenID+'|'+MIrowref+'|'+MItoStore+'|'+(slotCursed ? 'removeCurse' : ('?{Currently '+slotType+'. What type should '+slotName+' now be?|charged|uncharged|recharging|rechargeable|selfchargeable|absorbing|discharging|cursed|cursed+charged|cursed+recharging|cursed+rechargeable|cursed+selfchargeable|cursed+absorbing}'))+') ') : ' ')+' ' + + selectableSlot+(!slotCursed ? 'Change Type' : 'Remove Curse')+(chosenSlot ? ('](!magic --button GM-ChangeMItype|'+tokenID+'|'+MIrowref+'|'+MItoStore+'|'+(slotCursed ? 'removeCurse' : ('?{Currently '+slotType+'. What type should '+slotName+' now be?|charged|uncharged|recharging|rechargeable|selfchargeable|absorbing|discharging|cursed|cursed+charged|cursed+recharging|cursed+rechargeable|cursed+selfchargeable|cursed+absorbing|cursed+discharging}'))+') ') : ' ')+' ' + selectableSlot+'Change displayed charges'+(chosenSlot ? ('](!magic --button GM-ChangeDispCharges|'+tokenID+'|'+MIrowref+'|'+MItoStore+'|?{How many displayed charges should '+slotName+' now have (currently '+slotQty+')?|'+slotQty+'}) ') : ' ')+' ' + selectableSlot+'Change actual charges'+(chosenSlot ? ('](!magic --button GM-ChangeActCharges|'+tokenID+'|'+MIrowref+'|'+MItoStore+'|?{How many actual charges should '+slotActualName+' now have (currently '+slotActualQty+')?|'+slotActualQty+'}) ') : ' ')+' ' + storableSlot+'Store Spells/Powers in MI'+((spellStoring && chosenSlot) ? ('](!magic --store-spells '+tokenID+'|'+slotActualName+'|||GM-EDIT-MI) ') : ' ')+' | '
@@ -4509,6 +4547,8 @@ var MagicMaster = (function() {
storedLevel = attrLookup( charCS, fields.Spells_storedLevel, fields.Spells_table, args[3], args[4] );
if (storedLevel && storedLevel > 0) {
setAttr( charCS, fields.CastingLevel, storedLevel );
+ setAttr( charCS, fields.MU_CastingLevel, storedLevel );
+ setAttr( charCS, fields.PR_CastingLevel, storedLevel );
}
}
@@ -4524,7 +4564,7 @@ var MagicMaster = (function() {
var handleCastSpell = function( args, senderId ) {
const setValue = (...a) => libRPGMaster.setAttr(...a);
-
+
var tokenID = args[1],
rowIndex = args[3],
colIndex = args[4],
@@ -4618,12 +4658,12 @@ var MagicMaster = (function() {
Items = Items.tableSet( fields.Items_qty, itemRow, Math.max(parseInt(Items.tableLookup( fields.Items_qty, itemRow ) || 0)-level,0) );
Items = Items.tableSet( fields.Items_trueQty, itemRow, Math.max(parseInt(Items.tableLookup( fields.Items_trueQty, itemRow ) || 0)-level,0) );
}
- } else if (spellValue > 0) {
+ } else if (spellValue != 0) {
if (apiCommands.attk && apiCommands.attk.exists && spell.obj[1].body.match(/}}\s*tohitdata\s*=\s*\[.*?\]/im)) {
- sendAPI(fields.attackMaster+' --weapon '+tokenID+'|||'+miName);
+ sendAPI(fields.attackMaster+' '+senderId+' --weapon '+tokenID+'|Take '+spellName+' in-hand as a weapon and then Attack with it||'+miName);
} else {
- spellValue--;
+ if (spellValue > 0) spellValue--;
spellTables.tableSet( fields.Spells_castValue, rowIndex, spellValue );
}
}
@@ -5258,6 +5298,7 @@ var MagicMaster = (function() {
var MItables = getTable( charCS, fieldGroups.MI ),
MIname = MItables.tableLookup( fields.Items_name, MIrowref ),
+ MItrueName = MItables.tableLookup( fields.Items_trueName, MIrowref ),
MIreveal = MItables.tableLookup( fields.Items_reveal, MIrowref ).toLowerCase();
setAttr( charCS, fields.ItemChosen, MIname );
@@ -5272,7 +5313,7 @@ var MagicMaster = (function() {
}
content = '[Return to menu](!magic --button '+BT.CHOOSE_VIEW_MI+'|'+args[1]+'|'+args[2]+')';
setTimeout(() => sendResponse( charCS, content, senderId, flags.feedbackName, flags.feedbackImg, tokenID ),500);
- checkForBag( charCS, MIname );
+ checkForBag( charCS, MItrueName, MIrowref );
return;
}
if (isNaN(MIrowref) || (fields.Items_table[1] == 0 && MIrowref < 0)) {
@@ -5296,7 +5337,6 @@ var MagicMaster = (function() {
MIchangeTo = miData.changeTo;
}
MIcVal = parseInt(MIcVal);
-
if (!(_.isUndefined(MIcVal) || isNaN(MIcVal)) && (_.isUndefined(charges) || _.isNull(charges))) {
charges = MIcVal;
}
@@ -5315,7 +5355,8 @@ var MagicMaster = (function() {
switch (MItype.toLowerCase()) {
case 'change-each':
- if (MIchangeTo) {
+ case 'cursed+change-each':
+ if (MIchangeTo && charges > 0) {
let changeRow = MItables.tableFind( fields.Items_trueName, MIchangeTo );
if (isNaN(changeRow)) {
handleStoreMI( ['', tokenID, changeRow, MIchangeTo, charges, 'silent' ], false, senderId );
@@ -5326,12 +5367,13 @@ var MagicMaster = (function() {
};
case 'charged':
case 'perm-charged':
+ case 'cursed+charged':
case 'changing':
case 'change-last':
case 'cursed+change-last':
case 'discharging':
case 'perm-discharging':
- case 'cursed+charged':
+ case 'cursed+discharging':
case 'rechargeable':
case 'perm-rechargeable':
case 'cursed+rechargeable':
@@ -5371,7 +5413,7 @@ var MagicMaster = (function() {
setAttr( charCS, fields.ItemQty, MIqtyObj.get('current') );
- if (MIqty > charges) checkForBag( charCS, MIname );
+ if (MIqty > charges) checkForBag( charCS, MItrueName, MIrowref );
if (action.includes('USE') && (MIreveal == 'view' || MIreveal == 'use')) {
MIname = MItables.tableLookup( fields.Items_trueName, MIrowref );
@@ -5606,7 +5648,7 @@ var MagicMaster = (function() {
* spells into a spell-storing magic item.
*/
- var handleStoreMIspell = function( args, senderId ) {
+ var handleStoreMIspell = function( args, senderId ) { //split
var tokenID = args[1],
charCS = getCharacter(tokenID);
@@ -5686,15 +5728,17 @@ var MagicMaster = (function() {
muLevels = attrLookup( charCS, [fields.ItemMUspellValues[0]+item,fields.ItemMUspellValues[1]] ),
prLevels = attrLookup( charCS, [fields.ItemPRspellValues[0]+item,fields.ItemPRspellValues[1]] );
- if (!!muRows && !!muCols) {
- muRows = muRows.split(',');
- muCols = muCols.split(',');
- muSpells = muSpells.split(',');
- muLevels = muLevels.split(',');
- prRows = prRows.split(',');
- prCols = prCols.split(',');
- prSpells = prSpells.split(',');
- prLevels = prLevels.split(',');
+ log('handleStoreMIspell: muRows='+muRows+', muCols='+muCols+', muSpells='+muSpells+', muLevels='+muLevels+', prRows='+prRows+', prCols='+prCols+', prSpells='+prSpells+', prLevels='+prLevels);
+
+ if ((!!muRows && !!muCols) || (!!prRows && !!prCols)) {
+ muRows = (muRows || '').split(',');
+ muCols = (muCols || '').split(',');
+ muSpells = (muSpells || '').split(',');
+ muLevels = (muLevels || '').split(',');
+ prRows = (prRows || '').split(',');
+ prCols = (prCols || '').split(',');
+ prSpells = (prSpells || '').split(',');
+ prLevels = (prLevels || '').split(',');
let index = muRows.findIndex( (e,i) => e == MIrow && muCols[i] == MIcol ),
muSave = false, prSave = false;
if (index > -1 && muCols[index] === MIcol) {
@@ -6016,7 +6060,7 @@ var MagicMaster = (function() {
MIreveal = fromMIbag.tableLookup( fields.Items_reveal, fromRowRef ),
MItrueType = fromMIbag.tableLookup( fields.Items_trueType, fromRowRef ),
MItext = MIname,
- unrechargeable = ['charged','uncharged','cursed','cursed+change-last'],
+ splitable = ['charged','uncharged','cursed','change-each','cursed+change-each'],
recharging = ['recharging','cursed+recharging','absorbing','cursed+absorbing'],
slotInc = 1,
finalQty, finalCharges, pickQty, charges, content;
@@ -6033,7 +6077,7 @@ var MagicMaster = (function() {
switch (MIqty) {
case 0:
- if (!unrechargeable.includes(fromSlotType)) {
+ if (!splitable.includes(fromSlotType)) {
qty = pickQty = 0;
charges = MItrueQty;
} else {
@@ -6049,7 +6093,7 @@ var MagicMaster = (function() {
break;
default:
- if (!unrechargeable.includes(fromSlotType)) {
+ if (!splitable.includes(fromSlotType)) {
qty = MIqty;
pickQty = (recharging.includes(fromSlotType)) ? MIqty : MItrueQty;
charges = MItrueQty;
@@ -6071,7 +6115,7 @@ var MagicMaster = (function() {
finalQty = pickQty;
finalCharges = charges;
- if (unrechargeable.includes(fromSlotType) && stdEqual( toSlotName, MIname ) && stdEqual( toSlotType, MItype ) && stdEqual( toSlotTrueName, MItrueName )) {
+ if (splitable.includes(fromSlotType) && stdEqual( toSlotName, MIname ) && stdEqual( toSlotType, MItype ) && stdEqual( toSlotTrueName, MItrueName )) {
finalQty = (parseInt(finalQty)||0) + (parseInt(toSlotQty)||0);
finalCharges = (parseInt(finalCharges)||0) + (parseInt(toSlotCharges)||0);
slotInc = 0;
@@ -6131,11 +6175,11 @@ var MagicMaster = (function() {
if (!stdEqual(MIname,MItrueName)) {
await moveMIspells( senderId, fromCS, toCS, MItrueName );
}
- checkForBag( toCS, MItrueName );
+ checkForBag( toCS, MItrueName, toRowRef );
if (MIqty == 0) {
fromMIbag.addTableRow( fromRowRef ); // Blanks this row of the table
- removeMIability( fromCS, MIname, fromMIbag ); // Only removes ability if does not exist elsewhere in the equipment list
- removeMIability( fromCS, MItrueName, fromMIbag );
+ setTimeout(() => removeMIability( fromCS, MIname, fromMIbag ), 100); // Only removes ability if does not exist elsewhere in the equipment list
+ setTimeout(() => removeMIability( fromCS, MItrueName, fromMIbag ), 100);
} else {
fromMIbag.tableSet( fields.Items_trueQty, fromRowRef, (MItrueQty - charges) );
fromMIbag.tableSet( fields.Items_qty, fromRowRef, (MIqty - qty) );
@@ -6228,13 +6272,14 @@ var MagicMaster = (function() {
/*
* Handle storing an MI in a Magic Item bag.
+ * Can take either a tokenID or a Character ID.
* A flag parameter determines if this is a GM-only action
*/
async function handleStoreMI( args, GMonly, senderId ) {
var tokenID = args[1],
- MIrowref = args[2],
+ MIrowref = args[2] || '',
MIchosen = args[3],
MIqty = args[4] || 1,
silent = (args[5] || '').toUpperCase() === 'SILENT',
@@ -6242,6 +6287,11 @@ var MagicMaster = (function() {
charCS = getCharacter( tokenID ),
queries = args.slice(6);
+ if (!getObj( 'graphic', tokenID )) {
+ tokenID = undefined;
+ silent = true;
+ }
+
if (!charCS) {
sendDebug('handleStoreMI: invalid tokenID passed');
sendError('Internal MagicMaster error');
@@ -6269,9 +6319,9 @@ var MagicMaster = (function() {
MImaxQty = 0;
if (MIreplace) MIrowref = MItables.tableFind( fields.Items_trueName, MIreplace );
- if (inHand && isNaN(MIrowref)) MIrowref = MItables.tableFind( fields.Items_trueName, MIchosen );
- if (isNaN(MIrowref) || MIrowref<0) MIrowref = MItables.tableFind( fields.Items_name, '-' );
- if (isNaN(MIrowref)) {
+ if (inHand && isNaN(parseInt(MIrowref))) MIrowref = MItables.tableFind( fields.Items_trueName, MIchosen );
+ if (isNaN(parseInt(MIrowref)) || MIrowref<0) MIrowref = MItables.tableFind( fields.Items_name, '-' );
+ if (isNaN(parseInt(MIrowref))) {
MItables = MItables.addTableRow();
MIrowref = MItables.sortKeys.length-1;
}
@@ -6297,7 +6347,7 @@ var MagicMaster = (function() {
var midbCS, MIdisplayName;
if (!GMonly && slotType.toLowerCase().includes('cursed')) {
- sendParsedMsg( tokenID, messages.cursedSlot + '{{desc1=[Return to menu](!magic --edit-mi '+tokenID+')}}', senderId );
+ if (!silent) sendParsedMsg( tokenID, messages.cursedSlot + '{{desc1=[Return to menu](!magic --edit-mi '+tokenID+')}}', senderId );
return;
}
await moveMIspells( senderId, charCS, null, slotName );
@@ -6333,11 +6383,12 @@ var MagicMaster = (function() {
}
await moveMIspells( senderId, null, charCS, MIchosen );
+ checkForBag( charCS, MIchosen, MIrowref );
- sendAPI( (fields.attackMaster + ' --checkac ' + tokenID + '|Silent||' + senderId), senderId );
+ if (!!tokenID) sendAPI( (fields.attackMaster + ' --checkac ' + tokenID + '|Silent||' + senderId), senderId );
if (silent) {
- sendAPI( (fields.attackMaster + ' --button ' + (['PRIMARY','OFFHAND','BOTH','HAND'][Math.min(hand,3)]) + '-NOCURSE|' + tokenID + '|' + MIchosen + '|' + hand + '||Silent'), senderId );
+ if (tokenID && inHand) sendAPI( (fields.attackMaster + ' --button ' + (['PRIMARY','OFFHAND','BOTH','HAND'][Math.min(hand,3)]) + '-NOCURSE|' + tokenID + '|' + MIchosen + '|' + hand + '||Silent'), senderId );
sendWait(senderId,0);
return;
}
@@ -7837,6 +7888,9 @@ var MagicMaster = (function() {
log('doSearchForMIs: Not found trapMacro');
MIBagSecurity = 1;
}
+ } else if (!search && MIBagSecurity < 0) {
+ sendParsedMsg( putID, messages.noStoring, senderId );
+ return
} else if (!search || MIBagSecurity < 2 || hp <= 0 || intelligence <= 0) {
doPickOrPut( msg, senderId );
} else {
@@ -8013,20 +8067,12 @@ var MagicMaster = (function() {
return;
}
- var tokenID = args[0],
- miRowRef = args[1],
+ var miRowRef = args[1],
miChosen = args[2],
miQty = args[3] || 0,
miInHand = args[4],
- isGM = (args[5] || '').toUpperCase() === 'NOCURSE',
- charCS = getCharacter( tokenID );
+ isGM = (args[5] || '').toUpperCase() === 'NOCURSE';
- if (!charCS) {
- sendDebug('doAddItem: invalid token ID argument');
- sendResponseError(senderId,'Invalid token specified');
- return;
- };
-
args.unshift('');
args[2] = !isNaN(miInHand) ? 'inhand/'+miInHand : (!isNaN(miRowRef) ? miRowRef : '');
args[3] = !isNaN(miRowRef) ? miRowRef : miChosen+'/'+miRowRef;
@@ -8870,7 +8916,6 @@ var MagicMaster = (function() {
case BT.VIEW_MI:
case BT.USE_MI:
-
handleViewUseMI( args, playerIsGM(senderId), senderId );
break;
diff --git a/MagicMaster/script.json b/MagicMaster/script.json
index 7de9dc58e..0ec98abc8 100644
--- a/MagicMaster/script.json
+++ b/MagicMaster/script.json
@@ -2,8 +2,8 @@
"$schema": "https://github.com/DameryDad/roll20-api-scripts/blob/MagicMaster/MagicMaster/Script.json",
"name": "MagicMaster",
"script": "MagicMaster.js",
- "version": "3.2.1",
- "previousversions": ["2.044","2.045","2.046","2.048","3.051","1.3.00","1.3.01","1.3.02","1.3.03","1.4.01","1.4.02","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.1.1","2.2.0","2.2.1","2.3.0","2.3.1","2.3.2","2.3.3","2.3.4","3.0.0","3.1.2","3.2.0"],
+ "version": "3.3.0",
+ "previousversions": ["2.044","2.045","2.046","2.048","3.051","1.3.00","1.3.01","1.3.02","1.3.03","1.4.01","1.4.02","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.1.1","2.2.0","2.2.1","2.3.0","2.3.1","2.3.2","2.3.3","2.3.4","3.0.0","3.1.2","3.2.0","3.2.1"],
"description": "The MagicMaster API provides functions to manage all types of magic, including:\n* Wizard & Priest spell use and effects;\n* Character, NPC & Monster Powers; \n* discovery, looting, use and cursing of Magic Items;\n\n[MagicMaster Documentation](https://wiki.roll20.net/Script:MagicMaster) \n### Installation\nLoading MagicMaster via One-Click also loads the rest of the RPGMaster series of APIs \n[RPGMaster Documentation](https://wiki.roll20.net/RPGMaster) \n### Getting Started\n1. Ensure the CommandMaster API is also installed\n2. Run the CommandMaster `!cmd --initialise` command and add the player macros created to the Macro Bar, then\n3. Select tokens and use the `Token Setup` macro bar button just created to add all relevant Action Buttons to the token(s) (plus set the tokens/Characters up in any other way provided in the menu displayed)\n\n### Use In Play\nOnce the Getting Started steps have been done, the players and DM can then use the buttons displayed at the top of the screen when their character's token is selected to perform all actions needed in normal play.",
"authors": "Richard E.",
"roll20userid": "6497708",